From 8957308167bb9fcb900b491bd107db06c14e6506 Mon Sep 17 00:00:00 2001 From: leaf corcoran Date: Wed, 23 Oct 2013 23:10:48 -0700 Subject: [PATCH] more code --- elua.moon | 118 ++++++++++++++++++++++++++++---------------- spec/elua_spec.moon | 95 +++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 42 deletions(-) create mode 100644 spec/elua_spec.moon diff --git a/elua.moon b/elua.moon index ea132f7..8e318f2 100644 --- a/elua.moon +++ b/elua.moon @@ -1,10 +1,19 @@ -moon = require "moon" import insert, concat from table -escape_pattern = do - punct = "[%^$()%.%[%]*+%-?%%]" - (str) -> (str\gsub punct, (p) -> "%"..p) +setfenv = setfenv or (fn, env) -> + local name + i = 1 + while true + name = debug.getupvalue fn, i + break if not name or name == "_ENV" + i += 1 + + if name + debug.upvaluejoin fn, i, (-> env), 1 + + fn + class Parser open_tag: "<%" @@ -28,16 +37,53 @@ class Parser with @str\sub @pos, @pos @pos += 1 - -- TODO: find the next tag or string, whichever is closer - start, stop = @str\find @close_tag, @pos, true - kind = modifier == "=" and "interplate" or "code" - @push_code kind, @pos, start - 1 + close_start, close_stop = @str\find @close_tag, @pos, true + while @in_string @pos, close_start + close_start, close_stop = @str\find @close_tag, close_stop, true - @pos = stop + 1 + kind = modifier == "=" and "interplate" or "code" + @push_code kind, @pos, close_start - 1 + + @pos = close_stop + 1 true - -- closest_string: -> - -- start = @str\find "[=*[" + -- see if stop leaves us in the middle of a string + in_string: (start, stop) => + in_string = false + end_delim = nil + escape = false + + pos = 0 + skip_until = nil + + chunk = @str\sub start, stop + for char in chunk\gmatch "." + pos += 1 + + if skip_until + continue if pos <= skip_until + skip_until = nil + + if end_delim + if end_delim == char and not escape + in_string = false + end_delim = nil + else + if char == "'" or char == '"' + end_delim = char + in_string = true + + if char == "[" + if lstring = chunk\match "^%[=*%[", pos + lstring_end = lstring\gsub "%[", "]" + lstring_p1, lstring_p2 = chunk\find lstring_end, pos, true + -- no closing lstring, must be inside string + return true unless lstring_p1 + skip_until = lstring_p2 + + escape = char == "\\" + + in_string push_raw: (start, stop) => insert @chunks, @str\sub start, stop @@ -47,22 +93,32 @@ class Parser kind, @str\sub start, stop } + compile: (str) => + @parse str + @load @chunks_to_lua! + parse: (@str) => - assert type(@str) == "string" + assert type(@str) == "string", "expecting string for parse" @pos = 1 @chunks = {} while @next_tag! nil - @compile! + load: (code, name="elua") => + code_fn = coroutine.wrap -> + coroutine.yield code + + fn = load code_fn, name + (env={}) -> + setfenv fn, env + fn tostring, concat -- generates the code of the template - compile: => - -- moon.p @chunks + chunks_to_lua: => -- todo: find a no-conflict name for buffer buffer = { - "local _b, _b_i, _tostring = {}, 0, tostring" + "local _b, _b_i, _tostring, _concat = {}, 0, ..." } buffer_i = #buffer @@ -91,33 +147,11 @@ class Parser else error "unknown type #{t}" - push "return table.concat(_b)" + push "return _concat(_b)" concat buffer, "\n" -p = Parser! -code = p\parse [[ - This is my message to <%= "you" %> - This is my message to <%= 4 %> - <% if things then %> - I love things - <% end %> +compile = Parser!\compile +render = (str, env) -> compile(str) env - <% for i=1,10 do%> - hello <%= i %> - <% end %> - - message: <%= visitor %> - - This is my message to <%= "y%>u" %> - -]] - -print code --- fn = assert loadstring code --- setfenv fn, setmetatable { --- visitor: "HELLO VISITOR" --- things: true --- }, __index: _G --- --- print fn! +{ :compile, :render, :Parser } diff --git a/spec/elua_spec.moon b/spec/elua_spec.moon new file mode 100644 index 0000000..640ca7b --- /dev/null +++ b/spec/elua_spec.moon @@ -0,0 +1,95 @@ + +import compile, render, Parser from require "elua" + +describe "elua", -> + describe "Parser", -> + cases = { + { + "hello world" + "hello world" + } + + + { + "one surf-zone two" + "one <%= var %> two" + {var: "surf-zone"} + } + + { + "a ((1))((2))((3)) b" + "a <% for i=1,3 do %>((<%= i %>))<% end %> b" + } + + { + "y%>u" + [[<%= "y%>u" %>]] + } + + + { + [[ +This is my message to you +This is my message to 4 + + + + hello 1 + + hello 2 + + hello 3 + + hello 4 + + hello 5 + + hello 6 + + hello 7 + + hello 8 + + hello 9 + + hello 10 + + +message: yeah + +This is my message to oh yeah %>"]] + [[ +This is my message to <%= "you" %> +This is my message to <%= 4 %> +<% if things then %> + I love things +<% end %> + +<% for i=1,10 do%> + hello <%= i %> +<% end %> + +message: <%= visitor %> + +This is my message to <%= [=[oh yeah %>"]=] %>]] + { + visitor: "yeah" + } + } + } + + for case in *cases + it "should run template", -> + assert.same case[1], render unpack case, 2 + + describe "Parser.in_string", -> + cases = { + { "hello world", false } + { "hello 'world", true } + { [[hello "hello \" world]], true } + { "hello [=[ wor'ld ]=]dad", false } + } + + for {str, expected} in *cases + it "should detect if in string", -> + assert.same expected, Parser.in_string { :str }, 1