Macros working better.
This commit is contained in:
parent
7e9bacfb20
commit
e8f2b4fdd2
31
core.moon
31
core.moon
@ -36,6 +36,22 @@ class PermissionNomic extends Nomic
|
||||
|
||||
g = PermissionNomic()
|
||||
|
||||
g\defmacro [[lua %lua_code]], (vars,helpers,ftype)=>
|
||||
with helpers
|
||||
lua_code = vars.lua_code.value
|
||||
escapes = n:"\n", t:"\t", b:"\b", a:"\a", v:"\v", f:"\f", r:"\r"
|
||||
unescape = (s)-> s\gsub("\\(.)", ((c)-> escapes[c] or c))
|
||||
switch lua_code.type
|
||||
when "List"
|
||||
-- TODO: handle subexpressions
|
||||
.lua table.concat[unescape(i.value.value) for i in *lua_code.value]
|
||||
when "String"
|
||||
.lua(unescape(lua_code.value))
|
||||
when "Longstring"
|
||||
.lua(lua_code.value)
|
||||
else error("Unknown type: #{lua_code.type}")
|
||||
return nil
|
||||
|
||||
g\def {"restrict %fn to %whitelist"}, (vars)=>
|
||||
fns = if type(vars.fn) == 'string' then {vars.fn} else vars.fn
|
||||
whitelist = if type(vars.whitelist) == 'string' then {vars.whitelist} else vars.whitelist
|
||||
@ -308,22 +324,9 @@ if (not %condition) %body
|
||||
|
||||
g\def [[do %action]], (vars)=> return vars.action(self,vars)
|
||||
|
||||
g\defmacro [[lua %lua_code]], (vars,helpers,ftype)=>
|
||||
with helpers
|
||||
lua_code = vars.lua_code.value
|
||||
escapes = n:"\n", t:"\t", b:"\b", a:"\a", v:"\v", f:"\f", r:"\r"
|
||||
unescape = (s)-> s\gsub("\\(.)", ((c)-> escapes[c] or c))
|
||||
switch lua_code.type
|
||||
when "List"
|
||||
-- TODO: handle subexpressions
|
||||
.lua table.concat[unescape(i.value.value) for i in *lua_code.value]
|
||||
when "String"
|
||||
.lua(unescape(lua_code.value))
|
||||
else error("Unknown type: #{lua_code.type}")
|
||||
return nil
|
||||
|
||||
g\defmacro [[macro %spec %body]], (vars,helpers,ftype)=>
|
||||
self\simplemacro vars.spec.value.value, vars.body.value.value.src
|
||||
self\simplemacro vars.spec.value.value, vars.body.src
|
||||
|
||||
|
||||
return g
|
||||
|
162
nomic.moon
Normal file → Executable file
162
nomic.moon
Normal file → Executable file
@ -1,13 +1,19 @@
|
||||
#!/usr/bin/env moon
|
||||
re = require 're'
|
||||
lpeg = require 'lpeg'
|
||||
utils = require 'utils'
|
||||
moon = require 'moon'
|
||||
|
||||
-- TODO:
|
||||
-- string interpolation
|
||||
|
||||
lpeg.setmaxstack 10000 -- whoa
|
||||
{:P,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg
|
||||
|
||||
wordchar = P(1)-S(' \t\n\r%:;,.{}[]()"')
|
||||
spaces = S(" \t")^1
|
||||
nl = P("\n")
|
||||
blank_line = spaces^-1 * nl
|
||||
|
||||
get_line_indentation = (line)->
|
||||
indent_amounts = {[" "]:1, ["\t"]:4}
|
||||
@ -16,6 +22,64 @@ get_line_indentation = (line)->
|
||||
for c in leading_space\gmatch "[\t ]"
|
||||
sum += indent_amounts[c]
|
||||
|
||||
make_parser = (lingo, extra_definitions)->
|
||||
indent_stack = {0}
|
||||
push = (n)-> table.insert indent_stack, n
|
||||
pop = ()-> table.remove indent_stack
|
||||
check_indent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces <= indent_stack[#indent_stack] then return nil
|
||||
push num_spaces
|
||||
return end_pos
|
||||
check_dedent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces >= indent_stack[#indent_stack] then return nil
|
||||
pop!
|
||||
return end_pos
|
||||
check_nodent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces != indent_stack[#indent_stack] then return nil
|
||||
return end_pos
|
||||
|
||||
defs =
|
||||
:wordchar, :nl, :spaces
|
||||
ws: S(" \t")^1
|
||||
eol: #nl + (P("")-P(1))
|
||||
word_boundary: S(" \t")^1 + B(P("..")) + B(S("\";)]")) + #S("\":([") + #P("..")
|
||||
indent: #(nl * blank_line^0 * Cmt(spaces^-1, check_indent))
|
||||
dedent: #(nl * blank_line^0 * Cmt(spaces^-1, check_dedent))
|
||||
new_line: nl * blank_line^0 * Cmt(spaces^-1, check_nodent)
|
||||
error_handler: (src,pos,errors)->
|
||||
line_no = 1
|
||||
for _ in src\sub(1,-#errors)\gmatch("\n") do line_no += 1
|
||||
err_pos = #src - #errors + 1
|
||||
if errors\sub(1,1) == "\n"
|
||||
-- Indentation error
|
||||
err_pos += #errors\match("[ \t]*", 2)
|
||||
start_of_err_line = err_pos
|
||||
while src\sub(start_of_err_line, start_of_err_line) != "\n" do start_of_err_line -= 1
|
||||
start_of_prev_line = start_of_err_line - 1
|
||||
while src\sub(start_of_prev_line, start_of_prev_line) != "\n" do start_of_prev_line -= 1
|
||||
|
||||
prev_line,err_line,next_line = src\match("([^\n]*)\n([^\n]*)\n([^\n]*)", start_of_prev_line+1)
|
||||
|
||||
pointer = ("-")\rep(err_pos - start_of_err_line + 1) .. "^"
|
||||
error("\nParse error on line #{line_no}:\n|#{prev_line}\n|#{err_line}\n#{pointer}\n|#{next_line}")
|
||||
|
||||
if extra_definitions
|
||||
for k,v in pairs(extra_definitions) do defs[k] = v
|
||||
|
||||
setmetatable(defs, {
|
||||
__index: (t,key)->
|
||||
--print("WORKING for #{key}")
|
||||
fn = (src, value, errors)->
|
||||
token = {type: key, :src, :value, :errors}
|
||||
return token
|
||||
t[key] = fn
|
||||
return fn
|
||||
})
|
||||
return re.compile lingo, defs
|
||||
|
||||
class Game
|
||||
new:(parent)=>
|
||||
@defs = setmetatable({}, {__index:parent and parent.defs})
|
||||
@ -67,37 +131,6 @@ class Game
|
||||
simplemacro: (spec, replacement)=>
|
||||
spec = spec\gsub("\r", "")
|
||||
replacement = replacement\gsub("\r", "")
|
||||
indent_stack = {0}
|
||||
push = (n)-> table.insert indent_stack, n
|
||||
pop = ()-> table.remove indent_stack
|
||||
check_indent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces <= indent_stack[#indent_stack] then return nil
|
||||
push num_spaces
|
||||
return end_pos
|
||||
check_dedent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces >= indent_stack[#indent_stack] then return nil
|
||||
pop!
|
||||
return end_pos
|
||||
check_nodent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces != indent_stack[#indent_stack] then return nil
|
||||
return end_pos
|
||||
|
||||
nl = P("\n")
|
||||
blank_line = spaces^-1 * nl
|
||||
defs =
|
||||
eol: #(nl) + (P("")-P(1))
|
||||
ws: S(" \t")^1
|
||||
:wordchar
|
||||
:replacer
|
||||
:nl, :spaces
|
||||
word_boundary: S(" \t")^1 + B(P("..")) + B(S("\";)]")) + #S("\":([") + #P("..")
|
||||
indent: #(nl * blank_line^0 * Cmt(spaces^-1, check_indent))
|
||||
dedent: #(nl * blank_line^0 * Cmt(spaces^-1, check_dedent))
|
||||
new_line: nl * blank_line^0 * Cmt(spaces^-1, check_nodent)
|
||||
|
||||
replace_grammar = [[
|
||||
stuff <- {~ (var / longstring / string / .)+ ~}
|
||||
var <- ("%" {%wordchar+}) -> replacer
|
||||
@ -108,10 +141,11 @@ class Game
|
||||
replacer = (varname)->
|
||||
ret = vars[varname].src
|
||||
return ret
|
||||
replacement_grammar = re.compile(replace_grammar, defs)
|
||||
replaced = replacement_grammar\match(replacement)
|
||||
tree = lingo\match replaced
|
||||
result = helpers.transform(tree.value.body)
|
||||
replacement_grammar = make_parser replace_grammar, {:replacer}
|
||||
code = replacement_grammar\match(replacement)
|
||||
tree = self\parse(code)
|
||||
-- Ugh, this is magic code.
|
||||
result = helpers.transform(tree.value.body.value[1].value.value.value)
|
||||
helpers.lua(result)
|
||||
return
|
||||
|
||||
@ -192,64 +226,8 @@ class Game
|
||||
indented_list <-
|
||||
expression (((list_sep %new_line?) / %new_line) indented_list)?
|
||||
]=]
|
||||
lingo = make_parser lingo
|
||||
|
||||
str = str\gsub("\r", "")
|
||||
indent_stack = {0}
|
||||
push = (n)-> table.insert indent_stack, n
|
||||
pop = ()-> table.remove indent_stack
|
||||
check_indent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces <= indent_stack[#indent_stack] then return nil
|
||||
push num_spaces
|
||||
return end_pos
|
||||
check_dedent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces >= indent_stack[#indent_stack] then return nil
|
||||
pop!
|
||||
return end_pos
|
||||
check_nodent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces != indent_stack[#indent_stack] then return nil
|
||||
return end_pos
|
||||
|
||||
nl = P("\n")
|
||||
blank_line = spaces^-1 * nl
|
||||
defs =
|
||||
eol: #(nl) + (P("")-P(1))
|
||||
ws: S(" \t")^1
|
||||
:wordchar
|
||||
:nl, :spaces
|
||||
word_boundary: S(" \t")^1 + B(P("..")) + B(S("\";)]")) + #S("\":([") + #P("..")
|
||||
indent: #(nl * blank_line^0 * Cmt(spaces^-1, check_indent))
|
||||
dedent: #(nl * blank_line^0 * Cmt(spaces^-1, check_dedent))
|
||||
new_line: nl * blank_line^0 * Cmt(spaces^-1, check_nodent)
|
||||
error_handler: (src,pos,errors)->
|
||||
line_no = 1
|
||||
for _ in src\sub(1,-#errors)\gmatch("\n") do line_no += 1
|
||||
err_pos = #src - #errors + 1
|
||||
if errors\sub(1,1) == "\n"
|
||||
-- Indentation error
|
||||
err_pos += #errors\match("[ \t]*", 2)
|
||||
start_of_err_line = err_pos
|
||||
while src\sub(start_of_err_line, start_of_err_line) != "\n" do start_of_err_line -= 1
|
||||
start_of_prev_line = start_of_err_line - 1
|
||||
while src\sub(start_of_prev_line, start_of_prev_line) != "\n" do start_of_prev_line -= 1
|
||||
|
||||
prev_line,err_line,next_line = src\match("([^\n]*)\n([^\n]*)\n([^\n]*)", start_of_prev_line+1)
|
||||
|
||||
pointer = ("-")\rep(err_pos - start_of_err_line + 1) .. "^"
|
||||
error("\nParse error on line #{line_no}:\n|#{prev_line}\n|#{err_line}\n#{pointer}\n|#{next_line}")
|
||||
|
||||
setmetatable(defs, {
|
||||
__index: (t,key)->
|
||||
--print("WORKING for #{key}")
|
||||
fn = (src, value, errors)->
|
||||
token = {type: key, :src, :value, :errors}
|
||||
return token
|
||||
t[key] = fn
|
||||
return fn
|
||||
})
|
||||
lingo = re.compile lingo, defs
|
||||
tree = lingo\match(str\gsub("\r","").."\n")
|
||||
if @debug
|
||||
print("\nPARSE TREE:")
|
||||
|
Loading…
Reference in New Issue
Block a user