aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-01-23 19:22:20 -0800
committerBruce Hill <bitbucket@bruce-hill.com>2018-01-23 19:22:45 -0800
commit6d11354b3f7226a9a417ae89f53de7a225434647 (patch)
tree280778d0b46b314083a93c35210e10a93f8b669f
parent15dc0309c39981ff19dfdabd422e8f95fcc1a4b5 (diff)
Added implicit local declarations and new syntax of %var <- %value.
-rw-r--r--lib/class.nom9
-rw-r--r--lib/collections.nom6
-rw-r--r--lib/control_flow.nom468
-rw-r--r--lib/metaprogramming.nom22
-rw-r--r--lib/operators.nom154
-rw-r--r--nomsu.lua78
-rwxr-xr-xnomsu.moon51
7 files changed, 447 insertions, 341 deletions
diff --git a/lib/class.nom b/lib/class.nom
index 06f55d8..b871798 100644
--- a/lib/class.nom
+++ b/lib/class.nom
@@ -12,7 +12,7 @@ compile [@%var] to
end
return "_me["..key_lua.."]";
-compile [set @%var = %val] to code
+compile [@%var <- %val] to code
lua> ".."
local val_lua = \(%val as lua);
local key_lua = repr(\%var.value);
@@ -26,11 +26,10 @@ compile [set @%var = %val] to code
return "_me["..key_lua.."] = "..val_lua..";";
compile [object %classname %class_body] to
- local [%methods, %class_identifier]
- set %class_identifier = (=lua "nomsu:var_to_lua_identifier(\(%classname as value)):sub(2,-1)")
+ %class_identifier <- (=lua "nomsu:var_to_lua_identifier(\(%classname as value)):sub(2,-1)")
if: %class_identifier is ""
- set %class_identifier = "class"
- set %methods = []
+ %class_identifier <- "class"
+ %methods <- []
for %line in (%class_body's "value")
if: (%line's "type") is "Comment"
do next %line
diff --git a/lib/collections.nom b/lib/collections.nom
index 90bd08c..10dae75 100644
--- a/lib/collections.nom
+++ b/lib/collections.nom
@@ -11,7 +11,7 @@ use "lib/operators.nom"
# Indexing
parse [..]
%index st in %list, %index nd in %list, %index rd in %list
- %index th in %list, %index in %list
+ %index th in %list
..as: %list -> %index
compile [..]
%index st to last in %list, %index nd to last in %list, %index rd to last in %list
@@ -51,7 +51,7 @@ compile [length of %list, size of %list, size %list, number of %list, len %list]
compile [%list ->* %indices] to
assume ((%indices's "type") is "List") or barf ".."
Expected List for chained lookup, not \(%indices's "type")
- set %ret = "\(%list as lua)"
+ %ret <- "\(%list as lua)"
for %index in (%indices's "value")
%ret join= "[\(%index as lua)]"
return "\%ret"
@@ -75,7 +75,7 @@ compile [remove index %index from %list] to
action [flatten %lists]
- set %flat = []
+ %flat <- []
for %list in %lists
for %item in %list
add %item to %flat
diff --git a/lib/control_flow.nom b/lib/control_flow.nom
index b05862c..7347a29 100644
--- a/lib/control_flow.nom
+++ b/lib/control_flow.nom
@@ -40,10 +40,9 @@ immediately
%when_false_expr unless %condition else %when_true_expr
%when_false_expr unless %condition then %when_true_expr
..to
- local %safe
#.. 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: {Text:yes, List:yes, Dict:yes, Number:yes}->(%when_true_expr's "type")
+ if: (%when_true_expr's "type") in {Text:yes, List:yes, Dict:yes, Number:yes}
return "(\(%condition as lua) and \(%when_true_expr as lua) or \(%when_false_expr as lua))"
..else
#.. Otherwise, need to do an anonymous inline function (yuck, too bad lua
@@ -58,9 +57,10 @@ immediately
end
end)()
+
# GOTOs
immediately
- compile [-> %label] to code ".."
+ compile [=== %label ===, --- %label ---] to code ".."
::label_\(nomsu "var_to_lua_identifier" [%label])::;
compile [go to %label] to code ".."
goto label_\(nomsu "var_to_lua_identifier" [%label]);
@@ -72,46 +72,77 @@ immediately
# Helper function
immediately
- action [tree %tree has function call %call]
- lua> ".."
- local target = (\%call).stub;
- for subtree,depth in coroutine.wrap(function() nomsu:walk_tree(\%tree); end) do
- if type(subtree) == 'table' and subtree.type == "FunctionCall"
- and subtree.stub == target then
- return true;
+ compile [for subtree %subtree where %condition in %tree %body] to code ".."
+ for \(%subtree as lua) in coroutine.wrap(function() nomsu:walk_tree(\(%tree as lua)) end) do
+ if type(\(%subtree as lua)) == 'table' and \(%subtree as lua).type then
+ if \(%condition as lua) then
+ \(%body as lua statements)
end
end
- return false;
+ end
# While loops
immediately
- compile [do next repeat-loop] to code "goto continue_repeat;"
- compile [stop repeat-loop] to code "goto stop_repeat;"
+ compile [do next repetition] to code "goto continue_repeat;"
+ compile [stop repeating] to code "goto stop_repeat;"
compile [repeat while %condition %body] to code
- set %continue_labels =
- "\n::continue_repeat::;"
- ..if (tree %body has function call \(do next repeat-loop)) else ""
- set %code = ".."
+ %continue_labels <- ""
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and ((%'s "stub") == "do next repetition")
+ ..in %body
+ %continue_labels <- "\n::continue_repeat::;"
+ stop
+ %code <- ".."
while \(%condition as lua) do
\(%body as lua statements)\
..\%continue_labels
end --while-loop
- if: tree %body has function call \(stop repeat-loop)
- return ".."
- do --while-loop label scope
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and ((%'s "stub") == "stop repeating")
+ ..in %body
+ %code <- ".."
+ do -- scope of "stop repeating" label
\%code
::stop_repeat::;
- end --while-loop label scope
+ end -- end of "stop repeating" label scope
+ %continue_labels <- "\n::continue_repeat::;"
+ stop
return %code
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 code
+ %continue_labels <- ""
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and ((%'s "stub") == "do next repetition")
+ ..in %body
+ %continue_labels <- "\n::continue_repeat::;"
+ stop
+ # This trashes the loop variables, just like in Python.
+ %code <- ".."
+ for i=1,\(%n as lua) do
+ \(%body as lua statements)\
+ ..\%continue_labels
+ end --numeric for-loop
+ %stop_labels <- ""
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and ((%'s "stub") == "stop repeating")
+ ..in %body
+ %code <- ".."
+ do -- scope of "stop repeating" label
+ \%code
+ ::stop_repeat::;
+ end -- end of "stop repeating" label scope
+ %continue_labels <- "\n::continue_repeat::;"
+ stop
+ return %code
+
# For loop control flow:
immediately
- compile [stop for-loop] to code "goto stop_for;"
compile [stop %var] to code ".."
goto stop_\(nomsu "var_to_lua_identifier" [%var]);
- compile [do next for-loop] to code "goto continue_for;"
compile [do next %var] to code ".."
goto continue_\(nomsu "var_to_lua_identifier" [%var]);
@@ -121,37 +152,42 @@ immediately
for %var from %start to %stop by %step %body
for %var from %start to %stop via %step %body
..to code
- local [%continue_labels, %code, %stop_labels, %loop_var, %loop_var_shim]
- set %continue_labels = ""
- if: tree %body has function call \(do next for-loop)
- %continue_labels join= "\n::continue_for::;"
- if: tree %body has function call (tree \(do next %) with {"":%var})
- %continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
+ %continue_labels <- ""
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and
+ ((%'s "stub") == "do next %") and
+ ((3rd in (%'s "value"))'s "src") == (%var's "src")
+ ..in %body
+ %continue_labels <- "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
+ stop
+
if: (%var's "type") is "Var"
- set %loop_var = (%var as lua)
- set %loop_var_shim = ""
+ %loop_var <- (%var as lua)
+ %loop_var_shim <- ""
..else
- set %loop_var = "i"
- set %loop_var_shim = "\n\(%var as lua) = i;"
+ %loop_var <- "i"
+ %loop_var_shim <- "\n\(%var as lua) = i;"
# This trashes the loop variables, just like in Python.
- set %code = ".."
+ %code <- ".."
for \(%loop_var)=\(%start as lua),\(%stop as lua),\(%step as lua) do\
..\%loop_var_shim
\(%body as lua statements)\
..\%continue_labels
end --numeric for-loop
- set %stop_labels = ""
- if: tree %body has function call \(stop for-loop)
- %stop_labels join= "\n::stop_for::;"
- if: tree %body has function call (tree \(stop %) with {"":%var})
- %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;"
- return
- ".."
- do --for-loop label scope
- \%code\
- ..\%stop_labels
- end --for-loop label scope
- ..if (%stop_labels is not "") else %code
+
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and
+ ((%'s "stub") == "stop %") and
+ ((2nd in (%'s "value"))'s "src") == (%var's "src")
+ ..in %body
+ %code <- ".."
+ do -- scope for stopping for-loop
+ \%code
+ ::stop_\(nomsu "var_to_lua_identifier" [%var])::;
+ end -- end of scope for stopping for-loop
+ stop
+
+ return %code
immediately
parse [for %var from %start to %stop %body] as: for %var from %start to %stop via 1 %body
@@ -161,207 +197,209 @@ immediately
..as: for % from %start to %stop via %step %body
parse [for all %start to %stop %body] as: for all %start to %stop via 1 %body
+# For-each loop
immediately
compile [for %var in %iterable %body] to code
- local [%continue_labels, %code, %stop_labels, %loop_var, %loop_var_shim]
- set %continue_labels = ""
- if: tree %body has function call \(do next for-loop)
- %continue_labels join= "\n::continue_for::;"
- if: tree %body has function call (tree \(do next %) with {"":%var})
- %continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
+ %continue_labels <- ""
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and
+ ((%'s "stub") == "do next %") and
+ ((3rd in (%'s "value"))'s "src") == (%var's "src")
+ ..in %body
+ %continue_labels <- "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
+ stop
if: (%var's "type") is "Var"
- set %loop_var = (%var as lua)
- set %loop_var_shim = ""
+ %loop_var <- (%var as lua)
+ %loop_var_shim <- ""
..else
- set %loop_var = "value"
- set %loop_var_shim = "\n\(%var as lua) = value;"
+ %loop_var <- "value"
+ %loop_var_shim <- "\n\(%var as lua) = value;"
# This trashes the loop variables, just like in Python.
- set %code = ".."
+ %code <- ".."
for i,\%loop_var in ipairs(\(%iterable as lua)) do\
..\%loop_var_shim
\(%body as lua statements)\
..\%continue_labels
end --foreach-loop
- set %stop_labels = ""
- if: tree %body has function call \(stop for-loop)
- %stop_labels join= "\n::stop_for::;"
- if: tree %body has function call (tree \(stop %) with {"":%var})
- %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;"
- if: %stop_labels is not ""
- set %code = ".."
- do --for-loop label scope
- \%code\%stop_labels
- end --for-loop label scope
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and
+ ((%'s "stub") == "stop %") and
+ ((2nd in (%'s "value"))'s "src") == (%var's "src")
+ ..in %body
+ %code <- ".."
+ do -- scope for stopping for-loop
+ \%code
+ ::stop_\(nomsu "var_to_lua_identifier" [%var])::;
+ end -- end of scope for stopping for-loop
+ stop
return %code
parse [for all %iterable %body] as: for % in %iterable %body
-immediately
- compile [..]
- repeat %n times %body, repeat %n x %body
- ..to code
- local [%continue_labels, %code, %stop_labels]
- set %continue_labels = ""
- if: tree %body has function call \(do next repeat-loop)
- %continue_labels join= "\n::continue_repeat::;"
- # This trashes the loop variables, just like in Python.
- set %code = ".."
- for i=1,\(%n as lua) do
- \(%body as lua statements)\
- ..\%continue_labels
- end --numeric for-loop
- set %stop_labels = ""
- if: tree %body has function call \(stop repeat-loop)
- %stop_labels join= "\n::stop_repeat::;"
- return
- ".."
- do --repeat-loop label scope
- \%code\
- ..\%stop_labels
- end --repeat-loop label scope
- ..if (%stop_labels is not "") else %code
-
# Dict iteration (lua's "pairs()")
immediately
compile [for %key = %value in %iterable %body] to code
- local [..]
- %continue_labels, %code, %stop_labels, %key_loop_var, %key_loop_var_shim,
- %value_loop_var, %value_loop_var_shim
- set %continue_labels = ""
- if: tree %body has function call \(do next for-loop)
- %continue_labels join= "\n::continue_for::;"
- if: tree %body has function call (tree \(do next %) with {"":%key})
- %continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%key])::;"
- if: tree %body has function call (tree \(do next %) with {"":%value})
- %continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%value])::;"
+ %continue_labels <- ""
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and
+ ((%'s "stub") == "do next %") and
+ ((3rd in (%'s "value"))'s "src") == (%key's "src")
+ ..in %body
+ <- %continue_labels + "\n::continue_\(nomsu "var_to_lua_identifier" [%key])::;"
+ stop
+
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and
+ ((%'s "stub") == "do next %") and
+ ((3rd in (%'s "value"))'s "src") == (%value's "src")
+ ..in %body
+ <- %continue_labels + "\n::continue_\(nomsu "var_to_lua_identifier" [%value])::;"
+ stop
+
if: (%key's "type") is "Var"
- set %key_loop_var = (%key as lua)
- set %key_loop_var_shim = ""
+ %key_loop_var <- (%key as lua)
+ %key_loop_var_shim <- ""
..else
- set %key_loop_var = "key"
- set %key_loop_var_shim = "\n\(%key as lua) = key;"
+ %key_loop_var <- "key"
+ %key_loop_var_shim <- "\n\(%key as lua) = key;"
if: (%value's "type") is "Var"
- set %value_loop_var = (%value as lua)
- set %value_loop_var_shim = ""
+ %value_loop_var <- (%value as lua)
+ %value_loop_var_shim <- ""
..else
- set %value_loop_var = "value"
- set %value_loop_var_shim = "\n\(%value as lua) = value;"
+ %value_loop_var <- "value"
+ %value_loop_var_shim <- "\n\(%value as lua) = value;"
# This trashes the loop variables, just like in Python.
- set %code = ".."
+ %code <- ".."
for \%key_loop_var,\%value_loop_var in pairs(\(%iterable as lua)) do\
..\%key_loop_var_shim\%value_loop_var_shim
\(%body as lua statements)\
..\%continue_labels
end --foreach-loop
- set %stop_labels = ""
- if: tree %body has function call \(stop for-loop)
- %stop_labels join= "\n::stop_for::;"
- if: tree %body has function call (tree \(stop %) with {"":%key})
- %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%key])::;"
- if: tree %body has function call (tree \(stop %) with {"":%value})
- %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%value])::;"
- return
- ".."
- do --for-loop label scope
- \%code\
- ..\%stop_labels
- end --for-loop label scope
- ..if (%stop_labels is not "") else %code
+ %stop_labels <- ""
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and
+ ((%'s "stub") == "stop %") and
+ ((2nd in (%'s "value"))'s "src") == (%key's "src")
+ ..in %body
+ <- %stop_labels + "\n::stop_\(nomsu "var_to_lua_identifier" [%key])::;"
+ stop
+
+ for subtree % where
+ ((%'s "type") = "FunctionCall") and
+ ((%'s "stub") == "stop %") and
+ ((2nd in (%'s "value"))'s "src") == (%value's "src")
+ ..in %body
+ <- %stop_labels + "\n::stop_\(nomsu "var_to_lua_identifier" [%value])::;"
+ stop
+
+ if: %stop_labels is not ""
+ %code <- ".."
+ do -- scope for stopping for % = % loop
+ \%code\%stop_labels
+ end
+ return %code
# Switch statement/multi-branch if
immediately
compile [when %body] to code
- local [%result, %fallthroughs, %first]
- set %result = ""
- set %fallthroughs = []
- set %first = (yes)
+ %result <- ""
+ %fallthroughs <- []
+ %is_first <- (yes)
for %func_call in (%body's "value")
- local [%tokens, %star, %condition, %action]
assume ((%func_call's "type") is "FunctionCall") or barf ".."
Invalid format for 'when' statement. Only '*' blocks are allowed.
- set %tokens = (%func_call's "value")
- set %star = (%tokens -> 1)
- assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".."
- Invalid format for 'when' statement. Lines must begin with '*'
-
- set %condition = (%tokens -> 2)
- assume %condition or barf ".."
- Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
-
- set %action = (%tokens -> 3)
- if: %action is (nil)
- lua do> "table.insert(\%fallthroughs, \%condition)"
- do next %func_call
-
- if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
- %result join= ".."
-
- else
- \(%action as lua statements)
- stop for-loop
- ..else
- set %condition = (%condition as lua)
- for all %fallthroughs
- %condition join= " or \(% as lua)"
- %result join= ".."
-
- \("if" if %first else "elseif") \%condition then
- \(%action as lua statements)
-
- set %fallthroughs = []
- set %first = (no)
+ with [..]
+ %tokens <- (%func_call's "value")
+ %star <- (1st in %tokens)
+ %condition <- (2nd in %tokens)
+ %action <- (3rd in %tokens)
+ ..
+ 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 ".."
+ Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
+ if: %action is (nil)
+ lua do> "table.insert(\%fallthroughs, \%condition)"
+ do next %func_call
+
+ if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
+
+ <- %result + ".."
+
+ else
+ \(%action as lua statements)
+ stop
+ ..else
+ %condition <- (%condition as lua)
+ for all %fallthroughs
+ <- %condition + " or \(% as lua)"
+ <- %result + ".."
+
+ \("if" if %is_first else "elseif") \%condition then
+ \(%action as lua statements)
+
+ %fallthroughs <- []
+ %is_first <- (no)
if: %result is not ""
- %result join= "\nend"
+ <- %result + "\nend"
return %result
# Switch statement
immediately
compile [when %branch_value = ? %body, when %branch_value is ? %body] to code
- set %result = ""
- set %fallthroughs = []
- set %first = (yes)
+ %result <- ""
+ %fallthroughs <- []
+ %is_first <- (yes)
+ %seen_else <- (no)
for %func_call in (%body's "value")
assume ((%func_call's "type") is "FunctionCall") or barf ".."
Invalid format for 'when' statement. Only '*' blocks are allowed.
- set %tokens = (%func_call's "value")
- set %star = (%tokens -> 1)
- assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".."
- Invalid format for 'when' statement. Lines must begin with '*'
-
- set %condition = (%tokens -> 2)
- assume %condition or barf ".."
- Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
-
- set %action = (%tokens -> 3)
- if: %action is (nil)
- lua> "table.insert(\%fallthroughs, \%condition)"
- do next %func_call
-
- if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
- %result join= ".."
-
- else
- \(%action as lua statements)
- stop for-loop
- ..else
- set %condition = "branch_value == (\(%condition as lua))"
- for all %fallthroughs
- %condition join= " or (branch_value == \(% as lua))"
- %result join= ".."
-
- \("if" if %first else "elseif") \%condition then
- \(%action as lua statements)
-
- set %fallthroughs = []
- set %first = (no)
+ %tokens <- (%func_call's "value")
+ with [%star<-(1st in %tokens), %condition<-(2nd in %tokens), %action<-(3rd in %tokens)]
+ 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 ".."
+ Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
+ if: %action is (nil)
+ lua> "table.insert(\%fallthroughs, \%condition)"
+ do next %func_call
+
+ if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
+ assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block"
+ <- %result + ".."
+
+ else
+ \(%action as lua statements)
+ end
+ %seen_else <- (yes)
+ ..else
+ assume (not %seen_else) or barf "'else' clause needs to be last in 'when % = ?' block"
+ %clause <- ""
+ if: ((%condition's "type") is "Text") or ((%condition's "type") is "Number")
+ %clause <- "branch_value == (\(%condition as lua))"
+ ..else
+ %clause <- "utils.equivalent(branch_value, \(%condition as lua))"
+ for all %fallthroughs
+ if: ((%'s "type") is "Text") or ((%'s "type") is "Number")
+ <- %clause + " or branch_value == (\(%condition as lua))"
+ ..else
+ <- %clause + " or utils.equivalent(branch_value, \(%condition as lua))"
+ <- %result + ".."
+
+ \("if" if %is_first else "elseif") \%clause then
+ \(%action as lua statements)
- if: %result is not ""
- set %result = ".."
- do --when % = ?
- local branch_value = \(%branch_value as lua);\
- ..\%result
- end
- end --when % = ?
+ %fallthroughs <- []
+ %is_first <- (no)
+
+ assume (%result is not "") or barf "No body for 'when % = ?' block!"
+ unless %seen_else
+ <- %result + "\nend"
+ %result <- ".."
+ do --when % = ?
+ local branch_value = \(%branch_value as lua);\
+ ..\%result
+ end --when % = ?
return %result
# Try/except
@@ -417,27 +455,3 @@ immediately
end
end
-immediately
- compile [with %assignments %action] to code
- local [%lua, %olds, %old_vals, %new_vals]
- set {%temp_vars:[], %old_vals:[], %new_vals:[]}
- for %i=%assignment in (%assignments' "value")
- set (%temp_vars->%i) = "temp\%i"
- set (%old_vals->%i) = ((%assignment's "dict_key") as lua)
- set (%new_vals->%i) = ((%assignment's "dict_value") as lua)
- return ".."
- do
- local \(join %temp_vars with ", ") = \(join %old_vals with ", ");
- \(join %old_vals with ", ") = \(join %new_vals with ", ");
- local fell_through = false;
- local ok, ret = pcall(function()
- do
- \(%action as lua statements)
- end
- fell_through = true;
- end);
- \(join %old_vals with ", ") = \(join %temp_vars with ", ");
- if not ok then error(ret, 0); end
- if not fell_through then return ret end
- end
-
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
index 5ff1ab3..eec99a0 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -21,7 +21,6 @@ immediately
return names, args;
else
local alias = nomsu:tree_to_value(spec);
- print("ALIAS!!! "..repr(alias).." from "..repr(spec));
local junk, arg_names, junk = nomsu:get_stub(alias);
local args = {};
for i, a in ipairs(arg_names) do args[i] = nomsu:var_to_lua_identifier(a); end
@@ -37,7 +36,10 @@ immediately
local names, args = nomsu:parse_spec(\%names);
names, args = repr(names), table.concat(args, ", ");
local body_lua = nomsu:tree_to_lua(\%body);
- body_lua = body_lua.statements or ("return "..body_lua.expr..";");
+ local body_code = body_lua.statements or ("return "..body_lua.expr..";");
+ if body_lua.locals and #body_lua.locals > 0 then
+ body_code = "local "..table.concat(body_lua.locals, ", ")..";\\n"..body_code;
+ end
local lua = ([[
do
local function compile_action(%s)
@@ -45,7 +47,7 @@ immediately
end
local function compile_action_wrapper(%s) return {expr=compile_action(%s)}; end
nomsu:define_compile_action(%s, %s, compile_action_wrapper, %s);
- end]]):format(args, body_lua, args, args, names, repr(\%names:get_line_no()),
+ end]]):format(args, body_code, args, args, names, repr(\%names:get_line_no()),
repr(("compile %s\\n..to %s"):format(\%names.src, \%body.src)));
return {statements=lua};
end, \(__src__ 1));
@@ -55,7 +57,10 @@ immediately
local names, args = nomsu:parse_spec(\%names);
names, args = repr(names), table.concat(args, ", ");
local body_lua = nomsu:tree_to_lua(\%body);
- body_lua = body_lua.statements or ("return "..body_lua.expr..";");
+ local body_code = body_lua.statements or ("return "..body_lua.expr..";");
+ if body_lua.locals and #body_lua.locals > 0 then
+ body_code = "local "..table.concat(body_lua.locals, ", ")..";\\n"..body_code;
+ end
local lua = ([[
do
local function compile_action(%s)
@@ -63,7 +68,7 @@ immediately
end
local function compile_action_wrapper(%s) return {statements=compile_action(%s)}; end
nomsu:define_compile_action(%s, %s, compile_action_wrapper, %s);
- end]]):format(args, body_lua, args, args, names, repr(\%names:get_line_no()),
+ end]]):format(args, body_code, args, args, names, repr(\%names:get_line_no()),
repr(("compile %s\\n..to code %s"):format(\%names.src, \%body.src)));
return {statements=lua};
end, \(__src__ 1));
@@ -75,12 +80,15 @@ immediately
local names, args = nomsu:parse_spec(\%names);
names, args = repr(names), table.concat(args, ", ");
local body_lua = nomsu:tree_to_lua(\%body);
- body_lua = body_lua.statements or ("return "..body_lua.expr..";");
+ local body_code = body_lua.statements or ("return "..body_lua.expr..";");
+ if body_lua.locals and #body_lua.locals > 0 then
+ body_code = "local "..table.concat(body_lua.locals, ", ")..";\\n"..body_code;
+ end
local src = nomsu:dedent(nomsu:source_code(0));
local def_lua = ([[
nomsu:define_action(%s, \(__line_no__), function(%s)
%s
- end, %s);]]):format(names, args, body_lua, repr(src));
+ end, %s);]]):format(names, args, body_code, repr(src));
return def_lua;
# Macro to make nomsu macros:
diff --git a/lib/operators.nom b/lib/operators.nom
index 91410be..4f43dfd 100644
--- a/lib/operators.nom
+++ b/lib/operators.nom
@@ -11,7 +11,10 @@ immediately
It's also critical to have parens around %obj, otherwise Lua is too dumb to
realize that {x=1}["x"] is the same as ({x=1})["x"] or that
{x=1}.x is the same as ({x=1}).x
- compile [%obj'%key, %obj's %key, %obj -> %key] to
+ compile [..]
+ %obj' %key, %obj's %key, %key in %obj, %key'th in %obj, %key of %obj,
+ %key st in %obj, %key nd in %obj, %key rd in %obj, %key th in %obj,
+ ..to
lua> ".."
local obj_lua = \(%obj as lua);
if not obj_lua:sub(-1,-1):match("[a-zA-Z)]") then
@@ -56,70 +59,101 @@ immediately
# Variable assignment operator, and += type versions
immediately
- compile [local %vars] to code
- lua> ".."
- local locals = \%vars.type == "List" and \%vars.value or {\%vars};
- local identifiers = {};
- for i,x in ipairs(locals) do
- identifiers[i] = nomsu:tree_to_lua(x).expr;
+ lua> ".."
+ nomsu:define_compile_action("%var <- %value", \(__line_no__), function(\%var, \%value)
+ local lua = {};
+ lua.statements = ("%s = %s;"):format(
+ assert(nomsu:tree_to_lua(\%var).expr, "Invalid target for assignment: "..\%var.src),
+ assert(nomsu:tree_to_lua(\%value).expr, "Invalid value for assignment: "..\%value.src));
+ if \%var.type == "Var" then
+ lua.locals = {nomsu:tree_to_lua(\%var).expr};
end
- return "local "..table.concat(identifiers, ", ");
- compile [set %var = %val] to code "\(%var as lua) = \(%val as lua);"
- compile [set %assignments] to code
- assume ((%assignments' "type") is "Dict") or barf "Expected Dict, but got \(%assignments' "type")"
- lua> ".."
- local lhs, rhs = {}, {};
- for i,entry in ipairs(\%assignments.value) do
- lhs[i] = nomsu:tree_to_lua(entry.dict_key).expr;
- rhs[i] = nomsu:tree_to_lua(entry.dict_value).expr;
+ return lua;
+ end, \(__src__ 1));
+ lua> ".."
+ nomsu:define_compile_action("with %assignments %body", \(__line_no__), function(\%assignments, \%body)
+ local body_lua = nomsu:tree_to_lua(\%body);
+ local declarations = "";
+ local leftover_locals = {};
+ for _, body_local in ipairs(body_lua.locals or {}) do
+ leftover_locals[body_local] = true;
+ end
+ assert(\%assignments.type == "List",
+ "Expected a List for the assignments part of 'with' statement, not "..\%assignments.src);
+ for i, item in ipairs(\%assignments.value) do
+ if item.type == "Var" then
+ local var = nomsu:tree_to_lua(item).expr;
+ leftover_locals[var] = nil;
+ declarations = declarations.."local "..var..";\\n ";
+ else
+ assert(item.type == "FunctionCall" and #item.value == 3 and item.value[2].src == "<-",
+ "'with' statement expects entries of the form: '%var <- %value', not: "..item.src);
+ local target, value = item.value[1], item.value[3];
+ if target.type == "Var" then
+ local var = nomsu:tree_to_lua(target).expr;
+ leftover_locals[var] = nil;
+ declarations = declarations..(("local %s = %s;\\n "):format(
+ var, assert(nomsu:tree_to_lua(value).expr, "Invalid value for assignment: "..value.src)));
+ else
+ declarations = declarations..(("%s = %s;\\n "):format(
+ assert(nomsu:tree_to_lua(target).expr, "Invalid target for assignment: "..target.src),
+ assert(nomsu:tree_to_lua(value).expr, "Invalid value for assignment: "..value.src)));
+ end
+ end
end
- return table.concat(lhs, ", ").." = "..table.concat(rhs, ", ")..";";
+ local code = ([[
+ do
+ %s%s
+ end]]):format(declarations, body_lua.statements or (body_lua.expr..";"));
+ return {statements=code, locals=utils.keys(leftover_locals)};
+ end, \(__src__ 1));
-# Update assignment operators
-compile [%var += %val] to code "\(%var as lua) = \(%var as lua) + \(%val as lua);"
-compile [%var -= %val] to code "\(%var as lua) = \(%var as lua) - \(%val as lua);"
-compile [%var *= %val] to code "\(%var as lua) = \(%var as lua) * \(%val as lua);"
-compile [%var /= %val] to code "\(%var as lua) = \(%var as lua) / \(%val as lua);"
-compile [%var ^= %val] to code "\(%var as lua) = \(%var as lua) ^ \(%val as lua);"
-compile [%var and= %val] to code "\(%var as lua) = \(%var as lua) and\(%val as lua);"
-compile [%var or= %val] to code "\(%var as lua) = \(%var as lua) or \(%val as lua);"
-compile [%var join= %val] to code "\(%var as lua) = \(%var as lua) .. \(%val as lua);"
-compile [wrap %var around %val] to code "\(%var as lua) = \(%var as lua) % \(%val as lua);"
+immediately
+ # Math Operators
+ compile [%x + %y] to "(\(%x as lua) + \(%y as lua))"
+ compile [%x - %y] to "(\(%x as lua) - \(%y as lua))"
+ compile [%x * %y] to "(\(%x as lua) * \(%y as lua))"
+ compile [%x / %y] to "(\(%x as lua) / \(%y as lua))"
+ compile [%x ^ %y] to "(\(%x as lua) ^ \(%y as lua))"
+ compile [%x wrapped around %y, %x mod %y] to "(\(%x as lua) % \(%y as lua))"
-# Math Operators
-compile [%x + %y] to "(\(%x as lua) + \(%y as lua))"
-compile [%x - %y] to "(\(%x as lua) - \(%y as lua))"
-compile [%x * %y] to "(\(%x as lua) * \(%y as lua))"
-compile [%x / %y] to "(\(%x as lua) / \(%y as lua))"
-compile [%x ^ %y] to "(\(%x as lua) ^ \(%y as lua))"
-compile [%x wrapped around %y, %x mod %y] to "(\(%x as lua) % \(%y as lua))"
+ # 3-part chained comparisons
+ # (uses a lambda to avoid re-evaluating middle value, while still being an expression)
+ parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)"
+ parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)"
+ parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)"
+ parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)"
+ parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)"
+ parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)"
+ parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)"
+ parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)"
+ # TODO: optimize for common case where x,y,z are all either variables or number literals
-# 3-part chained comparisons
-# (uses a lambda to avoid re-evaluating middle value, while still being an expression)
-parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)"
-parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)"
-parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)"
-parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)"
-parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)"
-parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)"
-parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)"
-parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)"
-# TODO: optimize for common case where x,y,z are all either variables or number literals
+ # Boolean Operators
+ compile [%x and %y] to "(\(%x as lua) and \(%y as lua))"
+ compile [%x or %y] to "(\(%x as lua) or \(%y as lua))"
-# Boolean Operators
-compile [%x and %y] to "(\(%x as lua) and \(%y as lua))"
-compile [%x or %y] to "(\(%x as lua) or \(%y as lua))"
+ # Bitwise Operators
+ compile [%a OR %b, %a | %b] to "bit32.bor(\(%a as lua), \(%b as lua))"
+ compile [%a XOR %b] to "bit32.bxor(\(%a as lua), \(%b as lua))"
+ compile [%a AND %b, %a & %b] to "bit32.band(\(%a as lua), \(%b as lua))"
+ compile [NOT %, ~ %] to "bit32.bnot(\(% as lua))"
+ compile [%x LSHIFT %shift, %x << %shift] to "bit32.lshift(\(%x as lua), \(%shift as lua))"
+ compile [%x RSHIFT %shift, %x >>> %shift] to "bit32.rshift(\(%x as lua), \(%shift as lua))"
+ compile [%x ARSHIFT %shift, %x >> %shift] to "bit32.arshift(\(%x as lua), \(%shift as lua))"
+ # TODO: implement OR, XOR, AND for multiple operands?
-# Bitwise Operators
-compile [%a OR %b, %a | %b] to "bit32.bor(\(%a as lua), \(%b as lua))"
-compile [%a XOR %b] to "bit32.bxor(\(%a as lua), \(%b as lua))"
-compile [%a AND %b, %a & %b] to "bit32.band(\(%a as lua), \(%b as lua))"
-compile [NOT %, ~ %] to "bit32.bnot(\(% as lua))"
-compile [%x LSHIFT %shift, %x << %shift] to "bit32.lshift(\(%x as lua), \(%shift as lua))"
-compile [%x RSHIFT %shift, %x >>> %shift] to "bit32.rshift(\(%x as lua), \(%shift as lua))"
-compile [%x ARSHIFT %shift, %x >> %shift] to "bit32.arshift(\(%x as lua), \(%shift as lua))"
-# TODO: implement OR, XOR, AND for multiple operands?
+ # Unary operators
+ compile [- %] to "(- \(% as lua))"
+ compile [not %] to "(not \(% as lua))"
-# Unary operators
-compile [- %] to "-(\(% as lua))"
-compile [not %] to "not (\(% as lua))"
+# Update operators
+immediately
+ parse [<- %var + %] as: %var <- (%var + %)
+ parse [<- %var - %] as: %var <- (%var - %)
+ parse [<- %var * %] as: %var <- (%var * %)
+ parse [<- %var / %] as: %var <- (%var / %)
+ parse [<- %var ^ %] as: %var <- (%var ^ %)
+ parse [<- %var and %] as: %var <- (%var and %)
+ parse [<- %var or %] as: %var <- (%var or %)
+ parse [wrap %var around %] as: "\(%var as lua) = \(%var as lua) % \(% as lua);"
diff --git a/nomsu.lua b/nomsu.lua
index 8042e9b..ba6a834 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -21,6 +21,12 @@ do
local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end
+do
+ local STRING_METATABLE = getmetatable("")
+ STRING_METATABLE.__add = function(self, other)
+ return self .. stringify(other)
+ end
+end
lpeg.setmaxstack(10000)
local P, R, V, S, Cg, C, Cp, B, Cmt
P, R, V, S, Cg, C, Cp, B, Cmt = lpeg.P, lpeg.R, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt
@@ -416,6 +422,10 @@ do
assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type))
local lua = self:tree_to_lua(tree)
local lua_code = lua.statements or (lua.expr .. ";")
+ local locals = lua_code.locals or { }
+ if #locals > 0 then
+ lua_code = "local " .. concat(locals, ", ") .. ";\n" .. lua_code
+ end
lua_code = "-- File: " .. tostring(filename) .. "\n" .. lua_code
local ret = self:run_lua(lua_code)
if max_operations then
@@ -875,6 +885,7 @@ do
if #tree.value == 1 then
return self:tree_to_lua(tree.value[1])
end
+ local declared_locals = { }
local lua_bits = { }
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
@@ -883,10 +894,32 @@ do
if not lua then
error("No lua produced by " .. tostring(repr(line)))
end
+ if lua.locals then
+ local new_locals
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_1 = lua.locals
+ for _index_1 = 1, #_list_1 do
+ local l = _list_1[_index_1]
+ if not declared_locals[l] then
+ _accum_0[_len_0] = l
+ _len_0 = _len_0 + 1
+ end
+ end
+ new_locals = _accum_0
+ end
+ if #new_locals > 0 then
+ insert(lua_bits, "local " .. tostring(concat(new_locals, ", ")) .. ";")
+ for _index_1 = 1, #new_locals do
+ local l = new_locals[_index_1]
+ declared_locals[l] = true
+ end
+ end
+ end
if lua.statements then
insert(lua_bits, lua.statements)
- end
- if lua.expr then
+ elseif lua.expr then
insert(lua_bits, tostring(lua.expr) .. ";")
end
end
@@ -903,6 +936,7 @@ do
}
elseif "Block" == _exp_0 then
local lua_bits = { }
+ local locals = { }
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
local arg = _list_0[_index_0]
@@ -912,15 +946,22 @@ do
expr = lua.expr
}
end
+ if lua.locals then
+ local _list_1 = lua.locals
+ for _index_1 = 1, #_list_1 do
+ local l = _list_1[_index_1]
+ locals[l] = true
+ end
+ end
if lua.statements then
insert(lua_bits, lua.statements)
- end
- if lua.expr then
+ elseif lua.expr then
insert(lua_bits, tostring(lua.expr) .. ";")
end
end
return {
- statements = concat(lua_bits, "\n")
+ statements = concat(lua_bits, "\n"),
+ locals = (next(locals) and utils.keys(locals) or nil)
}
elseif "FunctionCall" == _exp_0 then
insert(self.compilestack, tree)
@@ -981,7 +1022,7 @@ do
insert(bits, tok.value)
else
local lua = self:tree_to_lua(tok)
- assert(lua.statements == nil, "non-expression value inside math expression")
+ assert(lua.expr, "non-expression value inside math expression: " .. tostring(tok.src))
insert(bits, lua.expr)
end
end
@@ -1001,7 +1042,7 @@ do
break
end
local lua = self:tree_to_lua(tok)
- assert(lua.expr, "Cannot use " .. tostring(tok.src) .. " as an argument, since it's not an expression, it produces: " .. tostring(repr(lua)))
+ assert(lua.expr, tostring(tree:get_line_no()) .. ": Cannot use:\n" .. tostring(colored.yellow(tok.src)) .. "\nas an argument to " .. tostring(tree.stub) .. ", since it's not an expression, it produces: " .. tostring(repr(lua)))
insert(args, lua.expr)
_continue_0 = true
until true
@@ -1051,9 +1092,7 @@ do
self:print_tree(bit)
self:writeln(tostring(colored.bright("EXPR:")) .. " " .. tostring(lua.expr) .. ", " .. tostring(colored.bright("STATEMENT:")) .. " " .. tostring(lua.statements))
end
- if lua.statements then
- error("Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
- end
+ assert(lua.expr, "Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
insert(concat_parts, "stringify(" .. tostring(lua.expr) .. ")")
_continue_0 = true
until true
@@ -1083,9 +1122,7 @@ do
for _index_0 = 1, #_list_0 do
local item = _list_0[_index_0]
local lua = self:tree_to_lua(item)
- if lua.statements then
- error("Cannot use [[" .. tostring(item.src) .. "]] as a list item, since it's not an expression.")
- end
+ assert(lua.expr, "Cannot use [[" .. tostring(item.src) .. "]] as a list item, since it's not an expression.")
insert(items, lua.expr)
end
return {
@@ -1104,13 +1141,9 @@ do
else
key_lua = self:tree_to_lua(entry.dict_key)
end
- if key_lua.statements then
- error("Cannot use [[" .. tostring(entry.dict_key.src) .. "]] as a dict key, since it's not an expression.")
- end
+ assert(key_lua.expr, "Cannot use [[" .. tostring(entry.dict_key.src) .. "]] as a dict key, since it's not an expression.")
local value_lua = self:tree_to_lua(entry.dict_value)
- if value_lua.statements then
- error("Cannot use [[" .. tostring(entry.dict_value.src) .. "]] as a dict value, since it's not an expression.")
- end
+ assert(value_lua.expr, "Cannot use [[" .. tostring(entry.dict_value.src) .. "]] as a dict value, since it's not an expression.")
local key_str = key_lua.expr:match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str then
insert(items, tostring(key_str) .. "=" .. tostring(value_lua.expr))
@@ -1338,9 +1371,7 @@ do
insert(concat_parts, bit)
else
local lua = nomsu:tree_to_lua(bit)
- if lua.statements then
- error("Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
- end
+ assert(lua.expr, "Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
insert(concat_parts, lua.expr)
end
end
@@ -1352,7 +1383,8 @@ do
lua_code = "-- Immediately:\n" .. lua_code
nomsu:run_lua(lua_code)
return {
- statements = lua_code
+ statements = lua_code,
+ locals = lua.locals
}
end)
self:define_compile_action("lua> %code", "nomsu.moon", function(_code)
diff --git a/nomsu.moon b/nomsu.moon
index 2fb5249..2ef3c6c 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -19,6 +19,12 @@ colors = setmetatable({}, {__index:->""})
colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..(msg or '')..colors.reset)})
{:insert, :remove, :concat} = table
+-- Use + operator for string coercive concatenation (note: "asdf" + 3 == "asdf3")
+-- Note: This globally affects all strings in this instance of Lua!
+do
+ STRING_METATABLE = getmetatable("")
+ STRING_METATABLE.__add = (other)=> @ .. stringify(other)
+
-- TODO:
-- consider non-linear codegen, rather than doing thunks for things like comprehensions
-- improve indentation of generated lua code
@@ -301,6 +307,9 @@ class NomsuCompiler
lua = @tree_to_lua(tree)
lua_code = lua.statements or (lua.expr..";")
+ locals = lua_code.locals or {}
+ if #locals > 0
+ lua_code = "local "..concat(locals, ", ")..";\n"..lua_code
lua_code = "-- File: #{filename}\n"..lua_code
ret = @run_lua(lua_code)
if max_operations
@@ -616,13 +625,19 @@ class NomsuCompiler
when "File"
if #tree.value == 1
return @tree_to_lua(tree.value[1])
+ declared_locals = {}
lua_bits = {}
for line in *tree.value
lua = @tree_to_lua line
if not lua
error "No lua produced by #{repr line}"
+ if lua.locals
+ new_locals = [l for l in *lua.locals when not declared_locals[l]]
+ if #new_locals > 0
+ insert lua_bits, "local #{concat new_locals, ", "};"
+ for l in *new_locals do declared_locals[l] = true
if lua.statements then insert lua_bits, lua.statements
- if lua.expr then insert lua_bits, "#{lua.expr};"
+ elseif lua.expr then insert lua_bits, "#{lua.expr};"
return statements:concat(lua_bits, "\n")
when "Comment"
@@ -633,13 +648,16 @@ class NomsuCompiler
when "Block"
lua_bits = {}
+ locals = {}
for arg in *tree.value
lua = @tree_to_lua arg
if #tree.value == 1 and lua.expr and not lua.statements
return expr:lua.expr
+ if lua.locals
+ for l in *lua.locals do locals[l] = true
if lua.statements then insert lua_bits, lua.statements
- if lua.expr then insert lua_bits, "#{lua.expr};"
- return statements:concat(lua_bits, "\n")
+ elseif lua.expr then insert lua_bits, "#{lua.expr};"
+ return statements:concat(lua_bits, "\n"), locals:(next(locals) and utils.keys(locals) or nil)
when "FunctionCall"
insert @compilestack, tree
@@ -668,7 +686,7 @@ class NomsuCompiler
insert bits, tok.value
else
lua = @tree_to_lua(tok)
- assert(lua.statements == nil, "non-expression value inside math expression")
+ assert(lua.expr, "non-expression value inside math expression: #{tok.src}")
insert bits, lua.expr
remove @compilestack
return expr:"(#{concat bits, " "})"
@@ -677,7 +695,8 @@ class NomsuCompiler
for tok in *tree.value
if tok.type == "Word" then continue
lua = @tree_to_lua(tok)
- assert(lua.expr, "Cannot use #{tok.src} as an argument, since it's not an expression, it produces: #{repr lua}")
+ assert lua.expr,
+ "#{tree\get_line_no!}: Cannot use:\n#{colored.yellow tok.src}\nas an argument to #{tree.stub}, since it's not an expression, it produces: #{repr lua}"
insert args, lua.expr
if metadata and metadata.arg_orders
@@ -702,8 +721,8 @@ class NomsuCompiler
@writeln (colored.bright "INTERP:")
@print_tree bit
@writeln "#{colored.bright "EXPR:"} #{lua.expr}, #{colored.bright "STATEMENT:"} #{lua.statements}"
- if lua.statements
- error "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
+ assert lua.expr,
+ "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
insert concat_parts, "stringify(#{lua.expr})"
if string_buffer ~= ""
@@ -719,8 +738,8 @@ class NomsuCompiler
items = {}
for item in *tree.value
lua = @tree_to_lua item
- if lua.statements
- error "Cannot use [[#{item.src}]] as a list item, since it's not an expression."
+ assert lua.expr,
+ "Cannot use [[#{item.src}]] as a list item, since it's not an expression."
insert items, lua.expr
return expr:@@comma_separated_items("{", items, "}")
@@ -731,11 +750,11 @@ class NomsuCompiler
{expr:repr(entry.dict_key.value)}
else
@tree_to_lua entry.dict_key
- if key_lua.statements
- error "Cannot use [[#{entry.dict_key.src}]] as a dict key, since it's not an expression."
+ assert key_lua.expr,
+ "Cannot use [[#{entry.dict_key.src}]] as a dict key, since it's not an expression."
value_lua = @tree_to_lua entry.dict_value
- if value_lua.statements
- error "Cannot use [[#{entry.dict_value.src}]] as a dict value, since it's not an expression."
+ assert value_lua.expr,
+ "Cannot use [[#{entry.dict_value.src}]] as a dict value, since it's not an expression."
key_str = key_lua.expr\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str
insert items, "#{key_str}=#{value_lua.expr}"
@@ -888,8 +907,8 @@ class NomsuCompiler
insert concat_parts, bit
else
lua = nomsu\tree_to_lua bit
- if lua.statements
- error "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
+ assert lua.expr,
+ "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
insert concat_parts, lua.expr
return concat(concat_parts)
@@ -898,7 +917,7 @@ class NomsuCompiler
lua_code = lua.statements or (lua.expr..";")
lua_code = "-- Immediately:\n"..lua_code
nomsu\run_lua(lua_code)
- return statements:lua_code
+ return statements:lua_code, locals:lua.locals
@define_compile_action "lua> %code", "nomsu.moon", (_code)->
lua = nomsu_string_as_lua(_code)