Moving tree_to_lua into each of the Nomsu tree types, which are now in

their own file.
This commit is contained in:
Bruce Hill 2018-04-17 14:18:23 -07:00
parent 25e06d1fce
commit 54fc7fc440
10 changed files with 1048 additions and 1008 deletions

View File

@ -13,29 +13,18 @@ immediately:
# Conditionals # Conditionals
immediately: immediately:
compile [if %condition %if_body] to: compile [if %condition %if_body] to:
%if_body <- (%if_body as lua) Lua ".."
return {..}
locals: %if_body.locals
statements:".."
if \(%condition as lua expr) then if \(%condition as lua expr) then
\(%if_body.statements or "\(%if_body.expr);") \(%if_body as lua statements)
end end
parse [unless %condition %unless_body] as: if (not %condition) %unless_body parse [unless %condition %unless_body] as: if (not %condition) %unless_body
compile [if %condition %if_body else %else_body, unless %condition %else_body else %if_body] to: compile [if %condition %if_body else %else_body, unless %condition %else_body else %if_body] to:
%if_body <- (%if_body as lua) Lua ".."
%else_body <- (%else_body as lua)
lua> ".."
local \%locals = {unpack(\%if_body.locals or {})};
for i,loc in ipairs(\%else_body.locals or {}) do table.insert(\%locals, loc); end
utils.deduplicate(\%locals);
return {..}
locals:%locals
statements:".."
if \(%condition as lua expr) then if \(%condition as lua expr) then
\(%if_body.statements or "\(%if_body.expr);") \(%if_body as lua statements)
else else
\(%else_body.statements or "\(%else_body.expr);") \(%else_body as lua statements)
end end
# Conditional expression (ternary operator) # Conditional expression (ternary operator)
@ -51,14 +40,15 @@ immediately
#.. If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic #.. If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic
equivalent of a conditional expression: (cond and if_true or if_false) equivalent of a conditional expression: (cond and if_true or if_false)
if: %when_true_expr.type in {Text:yes, List:yes, Dict:yes, Number:yes} if: %when_true_expr.type in {Text:yes, List:yes, Dict:yes, Number:yes}
return {..} return:
expr:"(\(%condition as lua expr) and \(%when_true_expr as lua expr) or \(%when_false_expr as lua expr))" Lua ".."
(\(%condition as lua expr) and \(%when_true_expr as lua expr) or \(%when_false_expr as lua expr))
..else: ..else:
#.. Otherwise, need to do an anonymous inline function (yuck, too bad lua #.. Otherwise, need to do an anonymous inline function (yuck, too bad lua
doesn't have a proper ternary operator!) doesn't have a proper ternary operator!)
To see why this is necessary consider: (random()<.5 and false or 99) To see why this is necessary consider: (random()<.5 and false or 99)
return {..} return:
expr: ".." Lua ".."
(function() (function()
if \(%condition as lua expr) then if \(%condition as lua expr) then
return \(%when_true_expr as lua expr); return \(%when_true_expr as lua expr);
@ -82,15 +72,11 @@ immediately:
# Helper function # Helper function
immediately: immediately:
compile [if %tree has subtree %subtree where %condition %body] to: compile [if %tree has subtree %subtree where %condition %body] to:
%body_lua <- (%body as lua) Lua ".."
%body_statements <- (%body_lua.statements or "\(%body_lua.expr);")
return {..}
locals: %body_lua.locals
statements:".."
for \(%subtree as lua expr) in coroutine.wrap(function() nomsu:walk_tree(\(%tree as lua expr)) end) do for \(%subtree as lua expr) in coroutine.wrap(function() nomsu:walk_tree(\(%tree as lua expr)) end) do
if Types.is_node(\(%subtree as lua expr)) then if Types.is_node(\(%subtree as lua expr)) then
if \(%condition as lua expr) then if \(%condition as lua expr) then
\%body_statements \(%body as lua statements)
break; break;
end end
end end
@ -101,48 +87,44 @@ immediately:
compile [do next repeat] to {statements:"goto continue_repeat;"} compile [do next repeat] to {statements:"goto continue_repeat;"}
compile [stop repeating] to {statements:"goto stop_repeat;"} compile [stop repeating] to {statements:"goto stop_repeat;"}
compile [repeat while %condition %body] to compile [repeat while %condition %body] to
%body_lua <- (%body as lua) %lua <-: Lua "while \(%condition as lua expr) do"
%body_statements <- (%body_lua.statements or "\(%body_lua.expr);")
if %body has subtree % where: if %body has subtree % where:
(%.type = "FunctionCall") and ((%'s stub) is "do next repeat") (%.type = "FunctionCall") and ((%'s stub) is "do next repeat")
..: %body_statments +<- "\n::continue_repeat::;" ..:
%code <- ".." %lua +<- "\n::continue_repeat::;"
while \(%condition as lua expr) do %lua +<- "\n"
\%body_statements %lua +<- (%body as lua statements)
end --while-loop %lua +<- "\nend --while-loop"
if %body has subtree % where: if %body has subtree % where:
(%.type = "FunctionCall") and ((%'s stub) is "stop repeating") (%.type = "FunctionCall") and ((%'s stub) is "stop repeating")
..: ..:
%code <- ".." %lua <- ".."
do -- scope of "stop repeating" label do -- scope of "stop repeating" label
\%code \%lua
::stop_repeat::; ::stop_repeat::;
end -- end of "stop repeating" label scope end -- end of "stop repeating" label scope
return {statements:%code, locals:%body_lua.locals} return %lua
parse [repeat %body] as: repeat while (yes) %body parse [repeat %body] as: repeat while (yes) %body
parse [repeat until %condition %body] as: repeat while (not %condition) %body parse [repeat until %condition %body] as: repeat while (not %condition) %body
compile [..] compile [..]
repeat %n times %body repeat %n times %body
..to: ..to:
%body_lua <- (%body as lua) %lua <-: Lua "for i=1,\(%n as lua expr) do"
%body_statements <- (%body_lua.statements or "\(%body_lua.expr);")
if %body has subtree % where if %body has subtree % where
(%.type = "FunctionCall") and ((%'s stub) is "do next repeat") (%.type = "FunctionCall") and ((%'s stub) is "do next repeat")
..: %body_statements +<- "\n::continue_repeat::;" ..: %lua +<- "\n::continue_repeat::;"
%code <- ".." %lua +<- "\n\(%body as lua statements)\nend --numeric for-loop"
for i=1,\(%n as lua expr) do
\%body_statements
end --numeric for-loop
if %body has subtree % where: if %body has subtree % where:
(%.type = "FunctionCall") and ((%'s stub) is "stop repeating") (%.type = "FunctionCall") and ((%'s stub) is "stop repeating")
..: ..:
%code <- ".." %lua <-:
Lua ".."
do -- scope of "stop repeating" label do -- scope of "stop repeating" label
\%code \%lua
::stop_repeat::; ::stop_repeat::;
end -- end of "stop repeating" label scope end -- end of "stop repeating" label scope
return {statements:%code, locals:%body_lua.locals} return %lua
# For loop control flow: # For loop control flow:
immediately: immediately:
@ -157,33 +139,30 @@ immediately:
for %var from %start to %stop by %step %body for %var from %start to %stop by %step %body
for %var from %start to %stop via %step %body for %var from %start to %stop via %step %body
..to: ..to:
%body_lua <- (%body as lua) # This uses Lua's approach of only allowing loop-scoped variables in a loop
%body_statements <- (%body_lua.statements or "\(%body_lua.expr);") assume (%var.type is "Var") or barf "Loop expected variable, not: \(%var's source code)"
%lua <-
Lua ".."
for \(%var as lua expr)=\(%start as lua expr),\(%n as lua expr) do
if %body has subtree % where: if %body has subtree % where:
(%.type = "FunctionCall") and (%.type = "FunctionCall") and
((%'s stub) is "do next %") and ((%'s stub) is "do next %") and
%.value.3.value is %var.value %.value.3.value is %var.value
..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;" ..: %lua write code "\n::continue_\(%var as lua identifier)::;"
%lua write code "\n\(%body as lua statements)\nend --numeric for-loop"
# This uses Lua's approach of only allowing loop-scoped variables in a loop
assume (%var.type is "Var") or barf "Loop expected variable, not: \(%var's source code)"
%code <- ".."
for \(%var as lua expr)=\(%start as lua expr),\(%stop as lua expr),\(%step as lua expr) do
\%body_statements
end --numeric for-loop
if %body has subtree % where: if %body has subtree % where:
(%.type = "FunctionCall") and: (%.type = "FunctionCall") and:
((%'s stub) is "stop %") and: ((%'s stub) is "stop %") and:
%.value.2.value is %var.value %.value.2.value is %var.value
..: ..:
%code <- ".." %lua write code ".."
do -- scope for stopping for-loop do -- scope for stopping for-loop
\%code \%lua
::stop_\(%var as lua identifier)::; ::stop_\(%var as lua identifier)::;
end -- end of scope for stopping for-loop end -- end of scope for stopping for-loop
return {statements:%code, locals:%body_lua.locals} return %lua
immediately: immediately:
parse [for %var from %start to %stop %body] as: for %var from %start to %stop via 1 %body parse [for %var from %start to %stop %body] as: for %var from %start to %stop via 1 %body
@ -196,57 +175,49 @@ immediately:
# For-each loop (lua's "ipairs()") # For-each loop (lua's "ipairs()")
immediately: immediately:
compile [for %var in %iterable %body] to: compile [for %var in %iterable %body] to:
%body_lua <- (%body as lua) # This uses Lua's approach of only allowing loop-scoped variables in a loop
%body_statements <- (%body_lua.statements or "\(%body_lua.expr);") assume (%var.type is "Var") or barf "Loop expected variable, not: \(%var's source code)"
%lua <-: Lua "for i,\(%var as lua identifier) in ipairs(\(%iterable as lua expr)) do"
if %body has subtree % where: if %body has subtree % where:
(%.type = "FunctionCall") and (%.type = "FunctionCall") and
((%'s stub) is "do next %") and ((%'s stub) is "do next %") and
%.value.3.value is %var.value %.value.3.value is %var.value
..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;" ..: %lua +<- (Lua "\n::continue_\(%var as lua identifier)::;")
# This uses Lua's approach of only allowing loop-scoped variables in a loop %lua +<- (Lua "\n\(%body as lua statements)\nend --foreach-loop")
assume (%var.type is "Var") or barf "Loop expected variable, not: \(%var's source code)"
%code <- ".."
for i,\(%var as lua expr) in ipairs(\(%iterable as lua expr)) do
\%body_statements
end --foreach-loop
if %body has subtree % where: if %body has subtree % where:
(%.type = "FunctionCall") and (%.type = "FunctionCall") and
((%'s stub) is "stop %") and ((%'s stub) is "stop %") and
%.value.2.value is %var.value %.value.2.value is %var.value
..: ..:
%code <- ".." %lua <-
Lua ".."
do -- scope for stopping for-loop do -- scope for stopping for-loop
\%code \%lua
::stop_\(%var as lua identifier)::; ::stop_\(%var as lua identifier)::;
end -- end of scope for stopping for-loop end -- end of scope for stopping for-loop
return {statements:%code, locals:%body_lua.locals} return %lua
parse [for all %iterable %body] as: for % in %iterable %body parse [for all %iterable %body] as: for % in %iterable %body
# Dict iteration (lua's "pairs()") # Dict iteration (lua's "pairs()")
immediately: immediately:
compile [for %key = %value in %iterable %body] to: compile [for %key = %value in %iterable %body] to:
%body_lua <- (%body as lua) # This uses Lua's approach of only allowing loop-scoped variables in a loop
%body_statements <- (%body_lua.statements or "\(%body_lua.expr);") assume (%key.type is "Var") or barf "Loop expected variable, not: \(%key's source code)"
assume (%value.type is "Var") or barf "Loop expected variable, not: \(%value's source code)"
%lua <- (Lua "for \(%key as lua identifier),\(%value as lua identifier) in pairs(\(%iterable as lua expr)) do")
if %body has subtree % where: if %body has subtree % where:
(%.type = "FunctionCall") and (%.type = "FunctionCall") and
((%'s stub) is "do next %") and ((%'s stub) is "do next %") and
%.value.3.value is %key.value %.value.3.value is %key.value
..: %body_statements +<- "\n::continue_\(%key as lua identifier)::;" ..: %lua +<- (Lua "\n::continue_\(%key as lua identifier)::;")
if %body has subtree % where: if %body has subtree % where:
(%.type = "FunctionCall") and (%.type = "FunctionCall") and
((%'s stub) is "do next %") and ((%'s stub) is "do next %") and
%.value.3.value is %value.value %.value.3.value is %value.value
..: %body_statements +<- "\n::continue_\(%value as lua identifier)::;" ..: %lua +<- (Lua "\n::continue_\(%value as lua identifier)::;")
%lua +<- (Lua "\n\(%body as lua statements)\nend --foreach-loop")
# This uses Lua's approach of only allowing loop-scoped variables in a loop
assume (%key.type is "Var") or barf "Loop expected variable, not: \(%key's source code)"
assume (%value.type is "Var") or barf "Loop expected variable, not: \(%value's source code)"
%code <- ".."
for \(%key as lua expr),\(%value as lua expr) in pairs(\(%iterable as lua expr)) do
\%body_statements
end --foreach-loop
%stop_labels <- "" %stop_labels <- ""
if %body has subtree % where: if %body has subtree % where:
@ -262,11 +233,12 @@ immediately:
..: %stop_labels +<- "\n::stop_\(%value as lua identifier)::;" ..: %stop_labels +<- "\n::stop_\(%value as lua identifier)::;"
if: %stop_labels is not "" if: %stop_labels is not ""
%code <- ".." %lua <-
Lua ".."
do -- scope for stopping for % = % loop do -- scope for stopping for % = % loop
\%code\%stop_labels \%lua\%stop_labels
end end
return {statements:%code, locals:%body_lua.locals} return %lua
# Switch statement/multi-branch if # Switch statement/multi-branch if
immediately: immediately:
@ -279,11 +251,11 @@ immediately:
for %func_call in %body.value: for %func_call in %body.value:
assume (%func_call.type is "FunctionCall") or barf ".." assume (%func_call.type is "FunctionCall") or barf ".."
Invalid format for 'when' statement. Only '*' blocks are allowed. Invalid format for 'when' statement. Only '*' blocks are allowed.
with [..] with {..}
%tokens <- %func_call.value %tokens: %func_call.value
%star <- (1st in %tokens) %star: %tokens.1
%condition <- (2nd in %tokens) %condition: %tokens.2
%action <- (3rd in %tokens) %action: %tokens.3
..: ..:
assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".." assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".."
Invalid format for 'when' statement. Lines must begin with '*' Invalid format for 'when' statement. Lines must begin with '*'
@ -334,7 +306,7 @@ immediately:
assume (%func_call.type is "FunctionCall") or barf ".." assume (%func_call.type is "FunctionCall") or barf ".."
Invalid format for 'when' statement. Only '*' blocks are allowed. Invalid format for 'when' statement. Only '*' blocks are allowed.
%tokens <- %func_call.value %tokens <- %func_call.value
with [%star<-(1st in %tokens), %condition<-(2nd in %tokens), %action<-(3rd in %tokens)]: with {%star:%tokens.1, %condition:%tokens.2, %action:%tokens.3}:
assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".." assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".."
Invalid format for 'when' statement. Lines must begin with '*' Invalid format for 'when' statement. Lines must begin with '*'
assume %condition or barf ".." assume %condition or barf ".."

View File

@ -9,7 +9,7 @@ immediately:
local lua = Lua(tree.source, "nomsu:define_compile_action("); local lua = Lua(tree.source, "nomsu:define_compile_action(");
local stubs = {}; local stubs = {};
for i, action in ipairs(\%actions.value) do for i, action in ipairs(\%actions.value) do
stubs[i] = nomsu:tree_to_named_stub(action); stubs[i] = action:get_stub(true);
end end
lua:append(repr(stubs), ", ", repr(tree.source:get_line()), ", function(tree"); lua:append(repr(stubs), ", ", repr(tree.source:get_line()), ", function(tree");
local args = {}; local args = {};
@ -23,7 +23,8 @@ immediately:
lua:append(", "); lua:append(", ");
lua:append(arg); lua:append(arg);
end end
local body_lua = nomsu:tree_to_lua(\%lua):as_statements("return "); local body_lua = \%lua:as_lua(nomsu);
body_lua:convert_to_statements("return ");
body_lua:declare_locals(args); body_lua:declare_locals(args);
lua:append(")\\n ", body_lua, "\\nend);") lua:append(")\\n ", body_lua, "\\nend);")
return lua; return lua;
@ -36,7 +37,7 @@ immediately:
local lua = Lua(tree.source, "nomsu:define_action("); local lua = Lua(tree.source, "nomsu:define_action(");
local stubs = {}; local stubs = {};
for i, action in ipairs(\%actions.value) do for i, action in ipairs(\%actions.value) do
stubs[i] = nomsu:tree_to_named_stub(action); stubs[i] = action:get_stub(true);
end end
lua:append(repr(stubs), ", ", repr(tree.source:get_line()), ", function("); lua:append(repr(stubs), ", ", repr(tree.source:get_line()), ", function(");
local args = {}; local args = {};
@ -47,7 +48,8 @@ immediately:
lua:append(arg); lua:append(arg);
if i < #args then lua:append(", ") end if i < #args then lua:append(", ") end
end end
local body_lua = nomsu:tree_to_lua(\%body):as_statements("return "); local body_lua = \%body:as_lua(nomsu);
body_lua:convert_to_statements("return ");
body_lua:declare_locals(args); body_lua:declare_locals(args);
lua:append(")\\n ", body_lua, "\\nend);") lua:append(")\\n ", body_lua, "\\nend);")
return lua; return lua;
@ -59,7 +61,7 @@ immediately:
local lua = Lua(tree.source, "nomsu:define_compile_action("); local lua = Lua(tree.source, "nomsu:define_compile_action(");
local stubs = {}; local stubs = {};
for i, action in ipairs(\%shorthand.value) do for i, action in ipairs(\%shorthand.value) do
stubs[i] = nomsu:tree_to_named_stub(action); stubs[i] = action:get_stub(true);
end end
lua:append(repr(stubs), ", ", repr(tree.source:get_line()), ", function(tree"); lua:append(repr(stubs), ", ", repr(tree.source:get_line()), ", function(tree");
local args = {}; local args = {};
@ -84,7 +86,7 @@ immediately:
lua:append([[) lua:append([[)
local template = nomsu:parse(]]..template..[[, ]]..repr(tree.source.filename)..[[); local template = nomsu:parse(]]..template..[[, ]]..repr(tree.source.filename)..[[);
local replacement = nomsu:tree_with_replaced_vars(template, ]]..replacements..[[); local replacement = nomsu:tree_with_replaced_vars(template, ]]..replacements..[[);
return nomsu:tree_to_lua(replacement, nomsu.compilestack[#nomsu.compilestack].source.filename); return replacement:as_lua(nomsu);
end); end);
]]); ]]);
return lua; return lua;
@ -101,11 +103,11 @@ action [remove action %stub]:
immediately: immediately:
action [%tree as lua]: action [%tree as lua]:
=lua "nomsu:tree_to_lua(\%tree)" =lua "\%tree:as_lua(nomsu)"
action [%tree as lua expr]: action [%tree as lua expr]:
lua> ".." lua> ".."
local lua = nomsu:tree_to_lua(\%tree); local lua = \%tree:as_lua(nomsu);
if not lua.is_value then if not lua.is_value then
error("Invalid thing to convert to lua expr: "..\%tree.source:get_text()); error("Invalid thing to convert to lua expr: "..\%tree.source:get_text());
end end
@ -113,7 +115,8 @@ immediately:
action [%tree as lua statements]: action [%tree as lua statements]:
lua> ".." lua> ".."
local lua = nomsu:tree_to_lua(\%tree):as_statements(); local lua = \%tree:as_lua(nomsu);
lua:convert_to_statements();
lua:declare_locals(); lua:declare_locals();
return lua; return lua;
@ -124,14 +127,17 @@ immediately:
=lua "nomsu:tree_to_stub(\%tree)" =lua "nomsu:tree_to_stub(\%tree)"
immediately: immediately:
compile [%tree's source code, %tree' source code] to: LuaValue "\(%tree as lua expr).source:get_text()" parse [%var write code %code] as: lua> "\%var:append(\%code);"
compile [repr %obj] to: LuaValue "repr(\(%obj as lua expr))"
compile [type of %obj] to: LuaValue "type(\(%obj as lua expr))"
immediately: immediately:
compile [nomsu] to: LuaValue "nomsu" compile [%tree's source code, %tree' source code] to: Lua value "\(%tree as lua expr).source:get_text()"
compile [%var as lua identifier] to: LuaValue "nomsu:var_to_lua_identifier(\(%var as lua expr))"
compile [repr %obj] to: Lua value "repr(\(%obj as lua expr))"
compile [type of %obj] to: Lua value "type(\(%obj as lua expr))"
immediately:
compile [nomsu] to: Lua value "nomsu"
compile [%var as lua identifier] to: Lua value "nomsu:var_to_lua_identifier(\(%var as lua expr))"
action [action %names metadata]: action [action %names metadata]:
=lua "nomsu.action_metadata[ACTIONS[\%names]]" =lua "nomsu.action_metadata[ACTIONS[\%names]]"
@ -149,7 +155,7 @@ action [help %action]:
# Compiler tools # Compiler tools
immediately: immediately:
compile [run %code] to: compile [run %code] to:
LuaValue ".." Lua value ".."
nomsu:run(\(%code as lua expr), '\ nomsu:run(\(%code as lua expr), '\
=lua "nomsu:get_line_number(nomsu.compilestack[#nomsu.compilestack])" =lua "nomsu:get_line_number(nomsu.compilestack[#nomsu.compilestack])"
..') ..')
@ -159,7 +165,7 @@ immediately:
immediately: immediately:
compile [show lua %block] to: compile [show lua %block] to:
lua> ".." lua> ".."
local \%lua = nomsu:tree_to_lua(\%block); local \%lua = \%block:as_lua(nomsu);
return Lua(\%block.source, "print(", repr(tostring(\%lua)), ");"); return Lua(\%block.source, "print(", repr(tostring(\%lua)), ");");
immediately: immediately:
@ -172,6 +178,13 @@ immediately:
return Lua(tree.source, "print(stringify(", \(%message as lua expr), "));"); return Lua(tree.source, "print(stringify(", \(%message as lua expr), "));");
end end
immediately:
compile [source] to: Lua value (=lua "tree.source") "tree.source"
immediately:
action [Lua %]: Lua (=lua "nomsu.compilestack[#nomsu.compilestack]") %
action [Lua value %]: Lua value (=lua "nomsu.compilestack[#nomsu.compilestack]") %
# Return # Return
immediately: immediately:
#.. Return statement is wrapped in a do..end block because Lua is unhappy if you #.. Return statement is wrapped in a do..end block because Lua is unhappy if you
@ -181,8 +194,8 @@ immediately:
# Error functions # Error functions
immediately: immediately:
compile [barf] to: LuaValue "error(nil, 0);" compile [barf] to: Lua value "error(nil, 0);"
compile [barf %msg] to: LuaValue "error(\(%msg as lua expr), 0);" compile [barf %msg] to: Lua value "error(\(%msg as lua expr), 0);"
compile [assume %condition] to: compile [assume %condition] to:
lua> "local \%assumption = 'Assumption failed: '..\%condition.source:get_text();" lua> "local \%assumption = 'Assumption failed: '..\%condition.source:get_text();"
return: return:
@ -200,7 +213,7 @@ immediately:
# Literals # Literals
immediately: immediately:
compile [yes] to: LuaValue "true" compile [yes] to: Lua value "true"
compile [no] to: LuaValue "false" compile [no] to: Lua value "false"
compile [nothing, nil, null] to: LuaValue "nil" compile [nothing, nil, null] to: Lua value "nil"

