Created
January 31, 2010 14:25
-
-
Save weepy/291087 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Interpolate | |
DQ = "\"" | |
SQ = "'" | |
ESCAPE = "\\" | |
CODE = "#" | |
DEBUG = false | |
def quote? char | |
char == DQ || char == SQ | |
end | |
def escape? char | |
char == ESCAPE | |
end | |
def transist state, opts = {} | |
old_state = @state | |
self.send("#{@state}_to_#{state}", opts ) | |
@state = state | |
@stream << [old_state, @buffer] if @buffer | |
@buffer = "" | |
end | |
#### HOOKS START | |
def init_to_code opts | |
end | |
def code_to_string opts | |
@quote = opts[:quote] | |
end | |
def string_to_code opts | |
@quote = nil | |
end | |
def string_to_interpolation opts | |
@braces = 1 | |
@buffer = @buffer.slice(0,@buffer.length - 1) | |
end | |
def interpolation_to_string opts; end | |
def string_to_interpolation_simple opts | |
@buffer = @buffer.slice(0,@buffer.length - 1) | |
end | |
def interpolation_simple_to_string opts; end | |
def interpolation_to_string_in_interpolation opts | |
@interp_quote = opts[:quote] | |
end | |
def string_in_interpolation_to_interpolation opts | |
@interp_quote = nil | |
@buffer = @buffer.slice(1, @buffer.length) | |
end | |
####### HOOKES END | |
def build_stream s | |
@input = s | |
history = [] | |
history[0] = nil | |
@stream = [] | |
@state = :init | |
transist :code | |
@braces = 0 | |
@input.split("").each do |char| | |
@char = char | |
case @state | |
when :code | |
if quote?(char) && !escape?(history[0]) | |
transist(:string, :quote => char) | |
else | |
@buffer += char | |
end | |
when :string | |
if history[1] != ESCAPE && history[0] == CODE && char.match(/[a-zA-Z_\$\{]/) | |
if char == "{" | |
transist :interpolation | |
else | |
transist :interpolation_simple | |
@buffer += char | |
end | |
elsif char == @quote && !escape?(history[0]) | |
transist :code | |
else | |
@buffer += char | |
end | |
when :interpolation | |
if @interpolation_quote | |
if char == @interpolation_quote && history[0] != ESCAPE | |
@interpolation_quote = nil | |
end | |
else | |
if char == "{" | |
@braces += 1 | |
elsif char == "}" | |
@braces -= 1 | |
elsif quote?(char) && history[0] != ESCAPE | |
@interpolation_quote = char | |
end | |
end | |
if @braces == 0 | |
transist :string | |
else | |
@buffer += char | |
end | |
when :interpolation_simple | |
if char.match(/[a-zA-Z_\$0-9]/) | |
@buffer += char | |
else | |
transist :string | |
if char == @quote | |
transist :code | |
else | |
@buffer += char | |
end | |
end | |
end | |
history.unshift(char).slice(0,5) | |
end | |
@stream | |
end | |
def stringify_token back, token, fwd | |
type = token[0] | |
data = token[1] | |
case type | |
when :init | |
"" | |
when :code | |
data | |
when :string | |
"'#{data}'" | |
when :interpolation | |
s = "" | |
s += " + " #if back[1].length > 0 | |
s += data.match(/[^A-Za-z\$_0-9"' ]/) ? "(#{data})" : data | |
s += " + " #if fwd[1].length > 0 | |
s | |
when :interpolation_simple | |
s = "" | |
s += " + " #if back[1].length > 0 #|| back[0] != :string | |
s += data | |
s += " + " #if fwd[1].length > 0 #|| fwd[0] != :string | |
s | |
when :string_in_interpolation | |
data | |
else | |
type | |
end | |
end | |
def stringify stream | |
ret = "" | |
stream.each_with_index do |token, i| | |
ret += stringify_token(stream[i-1] || [], stream[i], stream[i+1] || []) | |
end | |
return ret | |
end | |
def expand input | |
build_stream input | |
debug "tokens: " + @stream.inspect | |
stringify @stream | |
end | |
def run input | |
string = input | |
debug "---------------------------" | |
debug "input: " + input | |
while true | |
expanded = expand string | |
if expanded == string | |
debug "output: " + string | |
return expanded | |
end | |
debug "expanded: " + expanded | |
string = expanded | |
end | |
end | |
def debug x | |
puts x if DEBUG | |
end | |
end | |
coffee =<<EOF | |
just_some(code) | |
"1 + 2" | |
a: "1 + 2" + '1 + 2' | |
b: "$x$y$z" | |
b: "$x" + '${x}' + '${x}' | |
c: "abc $y def" | |
c: "${x ? 1 : 2}" | |
c: " ${x ? 1 : 2} " | |
c: " ${x ? 1 : "hello"} " | |
c: " ${x ? 1 : "hello ${xx} "} " | |
c: " ${x ? 1 : "hello ${fn('}')} "} " | |
c: " ${"nested ${"nested ${"nested ${"nested ${"nested"}"}"}"}"} " | |
""" | |
heredoc! | |
""" | |
EOF | |
coffee.gsub!("$", "#") | |
# per line: | |
coffee.split("\n").each do |line| | |
puts Interpolate.new.run(line) | |
end | |
# whole lot: | |
puts Interpolate.new.run(coffee) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment