mirror of
https://github.com/TangentFoxy/etlua.git
synced 2025-07-27 18:42:17 +00:00
more code
This commit is contained in:
118
elua.moon
118
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 }
|
||||
|
||||
|
95
spec/elua_spec.moon
Normal file
95
spec/elua_spec.moon
Normal 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
|
Reference in New Issue
Block a user