mirror of
https://github.com/TangentFoxy/etlua.git
synced 2025-07-28 11:02: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
|
import insert, concat from table
|
||||||
|
|
||||||
escape_pattern = do
|
setfenv = setfenv or (fn, env) ->
|
||||||
punct = "[%^$()%.%[%]*+%-?%%]"
|
local name
|
||||||
(str) -> (str\gsub punct, (p) -> "%"..p)
|
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
|
class Parser
|
||||||
open_tag: "<%"
|
open_tag: "<%"
|
||||||
@@ -28,16 +37,53 @@ class Parser
|
|||||||
with @str\sub @pos, @pos
|
with @str\sub @pos, @pos
|
||||||
@pos += 1
|
@pos += 1
|
||||||
|
|
||||||
-- TODO: find the next tag or string, whichever is closer
|
close_start, close_stop = @str\find @close_tag, @pos, true
|
||||||
start, stop = @str\find @close_tag, @pos, true
|
while @in_string @pos, close_start
|
||||||
kind = modifier == "=" and "interplate" or "code"
|
close_start, close_stop = @str\find @close_tag, close_stop, true
|
||||||
@push_code kind, @pos, start - 1
|
|
||||||
|
|
||||||
@pos = stop + 1
|
kind = modifier == "=" and "interplate" or "code"
|
||||||
|
@push_code kind, @pos, close_start - 1
|
||||||
|
|
||||||
|
@pos = close_stop + 1
|
||||||
true
|
true
|
||||||
|
|
||||||
-- closest_string: ->
|
-- see if stop leaves us in the middle of a string
|
||||||
-- start = @str\find "[=*["
|
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) =>
|
push_raw: (start, stop) =>
|
||||||
insert @chunks, @str\sub start, stop
|
insert @chunks, @str\sub start, stop
|
||||||
@@ -47,22 +93,32 @@ class Parser
|
|||||||
kind, @str\sub start, stop
|
kind, @str\sub start, stop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compile: (str) =>
|
||||||
|
@parse str
|
||||||
|
@load @chunks_to_lua!
|
||||||
|
|
||||||
parse: (@str) =>
|
parse: (@str) =>
|
||||||
assert type(@str) == "string"
|
assert type(@str) == "string", "expecting string for parse"
|
||||||
@pos = 1
|
@pos = 1
|
||||||
@chunks = {}
|
@chunks = {}
|
||||||
|
|
||||||
while @next_tag!
|
while @next_tag!
|
||||||
nil
|
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
|
-- generates the code of the template
|
||||||
compile: =>
|
chunks_to_lua: =>
|
||||||
-- moon.p @chunks
|
|
||||||
-- todo: find a no-conflict name for buffer
|
-- todo: find a no-conflict name for buffer
|
||||||
buffer = {
|
buffer = {
|
||||||
"local _b, _b_i, _tostring = {}, 0, tostring"
|
"local _b, _b_i, _tostring, _concat = {}, 0, ..."
|
||||||
}
|
}
|
||||||
buffer_i = #buffer
|
buffer_i = #buffer
|
||||||
|
|
||||||
@@ -91,33 +147,11 @@ class Parser
|
|||||||
else
|
else
|
||||||
error "unknown type #{t}"
|
error "unknown type #{t}"
|
||||||
|
|
||||||
push "return table.concat(_b)"
|
push "return _concat(_b)"
|
||||||
concat buffer, "\n"
|
concat buffer, "\n"
|
||||||
|
|
||||||
p = Parser!
|
compile = Parser!\compile
|
||||||
code = p\parse [[
|
render = (str, env) -> compile(str) env
|
||||||
This is my message to <%= "you" %>
|
|
||||||
This is my message to <%= 4 %>
|
|
||||||
<% if things then %>
|
|
||||||
I love things
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% for i=1,10 do%>
|
{ :compile, :render, :Parser }
|
||||||
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!
|
|
||||||
|
|
||||||
|
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