more code

This commit is contained in:
leaf corcoran
2013-10-23 23:10:48 -07:00
parent dfdeae21d7
commit 8957308167
2 changed files with 171 additions and 42 deletions

118
elua.moon
View File

@@ -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 }

95
spec/elua_spec.moon Normal file
View File

@@ -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