View File

@ -18,31 +18,31 @@ immediately:
# Comparison Operators # Comparison Operators
immediately: immediately:
compile [%x < %y] to: LuaValue "(\(%x as lua expr) < \(%y as lua expr))" compile [%x < %y] to: Lua value "(\(%x as lua expr) < \(%y as lua expr))"
compile [%x > %y] to: LuaValue "(\(%x as lua expr) > \(%y as lua expr))" compile [%x > %y] to: Lua value "(\(%x as lua expr) > \(%y as lua expr))"
compile [%x <= %y] to: LuaValue "(\(%x as lua expr) <= \(%y as lua expr))" compile [%x <= %y] to: Lua value "(\(%x as lua expr) <= \(%y as lua expr))"
compile [%x >= %y] to: LuaValue "(\(%x as lua expr) >= \(%y as lua expr))" compile [%x >= %y] to: Lua value "(\(%x as lua expr) >= \(%y as lua expr))"
# TODO: optimize case of [%x,%y] = [1,2] # TODO: optimize case of [%x,%y] = [1,2]
compile [%a is %b, %a = %b, %a == %b] to: compile [%a is %b, %a = %b, %a == %b] to:
lua> ".." lua> ".."
local safe = {Text=true, Number=true}; local safe = {Text=true, Number=true};
local a_lua, b_lua = nomsu:tree_to_lua(\%a), nomsu:tree_to_lua(\%b); local a_lua, b_lua = nomsu:tree_to_lua(\%a), nomsu:tree_to_lua(\%b);
if safe[\%a.type] or safe[\%b.type] then if safe[\%a.type] or safe[\%b.type] then
return LuaValue(tree.source, "(", a_lua, " == ", b_lua, ")"); return Lua.Value(tree.source, "(", a_lua, " == ", b_lua, ")");
else else
return LuaValue(tree.source, "utils.equivalent(", a_lua, ", ", b_lua, ")"); return Lua.Value(tree.source, "utils.equivalent(", a_lua, ", ", b_lua, ")");
end end
compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to: compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to:
lua> ".." lua> ".."
local safe = {Text=true, Number=true}; local safe = {Text=true, Number=true};
local a_lua, b_lua = nomsu:tree_to_lua(\%a), nomsu:tree_to_lua(\%b); local a_lua, b_lua = nomsu:tree_to_lua(\%a), nomsu:tree_to_lua(\%b);
if safe[\%a.type] or safe[\%b.type] then if safe[\%a.type] or safe[\%b.type] then
return LuaValue(tree.source, "(", a_lua, " ~= ", b_lua, ")"); return Lua.Value(tree.source, "(", a_lua, " ~= ", b_lua, ")");
else else
return LuaValue(tree.source, "(not utils.equivalent(", a_lua, ", ", b_lua, "))"); return Lua.Value(tree.source, "(not utils.equivalent(", a_lua, ", ", b_lua, "))");
end end
# For strict identity checking, use (%x's id) is (%y's id) # For strict identity checking, use (%x's id) is (%y's id)
compile [%'s id, id of %] to: LuaValue "nomsu.ids[\(% as lua expr)]" compile [%'s id, id of %] to: Lua value "nomsu.ids[\(% as lua expr)]"
# Variable assignment operator # Variable assignment operator
immediately: immediately:
@ -72,7 +72,7 @@ immediately:
local value_lua = nomsu:tree_to_lua(value); local value_lua = nomsu:tree_to_lua(value);
if not value_lua.is_value then error("Invalid value for assignment: "..value:get_src()); end if not value_lua.is_value then error("Invalid value for assignment: "..value:get_src()); end
if target.type == "Var" then if target.type == "Var" then
lhs:add_free_var(nomsu:var_to_lua_identifier(target.value)); lhs:add_free_vars(nomsu:var_to_lua_identifier(target.value));
end end
if i > 1 then if i > 1 then
lhs:append(", "); lhs:append(", ");
@ -104,12 +104,12 @@ immediately:
immediately: immediately:
# Math Operators # Math Operators
compile [%x + %y] to: LuaValue "(\(%x as lua expr) + \(%y as lua expr))" compile [%x + %y] to: Lua value "(\(%x as lua expr) + \(%y as lua expr))"
compile [%x - %y] to: LuaValue "(\(%x as lua expr) - \(%y as lua expr))" compile [%x - %y] to: Lua value "(\(%x as lua expr) - \(%y as lua expr))"
compile [%x * %y] to: LuaValue "(\(%x as lua expr) * \(%y as lua expr))" compile [%x * %y] to: Lua value "(\(%x as lua expr) * \(%y as lua expr))"
compile [%x / %y] to: LuaValue "(\(%x as lua expr) / \(%y as lua expr))" compile [%x / %y] to: Lua value "(\(%x as lua expr) / \(%y as lua expr))"
compile [%x ^ %y] to: LuaValue "(\(%x as lua expr) ^ \(%y as lua expr))" compile [%x ^ %y] to: Lua value "(\(%x as lua expr) ^ \(%y as lua expr))"
compile [%x wrapped around %y, %x mod %y] to: LuaValue "(\(%x as lua expr) % \(%y as lua expr))" compile [%x wrapped around %y, %x mod %y] to: Lua value "(\(%x as lua expr) % \(%y as lua expr))"
# 3-part chained comparisons # 3-part chained comparisons
# (uses a lambda to avoid re-evaluating middle value, while still being an expression) # (uses a lambda to avoid re-evaluating middle value, while still being an expression)
@ -124,22 +124,22 @@ immediately:
# TODO: optimize for common case where x,y,z are all either variables or number literals # TODO: optimize for common case where x,y,z are all either variables or number literals
# Boolean Operators # Boolean Operators
compile [%x and %y] to: LuaValue "(\(%x as lua expr) and \(%y as lua expr))" compile [%x and %y] to: Lua value "(\(%x as lua expr) and \(%y as lua expr))"
compile [%x or %y] to: LuaValue "(\(%x as lua expr) or \(%y as lua expr))" compile [%x or %y] to: Lua value "(\(%x as lua expr) or \(%y as lua expr))"
# Bitwise Operators # Bitwise Operators
compile [%a OR %b, %a | %b] to: LuaValue "bit32.bor(\(%a as lua expr), \(%b as lua expr))" compile [%a OR %b, %a | %b] to: Lua value "bit32.bor(\(%a as lua expr), \(%b as lua expr))"
compile [%a XOR %b] to: LuaValue "bit32.bxor(\(%a as lua expr), \(%b as lua expr))" compile [%a XOR %b] to: Lua value "bit32.bxor(\(%a as lua expr), \(%b as lua expr))"
compile [%a AND %b, %a & %b] to: LuaValue "bit32.band(\(%a as lua expr), \(%b as lua expr))" compile [%a AND %b, %a & %b] to: Lua value "bit32.band(\(%a as lua expr), \(%b as lua expr))"
compile [NOT %, ~ %] to: LuaValue "bit32.bnot(\(% as lua expr))" compile [NOT %, ~ %] to: Lua value "bit32.bnot(\(% as lua expr))"
compile [%x LSHIFT %shift, %x << %shift] to: LuaValue "bit32.lshift(\(%x as lua expr), \(%shift as lua expr))" compile [%x LSHIFT %shift, %x << %shift] to: Lua value "bit32.lshift(\(%x as lua expr), \(%shift as lua expr))"
compile [%x RSHIFT %shift, %x >>> %shift] to: LuaValue "bit32.rshift(\(%x as lua expr), \(%shift as lua expr))" compile [%x RSHIFT %shift, %x >>> %shift] to: Lua value "bit32.rshift(\(%x as lua expr), \(%shift as lua expr))"
compile [%x ARSHIFT %shift, %x >> %shift] to: LuaValue "bit32.arshift(\(%x as lua expr), \(%shift as lua expr))" compile [%x ARSHIFT %shift, %x >> %shift] to: Lua value "bit32.arshift(\(%x as lua expr), \(%shift as lua expr))"
# TODO: implement OR, XOR, AND for multiple operands? # TODO: implement OR, XOR, AND for multiple operands?
# Unary operators # Unary operators
compile [- %] to: LuaValue "(- \(% as lua expr))" compile [- %] to: Lua value "(- \(% as lua expr))"
compile [not %] to: LuaValue "(not \(% as lua expr))" compile [not %] to: Lua value "(not \(% as lua expr))"
# Update operators # Update operators
immediately: immediately:

