aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-04-17 14:18:23 -0700
committerBruce Hill <bitbucket@bruce-hill.com>2018-04-17 14:19:07 -0700
commit54fc7fc4404c02df2c38a7ae121e61e9b8bca78c (patch)
tree15142e627201978116985f56d05ab88bf237c59b
parent25e06d1fce660e7c8144425b440f7b1c698e2fb7 (diff)
Moving tree_to_lua into each of the Nomsu tree types, which are now in
their own file.
-rw-r--r--core/control_flow.nom192
-rw-r--r--core/metaprogramming.nom55
-rw-r--r--core/operators.nom54
-rw-r--r--lua_obj.lua153
-rw-r--r--lua_obj.moon87
-rw-r--r--nomsu.lua517
-rwxr-xr-xnomsu.moon296
-rw-r--r--nomsu.peg26
-rw-r--r--nomsu_tree.lua405
-rw-r--r--nomsu_tree.moon269
10 files changed, 1047 insertions, 1007 deletions
diff --git a/core/control_flow.nom b/core/control_flow.nom
index 3f6c59c..5a3a775 100644
--- a/core/control_flow.nom
+++ b/core/control_flow.nom
@@ -13,30 +13,19 @@ immediately:
# Conditionals
immediately:
compile [if %condition %if_body] to:
- %if_body <- (%if_body as lua)
- return {..}
- locals: %if_body.locals
- statements:".."
- if \(%condition as lua expr) then
- \(%if_body.statements or "\(%if_body.expr);")
- end
+ Lua ".."
+ if \(%condition as lua expr) then
+ \(%if_body as lua statements)
+ end
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:
- %if_body <- (%if_body as 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_body.statements or "\(%if_body.expr);")
- else
- \(%else_body.statements or "\(%else_body.expr);")
- end
+ Lua ".."
+ if \(%condition as lua expr) then
+ \(%if_body as lua statements)
+ else
+ \(%else_body as lua statements)
+ end
# Conditional expression (ternary operator)
#.. Note: this uses a function instead of "(condition and if_expr or else_expr)"
@@ -51,14 +40,15 @@ immediately
#.. 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)
if: %when_true_expr.type in {Text:yes, List:yes, Dict:yes, Number:yes}
- return {..}
- expr:"(\(%condition as lua expr) and \(%when_true_expr as lua expr) or \(%when_false_expr as lua expr))"
+ return:
+ Lua ".."
+ (\(%condition as lua expr) and \(%when_true_expr as lua expr) or \(%when_false_expr as lua expr))
..else:
#.. Otherwise, need to do an anonymous inline function (yuck, too bad lua
doesn't have a proper ternary operator!)
To see why this is necessary consider: (random()<.5 and false or 99)
- return {..}
- expr: ".."
+ return:
+ Lua ".."
(function()
if \(%condition as lua expr) then
return \(%when_true_expr as lua expr);
@@ -82,67 +72,59 @@ immediately:
# Helper function
immediately:
compile [if %tree has subtree %subtree where %condition %body] to:
- %body_lua <- (%body as 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
- if Types.is_node(\(%subtree as lua expr)) then
- if \(%condition as lua expr) then
- \%body_statements
- break;
- end
+ Lua ".."
+ 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 \(%condition as lua expr) then
+ \(%body as lua statements)
+ break;
end
end
+ end
# While loops
immediately:
compile [do next repeat] to {statements:"goto continue_repeat;"}
compile [stop repeating] to {statements:"goto stop_repeat;"}
compile [repeat while %condition %body] to
- %body_lua <- (%body as lua)
- %body_statements <- (%body_lua.statements or "\(%body_lua.expr);")
+ %lua <-: Lua "while \(%condition as lua expr) do"
if %body has subtree % where:
(%.type = "FunctionCall") and ((%'s stub) is "do next repeat")
- ..: %body_statments +<- "\n::continue_repeat::;"
- %code <- ".."
- while \(%condition as lua expr) do
- \%body_statements
- end --while-loop
+ ..:
+ %lua +<- "\n::continue_repeat::;"
+ %lua +<- "\n"
+ %lua +<- (%body as lua statements)
+ %lua +<- "\nend --while-loop"
if %body has subtree % where:
(%.type = "FunctionCall") and ((%'s stub) is "stop repeating")
..:
- %code <- ".."
+ %lua <- ".."
do -- scope of "stop repeating" label
- \%code
+ \%lua
::stop_repeat::;
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 until %condition %body] as: repeat while (not %condition) %body
compile [..]
repeat %n times %body
..to:
- %body_lua <- (%body as lua)
- %body_statements <- (%body_lua.statements or "\(%body_lua.expr);")
+ %lua <-: Lua "for i=1,\(%n as lua expr) do"
if %body has subtree % where
(%.type = "FunctionCall") and ((%'s stub) is "do next repeat")
- ..: %body_statements +<- "\n::continue_repeat::;"
- %code <- ".."
- for i=1,\(%n as lua expr) do
- \%body_statements
- end --numeric for-loop
+ ..: %lua +<- "\n::continue_repeat::;"
+ %lua +<- "\n\(%body as lua statements)\nend --numeric for-loop"
if %body has subtree % where:
(%.type = "FunctionCall") and ((%'s stub) is "stop repeating")
..:
- %code <- ".."
- do -- scope of "stop repeating" label
- \%code
- ::stop_repeat::;
- end -- end of "stop repeating" label scope
- return {statements:%code, locals:%body_lua.locals}
+ %lua <-:
+ Lua ".."
+ do -- scope of "stop repeating" label
+ \%lua
+ ::stop_repeat::;
+ end -- end of "stop repeating" label scope
+ return %lua
# For loop control flow:
immediately:
@@ -157,33 +139,30 @@ immediately:
for %var from %start to %stop by %step %body
for %var from %start to %stop via %step %body
..to:
- %body_lua <- (%body as lua)
- %body_statements <- (%body_lua.statements or "\(%body_lua.expr);")
+ # 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)"
+ %lua <-
+ Lua ".."
+ for \(%var as lua expr)=\(%start as lua expr),\(%n as lua expr) do
if %body has subtree % where:
(%.type = "FunctionCall") and
((%'s stub) is "do next %") and
%.value.3.value is %var.value
- ..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;"
-
- # 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
+ ..: %lua write code "\n::continue_\(%var as lua identifier)::;"
+ %lua write code "\n\(%body as lua statements)\nend --numeric for-loop"
if %body has subtree % where:
(%.type = "FunctionCall") and:
((%'s stub) is "stop %") and:
%.value.2.value is %var.value
..:
- %code <- ".."
+ %lua write code ".."
do -- scope for stopping for-loop
- \%code
+ \%lua
::stop_\(%var as lua identifier)::;
end -- end of scope for stopping for-loop
- return {statements:%code, locals:%body_lua.locals}
+ return %lua
immediately:
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()")
immediately:
compile [for %var in %iterable %body] to:
- %body_lua <- (%body as lua)
- %body_statements <- (%body_lua.statements or "\(%body_lua.expr);")
+ # 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)"
+ %lua <-: Lua "for i,\(%var as lua identifier) in ipairs(\(%iterable as lua expr)) do"
if %body has subtree % where:
(%.type = "FunctionCall") and
((%'s stub) is "do next %") and
%.value.3.value is %var.value
- ..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;"
- # 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 i,\(%var as lua expr) in ipairs(\(%iterable as lua expr)) do
- \%body_statements
- end --foreach-loop
+ ..: %lua +<- (Lua "\n::continue_\(%var as lua identifier)::;")
+ %lua +<- (Lua "\n\(%body as lua statements)\nend --foreach-loop")
if %body has subtree % where:
(%.type = "FunctionCall") and
((%'s stub) is "stop %") and
%.value.2.value is %var.value
..:
- %code <- ".."
- do -- scope for stopping for-loop
- \%code
- ::stop_\(%var as lua identifier)::;
- end -- end of scope for stopping for-loop
- return {statements:%code, locals:%body_lua.locals}
+ %lua <-
+ Lua ".."
+ do -- scope for stopping for-loop
+ \%lua
+ ::stop_\(%var as lua identifier)::;
+ end -- end of scope for stopping for-loop
+ return %lua
parse [for all %iterable %body] as: for % in %iterable %body
# Dict iteration (lua's "pairs()")
immediately:
compile [for %key = %value in %iterable %body] to:
- %body_lua <- (%body as lua)
- %body_statements <- (%body_lua.statements or "\(%body_lua.expr);")
+ # 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)"
+ %lua <- (Lua "for \(%key as lua identifier),\(%value as lua identifier) in pairs(\(%iterable as lua expr)) do")
if %body has subtree % where:
(%.type = "FunctionCall") and
((%'s stub) is "do next %") and
%.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:
(%.type = "FunctionCall") and
((%'s stub) is "do next %") and
%.value.3.value is %value.value
- ..: %body_statements +<- "\n::continue_\(%value as lua identifier)::;"
-
- # 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
+ ..: %lua +<- (Lua "\n::continue_\(%value as lua identifier)::;")
+ %lua +<- (Lua "\n\(%body as lua statements)\nend --foreach-loop")
%stop_labels <- ""
if %body has subtree % where:
@@ -262,11 +233,12 @@ immediately:
..: %stop_labels +<- "\n::stop_\(%value as lua identifier)::;"
if: %stop_labels is not ""
- %code <- ".."
- do -- scope for stopping for % = % loop
- \%code\%stop_labels
- end
- return {statements:%code, locals:%body_lua.locals}
+ %lua <-
+ Lua ".."
+ do -- scope for stopping for % = % loop
+ \%lua\%stop_labels
+ end
+ return %lua
# Switch statement/multi-branch if
immediately:
@@ -279,11 +251,11 @@ immediately:
for %func_call in %body.value:
assume (%func_call.type is "FunctionCall") or barf ".."
Invalid format for 'when' statement. Only '*' blocks are allowed.
- with [..]
- %tokens <- %func_call.value
- %star <- (1st in %tokens)
- %condition <- (2nd in %tokens)
- %action <- (3rd in %tokens)
+ with {..}
+ %tokens: %func_call.value
+ %star: %tokens.1
+ %condition: %tokens.2
+ %action: %tokens.3
..:
assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".."
Invalid format for 'when' statement. Lines must begin with '*'
@@ -334,7 +306,7 @@ immediately:
assume (%func_call.type is "FunctionCall") or barf ".."
Invalid format for 'when' statement. Only '*' blocks are allowed.
%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 ".."
Invalid format for 'when' statement. Lines must begin with '*'
assume %condition or barf ".."
diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom
index 8b1546b..9c7f166 100644
--- a/core/metaprogramming.nom
+++ b/core/metaprogramming.nom
@@ -9,7 +9,7 @@ immediately:
local lua = Lua(tree.source, "nomsu:define_compile_action(");
local stubs = {};
for i, action in ipairs(\%actions.value) do
- stubs[i] = nomsu:tree_to_named_stub(action);
+ stubs[i] = action:get_stub(true);
end
lua:append(repr(stubs), ", ", repr(tree.source:get_line()), ", function(tree");
local args = {};
@@ -23,7 +23,8 @@ immediately:
lua:append(", ");
lua:append(arg);
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);
lua:append(")\\n ", body_lua, "\\nend);")
return lua;
@@ -36,7 +37,7 @@ immediately:
local lua = Lua(tree.source, "nomsu:define_action(");
local stubs = {};
for i, action in ipairs(\%actions.value) do
- stubs[i] = nomsu:tree_to_named_stub(action);
+ stubs[i] = action:get_stub(true);
end
lua:append(repr(stubs), ", ", repr(tree.source:get_line()), ", function(");
local args = {};
@@ -47,7 +48,8 @@ immediately:
lua:append(arg);
if i < #args then lua:append(", ") 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);
lua:append(")\\n ", body_lua, "\\nend);")
return lua;
@@ -59,7 +61,7 @@ immediately:
local lua = Lua(tree.source, "nomsu:define_compile_action(");
local stubs = {};
for i, action in ipairs(\%shorthand.value) do
- stubs[i] = nomsu:tree_to_named_stub(action);
+ stubs[i] = action:get_stub(true);
end
lua:append(repr(stubs), ", ", repr(tree.source:get_line()), ", function(tree");
local args = {};
@@ -84,7 +86,7 @@ immediately:
lua:append([[)
local template = nomsu:parse(]]..template..[[, ]]..repr(tree.source.filename)..[[);
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);
]]);
return lua;
@@ -101,11 +103,11 @@ action [remove action %stub]:
immediately:
action [%tree as lua]:
- =lua "nomsu:tree_to_lua(\%tree)"
+ =lua "\%tree:as_lua(nomsu)"
action [%tree as lua expr]:
lua> ".."
- local lua = nomsu:tree_to_lua(\%tree);
+ local lua = \%tree:as_lua(nomsu);
if not lua.is_value then
error("Invalid thing to convert to lua expr: "..\%tree.source:get_text());
end
@@ -113,7 +115,8 @@ immediately:
action [%tree as lua statements]:
lua> ".."
- local lua = nomsu:tree_to_lua(\%tree):as_statements();
+ local lua = \%tree:as_lua(nomsu);
+ lua:convert_to_statements();
lua:declare_locals();
return lua;
@@ -124,14 +127,17 @@ immediately:
=lua "nomsu:tree_to_stub(\%tree)"
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:
+ compile [%tree's source code, %tree' source code] to: Lua value "\(%tree as lua expr).source:get_text()"
+
+ 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: LuaValue "nomsu"
- compile [%var as lua identifier] to: LuaValue "nomsu:var_to_lua_identifier(\(%var as lua expr))"
+ 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]:
=lua "nomsu.action_metadata[ACTIONS[\%names]]"
@@ -149,7 +155,7 @@ action [help %action]:
# Compiler tools
immediately:
compile [run %code] to:
- LuaValue ".."
+ Lua value ".."
nomsu:run(\(%code as lua expr), '\
=lua "nomsu:get_line_number(nomsu.compilestack[#nomsu.compilestack])"
..')
@@ -159,7 +165,7 @@ immediately:
immediately:
compile [show lua %block] to:
lua> ".."
- local \%lua = nomsu:tree_to_lua(\%block);
+ local \%lua = \%block:as_lua(nomsu);
return Lua(\%block.source, "print(", repr(tostring(\%lua)), ");");
immediately:
@@ -172,6 +178,13 @@ immediately:
return Lua(tree.source, "print(stringify(", \(%message as lua expr), "));");
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
immediately:
#.. Return statement is wrapped in a do..end block because Lua is unhappy if you
@@ -181,8 +194,8 @@ immediately:
# Error functions
immediately:
- compile [barf] to: LuaValue "error(nil, 0);"
- compile [barf %msg] to: LuaValue "error(\(%msg as lua expr), 0);"
+ compile [barf] to: Lua value "error(nil, 0);"
+ compile [barf %msg] to: Lua value "error(\(%msg as lua expr), 0);"
compile [assume %condition] to:
lua> "local \%assumption = 'Assumption failed: '..\%condition.source:get_text();"
return:
@@ -200,7 +213,7 @@ immediately:
# Literals
immediately:
- compile [yes] to: LuaValue "true"
- compile [no] to: LuaValue "false"
- compile [nothing, nil, null] to: LuaValue "nil"
+ compile [yes] to: Lua value "true"
+ compile [no] to: Lua value "false"
+ compile [nothing, nil, null] to: Lua value "nil"
diff --git a/core/operators.nom b/core/operators.nom
index 358962d..a6d9977 100644
--- a/core/operators.nom
+++ b/core/operators.nom
@@ -18,31 +18,31 @@ immediately:
# Comparison Operators
immediately:
- compile [%x < %y] to: LuaValue "(\(%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: LuaValue "(\(%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: Lua value "(\(%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: Lua value "(\(%x as lua expr) >= \(%y as lua expr))"
# TODO: optimize case of [%x,%y] = [1,2]
compile [%a is %b, %a = %b, %a == %b] to:
lua> ".."
local safe = {Text=true, Number=true};
local a_lua, b_lua = nomsu:tree_to_lua(\%a), nomsu:tree_to_lua(\%b);
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
- return LuaValue(tree.source, "utils.equivalent(", a_lua, ", ", b_lua, ")");
+ return Lua.Value(tree.source, "utils.equivalent(", a_lua, ", ", b_lua, ")");
end
compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to:
lua> ".."
local safe = {Text=true, Number=true};
local a_lua, b_lua = nomsu:tree_to_lua(\%a), nomsu:tree_to_lua(\%b);
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
- return LuaValue(tree.source, "(not utils.equivalent(", a_lua, ", ", b_lua, "))");
+ return Lua.Value(tree.source, "(not utils.equivalent(", a_lua, ", ", b_lua, "))");
end
# 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
immediately:
@@ -72,7 +72,7 @@ immediately:
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 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
if i > 1 then
lhs:append(", ");
@@ -104,12 +104,12 @@ immediately:
immediately:
# Math Operators
- compile [%x + %y] to: LuaValue "(\(%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: LuaValue "(\(%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: LuaValue "(\(%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 + %y] to: Lua value "(\(%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: Lua value "(\(%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: Lua value "(\(%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
# (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
# Boolean Operators
- compile [%x and %y] to: LuaValue "(\(%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 and %y] to: Lua value "(\(%x as lua expr) and \(%y as lua expr))"
+ compile [%x or %y] to: Lua value "(\(%x as lua expr) or \(%y as lua expr))"
# Bitwise Operators
- compile [%a OR %b, %a | %b] to: LuaValue "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 AND %b, %a & %b] to: LuaValue "bit32.band(\(%a as lua expr), \(%b as lua expr))"
- compile [NOT %, ~ %] to: LuaValue "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 RSHIFT %shift, %x >>> %shift] to: LuaValue "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 [%a OR %b, %a | %b] to: Lua value "bit32.bor(\(%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: Lua value "bit32.band(\(%a as lua expr), \(%b as lua expr))"
+ compile [NOT %, ~ %] to: Lua value "bit32.bnot(\(% 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: Lua value "bit32.rshift(\(%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?
# Unary operators
- compile [- %] to: LuaValue "(- \(% as lua expr))"
- compile [not %] to: LuaValue "(not \(% as lua expr))"
+ compile [- %] to: Lua value "(- \(% as lua expr))"
+ compile [not %] to: Lua value "(not \(% as lua expr))"
# Update operators
immediately:
diff --git a/lua_obj.lua b/lua_obj.lua
index 1378643..ba8277c 100644
--- a/lua_obj.lua
+++ b/lua_obj.lua
@@ -4,7 +4,7 @@ do
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end
local immutable = require('immutable')
-local Lua, LuaValue, Location
+local Lua, Location
Location = immutable({
"filename",
"start",
@@ -12,7 +12,6 @@ Location = immutable({
}, {
name = "Location",
__new = function(self, filename, start, stop)
- assert(type(filename) == 'string' and type(start) == 'number' and type(stop) == 'number')
return filename, start, stop or start
end,
__tostring = function(self)
@@ -38,13 +37,14 @@ Location = immutable({
return FILE_CACHE[self.filename]:sub(self.start, self.stop)
end,
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
- 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
end
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
end
return start_line, stop_line
@@ -64,9 +64,17 @@ Location = immutable({
do
local _class_0
local _base_0 = {
- is_statement = true,
- is_value = false,
- add_free_vars = function(self, free_vars)
+ clone = function(self)
+ local copy = Lua(self.source, {
+ 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
do
local _tbl_0 = { }
@@ -80,16 +88,30 @@ do
end
seen = _tbl_0
end
- for _index_0 = 1, #free_vars do
- local var = free_vars[_index_0]
+ for i = 1, select("#", ...) do
+ local var = select(i, ...)
if not (seen[var]) then
self.free_vars[#self.free_vars + 1] = var
seen[var] = true
end
end
end,
- as_statements = function(self)
- return self
+ convert_to_statements = function(self, prefix, suffix)
+ 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,
declare_locals = function(self, skip)
if skip == nil then
@@ -128,12 +150,11 @@ do
local buff = { }
for i, b in ipairs(self.bits) do
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"
end
end
local ret = concat(buff, "")
- assert(not ret:match(".*table: 0x.*"))
return ret
end,
__len = function(self)
@@ -145,12 +166,16 @@ do
end
return len
end,
+ __add = function(self, other)
+ return Lua(nil, self, other)
+ end,
+ __concat = function(self, other)
+ return Lua(nil, self, other)
+ end,
append = function(self, ...)
local n = select("#", ...)
local bits = self.bits
for i = 1, n do
- local x = select(i, ...)
- assert(type(x) ~= 'table' or getmetatable(x))
bits[#bits + 1] = select(i, ...)
end
end,
@@ -158,13 +183,9 @@ do
local n = select("#", ...)
local bits = self.bits
for i = #bits + n, n + 1, -1 do
- local x = select(i, ...)
- assert(type(x) ~= 'table' or getmetatable(x))
bits[i] = bits[i - n]
end
for i = 1, n do
- local x = select(i, ...)
- assert(type(x) ~= 'table' or getmetatable(x))
bits[i] = select(i, ...)
end
end,
@@ -197,6 +218,14 @@ do
end
walk(self)
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
}
_base_0.__index = _base_0
@@ -207,14 +236,11 @@ do
local filename, start, stop = self.source:match("^(.-)[(%d+):(%d+)]$")
self.source = Location(filename, tonumber(start), tonumber(stop))
end
- for i = 1, select("#", ...) do
- local x = select(i, ...)
- assert(type(x) ~= 'table' or getmetatable(x))
- end
self.bits = {
...
}
self.free_vars = { }
+ self.is_value = false
end,
__base = _base_0,
__name = "Lua"
@@ -227,82 +253,15 @@ do
end
})
_base_0.__class = _class_0
- Lua = _class_0
-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)
+ local self = _class_0
+ self.Value = function(...)
+ local lua = Lua(...)
+ lua.is_value = true
+ return lua
end
- LuaValue = _class_0
+ Lua = _class_0
end
return {
Lua = Lua,
- LuaValue = LuaValue,
Location = Location
}
diff --git a/lua_obj.moon b/lua_obj.moon
index 21017d7..ab5b5a3 100644
--- a/lua_obj.moon
+++ b/lua_obj.moon
@@ -1,12 +1,12 @@
{:insert, :remove, :concat} = table
immutable = require 'immutable'
-local Lua, LuaValue, Location
+local Lua, Location
export LINE_STARTS
Location = immutable {"filename","start","stop"}, {
name:"Location"
__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
__tostring: => "Location(\"#{@filename}\", #{@start}, #{@stop})"
__lt: (other)=>
@@ -22,12 +22,13 @@ Location = immutable {"filename","start","stop"}, {
get_text: => FILE_CACHE[@filename]\sub(@start,@stop)
get_line_number: =>
-- 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
- while (line_starts[start_line+1] or (#src+1)) <= @start
+ while (line_starts[start_line+1] or math.huge) <= @start
start_line += 1
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
return start_line, stop_line
get_line: => "#{@filename}:#{@get_line_number!}"
@@ -39,27 +40,41 @@ Location = immutable {"filename","start","stop"}, {
}
class Lua
- is_statement: true
- is_value: false
-
new: (@source, ...)=>
if type(@source) == 'string'
filename,start,stop = @source\match("^(.-)[(%d+):(%d+)]$")
@source = Location(filename, tonumber(start), tonumber(stop))
- for i=1,select("#",...)
- x = select(i,...)
- assert(type(x) != 'table' or getmetatable(x))
@bits = {...}
@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}
- for var in *free_vars
+ for i=1,select("#",...)
+ var = select(i, ...)
unless seen[var]
@free_vars[#@free_vars+1] = var
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={})=>
if next(skip) == 1
@@ -75,10 +90,9 @@ class Lua
buff = {}
for i,b in ipairs @bits
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"
ret = concat(buff, "")
- assert(not ret\match(".*table: 0x.*"))
return ret
__len: =>
@@ -87,24 +101,24 @@ class Lua
len += #b
return len
+ __add: (other)=>
+ Lua(nil, self, other)
+
+ __concat: (other)=>
+ Lua(nil, self, other)
+
append: (...)=>
n = select("#",...)
bits = @bits
for i=1,n
- x = select(i,...)
- assert(type(x) != 'table' or getmetatable(x))
bits[#bits+1] = select(i, ...)
prepend: (...)=>
n = select("#",...)
bits = @bits
for i=#bits+n,n+1,-1
- x = select(i,...)
- assert(type(x) != 'table' or getmetatable(x))
bits[i] = bits[i-n]
for i=1,n
- x = select(i,...)
- assert(type(x) != 'table' or getmetatable(x))
bits[i] = select(i, ...)
make_offset_table: (lua_chunkname)=>
@@ -130,28 +144,11 @@ class Lua
walk self
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: =>
- @prepend "("
- @append ")"
+ if @is_value
+ @prepend "("
+ @append ")"
+ else
+ error "Cannot parenthesize lua statements"
-return {:Lua, :LuaValue, :Location}
+return {:Lua, :Location}
diff --git a/nomsu.lua b/nomsu.lua
index 3f88b2f..5e6698e 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -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 new_uuid = require('uuid')
local immutable = require('immutable')
+Tuple = immutable(nil, {
+ name = "Tuple"
+})
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 colors = setmetatable({ }, {
@@ -27,10 +30,10 @@ do
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end
local debug_getinfo = debug.getinfo
-local Lua, LuaValue, Location
+local Lua, Location
do
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
FILE_CACHE = setmetatable({ }, {
__index = function(self, filename)
@@ -85,53 +88,7 @@ do
end
end
end
-local Types = { }
-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 Types = require("nomsu_tree")
local NOMSU_DEFS
do
local _with_0 = { }
@@ -376,7 +333,8 @@ do
local tree = self:parse(nomsu_code, filename)
assert(tree, "Failed to parse: " .. tostring(nomsu_code))
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:prepend("-- File: " .. tostring(filename) .. "\n")
return self:run_lua(lua, filename .. ".lua")
@@ -511,7 +469,7 @@ do
end
local nomsu = inline_expression(tok.value)
return nomsu and "(: " .. tostring(nomsu) .. ")"
- elseif "FunctionCall" == _exp_0 then
+ elseif "Action" == _exp_0 then
local buff = ""
for i, bit in ipairs(tok.value) do
if bit.type == "Word" then
@@ -529,7 +487,7 @@ do
buff = buff .. " "
end
buff = buff .. (function()
- if bit.type == "FunctionCall" then
+ if bit.type == "Action" then
return "(" .. nomsu .. ")"
else
return nomsu
@@ -540,9 +498,9 @@ do
return buff
elseif "IndexChain" == _exp_0 then
local bits = { }
- local _list_1 = tok.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local _list_0 = tok.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
local nomsu = inline_expression(bit)
if not (nomsu) then
return nil
@@ -552,9 +510,9 @@ do
return concat(bits, ".")
elseif "List" == _exp_0 then
local bits = { }
- local _list_1 = tok.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local _list_0 = tok.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
local nomsu = inline_expression(bit)
if not (nomsu) then
return nil
@@ -564,9 +522,9 @@ do
return "[" .. concat(bits, ", ") .. "]"
elseif "Dict" == _exp_0 then
local bits = { }
- local _list_1 = tok.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local _list_0 = tok.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
local key_nomsu
if bit.key.type == "Word" then
key_nomsu = bit.key.value
@@ -576,7 +534,7 @@ do
if not (key_nomsu) then
return nil
end
- if bit.key.type == "FunctionCall" then
+ if bit.key.type == "Action" then
key_nomsu = "(" .. key_nomsu .. ")"
end
local value_nomsu = inline_expression(bit.value)
@@ -588,9 +546,9 @@ do
return "{" .. concat(bits, ", ") .. "}"
elseif "Text" == _exp_0 then
local buff = '"'
- local _list_1 = tok.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local _list_0 = tok.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
if type(bit) == 'string' then
if bit:find("\n") then
return nil
@@ -636,9 +594,9 @@ do
local _exp_0 = tok.type
if "Block" == _exp_0 then
local buff = ":"
- local _list_1 = tok.value
- for _index_0 = 1, #_list_1 do
- local line = _list_1[_index_0]
+ local _list_0 = tok.value
+ for _index_0 = 1, #_list_0 do
+ local line = _list_0[_index_0]
nomsu = expression(line)
if not (nomsu) then
return nil
@@ -646,7 +604,7 @@ do
buff = buff .. ("\n " .. self:indent(nomsu))
end
return buff
- elseif "FunctionCall" == _exp_0 then
+ elseif "Action" == _exp_0 then
nomsu = expression(tok)
if not (nomsu) then
return nil
@@ -657,9 +615,9 @@ do
elseif "List" == _exp_0 then
local buff = "[..]"
local line = "\n "
- local _list_1 = tok.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local _list_0 = tok.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
nomsu = inline_expression(bit)
if line ~= "\n " and #line + #", " + #nomsu > max_line then
buff = buff .. line
@@ -685,14 +643,14 @@ do
elseif "Dict" == _exp_0 then
local buff = "{..}"
local line = "\n "
- local _list_1 = tok.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local _list_0 = tok.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
local key_nomsu = inline_expression(bit.key)
if not (key_nomsu) then
return nil
end
- if bit.key.type == "FunctionCall" then
+ if bit.key.type == "Action" then
key_nomsu = "(" .. key_nomsu .. ")"
end
local value_nomsu = inline_expression(bit.value)
@@ -714,9 +672,9 @@ do
return buff
elseif "Text" == _exp_0 then
local buff = '".."\n '
- local _list_1 = tok.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local _list_0 = tok.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
if type(bit) == 'string' then
buff = buff .. bit:gsub("\\", "\\\\"):gsub("\n", "\n ")
else
@@ -758,7 +716,7 @@ do
local _exp_0 = tok.type
if "Block" == _exp_0 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])
else
nomsu = noeol_expression(tok.value[1])
@@ -768,7 +726,7 @@ do
end
end
return noeol_expression(tok)
- elseif "FunctionCall" == _exp_0 then
+ elseif "Action" == _exp_0 then
local buff = ""
for i, bit in ipairs(tok.value) do
if bit.type == "Word" then
@@ -780,7 +738,7 @@ do
else
nomsu = inline_expression(bit)
if nomsu and #nomsu < max_line then
- if bit.type == "FunctionCall" then
+ if bit.type == "Action" then
nomsu = "(" .. nomsu .. ")"
end
else
@@ -788,7 +746,7 @@ do
if not (nomsu) then
return nil
end
- if bit.type == "FunctionCall" then
+ if bit.type == "Action" then
nomsu = "(..)\n " .. self:indent(nomsu)
end
if i < #tok.value then
@@ -804,9 +762,9 @@ do
return buff
elseif "File" == _exp_0 then
local lines = { }
- local _list_1 = tree.value
- for _index_0 = 1, #_list_1 do
- local line = _list_1[_index_0]
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local line = _list_0[_index_0]
nomsu = expression(line)
if not (nomsu) then
error("Failed to produce output for:\n" .. tostring(colored.yellow(line.source:get_text())), 0)
@@ -870,309 +828,7 @@ do
end
end,
tree_to_lua = function(self, tree)
- assert(tree, "No tree provided.")
- 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
+ return tree:as_lua(self)
end,
walk_tree = function(self, tree, depth)
if depth == nil then
@@ -1183,16 +839,16 @@ do
return
end
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
- local _list_1 = tree.value
- for _index_0 = 1, #_list_1 do
- local v = _list_1[_index_0]
+ 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_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local v = _list_0[_index_0]
self:walk_tree(v, depth + 1)
end
elseif "Dict" == _exp_0 then
- local _list_1 = tree.value
- for _index_0 = 1, #_list_1 do
- local e = _list_1[_index_0]
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local e = _list_0[_index_0]
self:walk_tree(e.key, depth + 1)
self:walk_tree(e.value, depth + 1)
end
@@ -1236,7 +892,7 @@ do
return replacement
end
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
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
@@ -1281,15 +937,15 @@ do
end)
end,
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)
end
return concat((function()
local _accum_0 = { }
local _len_0 = 1
- local _list_1 = tree.value
- for _index_0 = 1, #_list_1 do
- local t = _list_1[_index_0]
+ local _list_0 = tree.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
@@ -1297,15 +953,15 @@ do
end)(), " ")
end,
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)
end
return concat((function()
local _accum_0 = { }
local _len_0 = 1
- local _list_1 = tree.value
- for _index_0 = 1, #_list_1 do
- local t = _list_1[_index_0]
+ local _list_0 = tree.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 "%" .. tostring(t.value))
_len_0 = _len_0 + 1
end
@@ -1367,19 +1023,20 @@ do
end
local nomsu = self
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()
nomsu:run_lua(lua)
return Lua(self.source, "if IMMEDIATE then\n", lua, "\nend")
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
- 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
- local lua = LuaValue(self.source, "Lua(", repr(_code.source))
- local _list_1 = _code.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local lua = Lua.Value(_source, "Lua(", repr(_code.source))
+ local _list_0 = _code.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
lua:append(", ")
if type(bit) == "string" then
lua:append(repr(bit))
@@ -1395,14 +1052,14 @@ do
lua:append(")")
return lua
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
- 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
- local lua = LuaValue(self.source, "LuaValue(", repr(_code.source))
- local _list_1 = _code.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local lua = Lua.Value(_source, "Lua.Value(", repr(_code.source))
+ local _list_0 = _code.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
lua:append(", ")
if type(bit) == "string" then
lua:append(repr(bit))
@@ -1420,12 +1077,12 @@ do
end)
self:define_compile_action("lua> %code", get_line_no(), function(self, _code)
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
local lua = Lua(_code.source)
- local _list_1 = _code.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local _list_0 = _code.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
if type(bit) == "string" then
lua:append(bit)
else
@@ -1441,12 +1098,12 @@ do
end)
self:define_compile_action("=lua %code", get_line_no(), function(self, _code)
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
- local lua = LuaValue(self.source)
- local _list_1 = _code.value
- for _index_0 = 1, #_list_1 do
- local bit = _list_1[_index_0]
+ local lua = Lua.Value(self.source)
+ local _list_0 = _code.value
+ for _index_0 = 1, #_list_0 do
+ local bit = _list_0[_index_0]
if type(bit) == "string" then
lua:append(bit)
else
@@ -1461,7 +1118,7 @@ do
return lua
end)
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)
self:define_action("run file %filename", get_line_no(), function(_filename)
return nomsu:run_file(_filename)
@@ -1469,7 +1126,7 @@ do
return self:define_compile_action("use %filename", get_line_no(), function(self, _filename)
local filename = nomsu:tree_to_value(_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
}
@@ -1548,7 +1205,6 @@ do
end
self.environment.Tuple = Tuple
self.environment.Lua = Lua
- self.environment.LuaValue = LuaValue
self.environment.Location = Location
self.environment.ACTIONS = setmetatable({ }, {
__index = function(self, key)
@@ -1572,7 +1228,6 @@ do
_base_0.__class = _class_0
local self = _class_0
_chunk_counter = 0
- self.math_patt = re.compile([[ "%" (" " [*/^+-] " %")+ ]])
self.unescape_string = function(self, str)
return Cs(((P("\\\\") / "\\") + (P("\\\"") / '"') + NOMSU_DEFS.escaped_char + P(1)) ^ 0):match(str)
end
diff --git a/nomsu.moon b/nomsu.moon
index d22b891..21263ac 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -18,12 +18,14 @@ lpeg.setmaxstack 10000
utils = require 'utils'
new_uuid = require 'uuid'
immutable = require 'immutable'
+export Tuple
+Tuple = immutable(nil, {name:"Tuple"})
{:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils
colors = setmetatable({}, {__index:->""})
colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)})
{:insert, :remove, :concat} = table
debug_getinfo = debug.getinfo
-{:Lua, :LuaValue, :Location} = require "lua_obj"
+{:Lua, :Location} = require "lua_obj"
-- TODO:
-- 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
--STRING_METATABLE.__mul = (other)=> string.rep(@, other)
-Types = {}
-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
+Types = require "nomsu_tree"
NOMSU_DEFS = with {}
-- 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
@environment.Tuple = Tuple
@environment.Lua = Lua
- @environment.LuaValue = LuaValue
@environment.Location = Location
@environment.ACTIONS = setmetatable({}, {__index:(key)=>
error("Attempt to run undefined action: #{key}", 0)
@@ -305,7 +297,8 @@ class NomsuCompiler
tree = @parse(nomsu_code, filename)
assert tree, "Failed to parse: #{nomsu_code}"
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\prepend "-- File: #{filename}\n"
return @run_lua(lua, filename..".lua")
@@ -404,7 +397,7 @@ class NomsuCompiler
if #tok.value > 1 then return nil
nomsu = inline_expression tok.value
return nomsu and "(: #{nomsu})"
- when "FunctionCall"
+ when "Action"
buff = ""
for i,bit in ipairs tok.value
if bit.type == "Word"
@@ -416,7 +409,7 @@ class NomsuCompiler
return nil unless nomsu
unless i == 1 or bit.type == "Block"
buff ..= " "
- buff ..= if bit.type == "FunctionCall"
+ buff ..= if bit.type == "Action"
"("..nomsu..")"
else nomsu
return buff
@@ -441,7 +434,7 @@ class NomsuCompiler
bit.key.value
else inline_expression bit.key
return nil unless key_nomsu
- if bit.key.type == "FunctionCall"
+ if bit.key.type == "Action"
key_nomsu = "("..key_nomsu..")"
value_nomsu = inline_expression bit.value
return nil unless value_nomsu
@@ -482,7 +475,7 @@ class NomsuCompiler
return nil unless nomsu
buff ..= "\n "..@indent(nomsu)
return buff
- when "FunctionCall"
+ when "Action"
nomsu = expression(tok)
return nil unless nomsu
return "(..)\n "..@indent(nomsu)
@@ -515,7 +508,7 @@ class NomsuCompiler
for bit in *tok.value
key_nomsu = inline_expression bit.key
return nil unless key_nomsu
- if bit.key.type == "FunctionCall"
+ if bit.key.type == "Action"
key_nomsu = "("..key_nomsu..")"
value_nomsu = inline_expression bit.value
if value_nomsu and #key_nomsu + #value_nomsu < max_line
@@ -560,14 +553,14 @@ class NomsuCompiler
switch tok.type
when "Block"
if #tok.value == 1
- nomsu = if tok.value[1].type == "FunctionCall"
+ nomsu = if tok.value[1].type == "Action"
inline_expression(tok.value[1])
else
noeol_expression(tok.value[1])
if nomsu and #(nomsu\match("[^\n]*")) < max_line
return ": "..nomsu
return noeol_expression(tok)
- when "FunctionCall"
+ when "Action"
-- The hard task
buff = ""
for i,bit in ipairs tok.value
@@ -579,12 +572,12 @@ class NomsuCompiler
else
nomsu = inline_expression(bit)
if nomsu and #nomsu < max_line
- if bit.type == "FunctionCall"
+ if bit.type == "Action"
nomsu = "("..nomsu..")"
else
nomsu = expression(bit)
return nil unless nomsu
- if bit.type == "FunctionCall"
+ if bit.type == "Action"
nomsu = "(..)\n "..@indent(nomsu)
if i < #tok.value
nomsu ..= "\n.."
@@ -636,238 +629,14 @@ class NomsuCompiler
else
error("Unsupported value_to_nomsu type: #{type(value)}", 0)
- @math_patt: re.compile [[ "%" (" " [*/^+-] " %")+ ]]
tree_to_lua: (tree)=>
- -- Return <lua code for value>, <additional lua code>
- 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
+ return tree\as_lua(self)
- 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)=>
coroutine.yield(tree, depth)
return unless Types.is_node(tree)
switch tree.type
- when "List", "File", "Block", "FunctionCall", "Text", "IndexChain"
+ when "List", "File", "Block", "Action", "Text", "IndexChain"
for v in *tree.value
@walk_tree(v, depth+1)
when "Dict"
@@ -919,7 +688,7 @@ class NomsuCompiler
if replacement != nil
return replacement
switch tree.type
- when "File", "Nomsu", "Block", "List", "FunctionCall", "Text", "IndexChain"
+ when "File", "Nomsu", "Block", "List", "Action", "Text", "IndexChain"
new_values, is_changed = {}, false
for i,old_value in ipairs(tree.value)
new_value = type(old_value) != "string" and @tree_map(old_value, fn) or nil
@@ -956,11 +725,11 @@ class NomsuCompiler
return replacements[id]
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], " ")
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], " ")
stub_defs = {
@@ -1009,16 +778,17 @@ class NomsuCompiler
get_line_no = -> "nomsu.moon:#{debug_getinfo(2).currentline}"
nomsu = self
@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!
nomsu\run_lua(lua)
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"
- 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
lua\append ", "
if type(bit) == "string"
@@ -1032,11 +802,11 @@ class NomsuCompiler
lua\append ")"
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"
- 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
lua\append ", "
if type(bit) == "string"
@@ -1052,7 +822,7 @@ class NomsuCompiler
@define_compile_action "lua> %code", get_line_no!, (_code)=>
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)
for bit in *_code.value
@@ -1068,9 +838,9 @@ class NomsuCompiler
@define_compile_action "=lua %code", get_line_no!, (_code)=>
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
if type(bit) == "string"
lua\append bit
@@ -1083,7 +853,7 @@ class NomsuCompiler
return lua
@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)->
return nomsu\run_file(_filename)
@@ -1091,7 +861,7 @@ class NomsuCompiler
@define_compile_action "use %filename", get_line_no!, (_filename)=>
filename = nomsu\tree_to_value(_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:
if arg and debug_getinfo(2).func != require
diff --git a/nomsu.peg b/nomsu.peg
index 5758b17..462e03e 100644
--- a/nomsu.peg
+++ b/nomsu.peg
@@ -7,7 +7,7 @@ file (File):
shebang: "#!" [^%nl]* %nl
-statement: functioncall / expression
+statement: action / expression
indented_block (Block):
{| (":" / "(..)")? indent
@@ -17,27 +17,27 @@ indented_block (Block):
inline_nomsu (Nomsu): "\" noindex_inline_expression
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):
{| noindex_inline_expression ("." ((({} ({|{%operator / (!number plain_word)}|} -> Tuple) {}) -> Text) / noindex_inline_expression))+ |} -> Tuple
noindex_inline_expression:
number / variable / inline_text / inline_list / inline_dict / inline_nomsu
- / ("(" %ws* (inline_functioncall / inline_expression) %ws* ")")
+ / ("(" %ws* (inline_action / inline_expression) %ws* ")")
inline_expression:
index_chain / noindex_inline_expression
indented_expression:
indented_text / indented_nomsu / indented_list / indented_dict / indented_block
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
-inline_functioncall (FunctionCall):
+inline_action (Action):
{| (inline_expression %ws*)* word (%ws* (inline_expression / word))*
- (%ws* ":" %ws* (inline_functioncall / inline_expression))?|} -> Tuple
-functioncall (FunctionCall):
+ (%ws* ":" %ws* (inline_action / inline_expression))?|} -> Tuple
+action (Action):
{| (expression (dotdot / %ws*))* word ((dotdot / %ws*) (expression / word))* |} -> Tuple
word (Word): { %operator / (!number plain_word) }
@@ -60,13 +60,13 @@ indented_text (Text):
inline_text_interpolation:
"\" (
variable / inline_list / inline_dict / inline_text
- / ("(" %ws* (inline_functioncall / inline_expression) %ws* ")")
+ / ("(" %ws* (inline_action / inline_expression) %ws* ")")
)
text_interpolation:
inline_text_interpolation /
("\" (
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 "..")
/ (indented_text %nl+ %nodent "..")
/ ((indented_list / indented_block) nodent "..")
@@ -87,9 +87,9 @@ indented_list (List):
|} -> Tuple)
(dedent / (("" -> "Error while parsing list") => error))
list_line:
- ((functioncall / expression) !comma)
+ ((action / expression) !comma)
/ (inline_list_item (comma list_line?)?)
-inline_list_item: inline_functioncall / inline_expression
+inline_list_item: inline_action / inline_expression
inline_dict (Dict):
!('{..}')
@@ -100,10 +100,10 @@ indented_dict (Dict):
|} -> Tuple)
(dedent / (("" -> "Error while parsing dict") => error))
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:
- (dict_key %ws* ":" %ws* (inline_functioncall / inline_expression)) -> DictEntry
+ (dict_key %ws* ":" %ws* (inline_action / inline_expression)) -> DictEntry
dict_key:
(({} ({|{%operator / (!number plain_word)}|} -> Tuple) {}) -> Text) / inline_expression
diff --git a/nomsu_tree.lua b/nomsu_tree.lua
new file mode 100644
index 0000000..0f88ab4
--- /dev/null
+++ b/nomsu_tree.lua
@@ -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
diff --git a/nomsu_tree.moon b/nomsu_tree.moon
new file mode 100644
index 0000000..3162bf8
--- /dev/null
+++ b/nomsu_tree.moon
@@ -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