View File

@ -4,7 +4,7 @@ do
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end end
local immutable = require('immutable') local immutable = require('immutable')
local Lua, LuaValue, Location local Lua, Location
Location = immutable({ Location = immutable({
"filename", "filename",
"start", "start",
@ -12,7 +12,6 @@ Location = immutable({
}, { }, {
name = "Location", name = "Location",
__new = function(self, filename, start, stop) __new = function(self, filename, start, stop)
assert(type(filename) == 'string' and type(start) == 'number' and type(stop) == 'number')
return filename, start, stop or start return filename, start, stop or start
end, end,
__tostring = function(self) __tostring = function(self)
@ -38,13 +37,14 @@ Location = immutable({
return FILE_CACHE[self.filename]:sub(self.start, self.stop) return FILE_CACHE[self.filename]:sub(self.start, self.stop)
end, end,
get_line_number = function(self) get_line_number = function(self)
local line_starts = LINE_STARTS[FILE_CACHE[self.filename]] local src = FILE_CACHE[self.filename]
local line_starts = LINE_STARTS[src]
local start_line = 1 local start_line = 1
while (line_starts[start_line + 1] or (#src + 1)) <= self.start do while (line_starts[start_line + 1] or math.huge) <= self.start do
start_line = start_line + 1 start_line = start_line + 1
end end
local stop_line = start_line local stop_line = start_line
while (line_starts[stop_line + 1] or (#src + 1)) <= self.stop do while (line_starts[stop_line + 1] or math.huge) <= self.stop do
stop_line = stop_line + 1 stop_line = stop_line + 1
end end
return start_line, stop_line return start_line, stop_line
@ -64,9 +64,17 @@ Location = immutable({
do do
local _class_0 local _class_0
local _base_0 = { local _base_0 = {
is_statement = true, clone = function(self)
is_value = false, local copy = Lua(self.source, {
add_free_vars = function(self, free_vars) unpack(self.bits)
})
copy.is_value = self.is_value
for k, v in pairs(self.free_vars) do
copy.free_vars[k] = v
end
return copy
end,
add_free_vars = function(self, ...)
local seen local seen
do do
local _tbl_0 = { } local _tbl_0 = { }
@ -80,16 +88,30 @@ do
end end
seen = _tbl_0 seen = _tbl_0
end end
for _index_0 = 1, #free_vars do for i = 1, select("#", ...) do
local var = free_vars[_index_0] local var = select(i, ...)
if not (seen[var]) then if not (seen[var]) then
self.free_vars[#self.free_vars + 1] = var self.free_vars[#self.free_vars + 1] = var
seen[var] = true seen[var] = true
end end
end end
end, end,
as_statements = function(self) convert_to_statements = function(self, prefix, suffix)
return self if prefix == nil then
prefix = ""
end
if suffix == nil then
suffix = ";"
end
if not (self.is_value) then
return
end
if prefix ~= "" then
self:prepend(prefix)
end
if suffix ~= "" then
return self:append(suffix)
end
end, end,
declare_locals = function(self, skip) declare_locals = function(self, skip)
if skip == nil then if skip == nil then
@ -128,12 +150,11 @@ do
local buff = { } local buff = { }
for i, b in ipairs(self.bits) do for i, b in ipairs(self.bits) do
buff[#buff + 1] = tostring(b) buff[#buff + 1] = tostring(b)
if i < #self.bits and type(b) ~= 'string' and b.is_statement then if i < #self.bits and type(b) ~= 'string' and not b.is_value then
buff[#buff + 1] = "\n" buff[#buff + 1] = "\n"
end end
end end
local ret = concat(buff, "") local ret = concat(buff, "")
assert(not ret:match(".*table: 0x.*"))
return ret return ret
end, end,
__len = function(self) __len = function(self)
@ -145,12 +166,16 @@ do
end end
return len return len
end, end,
__add = function(self, other)
return Lua(nil, self, other)
end,
__concat = function(self, other)
return Lua(nil, self, other)
end,
append = function(self, ...) append = function(self, ...)
local n = select("#", ...) local n = select("#", ...)
local bits = self.bits local bits = self.bits
for i = 1, n do for i = 1, n do
local x = select(i, ...)
assert(type(x) ~= 'table' or getmetatable(x))
bits[#bits + 1] = select(i, ...) bits[#bits + 1] = select(i, ...)
end end
end, end,
@ -158,13 +183,9 @@ do
local n = select("#", ...) local n = select("#", ...)
local bits = self.bits local bits = self.bits
for i = #bits + n, n + 1, -1 do for i = #bits + n, n + 1, -1 do
local x = select(i, ...)
assert(type(x) ~= 'table' or getmetatable(x))
bits[i] = bits[i - n] bits[i] = bits[i - n]
end end
for i = 1, n do for i = 1, n do
local x = select(i, ...)
assert(type(x) ~= 'table' or getmetatable(x))
bits[i] = select(i, ...) bits[i] = select(i, ...)
end end
end, end,
@ -197,6 +218,14 @@ do
end end
walk(self) walk(self)
return lua_str, metadata return lua_str, metadata
end,
parenthesize = function(self)
if self.is_value then
self:prepend("(")
return self:append(")")
else
return error("Cannot parenthesize lua statements")
end
end end
} }
_base_0.__index = _base_0 _base_0.__index = _base_0
@ -207,14 +236,11 @@ do
local filename, start, stop = self.source:match("^(.-)[(%d+):(%d+)]$") local filename, start, stop = self.source:match("^(.-)[(%d+):(%d+)]$")
self.source = Location(filename, tonumber(start), tonumber(stop)) self.source = Location(filename, tonumber(start), tonumber(stop))
end end
for i = 1, select("#", ...) do
local x = select(i, ...)
assert(type(x) ~= 'table' or getmetatable(x))
end
self.bits = { self.bits = {
... ...
} }
self.free_vars = { } self.free_vars = { }
self.is_value = false
end, end,
__base = _base_0, __base = _base_0,
__name = "Lua" __name = "Lua"
@ -227,82 +253,15 @@ do
end end
}) })
_base_0.__class = _class_0 _base_0.__class = _class_0
local self = _class_0
self.Value = function(...)
local lua = Lua(...)
lua.is_value = true
return lua
end
Lua = _class_0 Lua = _class_0
end end
do
local _class_0
local _parent_0 = Lua
local _base_0 = {
is_statement = false,
is_value = true,
__tostring = function(self)
local buff = { }
local _list_0 = self.bits
for _index_0 = 1, #_list_0 do
local b = _list_0[_index_0]
buff[#buff + 1] = tostring(b)
end
local ret = concat(buff, "")
assert(not ret:match(".*table: 0x.*"))
return ret
end,
as_statements = function(self, prefix, suffix)
if prefix == nil then
prefix = ""
end
if suffix == nil then
suffix = ";"
end
local bits = {
prefix,
unpack(self.bits)
}
bits[#bits + 1] = suffix
return Lua(self.source, unpack(bits))
end,
parenthesize = function(self)
self:prepend("(")
return self:append(")")
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, source, ...)
self.source = source
self.bits = {
...
}
end,
__base = _base_0,
__name = "LuaValue",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
LuaValue = _class_0
end
return { return {
Lua = Lua, Lua = Lua,
LuaValue = LuaValue,
Location = Location Location = Location
} }

View File

@ -1,12 +1,12 @@
{:insert, :remove, :concat} = table {:insert, :remove, :concat} = table
immutable = require 'immutable' immutable = require 'immutable'
local Lua, LuaValue, Location local Lua, Location
export LINE_STARTS export LINE_STARTS
Location = immutable {"filename","start","stop"}, { Location = immutable {"filename","start","stop"}, {
name:"Location" name:"Location"
__new: (filename, start, stop)=> __new: (filename, start, stop)=>
assert(type(filename) == 'string' and type(start) == 'number' and type(stop) == 'number') --assert(type(filename) == 'string' and type(start) == 'number' and type(stop) == 'number')
return filename, start, stop or start return filename, start, stop or start
__tostring: => "Location(\"#{@filename}\", #{@start}, #{@stop})" __tostring: => "Location(\"#{@filename}\", #{@start}, #{@stop})"
__lt: (other)=> __lt: (other)=>
@ -22,12 +22,13 @@ Location = immutable {"filename","start","stop"}, {
get_text: => FILE_CACHE[@filename]\sub(@start,@stop) get_text: => FILE_CACHE[@filename]\sub(@start,@stop)
get_line_number: => get_line_number: =>
-- TODO: do a binary search if this is actually slow, which I doubt -- TODO: do a binary search if this is actually slow, which I doubt
line_starts = LINE_STARTS[FILE_CACHE[@filename]] src = FILE_CACHE[@filename]
line_starts = LINE_STARTS[src]
start_line = 1 start_line = 1
while (line_starts[start_line+1] or (#src+1)) <= @start while (line_starts[start_line+1] or math.huge) <= @start
start_line += 1 start_line += 1
stop_line = start_line stop_line = start_line
while (line_starts[stop_line+1] or (#src+1)) <= @stop while (line_starts[stop_line+1] or math.huge) <= @stop
stop_line += 1 stop_line += 1
return start_line, stop_line return start_line, stop_line
get_line: => "#{@filename}:#{@get_line_number!}" get_line: => "#{@filename}:#{@get_line_number!}"
@ -39,27 +40,41 @@ Location = immutable {"filename","start","stop"}, {
} }
class Lua class Lua
is_statement: true
is_value: false
new: (@source, ...)=> new: (@source, ...)=>
if type(@source) == 'string' if type(@source) == 'string'
filename,start,stop = @source\match("^(.-)[(%d+):(%d+)]$") filename,start,stop = @source\match("^(.-)[(%d+):(%d+)]$")
@source = Location(filename, tonumber(start), tonumber(stop)) @source = Location(filename, tonumber(start), tonumber(stop))
for i=1,select("#",...)
x = select(i,...)
assert(type(x) != 'table' or getmetatable(x))
@bits = {...} @bits = {...}
@free_vars = {} @free_vars = {}
@is_value = false
add_free_vars: (free_vars)=> @Value = (...)->
lua = Lua(...)
lua.is_value = true
return lua
clone: =>
copy = Lua(@source, {unpack(@bits)})
copy.is_value = @is_value
for k,v in pairs @free_vars
copy.free_vars[k] = v
return copy
add_free_vars: (...)=>
seen = {[v]:true for v in *@free_vars} seen = {[v]:true for v in *@free_vars}
for var in *free_vars for i=1,select("#",...)
var = select(i, ...)
unless seen[var] unless seen[var]
@free_vars[#@free_vars+1] = var @free_vars[#@free_vars+1] = var
seen[var] = true seen[var] = true
as_statements: => self convert_to_statements: (prefix="", suffix=";")=>
unless @is_value
return
if prefix != ""
@prepend prefix
if suffix != ""
@append suffix
declare_locals: (skip={})=> declare_locals: (skip={})=>
if next(skip) == 1 if next(skip) == 1
@ -75,10 +90,9 @@ class Lua
buff = {} buff = {}
for i,b in ipairs @bits for i,b in ipairs @bits
buff[#buff+1] = tostring(b) buff[#buff+1] = tostring(b)
if i < #@bits and type(b) != 'string' and b.is_statement if i < #@bits and type(b) != 'string' and not b.is_value
buff[#buff+1] = "\n" buff[#buff+1] = "\n"
ret = concat(buff, "") ret = concat(buff, "")
assert(not ret\match(".*table: 0x.*"))
return ret return ret
__len: => __len: =>
@ -87,24 +101,24 @@ class Lua
len += #b len += #b
return len return len
__add: (other)=>
Lua(nil, self, other)
__concat: (other)=>
Lua(nil, self, other)
append: (...)=> append: (...)=>
n = select("#",...) n = select("#",...)
bits = @bits bits = @bits
for i=1,n for i=1,n
x = select(i,...)
assert(type(x) != 'table' or getmetatable(x))
bits[#bits+1] = select(i, ...) bits[#bits+1] = select(i, ...)
prepend: (...)=> prepend: (...)=>
n = select("#",...) n = select("#",...)
bits = @bits bits = @bits
for i=#bits+n,n+1,-1 for i=#bits+n,n+1,-1
x = select(i,...)
assert(type(x) != 'table' or getmetatable(x))
bits[i] = bits[i-n] bits[i] = bits[i-n]
for i=1,n for i=1,n
x = select(i,...)
assert(type(x) != 'table' or getmetatable(x))
bits[i] = select(i, ...) bits[i] = select(i, ...)
make_offset_table: (lua_chunkname)=> make_offset_table: (lua_chunkname)=>
@ -130,28 +144,11 @@ class Lua
walk self walk self
return lua_str, metadata return lua_str, metadata
class LuaValue extends Lua
is_statement: false
is_value: true
new: (@source, ...)=>
@bits = {...}
__tostring: =>
buff = {}
for b in *@bits
buff[#buff+1] = tostring(b)
ret = concat(buff, "")
assert(not ret\match(".*table: 0x.*"))
return ret
as_statements: (prefix="", suffix=";")=>
bits = {prefix, unpack @bits}
bits[#bits+1] = suffix
return Lua(@source, unpack(bits))
parenthesize: => parenthesize: =>
if @is_value
@prepend "(" @prepend "("
@append ")" @append ")"
else
error "Cannot parenthesize lua statements"
return {:Lua, :LuaValue, :Location} return {:Lua, :Location}

517
nomsu.lua
View File

@ -7,6 +7,9 @@ P, R, V, S, Cg, C, Cp, B, Cmt = lpeg.P, lpeg.R, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C,
local utils = require('utils') local utils = require('utils')
local new_uuid = require('uuid') local new_uuid = require('uuid')
local immutable = require('immutable') local immutable = require('immutable')
Tuple = immutable(nil, {
name = "Tuple"
})
local repr, stringify, min, max, equivalent, set, is_list, sum local repr, stringify, min, max, equivalent, set, is_list, sum
repr, stringify, min, max, equivalent, set, is_list, sum = utils.repr, utils.stringify, utils.min, utils.max, utils.equivalent, utils.set, utils.is_list, utils.sum repr, stringify, min, max, equivalent, set, is_list, sum = utils.repr, utils.stringify, utils.min, utils.max, utils.equivalent, utils.set, utils.is_list, utils.sum
local colors = setmetatable({ }, { local colors = setmetatable({ }, {
@ -27,10 +30,10 @@ do
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end end
local debug_getinfo = debug.getinfo local debug_getinfo = debug.getinfo
local Lua, LuaValue, Location local Lua, Location
do do
local _obj_0 = require("lua_obj") local _obj_0 = require("lua_obj")
Lua, LuaValue, Location = _obj_0.Lua, _obj_0.LuaValue, _obj_0.Location Lua, Location = _obj_0.Lua, _obj_0.Location
end end
FILE_CACHE = setmetatable({ }, { FILE_CACHE = setmetatable({ }, {
__index = function(self, filename) __index = function(self, filename)
@ -85,53 +88,7 @@ do
end end
end end
end end
local Types = { } local Types = require("nomsu_tree")
local type_tostring
type_tostring = function(self)
return tostring(self.name) .. "(" .. tostring(repr(self.value)) .. ")"
end
local type_with_value
type_with_value = function(self, value)
return getmetatable(self)(self.source, value)
end
local Tuple = immutable(nil, {
name = "Tuple"
})
local _list_0 = {
"File",
"Nomsu",
"Block",
"List",
"FunctionCall",
"Text",
"Dict",
"Number",
"Word",
"Var",
"Comment",
"IndexChain"
}
for _index_0 = 1, #_list_0 do
local t = _list_0[_index_0]
Types[t] = immutable({
"value",
"source"
}, {
type = t,
name = t,
__tostring = type_tostring,
with_value = type_with_value
})
end
Types.DictEntry = immutable({
"key",
"value"
}, {
name = "DictEntry"
})
Types.is_node = function(n)
return type(n) == 'userdata' and n.type
end
local NOMSU_DEFS local NOMSU_DEFS
do do
local _with_0 = { } local _with_0 = { }
@ -376,7 +333,8 @@ do
local tree = self:parse(nomsu_code, filename) local tree = self:parse(nomsu_code, filename)
assert(tree, "Failed to parse: " .. tostring(nomsu_code)) assert(tree, "Failed to parse: " .. tostring(nomsu_code))
assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type)) assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type))
local lua = self:tree_to_lua(tree):as_statements() local lua = self:tree_to_lua(tree)
lua:convert_to_statements()
lua:declare_locals() lua:declare_locals()
lua:prepend("-- File: " .. tostring(filename) .. "\n") lua:prepend("-- File: " .. tostring(filename) .. "\n")
return self:run_lua(lua, filename .. ".lua") return self:run_lua(lua, filename .. ".lua")
@ -511,7 +469,7 @@ do
end end
local nomsu = inline_expression(tok.value) local nomsu = inline_expression(tok.value)
return nomsu and "(: " .. tostring(nomsu) .. ")" return nomsu and "(: " .. tostring(nomsu) .. ")"
elseif "FunctionCall" == _exp_0 then elseif "Action" == _exp_0 then
local buff = "" local buff = ""
for i, bit in ipairs(tok.value) do for i, bit in ipairs(tok.value) do
if bit.type == "Word" then if bit.type == "Word" then
@ -529,7 +487,7 @@ do
buff = buff .. " " buff = buff .. " "
end end
buff = buff .. (function() buff = buff .. (function()
if bit.type == "FunctionCall" then if bit.type == "Action" then
return "(" .. nomsu .. ")" return "(" .. nomsu .. ")"
else else
return nomsu return nomsu
@ -540,9 +498,9 @@ do
return buff return buff
elseif "IndexChain" == _exp_0 then elseif "IndexChain" == _exp_0 then
local bits = { } local bits = { }
local _list_1 = tok.value local _list_0 = tok.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
local nomsu = inline_expression(bit) local nomsu = inline_expression(bit)
if not (nomsu) then if not (nomsu) then
return nil return nil
@ -552,9 +510,9 @@ do
return concat(bits, ".") return concat(bits, ".")
elseif "List" == _exp_0 then elseif "List" == _exp_0 then
local bits = { } local bits = { }
local _list_1 = tok.value local _list_0 = tok.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
local nomsu = inline_expression(bit) local nomsu = inline_expression(bit)
if not (nomsu) then if not (nomsu) then
return nil return nil
@ -564,9 +522,9 @@ do
return "[" .. concat(bits, ", ") .. "]" return "[" .. concat(bits, ", ") .. "]"
elseif "Dict" == _exp_0 then elseif "Dict" == _exp_0 then
local bits = { } local bits = { }
local _list_1 = tok.value local _list_0 = tok.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
local key_nomsu local key_nomsu
if bit.key.type == "Word" then if bit.key.type == "Word" then
key_nomsu = bit.key.value key_nomsu = bit.key.value
@ -576,7 +534,7 @@ do
if not (key_nomsu) then if not (key_nomsu) then
return nil return nil
end end
if bit.key.type == "FunctionCall" then if bit.key.type == "Action" then
key_nomsu = "(" .. key_nomsu .. ")" key_nomsu = "(" .. key_nomsu .. ")"
end end
local value_nomsu = inline_expression(bit.value) local value_nomsu = inline_expression(bit.value)
@ -588,9 +546,9 @@ do
return "{" .. concat(bits, ", ") .. "}" return "{" .. concat(bits, ", ") .. "}"
elseif "Text" == _exp_0 then elseif "Text" == _exp_0 then
local buff = '"' local buff = '"'
local _list_1 = tok.value local _list_0 = tok.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
if type(bit) == 'string' then if type(bit) == 'string' then
if bit:find("\n") then if bit:find("\n") then
return nil return nil
@ -636,9 +594,9 @@ do
local _exp_0 = tok.type local _exp_0 = tok.type
if "Block" == _exp_0 then if "Block" == _exp_0 then
local buff = ":" local buff = ":"
local _list_1 = tok.value local _list_0 = tok.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local line = _list_1[_index_0] local line = _list_0[_index_0]
nomsu = expression(line) nomsu = expression(line)
if not (nomsu) then if not (nomsu) then
return nil return nil
@ -646,7 +604,7 @@ do
buff = buff .. ("\n " .. self:indent(nomsu)) buff = buff .. ("\n " .. self:indent(nomsu))
end end
return buff return buff
elseif "FunctionCall" == _exp_0 then elseif "Action" == _exp_0 then
nomsu = expression(tok) nomsu = expression(tok)
if not (nomsu) then if not (nomsu) then
return nil return nil
@ -657,9 +615,9 @@ do
elseif "List" == _exp_0 then elseif "List" == _exp_0 then
local buff = "[..]" local buff = "[..]"
local line = "\n " local line = "\n "
local _list_1 = tok.value local _list_0 = tok.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
nomsu = inline_expression(bit) nomsu = inline_expression(bit)
if line ~= "\n " and #line + #", " + #nomsu > max_line then if line ~= "\n " and #line + #", " + #nomsu > max_line then
buff = buff .. line buff = buff .. line
@ -685,14 +643,14 @@ do
elseif "Dict" == _exp_0 then elseif "Dict" == _exp_0 then
local buff = "{..}" local buff = "{..}"
local line = "\n " local line = "\n "
local _list_1 = tok.value local _list_0 = tok.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
local key_nomsu = inline_expression(bit.key) local key_nomsu = inline_expression(bit.key)
if not (key_nomsu) then if not (key_nomsu) then
return nil return nil
end end
if bit.key.type == "FunctionCall" then if bit.key.type == "Action" then
key_nomsu = "(" .. key_nomsu .. ")" key_nomsu = "(" .. key_nomsu .. ")"
end end
local value_nomsu = inline_expression(bit.value) local value_nomsu = inline_expression(bit.value)
@ -714,9 +672,9 @@ do
return buff return buff
elseif "Text" == _exp_0 then elseif "Text" == _exp_0 then
local buff = '".."\n ' local buff = '".."\n '
local _list_1 = tok.value local _list_0 = tok.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
if type(bit) == 'string' then if type(bit) == 'string' then
buff = buff .. bit:gsub("\\", "\\\\"):gsub("\n", "\n ") buff = buff .. bit:gsub("\\", "\\\\"):gsub("\n", "\n ")
else else
@ -758,7 +716,7 @@ do
local _exp_0 = tok.type local _exp_0 = tok.type
if "Block" == _exp_0 then if "Block" == _exp_0 then
if #tok.value == 1 then if #tok.value == 1 then
if tok.value[1].type == "FunctionCall" then if tok.value[1].type == "Action" then
nomsu = inline_expression(tok.value[1]) nomsu = inline_expression(tok.value[1])
else else
nomsu = noeol_expression(tok.value[1]) nomsu = noeol_expression(tok.value[1])
@ -768,7 +726,7 @@ do
end end
end end
return noeol_expression(tok) return noeol_expression(tok)
elseif "FunctionCall" == _exp_0 then elseif "Action" == _exp_0 then
local buff = "" local buff = ""
for i, bit in ipairs(tok.value) do for i, bit in ipairs(tok.value) do
if bit.type == "Word" then if bit.type == "Word" then
@ -780,7 +738,7 @@ do
else else
nomsu = inline_expression(bit) nomsu = inline_expression(bit)
if nomsu and #nomsu < max_line then if nomsu and #nomsu < max_line then
if bit.type == "FunctionCall" then if bit.type == "Action" then
nomsu = "(" .. nomsu .. ")" nomsu = "(" .. nomsu .. ")"
end end
else else
@ -788,7 +746,7 @@ do
if not (nomsu) then if not (nomsu) then
return nil return nil
end end
if bit.type == "FunctionCall" then if bit.type == "Action" then
nomsu = "(..)\n " .. self:indent(nomsu) nomsu = "(..)\n " .. self:indent(nomsu)
end end
if i < #tok.value then if i < #tok.value then
@ -804,9 +762,9 @@ do
return buff return buff
elseif "File" == _exp_0 then elseif "File" == _exp_0 then
local lines = { } local lines = { }
local _list_1 = tree.value local _list_0 = tree.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local line = _list_1[_index_0] local line = _list_0[_index_0]
nomsu = expression(line) nomsu = expression(line)
if not (nomsu) then if not (nomsu) then
error("Failed to produce output for:\n" .. tostring(colored.yellow(line.source:get_text())), 0) error("Failed to produce output for:\n" .. tostring(colored.yellow(line.source:get_text())), 0)
@ -870,309 +828,7 @@ do
end end
end, end,
tree_to_lua = function(self, tree) tree_to_lua = function(self, tree)
assert(tree, "No tree provided.") return tree:as_lua(self)
if not Types.is_node(tree) then
error("Invalid tree")
end
local _exp_0 = tree.type
if "File" == _exp_0 then
if #tree.value == 1 then
return self:tree_to_lua(tree.value[1])
end
local file_lua = Lua(tree.source)
local declared_locals = { }
for i, line in ipairs(tree.value) do
local line_lua = self:tree_to_lua(line)
if not line_lua then
error("No lua produced by " .. tostring(repr(line)), 0)
end
line_lua = line_lua:as_statements()
if i < #tree.value then
file_lua:append("\n")
end
file_lua:append(line_lua)
end
file_lua:declare_locals()
return file_lua
elseif "Comment" == _exp_0 then
return Lua(tree.source, "--" .. tree.value:gsub("\n", "\n--") .. "\n")
elseif "Nomsu" == _exp_0 then
return Lua(tree.source, "nomsu:parse(", tree.source:get_text(), ", ", repr(tree.source.filename), ")")
elseif "Block" == _exp_0 then
local block_lua = Lua(tree.source)
for i, arg in ipairs(tree.value) do
local lua = self:tree_to_lua(arg)
if i == 1 and #tree.value == 1 and lua.is_value then
return lua
end
lua = lua:as_statements()
if i < #tree.value then
block_lua:append("\n")
end
block_lua:append(lua)
end
return block_lua
elseif "FunctionCall" == _exp_0 then
insert(self.compilestack, tree)
local stub = self:tree_to_stub(tree)
local action = rawget(self.environment.ACTIONS, stub)
local metadata = self.action_metadata[action]
if metadata and metadata.compile_time then
local args
do
local _accum_0 = { }
local _len_0 = 1
local _list_1 = tree.value
for _index_0 = 1, #_list_1 do
local arg = _list_1[_index_0]
if arg.type ~= "Word" then
_accum_0[_len_0] = arg
_len_0 = _len_0 + 1
end
end
args = _accum_0
end
if metadata and metadata.arg_orders then
local new_args
do
local _accum_0 = { }
local _len_0 = 1
local _list_1 = metadata.arg_orders[stub]
for _index_0 = 1, #_list_1 do
local p = _list_1[_index_0]
_accum_0[_len_0] = args[p - 1]
_len_0 = _len_0 + 1
end
new_args = _accum_0
end
args = new_args
end
local lua = action(tree, unpack(args))
remove(self.compilestack)
return lua
elseif not metadata and self.__class.math_patt:match(stub) then
local lua = LuaValue(tree.source)
for i, tok in ipairs(tree.value) do
if tok.type == "Word" then
lua:append(tok.value)
else
local tok_lua = self:tree_to_lua(tok)
if not (tok_lua.is_value) then
local src = tok.source:get_text()
error("non-expression value inside math expression: " .. tostring(colored.yellow(src)))
end
lua:append(tok_lua)
end
if i < #tree.value then
lua:append(" ")
end
end
remove(self.compilestack)
lua:parenthesize()
return lua
end
local args = { }
for i, tok in ipairs(tree.value) do
local _continue_0 = false
repeat
if tok.type == "Word" then
_continue_0 = true
break
end
local arg_lua = self:tree_to_lua(tok)
if not (arg_lua.is_value) then
local line, src = tok.source:get_line(), tok.source:get_text()
error(tostring(line) .. ": Cannot use:\n" .. tostring(colored.yellow(src)) .. "\nas an argument to " .. tostring(stub) .. ", since it's not an expression, it produces: " .. tostring(repr(arg_lua)), 0)
end
insert(args, arg_lua)
_continue_0 = true
until true
if not _continue_0 then
break
end
end
if metadata and metadata.arg_orders then
do
local _accum_0 = { }
local _len_0 = 1
local _list_1 = metadata.arg_orders[stub]
for _index_0 = 1, #_list_1 do
local p = _list_1[_index_0]
_accum_0[_len_0] = args[p]
_len_0 = _len_0 + 1
end
args = _accum_0
end
end
local lua = LuaValue(tree.source, "ACTIONS[" .. tostring(repr(stub)) .. "](")
for i, arg in ipairs(args) do
lua:append(arg)
if i < #args then
lua:append(", ")
end
end
lua:append(")")
remove(self.compilestack)
return lua
elseif "Text" == _exp_0 then
local lua = LuaValue(tree.source)
local string_buffer = ""
local _list_1 = tree.value
for _index_0 = 1, #_list_1 do
local _continue_0 = false
repeat
local bit = _list_1[_index_0]
if type(bit) == "string" then
string_buffer = string_buffer .. bit
_continue_0 = true
break
end
if string_buffer ~= "" then
if #lua.bits > 0 then
lua:append("..")
end
lua:append(repr(string_buffer))
string_buffer = ""
end
local bit_lua = self:tree_to_lua(bit)
if not (bit_lua.is_value) then
local line, src = bit.source:get_line(), bit.source:get_text()
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(bit)) .. " as a string interpolation value, since it's not an expression.", 0)
end
if #lua.bits > 0 then
lua:append("..")
end
if bit.type ~= "Text" then
bit_lua = LuaValue(bit.source, "stringify(", bit_lua, ")")
end
lua:append(bit_lua)
_continue_0 = true
until true
if not _continue_0 then
break
end
end
if string_buffer ~= "" or #lua.bits == 0 then
if #lua.bits > 0 then
lua:append("..")
end
lua:append(repr(string_buffer))
end
if #lua.bits > 1 then
lua:parenthesize()
end
return lua
elseif "IndexChain" == _exp_0 then
local lua = self:tree_to_lua(tree.value[1])
if not (lua.is_value) then
local line, src = tree.value[1].source:get_line(), tree.value[1].source:get_text()
error(tostring(line) .. ": Cannot index " .. tostring(colored.yellow(src)) .. ", since it's not an expression.", 0)
end
local last_char = tostring(lua):sub(1, 1)
if last_char == "}" or last_char == '"' then
lua:parenthesize()
end
for i = 2, #tree.value do
local _continue_0 = false
repeat
local key = tree.value[i]
if key.type == 'Text' and #key.value == 1 and type(key.value[1]) == 'string' and key.value[1]:match("^[a-zA-Z_][a-zA-Z0-9_]$") then
lua:append("." .. tostring(key.value[1]))
_continue_0 = true
break
end
local key_lua = self:tree_to_lua(key)
if not (key_lua.is_value) then
local line, src = key.source:get_line(), key.source:get_text()
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as an index, since it's not an expression.", 0)
end
if tostring(key_lua):sub(1, 1) == '[' then
lua:append("[ ", key_lua, "]")
else
lua:append("[", key_lua, "]")
end
_continue_0 = true
until true
if not _continue_0 then
break
end
end
return lua
elseif "List" == _exp_0 then
local lua = LuaValue(tree.source, "{")
local line_length = 0
for i, item in ipairs(tree.value) do
local item_lua = self:tree_to_lua(item)
if not (item_lua.is_value) then
local line, src = item.source:get_line(), item.source:get_text()
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a list item, since it's not an expression.", 0)
end
lua:append(item_lua)
local newlines, last_line = tostring(item_lua):match("^(.-)([^\n]*)$")
if #newlines > 0 then
line_length = #last_line
else
line_length = line_length + #last_line
end
if i < #tree.value then
if line_length >= 80 then
lua:append(",\n")
line_length = 0
else
lua:append(", ")
line_length = line_length + 2
end
end
end
lua:append("}")
return lua
elseif "Dict" == _exp_0 then
local lua = LuaValue(tree.source, "{")
local line_length = 0
for i, entry in ipairs(tree.value) do
local key_lua = self:tree_to_lua(entry.key)
if not (key_lua.is_value) then
local line, src = key.source:get_line(), key.source:get_text()
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a dict key, since it's not an expression.", 0)
end
local value_lua = self:tree_to_lua(entry.value)
if not (value_lua.is_value) then
local line, src = value.source:get_line(), value.source:get_text()
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a dict value, since it's not an expression.", 0)
end
local key_str = tostring(key_lua):match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str then
lua:append(key_str, "=", value_lua)
elseif tostring(key_lua):sub(1, 1) == "[" then
lua:append("[ ", key_lua, "]=", value_lua)
else
lua:append("[", key_lua, "]=", value_lua)
end
local newlines, last_line = ("[" .. tostring(key_lua) .. "=" .. tostring(value_lua)):match("^(.-)([^\n]*)$")
if #newlines > 0 then
line_length = #last_line
else
line_length = line_length + #last_line
end
if i < #tree.value then
if line_length >= 80 then
lua:append(",\n")
line_length = 0
else
lua:append(", ")
line_length = line_length + 2
end
end
end
lua:append("}")
return lua
elseif "Number" == _exp_0 then
return LuaValue(tree.source, tostring(tree.value))
elseif "Var" == _exp_0 then
return LuaValue(tree.source, self:var_to_lua_identifier(tree.value))
else
return error("Unknown/unimplemented thingy: " .. tostring(tree.type), 0)
end
end, end,
walk_tree = function(self, tree, depth) walk_tree = function(self, tree, depth)
if depth == nil then if depth == nil then
@ -1183,16 +839,16 @@ do
return return
end end
local _exp_0 = tree.type local _exp_0 = tree.type
if "List" == _exp_0 or "File" == _exp_0 or "Block" == _exp_0 or "FunctionCall" == _exp_0 or "Text" == _exp_0 or "IndexChain" == _exp_0 then if "List" == _exp_0 or "File" == _exp_0 or "Block" == _exp_0 or "Action" == _exp_0 or "Text" == _exp_0 or "IndexChain" == _exp_0 then
local _list_1 = tree.value local _list_0 = tree.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local v = _list_1[_index_0] local v = _list_0[_index_0]
self:walk_tree(v, depth + 1) self:walk_tree(v, depth + 1)
end end
elseif "Dict" == _exp_0 then elseif "Dict" == _exp_0 then
local _list_1 = tree.value local _list_0 = tree.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local e = _list_1[_index_0] local e = _list_0[_index_0]
self:walk_tree(e.key, depth + 1) self:walk_tree(e.key, depth + 1)
self:walk_tree(e.value, depth + 1) self:walk_tree(e.value, depth + 1)
end end
@ -1236,7 +892,7 @@ do
return replacement return replacement
end end
local _exp_0 = tree.type local _exp_0 = tree.type
if "File" == _exp_0 or "Nomsu" == _exp_0 or "Block" == _exp_0 or "List" == _exp_0 or "FunctionCall" == _exp_0 or "Text" == _exp_0 or "IndexChain" == _exp_0 then if "File" == _exp_0 or "Nomsu" == _exp_0 or "Block" == _exp_0 or "List" == _exp_0 or "Action" == _exp_0 or "Text" == _exp_0 or "IndexChain" == _exp_0 then
local new_values, is_changed = { }, false local new_values, is_changed = { }, false
for i, old_value in ipairs(tree.value) do for i, old_value in ipairs(tree.value) do
local new_value = type(old_value) ~= "string" and self:tree_map(old_value, fn) or nil local new_value = type(old_value) ~= "string" and self:tree_map(old_value, fn) or nil
@ -1281,15 +937,15 @@ do
end) end)
end, end,
tree_to_stub = function(self, tree) tree_to_stub = function(self, tree)
if tree.type ~= "FunctionCall" then if tree.type ~= "Action" then
error("Tried to get stub from non-functioncall tree: " .. tostring(tree.type), 0) error("Tried to get stub from non-functioncall tree: " .. tostring(tree.type), 0)
end end
return concat((function() return concat((function()
local _accum_0 = { } local _accum_0 = { }
local _len_0 = 1 local _len_0 = 1
local _list_1 = tree.value local _list_0 = tree.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local t = _list_1[_index_0] local t = _list_0[_index_0]
_accum_0[_len_0] = (t.type == "Word" and t.value or "%") _accum_0[_len_0] = (t.type == "Word" and t.value or "%")
_len_0 = _len_0 + 1 _len_0 = _len_0 + 1
end end
@ -1297,15 +953,15 @@ do
end)(), " ") end)(), " ")
end, end,
tree_to_named_stub = function(self, tree) tree_to_named_stub = function(self, tree)
if tree.type ~= "FunctionCall" then if tree.type ~= "Action" then
error("Tried to get stub from non-functioncall tree: " .. tostring(tree.type), 0) error("Tried to get stub from non-functioncall tree: " .. tostring(tree.type), 0)
end end
return concat((function() return concat((function()
local _accum_0 = { } local _accum_0 = { }
local _len_0 = 1 local _len_0 = 1
local _list_1 = tree.value local _list_0 = tree.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local t = _list_1[_index_0] local t = _list_0[_index_0]
_accum_0[_len_0] = (t.type == "Word" and t.value or "%" .. tostring(t.value)) _accum_0[_len_0] = (t.type == "Word" and t.value or "%" .. tostring(t.value))
_len_0 = _len_0 + 1 _len_0 = _len_0 + 1
end end
@ -1367,19 +1023,20 @@ do
end end
local nomsu = self local nomsu = self
self:define_compile_action("immediately %block", get_line_no(), function(self, _block) self:define_compile_action("immediately %block", get_line_no(), function(self, _block)
local lua = nomsu:tree_to_lua(_block):as_statements() local lua = nomsu:tree_to_lua(_block)
lua:convert_to_statements()
lua:declare_locals() lua:declare_locals()
nomsu:run_lua(lua) nomsu:run_lua(lua)
return Lua(self.source, "if IMMEDIATE then\n", lua, "\nend") return Lua(self.source, "if IMMEDIATE then\n", lua, "\nend")
end) end)
self:define_compile_action("Lua %code", get_line_no(), function(self, _code) self:define_compile_action("Lua %source %code", get_line_no(), function(self, _source, _code)
if _code.type ~= "Text" then if _code.type ~= "Text" then
return LuaValue(self.source, "Lua(", repr(_code.source), ", ", nomsu:tree_to_lua(_code), ")") return Lua.Value(_source, "Lua(", repr(_code.source), ", ", nomsu:tree_to_lua(_code), ")")
end end
local lua = LuaValue(self.source, "Lua(", repr(_code.source)) local lua = Lua.Value(_source, "Lua(", repr(_code.source))
local _list_1 = _code.value local _list_0 = _code.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
lua:append(", ") lua:append(", ")
if type(bit) == "string" then if type(bit) == "string" then
lua:append(repr(bit)) lua:append(repr(bit))
@ -1395,14 +1052,14 @@ do
lua:append(")") lua:append(")")
return lua return lua
end) end)
self:define_compile_action("LuaValue %code", get_line_no(), function(self, _code) self:define_compile_action("Lua value %source %code", get_line_no(), function(self, _source, _code)
if _code.type ~= "Text" then if _code.type ~= "Text" then
return LuaValue(self.source, "LuaValue(", repr(_code.source), ", ", nomsu:tree_to_lua(_code), ")") return Lua.Value(_source, "Lua.Value(", repr(_code.source), ", ", nomsu:tree_to_lua(_code), ")")
end end
local lua = LuaValue(self.source, "LuaValue(", repr(_code.source)) local lua = Lua.Value(_source, "Lua.Value(", repr(_code.source))
local _list_1 = _code.value local _list_0 = _code.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
lua:append(", ") lua:append(", ")
if type(bit) == "string" then if type(bit) == "string" then
lua:append(repr(bit)) lua:append(repr(bit))
@ -1420,12 +1077,12 @@ do
end) end)
self:define_compile_action("lua> %code", get_line_no(), function(self, _code) self:define_compile_action("lua> %code", get_line_no(), function(self, _code)
if _code.type ~= "Text" then if _code.type ~= "Text" then
return LuaValue(self.source, "nomsu:run_lua(", nomsu:tree_to_lua(_code), ")") return Lua.Value(self.source, "nomsu:run_lua(", nomsu:tree_to_lua(_code), ")")
end end
local lua = Lua(_code.source) local lua = Lua(_code.source)
local _list_1 = _code.value local _list_0 = _code.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
if type(bit) == "string" then if type(bit) == "string" then
lua:append(bit) lua:append(bit)
else else
@ -1441,12 +1098,12 @@ do
end) end)
self:define_compile_action("=lua %code", get_line_no(), function(self, _code) self:define_compile_action("=lua %code", get_line_no(), function(self, _code)
if _code.type ~= "Text" then if _code.type ~= "Text" then
return LuaValue(self.source, "nomsu:run_lua(", nomsu:tree_to_lua(_code), ")") return Lua.Value(self.source, "nomsu:run_lua(", nomsu:tree_to_lua(_code), ")")
end end
local lua = LuaValue(self.source) local lua = Lua.Value(self.source)
local _list_1 = _code.value local _list_0 = _code.value
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_0 do
local bit = _list_1[_index_0] local bit = _list_0[_index_0]
if type(bit) == "string" then if type(bit) == "string" then
lua:append(bit) lua:append(bit)
else else
@ -1461,7 +1118,7 @@ do
return lua return lua
end) end)
self:define_compile_action("!! code location !!", get_line_no(), function(self) self:define_compile_action("!! code location !!", get_line_no(), function(self)
return LuaValue(self.source, repr(self.source)) return Lua.Value(self.source, repr(tostring(self.source)))
end) end)
self:define_action("run file %filename", get_line_no(), function(_filename) self:define_action("run file %filename", get_line_no(), function(_filename)
return nomsu:run_file(_filename) return nomsu:run_file(_filename)
@ -1469,7 +1126,7 @@ do
return self:define_compile_action("use %filename", get_line_no(), function(self, _filename) return self:define_compile_action("use %filename", get_line_no(), function(self, _filename)
local filename = nomsu:tree_to_value(_filename) local filename = nomsu:tree_to_value(_filename)
nomsu:use_file(filename) nomsu:use_file(filename)
return LuaValue(self.source, "nomsu:use_file(" .. tostring(repr(filename)) .. ")") return Lua.Value(self.source, "nomsu:use_file(" .. tostring(repr(filename)) .. ")")
end) end)
end end
} }
@ -1548,7 +1205,6 @@ do
end end
self.environment.Tuple = Tuple self.environment.Tuple = Tuple
self.environment.Lua = Lua self.environment.Lua = Lua
self.environment.LuaValue = LuaValue
self.environment.Location = Location self.environment.Location = Location
self.environment.ACTIONS = setmetatable({ }, { self.environment.ACTIONS = setmetatable({ }, {
__index = function(self, key) __index = function(self, key)
@ -1572,7 +1228,6 @@ do
_base_0.__class = _class_0 _base_0.__class = _class_0
local self = _class_0 local self = _class_0
_chunk_counter = 0 _chunk_counter = 0
self.math_patt = re.compile([[ "%" (" " [*/^+-] " %")+ ]])
self.unescape_string = function(self, str) self.unescape_string = function(self, str)
return Cs(((P("\\\\") / "\\") + (P("\\\"") / '"') + NOMSU_DEFS.escaped_char + P(1)) ^ 0):match(str) return Cs(((P("\\\\") / "\\") + (P("\\\"") / '"') + NOMSU_DEFS.escaped_char + P(1)) ^ 0):match(str)
end end

View File

@ -18,12 +18,14 @@ lpeg.setmaxstack 10000
utils = require 'utils' utils = require 'utils'
new_uuid = require 'uuid' new_uuid = require 'uuid'
immutable = require 'immutable' immutable = require 'immutable'
export Tuple
Tuple = immutable(nil, {name:"Tuple"})
{:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils {:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils
colors = setmetatable({}, {__index:->""}) colors = setmetatable({}, {__index:->""})
colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)}) colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)})
{:insert, :remove, :concat} = table {:insert, :remove, :concat} = table
debug_getinfo = debug.getinfo debug_getinfo = debug.getinfo
{:Lua, :LuaValue, :Location} = require "lua_obj" {:Lua, :Location} = require "lua_obj"
-- TODO: -- TODO:
-- consider non-linear codegen, rather than doing thunks for things like comprehensions -- consider non-linear codegen, rather than doing thunks for things like comprehensions
@ -88,16 +90,7 @@ do
-- Can't use this because it breaks some LPEG stuff -- Can't use this because it breaks some LPEG stuff
--STRING_METATABLE.__mul = (other)=> string.rep(@, other) --STRING_METATABLE.__mul = (other)=> string.rep(@, other)
Types = {} Types = require "nomsu_tree"
type_tostring = =>
"#{@name}(#{repr(@value)})"
type_with_value = (value)=> getmetatable(self)(@source, value)
Tuple = immutable(nil, {name:"Tuple"})
for t in *{"File", "Nomsu", "Block", "List", "FunctionCall", "Text", "Dict", "Number", "Word", "Var", "Comment", "IndexChain"}
Types[t] = immutable({"value","source"}, {type:t, name:t, __tostring:type_tostring, with_value:type_with_value})
Types.DictEntry = immutable({"key","value"}, {name:"DictEntry"})
Types.is_node = (n)->
type(n) == 'userdata' and n.type
NOMSU_DEFS = with {} NOMSU_DEFS = with {}
-- Newline supports either windows-style CR+LF or unix-style LF -- Newline supports either windows-style CR+LF or unix-style LF
@ -218,7 +211,6 @@ class NomsuCompiler
for k,v in pairs(Types) do @environment[k] = v for k,v in pairs(Types) do @environment[k] = v
@environment.Tuple = Tuple @environment.Tuple = Tuple
@environment.Lua = Lua @environment.Lua = Lua
@environment.LuaValue = LuaValue
@environment.Location = Location @environment.Location = Location
@environment.ACTIONS = setmetatable({}, {__index:(key)=> @environment.ACTIONS = setmetatable({}, {__index:(key)=>
error("Attempt to run undefined action: #{key}", 0) error("Attempt to run undefined action: #{key}", 0)
@ -305,7 +297,8 @@ class NomsuCompiler
tree = @parse(nomsu_code, filename) tree = @parse(nomsu_code, filename)
assert tree, "Failed to parse: #{nomsu_code}" assert tree, "Failed to parse: #{nomsu_code}"
assert tree.type == "File", "Attempt to run non-file: #{tree.type}" assert tree.type == "File", "Attempt to run non-file: #{tree.type}"
lua = @tree_to_lua(tree)\as_statements! lua = @tree_to_lua(tree)
lua\convert_to_statements!
lua\declare_locals! lua\declare_locals!
lua\prepend "-- File: #{filename}\n" lua\prepend "-- File: #{filename}\n"
return @run_lua(lua, filename..".lua") return @run_lua(lua, filename..".lua")
@ -404,7 +397,7 @@ class NomsuCompiler
if #tok.value > 1 then return nil if #tok.value > 1 then return nil
nomsu = inline_expression tok.value nomsu = inline_expression tok.value
return nomsu and "(: #{nomsu})" return nomsu and "(: #{nomsu})"
when "FunctionCall" when "Action"
buff = "" buff = ""
for i,bit in ipairs tok.value for i,bit in ipairs tok.value
if bit.type == "Word" if bit.type == "Word"
@ -416,7 +409,7 @@ class NomsuCompiler
return nil unless nomsu return nil unless nomsu
unless i == 1 or bit.type == "Block" unless i == 1 or bit.type == "Block"
buff ..= " " buff ..= " "
buff ..= if bit.type == "FunctionCall" buff ..= if bit.type == "Action"
"("..nomsu..")" "("..nomsu..")"
else nomsu else nomsu
return buff return buff
@ -441,7 +434,7 @@ class NomsuCompiler
bit.key.value bit.key.value
else inline_expression bit.key else inline_expression bit.key
return nil unless key_nomsu return nil unless key_nomsu
if bit.key.type == "FunctionCall" if bit.key.type == "Action"
key_nomsu = "("..key_nomsu..")" key_nomsu = "("..key_nomsu..")"
value_nomsu = inline_expression bit.value value_nomsu = inline_expression bit.value
return nil unless value_nomsu return nil unless value_nomsu
@ -482,7 +475,7 @@ class NomsuCompiler
return nil unless nomsu return nil unless nomsu
buff ..= "\n "..@indent(nomsu) buff ..= "\n "..@indent(nomsu)
return buff return buff
when "FunctionCall" when "Action"
nomsu = expression(tok) nomsu = expression(tok)
return nil unless nomsu return nil unless nomsu
return "(..)\n "..@indent(nomsu) return "(..)\n "..@indent(nomsu)
@ -515,7 +508,7 @@ class NomsuCompiler
for bit in *tok.value for bit in *tok.value
key_nomsu = inline_expression bit.key key_nomsu = inline_expression bit.key
return nil unless key_nomsu return nil unless key_nomsu
if bit.key.type == "FunctionCall" if bit.key.type == "Action"
key_nomsu = "("..key_nomsu..")" key_nomsu = "("..key_nomsu..")"
value_nomsu = inline_expression bit.value value_nomsu = inline_expression bit.value
if value_nomsu and #key_nomsu + #value_nomsu < max_line if value_nomsu and #key_nomsu + #value_nomsu < max_line
@ -560,14 +553,14 @@ class NomsuCompiler
switch tok.type switch tok.type
when "Block" when "Block"
if #tok.value == 1 if #tok.value == 1
nomsu = if tok.value[1].type == "FunctionCall" nomsu = if tok.value[1].type == "Action"
inline_expression(tok.value[1]) inline_expression(tok.value[1])
else else
noeol_expression(tok.value[1]) noeol_expression(tok.value[1])
if nomsu and #(nomsu\match("[^\n]*")) < max_line if nomsu and #(nomsu\match("[^\n]*")) < max_line
return ": "..nomsu return ": "..nomsu
return noeol_expression(tok) return noeol_expression(tok)
when "FunctionCall" when "Action"
-- The hard task -- The hard task
buff = "" buff = ""
for i,bit in ipairs tok.value for i,bit in ipairs tok.value
@ -579,12 +572,12 @@ class NomsuCompiler
else else
nomsu = inline_expression(bit) nomsu = inline_expression(bit)
if nomsu and #nomsu < max_line if nomsu and #nomsu < max_line
if bit.type == "FunctionCall" if bit.type == "Action"
nomsu = "("..nomsu..")" nomsu = "("..nomsu..")"
else else
nomsu = expression(bit) nomsu = expression(bit)
return nil unless nomsu return nil unless nomsu
if bit.type == "FunctionCall" if bit.type == "Action"
nomsu = "(..)\n "..@indent(nomsu) nomsu = "(..)\n "..@indent(nomsu)
if i < #tok.value if i < #tok.value
nomsu ..= "\n.." nomsu ..= "\n.."
@ -636,238 +629,14 @@ class NomsuCompiler
else else
error("Unsupported value_to_nomsu type: #{type(value)}", 0) error("Unsupported value_to_nomsu type: #{type(value)}", 0)
@math_patt: re.compile [[ "%" (" " [*/^+-] " %")+ ]]
tree_to_lua: (tree)=> tree_to_lua: (tree)=>
-- Return <lua code for value>, <additional lua code> return tree\as_lua(self)
assert tree, "No tree provided."
if not Types.is_node(tree)
--error("Invalid tree: #{repr(tree)}", 0)
error("Invalid tree")
switch tree.type
when "File"
if #tree.value == 1
return @tree_to_lua(tree.value[1])
file_lua = Lua(tree.source)
declared_locals = {}
for i, line in ipairs tree.value
line_lua = @tree_to_lua line
if not line_lua
error("No lua produced by #{repr line}", 0)
line_lua = line_lua\as_statements!
if i < #tree.value
file_lua\append "\n"
file_lua\append line_lua
file_lua\declare_locals!
return file_lua
when "Comment"
return Lua(tree.source, "--"..tree.value\gsub("\n","\n--").."\n")
when "Nomsu"
--return Lua(tree.source, repr(tree.value))
return Lua(tree.source, "nomsu:parse(",tree.source\get_text!,", ",repr(tree.source.filename),")")
when "Block"
block_lua = Lua(tree.source)
for i,arg in ipairs tree.value
lua = @tree_to_lua arg
if i == 1 and #tree.value == 1 and lua.is_value
return lua
lua = lua\as_statements!
if i < #tree.value
block_lua\append "\n"
block_lua\append lua
return block_lua
when "FunctionCall"
insert @compilestack, tree
stub = @tree_to_stub tree
action = rawget(@environment.ACTIONS, stub)
metadata = @action_metadata[action]
if metadata and metadata.compile_time
args = [arg for arg in *tree.value when arg.type != "Word"]
-- Force all compile-time actions to take a tree location
if metadata and metadata.arg_orders
new_args = [args[p-1] for p in *metadata.arg_orders[stub]]
args = new_args
lua = action(tree, unpack(args))
remove @compilestack
return lua
elseif not metadata and @@math_patt\match(stub)
-- This is a bit of a hack, but this code handles arbitrarily complex
-- math expressions like 2*x + 3^2 without having to define a single
-- action for every possibility.
lua = LuaValue(tree.source)
for i,tok in ipairs tree.value
if tok.type == "Word"
lua\append tok.value
else
tok_lua = @tree_to_lua(tok)
unless tok_lua.is_value
src = tok.source\get_text!
error("non-expression value inside math expression: #{colored.yellow src}")
lua\append tok_lua
if i < #tree.value
lua\append " "
remove @compilestack
lua\parenthesize!
return lua
args = {}
for i, tok in ipairs tree.value
if tok.type == "Word" then continue
arg_lua = @tree_to_lua(tok)
unless arg_lua.is_value
line, src = tok.source\get_line!, tok.source\get_text!
error "#{line}: Cannot use:\n#{colored.yellow src}\nas an argument to #{stub}, since it's not an expression, it produces: #{repr arg_lua}", 0
insert args, arg_lua
if metadata and metadata.arg_orders
args = [args[p] for p in *metadata.arg_orders[stub]]
lua = LuaValue(tree.source, "ACTIONS[#{repr stub}](")
for i, arg in ipairs args
lua\append arg
if i < #args then lua\append ", "
lua\append ")"
remove @compilestack
return lua
when "Text"
lua = LuaValue(tree.source)
string_buffer = ""
for bit in *tree.value
if type(bit) == "string"
string_buffer ..= bit
continue
if string_buffer ~= ""
if #lua.bits > 0 then lua\append ".."
lua\append repr(string_buffer)
string_buffer = ""
bit_lua = @tree_to_lua bit
unless bit_lua.is_value
line, src = bit.source\get_line!, bit.source\get_text!
error "#{line}: Cannot use #{colored.yellow bit} as a string interpolation value, since it's not an expression.", 0
if #lua.bits > 0 then lua\append ".."
if bit.type != "Text"
bit_lua = LuaValue(bit.source, "stringify(",bit_lua,")")
lua\append bit_lua
if string_buffer ~= "" or #lua.bits == 0
if #lua.bits > 0 then lua\append ".."
lua\append repr(string_buffer)
if #lua.bits > 1
lua\parenthesize!
return lua
when "IndexChain"
lua = @tree_to_lua tree.value[1]
unless lua.is_value
line, src = tree.value[1].source\get_line!, tree.value[1].source\get_text!
error "#{line}: Cannot index #{colored.yellow src}, since it's not an expression.", 0
last_char = tostring(lua)\sub(1,1)
if last_char == "}" or last_char == '"'
lua\parenthesize!
for i=2,#tree.value
key = tree.value[i]
if key.type == 'Text' and #key.value == 1 and type(key.value[1]) == 'string' and key.value[1]\match("^[a-zA-Z_][a-zA-Z0-9_]$")
lua\append ".#{key.value[1]}"
continue
key_lua = @tree_to_lua key
unless key_lua.is_value
line, src = key.source\get_line!, key.source\get_text!
error "#{line}: Cannot use #{colored.yellow src} as an index, since it's not an expression.", 0
-- NOTE: this *must* use a space after the [ to avoid freaking out
-- Lua's parser if the inner expression is a long string. Lua
-- parses x[[[y]]] as x("[y]"), not as x["y"]
if tostring(key_lua)\sub(1,1) == '['
lua\append "[ ",key_lua,"]"
else
lua\append "[",key_lua,"]"
return lua
when "List"
lua = LuaValue tree.source, "{"
line_length = 0
for i, item in ipairs tree.value
item_lua = @tree_to_lua item
unless item_lua.is_value
line, src = item.source\get_line!, item.source\get_text!
error "#{line}: Cannot use #{colored.yellow src} as a list item, since it's not an expression.", 0
lua\append item_lua
newlines, last_line = tostring(item_lua)\match("^(.-)([^\n]*)$")
if #newlines > 0
line_length = #last_line
else
line_length += #last_line
if i < #tree.value
if line_length >= 80
lua\append ",\n"
line_length = 0
else
lua\append ", "
line_length += 2
lua\append "}"
return lua
when "Dict"
lua = LuaValue tree.source, "{"
line_length = 0
for i, entry in ipairs tree.value
key_lua = @tree_to_lua entry.key
unless key_lua.is_value
line, src = key.source\get_line!, key.source\get_text!
error "#{line}: Cannot use #{colored.yellow src} as a dict key, since it's not an expression.", 0
value_lua = @tree_to_lua entry.value
unless value_lua.is_value
line, src = value.source\get_line!, value.source\get_text!
error "#{line}: Cannot use #{colored.yellow src} as a dict value, since it's not an expression.", 0
key_str = tostring(key_lua)\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str
lua\append key_str,"=",value_lua
elseif tostring(key_lua)\sub(1,1) == "["
-- NOTE: this *must* use a space after the [ to avoid freaking out
-- Lua's parser if the inner expression is a long string. Lua
-- parses x[[[y]]] as x("[y]"), not as x["y"]
lua\append "[ ",key_lua,"]=",value_lua
else
lua\append "[",key_lua,"]=",value_lua
-- TODO: maybe make this more accurate? It's only a heuristic, so eh...
newlines, last_line = ("[#{key_lua}=#{value_lua}")\match("^(.-)([^\n]*)$")
if #newlines > 0
line_length = #last_line
else
line_length += #last_line
if i < #tree.value
if line_length >= 80
lua\append ",\n"
line_length = 0
else
lua\append ", "
line_length += 2
lua\append "}"
return lua
when "Number"
return LuaValue(tree.source, tostring(tree.value))
when "Var"
return LuaValue(tree.source, @var_to_lua_identifier(tree.value))
else
error("Unknown/unimplemented thingy: #{tree.type}", 0)
walk_tree: (tree, depth=0)=> walk_tree: (tree, depth=0)=>
coroutine.yield(tree, depth) coroutine.yield(tree, depth)
return unless Types.is_node(tree) return unless Types.is_node(tree)
switch tree.type switch tree.type
when "List", "File", "Block", "FunctionCall", "Text", "IndexChain" when "List", "File", "Block", "Action", "Text", "IndexChain"
for v in *tree.value for v in *tree.value
@walk_tree(v, depth+1) @walk_tree(v, depth+1)
when "Dict" when "Dict"
@ -919,7 +688,7 @@ class NomsuCompiler
if replacement != nil if replacement != nil
return replacement return replacement
switch tree.type switch tree.type
when "File", "Nomsu", "Block", "List", "FunctionCall", "Text", "IndexChain" when "File", "Nomsu", "Block", "List", "Action", "Text", "IndexChain"
new_values, is_changed = {}, false new_values, is_changed = {}, false
for i,old_value in ipairs(tree.value) for i,old_value in ipairs(tree.value)
new_value = type(old_value) != "string" and @tree_map(old_value, fn) or nil new_value = type(old_value) != "string" and @tree_map(old_value, fn) or nil
@ -956,11 +725,11 @@ class NomsuCompiler
return replacements[id] return replacements[id]
tree_to_stub: (tree)=> tree_to_stub: (tree)=>
if tree.type != "FunctionCall" then error "Tried to get stub from non-functioncall tree: #{tree.type}", 0 if tree.type != "Action" then error "Tried to get stub from non-functioncall tree: #{tree.type}", 0
return concat([(t.type == "Word" and t.value or "%") for t in *tree.value], " ") return concat([(t.type == "Word" and t.value or "%") for t in *tree.value], " ")
tree_to_named_stub: (tree)=> tree_to_named_stub: (tree)=>
if tree.type != "FunctionCall" then error "Tried to get stub from non-functioncall tree: #{tree.type}", 0 if tree.type != "Action" then error "Tried to get stub from non-functioncall tree: #{tree.type}", 0
return concat([(t.type == "Word" and t.value or "%#{t.value}") for t in *tree.value], " ") return concat([(t.type == "Word" and t.value or "%#{t.value}") for t in *tree.value], " ")
stub_defs = { stub_defs = {
@ -1009,16 +778,17 @@ class NomsuCompiler
get_line_no = -> "nomsu.moon:#{debug_getinfo(2).currentline}" get_line_no = -> "nomsu.moon:#{debug_getinfo(2).currentline}"
nomsu = self nomsu = self
@define_compile_action "immediately %block", get_line_no!, (_block)=> @define_compile_action "immediately %block", get_line_no!, (_block)=>
lua = nomsu\tree_to_lua(_block)\as_statements! lua = nomsu\tree_to_lua(_block)
lua\convert_to_statements!
lua\declare_locals! lua\declare_locals!
nomsu\run_lua(lua) nomsu\run_lua(lua)
return Lua(@source, "if IMMEDIATE then\n", lua, "\nend") return Lua(@source, "if IMMEDIATE then\n", lua, "\nend")
@define_compile_action "Lua %code", get_line_no!, (_code)=> @define_compile_action "Lua %source %code", get_line_no!, (_source, _code)=>
if _code.type != "Text" if _code.type != "Text"
return LuaValue(@source, "Lua(", repr(_code.source),", ",nomsu\tree_to_lua(_code),")") return Lua.Value(_source, "Lua(", repr(_code.source),", ",nomsu\tree_to_lua(_code),")")
lua = LuaValue(@source, "Lua(", repr(_code.source)) lua = Lua.Value(_source, "Lua(", repr(_code.source))
for bit in *_code.value for bit in *_code.value
lua\append ", " lua\append ", "
if type(bit) == "string" if type(bit) == "string"
@ -1032,11 +802,11 @@ class NomsuCompiler
lua\append ")" lua\append ")"
return lua return lua
@define_compile_action "LuaValue %code", get_line_no!, (_code)=> @define_compile_action "Lua value %source %code", get_line_no!, (_source, _code)=>
if _code.type != "Text" if _code.type != "Text"
return LuaValue(@source, "LuaValue(", repr(_code.source),", ",nomsu\tree_to_lua(_code),")") return Lua.Value(_source, "Lua.Value(", repr(_code.source),", ",nomsu\tree_to_lua(_code),")")
lua = LuaValue(@source, "LuaValue(", repr(_code.source)) lua = Lua.Value(_source, "Lua.Value(", repr(_code.source))
for bit in *_code.value for bit in *_code.value
lua\append ", " lua\append ", "
if type(bit) == "string" if type(bit) == "string"
@ -1052,7 +822,7 @@ class NomsuCompiler
@define_compile_action "lua> %code", get_line_no!, (_code)=> @define_compile_action "lua> %code", get_line_no!, (_code)=>
if _code.type != "Text" if _code.type != "Text"
return LuaValue(@source, "nomsu:run_lua(",nomsu\tree_to_lua(_code),")") return Lua.Value(@source, "nomsu:run_lua(",nomsu\tree_to_lua(_code),")")
lua = Lua(_code.source) lua = Lua(_code.source)
for bit in *_code.value for bit in *_code.value
@ -1068,9 +838,9 @@ class NomsuCompiler
@define_compile_action "=lua %code", get_line_no!, (_code)=> @define_compile_action "=lua %code", get_line_no!, (_code)=>
if _code.type != "Text" if _code.type != "Text"
return LuaValue(@source, "nomsu:run_lua(",nomsu\tree_to_lua(_code),")") return Lua.Value(@source, "nomsu:run_lua(",nomsu\tree_to_lua(_code),")")
lua = LuaValue(@source) lua = Lua.Value(@source)
for bit in *_code.value for bit in *_code.value
if type(bit) == "string" if type(bit) == "string"
lua\append bit lua\append bit
@ -1083,7 +853,7 @@ class NomsuCompiler
return lua return lua
@define_compile_action "!! code location !!", get_line_no!, => @define_compile_action "!! code location !!", get_line_no!, =>
return LuaValue(@source, repr(@source)) return Lua.Value(@source, repr(tostring(@source)))
@define_action "run file %filename", get_line_no!, (_filename)-> @define_action "run file %filename", get_line_no!, (_filename)->
return nomsu\run_file(_filename) return nomsu\run_file(_filename)
@ -1091,7 +861,7 @@ class NomsuCompiler
@define_compile_action "use %filename", get_line_no!, (_filename)=> @define_compile_action "use %filename", get_line_no!, (_filename)=>
filename = nomsu\tree_to_value(_filename) filename = nomsu\tree_to_value(_filename)
nomsu\use_file(filename) nomsu\use_file(filename)
return LuaValue(@source, "nomsu:use_file(#{repr filename})") return Lua.Value(@source, "nomsu:use_file(#{repr filename})")
-- Only run this code if this file was run directly with command line arguments, and not require()'d: -- Only run this code if this file was run directly with command line arguments, and not require()'d:
if arg and debug_getinfo(2).func != require if arg and debug_getinfo(2).func != require

View File

@ -7,7 +7,7 @@ file (File):
shebang: "#!" [^%nl]* %nl shebang: "#!" [^%nl]* %nl
statement: functioncall / expression statement: action / expression
indented_block (Block): indented_block (Block):
{| (":" / "(..)")? indent {| (":" / "(..)")? indent
@ -17,27 +17,27 @@ indented_block (Block):
inline_nomsu (Nomsu): "\" noindex_inline_expression inline_nomsu (Nomsu): "\" noindex_inline_expression
indented_nomsu (Nomsu): indented_nomsu (Nomsu):
"\" (noindex_inline_expression / (":" %ws* (inline_functioncall / inline_expression) eol) / indented_expression) "\" (noindex_inline_expression / (":" %ws* (inline_action / inline_expression) eol) / indented_expression)
index_chain (IndexChain): index_chain (IndexChain):
{| noindex_inline_expression ("." ((({} ({|{%operator / (!number plain_word)}|} -> Tuple) {}) -> Text) / noindex_inline_expression))+ |} -> Tuple {| noindex_inline_expression ("." ((({} ({|{%operator / (!number plain_word)}|} -> Tuple) {}) -> Text) / noindex_inline_expression))+ |} -> Tuple
noindex_inline_expression: noindex_inline_expression:
number / variable / inline_text / inline_list / inline_dict / inline_nomsu number / variable / inline_text / inline_list / inline_dict / inline_nomsu
/ ("(" %ws* (inline_functioncall / inline_expression) %ws* ")") / ("(" %ws* (inline_action / inline_expression) %ws* ")")
inline_expression: inline_expression:
index_chain / noindex_inline_expression index_chain / noindex_inline_expression
indented_expression: indented_expression:
indented_text / indented_nomsu / indented_list / indented_dict / indented_block indented_text / indented_nomsu / indented_list / indented_dict / indented_block
expression: expression:
inline_expression / (":" %ws* (inline_functioncall / inline_expression) eol) / indented_expression inline_expression / (":" %ws* (inline_action / inline_expression) eol) / indented_expression
-- Function calls need at least one word in them -- Function calls need at least one word in them
inline_functioncall (FunctionCall): inline_action (Action):
{| (inline_expression %ws*)* word (%ws* (inline_expression / word))* {| (inline_expression %ws*)* word (%ws* (inline_expression / word))*
(%ws* ":" %ws* (inline_functioncall / inline_expression))?|} -> Tuple (%ws* ":" %ws* (inline_action / inline_expression))?|} -> Tuple
functioncall (FunctionCall): action (Action):
{| (expression (dotdot / %ws*))* word ((dotdot / %ws*) (expression / word))* |} -> Tuple {| (expression (dotdot / %ws*))* word ((dotdot / %ws*) (expression / word))* |} -> Tuple
word (Word): { %operator / (!number plain_word) } word (Word): { %operator / (!number plain_word) }
@ -60,13 +60,13 @@ indented_text (Text):
inline_text_interpolation: inline_text_interpolation:
"\" ( "\" (
variable / inline_list / inline_dict / inline_text variable / inline_list / inline_dict / inline_text
/ ("(" %ws* (inline_functioncall / inline_expression) %ws* ")") / ("(" %ws* (inline_action / inline_expression) %ws* ")")
) )
text_interpolation: text_interpolation:
inline_text_interpolation / inline_text_interpolation /
("\" ( ("\" (
variable / inline_list / inline_dict / inline_text variable / inline_list / inline_dict / inline_text
/ ("(" %ws* (inline_functioncall / inline_expression) %ws* ")") / ("(" %ws* (inline_action / inline_expression) %ws* ")")
/ (%ws* (block_comment / line_comment)? nodent "..") / (%ws* (block_comment / line_comment)? nodent "..")
/ (indented_text %nl+ %nodent "..") / (indented_text %nl+ %nodent "..")
/ ((indented_list / indented_block) nodent "..") / ((indented_list / indented_block) nodent "..")
@ -87,9 +87,9 @@ indented_list (List):
|} -> Tuple) |} -> Tuple)
(dedent / (("" -> "Error while parsing list") => error)) (dedent / (("" -> "Error while parsing list") => error))
list_line: list_line:
((functioncall / expression) !comma) ((action / expression) !comma)
/ (inline_list_item (comma list_line?)?) / (inline_list_item (comma list_line?)?)
inline_list_item: inline_functioncall / inline_expression inline_list_item: inline_action / inline_expression
inline_dict (Dict): inline_dict (Dict):
!('{..}') !('{..}')
@ -100,10 +100,10 @@ indented_dict (Dict):
|} -> Tuple) |} -> Tuple)
(dedent / (("" -> "Error while parsing dict") => error)) (dedent / (("" -> "Error while parsing dict") => error))
dict_line: dict_line:
((dict_key %ws* ":" %ws* (functioncall / expression)) -> DictEntry !comma) ((dict_key %ws* ":" %ws* (action / expression)) -> DictEntry !comma)
/ (inline_dict_item (comma dict_line?)?) / (inline_dict_item (comma dict_line?)?)
inline_dict_item: inline_dict_item:
(dict_key %ws* ":" %ws* (inline_functioncall / inline_expression)) -> DictEntry (dict_key %ws* ":" %ws* (inline_action / inline_expression)) -> DictEntry
dict_key: dict_key:
(({} ({|{%operator / (!number plain_word)}|} -> Tuple) {}) -> Text) / inline_expression (({} ({|{%operator / (!number plain_word)}|} -> Tuple) {}) -> Text) / inline_expression

405
nomsu_tree.lua Normal file
View File

@ -0,0 +1,405 @@
local utils = require('utils')
local re = require('re')
local repr, stringify, min, max, equivalent, set, is_list, sum
repr, stringify, min, max, equivalent, set, is_list, sum = utils.repr, utils.stringify, utils.min, utils.max, utils.equivalent, utils.set, utils.is_list, utils.sum
local immutable = require('immutable')
local insert, remove, concat
do
local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end
local Lua, Location
do
local _obj_0 = require("lua_obj")
Lua, Location = _obj_0.Lua, _obj_0.Location
end
local common_methods = {
__tostring = function(self)
return tostring(self.name) .. "(" .. tostring(repr(self.value)) .. ")"
end,
with_value = function(self, value)
return getmetatable(self)(value, self.source)
end
}
local fields = {
"value",
"source"
}
local Types = { }
Types.DictEntry = immutable({
"key",
"value"
}, {
name = "DictEntry"
})
Types.is_node = function(n)
return type(n) == 'userdata' and getmetatable(n) and Types[n.type] == getmetatable(n)
end
local Tree
Tree = function(name, methods)
do
methods.__tostring = function(self)
return tostring(self.name) .. "(" .. tostring(repr(self.value)) .. ")"
end
methods.with_value = function(self, value)
return getmetatable(self)(value, self.source)
end
methods.type = name
methods.name = name
end
Types[name] = immutable({
"value",
"source"
}, methods)
end
Tree("File", {
as_lua = function(self, nomsu)
if #self.value == 1 then
return self.value[1]:as_lua(nomsu)
end
local declared_locals = { }
local lua = Lua(self.source)
for i, line in ipairs(self.value) do
local line_lua = line:as_lua(nomsu)
if not line_lua then
error("No lua produced by " .. tostring(repr(line)), 0)
end
if i < #self.value then
lua:append("\n")
end
lua:convert_to_statements()
lua:append(line_lua)
end
lua:declare_locals()
return lua
end
})
Tree("Nomsu", {
as_lua = function(self, nomsu)
return Lua.Value(self.source, "nomsu:parse(", repr(self.source:get_text()), ", ", repr(self.source.filename), ")")
end
})
Tree("Block", {
as_lua = function(self, nomsu)
if #self.value == 1 then
return self.value[1]:as_lua(nomsu)
end
local lua = Lua(self.source)
for i, line in ipairs(self.value) do
local line_lua = line:as_lua(nomsu)
if i < #self.value then
lua:append("\n")
end
line_lua:convert_to_statements()
lua:append(line_lua)
end
return lua
end
})
local math_patt = re.compile([[ "%" (" " [*/^+-] " %")+ ]])
Tree("Action", {
as_lua = function(self, nomsu)
local stub = self:get_stub()
local action = rawget(nomsu.environment.ACTIONS, stub)
local metadata = nomsu.action_metadata[action]
if metadata and metadata.compile_time then
local args
do
local _accum_0 = { }
local _len_0 = 1
local _list_0 = self.value
for _index_0 = 1, #_list_0 do
local arg = _list_0[_index_0]
if arg.type ~= "Word" then
_accum_0[_len_0] = arg
_len_0 = _len_0 + 1
end
end
args = _accum_0
end
if metadata.arg_orders then
local new_args
do
local _accum_0 = { }
local _len_0 = 1
local _list_0 = metadata.arg_orders[stub]
for _index_0 = 1, #_list_0 do
local p = _list_0[_index_0]
_accum_0[_len_0] = args[p - 1]
_len_0 = _len_0 + 1
end
new_args = _accum_0
end
args = new_args
end
return action(Lua(self.source), unpack(args))
end
local lua = Lua.Value(self.source)
if not metadata and math_patt:match(stub) then
for i, tok in ipairs(self.value) do
if tok.type == "Word" then
lua:append(tok.value)
else
local tok_lua = tok:as_lua(nomsu)
if not (tok_lua.is_value) then
local src = tok.source:get_text()
error("non-expression value inside math expression: " .. tostring(colored.yellow(src)))
end
lua:append(tok_lua)
end
if i < #self.value then
lua:append(" ")
end
end
lua:parenthesize()
return lua
end
local args = { }
for i, tok in ipairs(self.value) do
local _continue_0 = false
repeat
if tok.type == "Word" then
_continue_0 = true
break
end
local arg_lua = tok:as_lua(nomsu)
if not (arg_lua.is_value) then
local line, src = tok.source:get_line(), tok.source:get_text()
error(tostring(line) .. ": Cannot use:\n" .. tostring(colored.yellow(src)) .. "\nas an argument to " .. tostring(stub) .. ", since it's not an expression, it produces: " .. tostring(repr(arg_lua)), 0)
end
insert(args, arg_lua)
_continue_0 = true
until true
if not _continue_0 then
break
end
end
if metadata and metadata.arg_orders then
do
local _accum_0 = { }
local _len_0 = 1
local _list_0 = metadata.arg_orders[stub]
for _index_0 = 1, #_list_0 do
local p = _list_0[_index_0]
_accum_0[_len_0] = args[p]
_len_0 = _len_0 + 1
end
args = _accum_0
end
end
lua:append("ACTIONS[", repr(stub), "(")
for i, arg in ipairs(args) do
lua:append(arg)
if i < #args then
lua:append(", ")
end
end
lua:append(")")
return lua
end,
get_stub = function(self)
return concat((function()
local _accum_0 = { }
local _len_0 = 1
local _list_0 = self.value
for _index_0 = 1, #_list_0 do
local t = _list_0[_index_0]
_accum_0[_len_0] = (t.type == "Word" and t.value or "%")
_len_0 = _len_0 + 1
end
return _accum_0
end)(), " ")
end
})
Tree("Text", {
as_lua = function(self, nomsu)
local lua = Lua.Value(self.source)
local string_buffer = ""
local _list_0 = self.value
for _index_0 = 1, #_list_0 do
local _continue_0 = false
repeat
local bit = _list_0[_index_0]
if type(bit) == "string" then
string_buffer = string_buffer .. bit
_continue_0 = true
break
end
if string_buffer ~= "" then
if #lua.bits > 0 then
lua:append("..")
end
lua:append(repr(string_buffer))
string_buffer = ""
end
local bit_lua = bit:as_lua(nomsu)
if not (bit_lua.is_value) then
local line, src = bit.source:get_line(), bit.source:get_text()
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(bit)) .. " as a string interpolation value, since it's not an expression.", 0)
end
if #lua.bits > 0 then
lua:append("..")
end
if bit.type ~= "Text" then
bit_lua = Lua.Value(bit.source, "stringify(", bit_lua, ")")
end
lua:append(bit_lua)
_continue_0 = true
until true
if not _continue_0 then
break
end
end
if string_buffer ~= "" or #lua.bits == 0 then
if #lua.bits > 0 then
lua:append("..")
end
lua:append(repr(string_buffer))
end
if #lua.bits > 1 then
lua:parenthesize()
end
return lua
end
})
Tree("List", {
as_lua = function(self, nomsu)
local lua = Lua.Value(self.source, "{")
local line_length = 0
for i, item in ipairs(self.value) do
local item_lua = item:as_lua(nomsu)
if not (item_lua.is_value) then
local line, src = item.source:get_line(), item.source:get_text()
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a list item, since it's not an expression.", 0)
end
lua:append(item_lua)
local newlines, last_line = tostring(item_lua):match("^(.-)([^\n]*)$")
if #newlines > 0 then
line_length = #last_line
else
line_length = line_length + #last_line
end
if i < #self.value then
if line_length >= 80 then
lua:append(",\n")
line_length = 0
else
lua:append(", ")
line_length = line_length + 2
end
end
end
lua:append("}")
return lua
end
})
Tree("Dict", {
as_lua = function(self, nomsu)
local lua = Lua.Value(self.source, "{")
local line_length = 0
for i, entry in ipairs(self.value) do
local key_lua = entry.key:as_lua(nomsu)
if not (key_lua.is_value) then
local line, src = key.source:get_line(), key.source:get_text()
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a dict key, since it's not an expression.", 0)
end
local value_lua = entry.value:as_lua(nomsu)
if not (value_lua.is_value) then
local line, src = value.source:get_line(), value.source:get_text()
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as a dict value, since it's not an expression.", 0)
end
local key_str = tostring(key_lua):match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str then
lua:append(key_str, "=", value_lua)
elseif tostring(key_lua):sub(1, 1) == "[" then
lua:append("[ ", key_lua, "]=", value_lua)
else
lua:append("[", key_lua, "]=", value_lua)
end
local newlines, last_line = ("[" .. tostring(key_lua) .. "=" .. tostring(value_lua)):match("^(.-)([^\n]*)$")
if #newlines > 0 then
line_length = #last_line
else
line_length = line_length + #last_line
end
if i < #self.value then
if line_length >= 80 then
lua:append(",\n")
line_length = 0
else
lua:append(", ")
line_length = line_length + 2
end
end
end
lua:append("}")
return lua
end
})
Tree("IndexChain", {
as_lua = function(self, nomsu)
local lua = self.value[1]:as_lua(nomsu)
if not (lua.is_value) then
local line, src = self.value[1].source:get_line(), self.value[1].source:get_text()
error(tostring(line) .. ": Cannot index " .. tostring(colored.yellow(src)) .. ", since it's not an expression.", 0)
end
local last_char = tostring(lua):sub(-1, -1)
if last_char == "}" or last_char == '"' or last_char == "]" then
lua:parenthesize()
end
for i = 2, #self.value do
local _continue_0 = false
repeat
local key = self.value[i]
if key.type == 'Text' and #key.value == 1 and type(key.value[1]) == 'string' and key.value[1]:match("^[a-zA-Z_][a-zA-Z0-9_]$") then
lua:append("." .. tostring(key.value[1]))
_continue_0 = true
break
end
local key_lua = self:tree_to_lua(key)
if not (key_lua.is_value) then
local line, src = key.source:get_line(), key.source:get_text()
error(tostring(line) .. ": Cannot use " .. tostring(colored.yellow(src)) .. " as an index, since it's not an expression.", 0)
end
if tostring(key_lua):sub(1, 1) == '[' then
lua:append("[ ", key_lua, "]")
else
lua:append("[", key_lua, "]")
end
_continue_0 = true
until true
if not _continue_0 then
break
end
end
return lua
end
})
Tree("Number", {
as_lua = function(self, nomsu)
return Lua.Value(self.source, tostring(self.value))
end
})
Tree("Var", {
as_lua = function(self, nomsu)
local lua_id = "_" .. (self.value:gsub("%W", function(verboten)
if verboten == "_" then
return "__"
else
return ("_%x"):format(verboten:byte())
end
end))
return Lua.Value(self.source, lua_id)
end
})
Tree("Word", {
as_lua = function(self, nomsu)
return error("Attempt to convert Word to lua")
end
})
Tree("Comment", {
as_lua = function(self, nomsu)
return Lua(self.source, "--" .. self.value:gsub("\n", "\n--") .. "\n")
end
})
return Types

269
nomsu_tree.moon Normal file
View File

@ -0,0 +1,269 @@
-- This file contains the datastructures used to represent parsed Nomsu syntax trees,
-- as well as the logic for converting them to Lua code.
utils = require 'utils'
re = require 're'
{:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils
immutable = require 'immutable'
{:insert, :remove, :concat} = table
{:Lua, :Location} = require "lua_obj"
common_methods = {
__tostring: =>
"#{@name}(#{repr(@value)})"
with_value: (value)=> getmetatable(self)(value, @source)
}
fields = {"value","source"}
Types = {}
Types.DictEntry = immutable({"key","value"}, {name:"DictEntry"})
Types.is_node = (n)->
type(n) == 'userdata' and getmetatable(n) and Types[n.type] == getmetatable(n)
-- Helper method:
Tree = (name, methods)->
with methods
.__tostring = => "#{@name}(#{repr(@value)})"
.with_value = (value)=> getmetatable(self)(value, @source)
.type = name
.name = name
Types[name] = immutable {"value","source"}, methods
Tree "File",
as_lua: (nomsu)=>
if #@value == 1
return @value[1]\as_lua(nomsu)
declared_locals = {}
lua = Lua(@source)
for i, line in ipairs @value
line_lua = line\as_lua(nomsu)
if not line_lua
error("No lua produced by #{repr line}", 0)
if i < #@value
lua\append "\n"
lua\convert_to_statements!
lua\append line_lua
lua\declare_locals!
return lua
Tree "Nomsu",
as_lua: (nomsu)=>
Lua.Value(@source, "nomsu:parse(",repr(@source\get_text!),", ",repr(@source.filename),")")
Tree "Block",
as_lua: (nomsu)=>
if #@value == 1
return @value[1]\as_lua(nomsu)
lua = Lua(@source)
for i,line in ipairs @value
line_lua = line\as_lua(nomsu)
if i < #@value
lua\append "\n"
line_lua\convert_to_statements!
lua\append line_lua
return lua
math_patt = re.compile [[ "%" (" " [*/^+-] " %")+ ]]
Tree "Action",
as_lua: (nomsu)=>
stub = @get_stub!
action = rawget(nomsu.environment.ACTIONS, stub)
metadata = nomsu.action_metadata[action]
if metadata and metadata.compile_time
args = [arg for arg in *@value when arg.type != "Word"]
-- Force all compile-time actions to take a tree location
if metadata.arg_orders
new_args = [args[p-1] for p in *metadata.arg_orders[stub]]
args = new_args
return action(Lua(@source), unpack(args))
lua = Lua.Value(@source)
if not metadata and math_patt\match(stub)
-- This is a bit of a hack, but this code handles arbitrarily complex
-- math expressions like 2*x + 3^2 without having to define a single
-- action for every possibility.
for i,tok in ipairs @value
if tok.type == "Word"
lua\append tok.value
else
tok_lua = tok\as_lua(nomsu)
unless tok_lua.is_value
src = tok.source\get_text!
error("non-expression value inside math expression: #{colored.yellow src}")
lua\append tok_lua
if i < #@value
lua\append " "
lua\parenthesize!
return lua
args = {}
for i, tok in ipairs @value
if tok.type == "Word" then continue
arg_lua = tok\as_lua(nomsu)
unless arg_lua.is_value
line, src = tok.source\get_line!, tok.source\get_text!
error "#{line}: Cannot use:\n#{colored.yellow src}\nas an argument to #{stub}, since it's not an expression, it produces: #{repr arg_lua}", 0
insert args, arg_lua
if metadata and metadata.arg_orders
args = [args[p] for p in *metadata.arg_orders[stub]]
-- Not really worth bothering with ACTIONS.foo(...) style since almost every action
-- has arguments, so it won't work
lua\append "ACTIONS[",repr(stub),"]("
for i, arg in ipairs args
lua\append arg
if i < #args then lua\append ", "
lua\append ")"
return lua
get_stub: (include_names=false)=>
bits = if include_names
[(t.type == "Word" and t.value or "%#{t.value}") for t in *@value]
else [(t.type == "Word" and t.value or "%") for t in *@value]
return concat(bits, " ")
Tree "Text",
as_lua: (nomsu)=>
lua = Lua.Value(@source)
string_buffer = ""
for bit in *@value
if type(bit) == "string"
string_buffer ..= bit
continue
if string_buffer ~= ""
if #lua.bits > 0 then lua\append ".."
lua\append repr(string_buffer)
string_buffer = ""
bit_lua = bit\as_lua(nomsu)
unless bit_lua.is_value
line, src = bit.source\get_line!, bit.source\get_text!
error "#{line}: Cannot use #{colored.yellow bit} as a string interpolation value, since it's not an expression.", 0
if #lua.bits > 0 then lua\append ".."
if bit.type != "Text"
bit_lua = Lua.Value(bit.source, "stringify(",bit_lua,")")
lua\append bit_lua
if string_buffer ~= "" or #lua.bits == 0
if #lua.bits > 0 then lua\append ".."
lua\append repr(string_buffer)
if #lua.bits > 1
lua\parenthesize!
return lua
Tree "List",
as_lua: (nomsu)=>
lua = Lua.Value @source, "{"
line_length = 0
for i, item in ipairs @value
item_lua = item\as_lua(nomsu)
unless item_lua.is_value
line, src = item.source\get_line!, item.source\get_text!
error "#{line}: Cannot use #{colored.yellow src} as a list item, since it's not an expression.", 0
lua\append item_lua
newlines, last_line = tostring(item_lua)\match("^(.-)([^\n]*)$")
if #newlines > 0
line_length = #last_line
else
line_length += #last_line
if i < #@value
if line_length >= 80
lua\append ",\n"
line_length = 0
else
lua\append ", "
line_length += 2
lua\append "}"
return lua
Tree "Dict",
as_lua: (nomsu)=>
lua = Lua.Value @source, "{"
line_length = 0
for i, entry in ipairs @value
key_lua = entry.key\as_lua(nomsu)
unless key_lua.is_value
line, src = key.source\get_line!, key.source\get_text!
error "#{line}: Cannot use #{colored.yellow src} as a dict key, since it's not an expression.", 0
value_lua = entry.value\as_lua(nomsu)
unless value_lua.is_value
line, src = value.source\get_line!, value.source\get_text!
error "#{line}: Cannot use #{colored.yellow src} as a dict value, since it's not an expression.", 0
key_str = tostring(key_lua)\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str
lua\append key_str,"=",value_lua
elseif tostring(key_lua)\sub(1,1) == "["
-- NOTE: this *must* use a space after the [ to avoid freaking out
-- Lua's parser if the inner expression is a long string. Lua
-- parses x[[[y]]] as x("[y]"), not as x["y"]
lua\append "[ ",key_lua,"]=",value_lua
else
lua\append "[",key_lua,"]=",value_lua
-- TODO: maybe make this more accurate? It's only a heuristic, so eh...
newlines, last_line = ("[#{key_lua}=#{value_lua}")\match("^(.-)([^\n]*)$")
if #newlines > 0
line_length = #last_line
else
line_length += #last_line
if i < #@value
if line_length >= 80
lua\append ",\n"
line_length = 0
else
lua\append ", "
line_length += 2
lua\append "}"
return lua
Tree "IndexChain",
as_lua: (nomsu)=>
lua = @value[1]\as_lua(nomsu)
unless lua.is_value
line, src = @value[1].source\get_line!, @value[1].source\get_text!
error "#{line}: Cannot index #{colored.yellow src}, since it's not an expression.", 0
last_char = tostring(lua)\sub(-1,-1)
if last_char == "}" or last_char == '"' or last_char == "]"
lua\parenthesize!
for i=2,#@value
key = @value[i]
if key.type == 'Text' and #key.value == 1 and type(key.value[1]) == 'string' and key.value[1]\match("^[a-zA-Z_][a-zA-Z0-9_]$")
lua\append ".#{key.value[1]}"
continue
key_lua = key\as_lua(nomsu)
unless key_lua.is_value
line, src = key.source\get_line!, key.source\get_text!
error "#{line}: Cannot use #{colored.yellow src} as an index, since it's not an expression.", 0
-- NOTE: this *must* use a space after the [ to avoid freaking out
-- Lua's parser if the inner expression is a long string. Lua
-- parses x[[[y]]] as x("[y]"), not as x["y"]
if tostring(key_lua)\sub(1,1) == '['
lua\append "[ ",key_lua,"]"
else
lua\append "[",key_lua,"]"
return lua
Tree "Number",
as_lua: (nomsu)=>
Lua.Value(@source, tostring(@value))
Tree "Var",
as_lua: (nomsu)=>
lua_id = "_"..(@value\gsub "%W", (verboten)->
if verboten == "_" then "__" else ("_%x")\format(verboten\byte!))
Lua.Value(@source, lua_id)
Tree "Word",
as_lua: (nomsu)=>
error("Attempt to convert Word to lua")
Tree "Comment",
as_lua: (nomsu)=>
Lua(@source, "--"..@value\gsub("\n","\n--").."\n")
return Types