aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-01-25 17:34:49 -0800
committerBruce Hill <bitbucket@bruce-hill.com>2018-01-25 17:36:05 -0800
commitc79bea44016daf43f05300b772011b14125fa0df (patch)
tree10dbbc3bef495662be829b73893660622bd2d2c1
parentf769351556cceed58ab6bf844c671114ec1862c2 (diff)
Overhaul of compiling API (eliminated some of the expr/statements
helpers and forced the use of {expr=..., locals=...}-type syntax. This helped fix up all of the cases like loops where locals were being mishandled and led to some cleaner code.
-rw-r--r--lib/class.nom2
-rw-r--r--lib/collections.nom123
-rw-r--r--lib/control_flow.nom493
-rw-r--r--lib/math.nom110
-rw-r--r--lib/metaprogramming.nom245
-rw-r--r--lib/operators.nom185
-rw-r--r--lib/text.nom64
-rw-r--r--nomsu.lua374
-rwxr-xr-xnomsu.moon290
-rw-r--r--utils.lua27
10 files changed, 937 insertions, 976 deletions
diff --git a/lib/class.nom b/lib/class.nom
index e65fb81..b7506f3 100644
--- a/lib/class.nom
+++ b/lib/class.nom
@@ -57,7 +57,7 @@ compile [object %classname %class_body] to
body_lua,
repr(names), repr(\%line:get_line_no()), table.concat(args, ", "),
repr(repr(stub)), table.concat(arg_nomsus, ".."),
- repr(nomsu:dedent(\%line.src)),
+ repr(nomsu:dedent(\%line:get_src())),
\%class_identifier, repr(stub), repr(stub)));
return ".."
diff --git a/lib/collections.nom b/lib/collections.nom
index 15f27ed..c31bbee 100644
--- a/lib/collections.nom
+++ b/lib/collections.nom
@@ -12,7 +12,7 @@ use "lib/operators.nom"
compile [..]
%index st to last in %list, %index nd to last in %list, %index rd to last in %list
%index th to last in %list
-..to "utils.nth_to_last(\(%list as lua), \(%index as lua))"
+..to {expr:"utils.nth_to_last(\(%list as lua expr), \(%index as lua expr))"}
parse [first in %list, first %list] as: 1 st in %list
parse [last in %list, last %list] as: 1 st to last in %list
@@ -32,25 +32,28 @@ action [..]
if (%key is %item): return (no)
return (yes)
-compile [%list has key %index, %list has index %index] to ".."
- ((\(%list as lua))[\(%index as lua)] ~= nil)
+# Note: it's important to have the space after "[" to prevent confusion if %index is a string
+compile [%list has key %index, %list has index %index] to {..}
+ expr: ".."
+ ((\(%list as lua expr))[ \(%index as lua expr)] ~= nil)
+# Note: it's important to have the space after "[" to prevent confusion if %index is a string
compile [..]
%list doesn't have key %index, %list does not have key %index
%list doesn't have index %index, %list does not have index %index
-..to "((\(%list as lua))[\(%index as lua)] ~= nil)"
+..to {expr:"((\(%list as lua expr))[ \(%index as lua expr)] ~= nil)"}
compile [length of %list, size of %list, size %list, number of %list, len %list] to
- "utils.size(\(%list as lua))"
+ {expr:"utils.size(\(%list as lua expr))"}
compile [append %item to %list, add %item to %list] to
- "table.insert(\(%list as lua), \(%item as lua))"
+ {statements:"table.insert(\(%list as lua expr), \(%item as lua expr))"}
compile [pop from %list, remove last from %list] to
- "table.remove(\(%list as lua))"
+ {statements:"table.remove(\(%list as lua expr))"}
compile [remove index %index from %list] to
- "table.remove(\(%list as lua), \(%index as lua))"
+ {statements:"table.remove(\(%list as lua expr), \(%index as lua expr))"}
action [flatten %lists]
@@ -74,14 +77,15 @@ immediately
compile [%expression for %item in %iterable] to
assume ((%item's "type") is "Var") or barf ".."
List comprehension has the wrong type for the loop variable. Expected Var, but got: \(%item's "type")
- return ".."
- (function()
- local comprehension = {};
- for i,\(%item as lua) in ipairs(\(%iterable as lua)) do
- comprehension[i] = \(%expression as lua);
- end
- return comprehension;
- end)()
+ return {..}
+ expr:".."
+ (function()
+ local comprehension = {};
+ for i,\(%item as lua expr) in ipairs(\(%iterable as lua expr)) do
+ comprehension[i] = \(%expression as lua expr);
+ end
+ return comprehension;
+ end)()
parse [%expression for all %iterable] as: %expression for % in %iterable
compile [%expression for %key = %value in %iterable] to
@@ -89,28 +93,31 @@ immediately
List comprehension has the wrong type for the key loop variable. Expected Var, but got: \(%key's "type")
assume ((%value's "type") is "Var") or barf ".."
List comprehension has the wrong type for the value loop variable. Expected Var, but got: \(%value's "type")
- return ".."
- (function()
- local comprehension = {};
- for \(%key as lua), \(%value as lua) in pairs(\(%iterable as lua)) do
- comprehension[i] = \(%expression as lua)
- end
- return comprehension;
- end)()
+ return {..}
+ expr: ".."
+ (function()
+ local comprehension = {};
+ for \(%key as lua expr), \(%value as lua expr) in pairs(\(%iterable as lua expr)) do
+ comprehension[i] = \(%expression as lua expr)
+ end
+ return comprehension;
+ end)()
# Dict comprehensions
immediately
compile [%key = %value for %item in %iterable] to
assume ((%item's "type") is "Var") or barf ".."
Dict comprehension has the wrong type for the loop variable. Expected Var, but got: \(%item's "type")
- return ".."
- (function()
- local comprehension = {};
- for i,\(%item as lua) in ipairs(\(%iterable as lua)) do
- comprehension[\(%key as lua)] = \(%value as lua)
- end
- return comprehension;
- end)()
+ # Note: it's important to have the space after "[" to prevent confusion if %key is a string
+ return {..}
+ expr: ".."
+ (function()
+ local comprehension = {};
+ for i,\(%item as lua expr) in ipairs(\(%iterable as lua expr)) do
+ comprehension[ \(%key as lua expr)] = \(%value as lua expr)
+ end
+ return comprehension;
+ end)()
parse [%key = %value for all %iterable] as: %key = %value for % in %iterable
compile [%key = %value for %src_key = %src_value in %iterable] to
@@ -118,21 +125,24 @@ immediately
Dict comprehension has the wrong type for the key loop variable. Expected Var, but got: \(%src_key's "type")
assume ((%src_value's "type") is "Var") or barf ".."
Dict comprehension has the wrong type for the value loop variable. Expected Var, but got: \(%src_value's "type")
- return ".."
- (function()
- local comprehension = {};
- for \(%src_key as lua), \(%src_value as lua) in pairs(\(%iterable as lua)) do
- comprehension[\(%key as lua)] = \(%value as lua);
- end
- return comprehension;
- end)()
+ # Note: it's important to have the space after "[" to prevent confusion if %key is a string
+ return {..}
+ expr: ".."
+ (function()
+ local comprehension = {};
+ for \(%src_key as lua expr), \(%src_value as lua expr) in pairs(\(%iterable as lua expr)) do
+ comprehension[ \(%key as lua expr)] = \(%value as lua expr);
+ end
+ return comprehension;
+ end)()
# Sorting:
-compile [sort %items] to "table.sort(\(%items as lua))"
-compile [sort %items by %key_expr] to ".."
- utils.sort(\(%items as lua), function(\(\% as lua))
- return \(%key_expr as lua);
- end)
+compile [sort %items] to {statements:"table.sort(\(%items as lua expr))"}
+compile [sort %items by %key_expr] to {..}
+ statements: ".."
+ utils.sort(\(%items as lua expr), function(\(\% as lua expr))
+ return \(%key_expr as lua expr);
+ end)
action [%items sorted]
%copy <- (% for all %items)
@@ -147,16 +157,17 @@ action [unique %items]
[%k for %k=%v in (%=(yes) for all %items)]
# Metatable stuff
-compile [set %dict's metatable to %metatable] to code ".."
- setmetatable(\(%dict as lua), \(%metatable as lua));
-
-compile [new counter] to "setmetatable({}, {__index=function() return 0; end})"
-
-compile [new default dict] to ".."
- setmetatable({}, {__index=function(self, key)
- t = {};
- self[key] = t;
- return t;
- end})"
+compile [set %dict's metatable to %metatable] to {..}
+ statements: "setmetatable(\(%dict as lua expr), \(%metatable as lua expr));"
+
+compile [new counter] to {expr:"setmetatable({}, {__index=function() return 0; end})"}
+
+compile [new default dict] to {..}
+ expr: ".."
+ setmetatable({}, {__index=function(self, key)
+ t = {};
+ self[key] = t;
+ return t;
+ end})
# TODO: maybe make a generator/coroutine?
diff --git a/lib/control_flow.nom b/lib/control_flow.nom
index 7347a29..a6f460a 100644
--- a/lib/control_flow.nom
+++ b/lib/control_flow.nom
@@ -8,27 +8,35 @@ use "lib/operators.nom"
# No-Op
immediately
- compile [do nothing] to code ""
-
-# Return
-immediately
- compile [return] to code "do return; end"
- compile [return %return_value] to code "do return \(%return_value as lua); end"
+ compile [do nothing] to {statements:""}
# Conditionals
immediately
- compile [if %condition %if_body] to code ".."
- if \(%condition as lua) then
- \(%if_body as lua statements)
- end --end if
+ compile [if %condition %if_body] to
+ %if_body <- (%if_body as lua)
+ return {..}
+ locals: %if_body's "locals"
+ statements:".."
+ if \(%condition as lua expr) then
+ \((%if_body's "statements") or "\(%if_body's "expr");")
+ 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 code ".."
- if \(%condition as lua) then
- \(%if_body as lua statements)
- else
- \(%else_body as lua statements)
- end --end if
+ 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's "statements") or "\(%if_body's "expr");")
+ else
+ \((%else_body's "statements") or "\(%else_body's "expr");")
+ end
# Conditional expression (ternary operator)
#.. Note: this uses a function instead of "(condition and if_expr or else_expr)"
@@ -43,151 +51,139 @@ 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'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))"
+ return {..}
+ expr:"(\(%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 ".."
- (function()
- if \(%condition as lua) then
- return \(%when_true_expr as lua);
- else
- return \(%when_false_expr as lua);
- end
- end)()
-
+ return {..}
+ expr: ".."
+ (function()
+ if \(%condition as lua expr) then
+ return \(%when_true_expr as lua expr);
+ else
+ return \(%when_false_expr as lua expr);
+ end
+ end)()
# GOTOs
immediately
- 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]);
+ compile [=== %label ===, --- %label ---, *** %label ***] to {..}
+ statements:"::label_\(nomsu "var_to_lua_identifier" [%label])::;"
+ compile [go to %label] to {..}
+ statements:"goto label_\(nomsu "var_to_lua_identifier" [%label]);"
# Basic loop control
immediately
- compile [do next] to code "continue;"
- compile [stop] to code "break;"
+ compile [do next] to {statements:"continue;"}
+ compile [stop] to {statements:"break;"}
# Helper function
immediately
- 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)
+ compile [if %tree has subtree %subtree where %condition %body] to
+ %body_lua <- (%body as lua)
+ %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
+ return {..}
+ locals: %body_lua's "locals"
+ statements:".."
+ for \(%subtree as lua expr) in coroutine.wrap(function() nomsu:walk_tree(\(%tree as lua expr)) end) do
+ if type(\(%subtree as lua expr)) == 'table' and \(%subtree as lua expr).type then
+ if \(%condition as lua expr) then
+ \%body_statements
+ break;
+ end
+ end
end
- end
- end
# While loops
immediately
- compile [do next repetition] to code "goto continue_repeat;"
- compile [stop repeating] to code "goto stop_repeat;"
- compile [repeat while %condition %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
+ compile [do next repetition] 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's "statements") or "\(%body_lua's "expr");")
+ if %body has subtree % where
+ ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repetition")
+ ..: <- %body_statments + "\n::continue_repeat::;"
%code <- ".."
- while \(%condition as lua) do
- \(%body as lua statements)\
- ..\%continue_labels
+ while \(%condition as lua expr) do
+ \%body_statements
end --while-loop
- for subtree % where
- ((%'s "type") = "FunctionCall") and ((%'s "stub") == "stop repeating")
- ..in %body
+ if %body has subtree % where
+ ((%'s "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
- %continue_labels <- "\n::continue_repeat::;"
- stop
- return %code
+ return {statements:%code, locals:%body_lua's "locals"}
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.
+ ..to
+ %body_lua <- (%body as lua)
+ %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
+ if %body has subtree % where
+ ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repetition")
+ ..: <- %body_statements + "\n::continue_repeat::;"
%code <- ".."
- for i=1,\(%n as lua) do
- \(%body as lua statements)\
- ..\%continue_labels
+ for i=1,\(%n as lua expr) do
+ \%body_statements
end --numeric for-loop
- %stop_labels <- ""
- for subtree % where
- ((%'s "type") = "FunctionCall") and ((%'s "stub") == "stop repeating")
- ..in %body
+ if %body has subtree % where
+ ((%'s "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
- %continue_labels <- "\n::continue_repeat::;"
- stop
- return %code
+ return {statements:%code, locals:%body_lua's "locals"}
# For loop control flow:
immediately
- compile [stop %var] to code ".."
- goto stop_\(nomsu "var_to_lua_identifier" [%var]);
- compile [do next %var] to code ".."
- goto continue_\(nomsu "var_to_lua_identifier" [%var]);
+ compile [stop %var] to {..}
+ statements:"goto stop_\(nomsu "var_to_lua_identifier" [%var]);"
+ compile [do next %var] to {..}
+ statements:"goto continue_\(nomsu "var_to_lua_identifier" [%var]);"
# Numeric range for loops
immediately
compile [..]
for %var from %start to %stop by %step %body
for %var from %start to %stop via %step %body
- ..to code
- %continue_labels <- ""
- for subtree % where
+ ..to
+ %body_lua <- (%body as lua)
+ %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
+ if %body has 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"
- %loop_var <- (%var as lua)
- %loop_var_shim <- ""
- ..else
- %loop_var <- "i"
- %loop_var_shim <- "\n\(%var as lua) = i;"
- # This trashes the loop variables, just like in Python.
+ ((%'s "stub") is "do next %") and
+ ((3rd in (%'s "value"))'s "value") is (%var's "value")
+ ..: <- %body_statements + "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
+
+ # This uses Lua's approach of only allowing loop-scoped variables in a loop
+ assume ((%var's "type") is "Var") or barf "Loop expected variable, not: \(%var's source code)"
%code <- ".."
- for \(%loop_var)=\(%start as lua),\(%stop as lua),\(%step as lua) do\
- ..\%loop_var_shim
- \(%body as lua statements)\
- ..\%continue_labels
+ for \(%var as lua expr)=\(%start as lua expr),\(%stop as lua expr),\(%step as lua expr) do
+ \%body_statements
end --numeric for-loop
- for subtree % where
+ if %body has subtree % where
((%'s "type") = "FunctionCall") and
- ((%'s "stub") == "stop %") and
- ((2nd in (%'s "value"))'s "src") == (%var's "src")
- ..in %body
+ ((%'s "stub") is "stop %") and
+ ((2nd in (%'s "value"))'s "value") is (%var's "value")
+ ..
%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
+ return {statements:%code, locals:%body_lua's "locals"}
immediately
parse [for %var from %start to %stop %body] as: for %var from %start to %stop via 1 %body
@@ -197,113 +193,89 @@ 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
+# For-each loop (lua's "ipairs()")
immediately
- compile [for %var in %iterable %body] to code
- %continue_labels <- ""
- for subtree % where
+ compile [for %var in %iterable %body] to
+ %body_lua <- (%body as lua)
+ %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
+ if %body has 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"
- %loop_var <- (%var as lua)
- %loop_var_shim <- ""
- ..else
- %loop_var <- "value"
- %loop_var_shim <- "\n\(%var as lua) = value;"
- # This trashes the loop variables, just like in Python.
+ ((%'s "stub") is "do next %") and
+ ((3rd in (%'s "value"))'s "value") is (%var's "value")
+ ..: <- %body_statements + "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
+ # This uses Lua's approach of only allowing loop-scoped variables in a loop
+ assume ((%var's "type") is "Var") or barf "Loop expected variable, not: \(%var's source code)"
%code <- ".."
- for i,\%loop_var in ipairs(\(%iterable as lua)) do\
- ..\%loop_var_shim
- \(%body as lua statements)\
- ..\%continue_labels
+ for i,\(%var as lua expr) in ipairs(\(%iterable as lua expr)) do
+ \%body_statements
end --foreach-loop
- for subtree % where
+ if %body has subtree % where
((%'s "type") = "FunctionCall") and
- ((%'s "stub") == "stop %") and
- ((2nd in (%'s "value"))'s "src") == (%var's "src")
- ..in %body
+ ((%'s "stub") is "stop %") and
+ ((2nd in (%'s "value"))'s "value") is (%var's "value")
+ ..
%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
+ return {statements:%code, locals:%body_lua's "locals"}
+
parse [for all %iterable %body] as: for % in %iterable %body
# Dict iteration (lua's "pairs()")
immediately
- compile [for %key = %value in %iterable %body] to code
- %continue_labels <- ""
- for subtree % where
+ compile [for %key = %value in %iterable %body] to
+ %body_lua <- (%body as lua)
+ %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
+ if %body has 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
+ ((%'s "stub") is "do next %") and
+ ((3rd in (%'s "value"))'s "value") is (%key's "value")
+ ..: <- %body_statements + "\n::continue_\(nomsu "var_to_lua_identifier" [%key])::;"
- for subtree % where
+ if %body has 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"
- %key_loop_var <- (%key as lua)
- %key_loop_var_shim <- ""
- ..else
- %key_loop_var <- "key"
- %key_loop_var_shim <- "\n\(%key as lua) = key;"
- if: (%value's "type") is "Var"
- %value_loop_var <- (%value as lua)
- %value_loop_var_shim <- ""
- ..else
- %value_loop_var <- "value"
- %value_loop_var_shim <- "\n\(%value as lua) = value;"
- # This trashes the loop variables, just like in Python.
+ ((%'s "stub") is "do next %") and
+ ((3rd in (%'s "value"))'s "value") is (%value's "value")
+ ..: <- %body_statements + "\n::continue_\(nomsu "var_to_lua_identifier" [%value])::;"
+
+ # This uses Lua's approach of only allowing loop-scoped variables in a loop
+ assume ((%key's "type") is "Var") or barf "Loop expected variable, not: \(%key's source code)"
+ assume ((%value's "type") is "Var") or barf "Loop expected variable, not: \(%value's source 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
+ for \(%key as lua expr),\(%value as lua expr) in pairs(\(%iterable as lua expr)) do
+ \%body_statements
end --foreach-loop
+
%stop_labels <- ""
- for subtree % where
+ if %body has 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
+ ((%'s "stub") is "stop %") and
+ ((2nd in (%'s "value"))'s "value") is (%key's "value")
+ ..: <- %stop_labels + "\n::stop_\(nomsu "var_to_lua_identifier" [%key])::;"
- for subtree % where
+ if %body has 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
+ ((%'s "stub") is "stop %") and
+ ((2nd in (%'s "value"))'s "value") is (%value's "value")
+ ..: <- %stop_labels + "\n::stop_\(nomsu "var_to_lua_identifier" [%value])::;"
if: %stop_labels is not ""
%code <- ".."
do -- scope for stopping for % = % loop
\%code\%stop_labels
end
- return %code
+ return {statements:%code, locals:%body_lua's "locals"}
# Switch statement/multi-branch if
immediately
- compile [when %body] to code
- %result <- ""
+ compile [when %body] to
+ %code <- ""
%fallthroughs <- []
+ %locals <- []
%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.
@@ -318,37 +290,44 @@ immediately
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)"
+ lua> "table.insert(\%fallthroughs, \%condition);"
do next %func_call
+ %action <- (%action as lua)
+ %action_statements <- ((%action's "statements") or "\(%action's "expr");")
+ for %local in ((%action's "locals") or [])
+ lua> "table.insert(\%locals, \%local);"
if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
-
- <- %result + ".."
+ <- %code + ".."
else
- \(%action as lua statements)
- stop
+ \%action_statements
+ %seen_else <- (yes)
..else
- %condition <- (%condition as lua)
+ assume (not %seen_else) or barf "'else' clause needs to be last in 'when' block"
+ %condition <- (%condition as lua expr)
for all %fallthroughs
<- %condition + " or \(% as lua)"
- <- %result + ".."
+ <- %code + ".."
\("if" if %is_first else "elseif") \%condition then
- \(%action as lua statements)
+ \%action_statements
%fallthroughs <- []
%is_first <- (no)
- if: %result is not ""
- <- %result + "\nend"
- return %result
+ assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block"
+ if: %code is not ""
+ <- %code + "\nend"
+ lua> "utils.deduplicate(\%locals);"
+ return {statements:%code, locals:%locals}
# Switch statement
immediately
- compile [when %branch_value = ? %body, when %branch_value is ? %body] to code
- %result <- ""
+ compile [when %branch_value = ? %body, when %branch_value is ? %body] to
+ %code <- ""
%fallthroughs <- []
+ %locals <- []
%is_first <- (yes)
%seen_else <- (no)
for %func_call in (%body's "value")
@@ -364,65 +343,82 @@ immediately
lua> "table.insert(\%fallthroughs, \%condition)"
do next %func_call
+ %action <- (%action as lua)
+ %action_statements <- ((%action's "statements") or "\(%action's "expr");")
+ for %local in ((%action's "locals") or [])
+ lua> "table.insert(\%locals, \%local);"
+
if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block"
- <- %result + ".."
+ <- %code + ".."
else
- \(%action as lua statements)
+ \%action_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))"
+ %clause <- "branch_value == (\(%condition as lua expr))"
..else
- %clause <- "utils.equivalent(branch_value, \(%condition as lua))"
+ %clause <- "utils.equivalent(branch_value, \(%condition as lua expr))"
for all %fallthroughs
if: ((%'s "type") is "Text") or ((%'s "type") is "Number")
- <- %clause + " or branch_value == (\(%condition as lua))"
+ <- %clause + " or branch_value == (\(%condition as lua expr))"
..else
- <- %clause + " or utils.equivalent(branch_value, \(%condition as lua))"
- <- %result + ".."
+ <- %clause + " or utils.equivalent(branch_value, \(%condition as lua expr))"
+ <- %code + ".."
\("if" if %is_first else "elseif") \%clause then
- \(%action as lua statements)
+ \%action_statements
%fallthroughs <- []
%is_first <- (no)
- assume (%result is not "") or barf "No body for 'when % = ?' block!"
+ assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block"
+ assume (%code is not "") or barf "No body for 'when % = ?' block!"
unless %seen_else
- <- %result + "\nend"
- %result <- ".."
+ <- %code + "\nend"
+ %code <- ".."
do --when % = ?
- local branch_value = \(%branch_value as lua);\
- ..\%result
+ local branch_value = \(%branch_value as lua expr);\
+ ..\%code
end --when % = ?
- return %result
+ lua> "utils.deduplicate(\%locals);"
+ return {statements:%code, locals:%locals}
# Try/except
immediately
compile [..]
try %action and if it succeeds %success or if it fails %fallback
try %action and if it fails %fallback or if it succeeds %success
- ..to code ".."
- do
- local fell_through = false;
- local ok, ret = pcall(function()
- \(%action as lua statements)
- fell_through = true;
- end);
- if ok then
- \(%success as lua statements)
- end
- if not ok then
- \(%fallback as lua statements)
- elseif not fell_through then
- return ret;
- end
- end
+ ..to
+ %locals <- []
+ %action <- (%action as lua)
+ %fallback <- (%fallback as lua)
+ for %sub_locals in [%action's "locals", %success's "locals", %fallback's "locals"]
+ for %local in %sub_locals
+ lua> "table.insert(\%locals, \%local);"
+ lua> "utils.deduplicate(\%locals);"
+ return {..}
+ locals: %locals
+ statements: ".."
+ do
+ local fell_through = false;
+ local ok, ret = pcall(function()
+ \((%action's "statements") or "\(%action's "expr");")
+ fell_through = true;
+ end);
+ if ok then
+ \((%success's "statements") or "\(%success's "expr");")
+ end
+ if not ok then
+ \((%fallback's "statements") or "\(%fallback's "expr");")
+ elseif not fell_through then
+ return ret;
+ end
+ end
parse [try %action] as
try %action and if it succeeds: do nothing
..or if it fails: do nothing
@@ -434,24 +430,39 @@ immediately
# Do/finally:
immediately
- compile [do %action] to code
- "do\n \(%action as lua statements)\nend" if ((%action's "type") is "Block")
- ..else "(\(%action as lua))(nomsu);"
-
- compile [do %action then always %final_action] to code ".."
- do
- local fell_through = false;
- local ok, ret1 = pcall(function()
- \(%action as lua statements)
- fell_through = true;
- end);
- local ok2, ret2 = pcall(function()
- \(%final_action as lua statements)
- end);
- if not ok then error(ret1); end
- if not ok2 then error(ret2); end
- if not fell_through then
- return ret1;
- end
- end
+ compile [do %action] to
+ %action <- (%action as lua)
+ return {..}
+ locals: %action's "locals"
+ statements: ".."
+ do
+ \((%action's "statements") or "\(%action's "expr");")
+ end
+
+ compile [do %action then always %final_action] to
+ %action <- (%action as lua)
+ %final_action <- (%action as lua)
+ %locals <- []
+ for %sub_locals in [%action's "locals", %final_action's "locals"]
+ for %local in %sub_locals
+ lua> "table.insert(\%locals, \%local);"
+ lua> "utils.deduplicate(\%locals);"
+ return {..}
+ locals: %locals
+ statements: ".."
+ do
+ local fell_through = false;
+ local ok, ret1 = pcall(function()
+ \((%action's "statements") or "\(%action's "expr");")
+ fell_through = true;
+ end);
+ local ok2, ret2 = pcall(function()
+ \((%final_action's "statements") or "\(%final_action's "expr");")
+ end);
+ if not ok then error(ret1); end
+ if not ok2 then error(ret2); end
+ if not fell_through then
+ return ret1;
+ end
+ end
diff --git a/lib/math.nom b/lib/math.nom
index 0210293..ea1769a 100644
--- a/lib/math.nom
+++ b/lib/math.nom
@@ -5,65 +5,71 @@ use "lib/metaprogramming.nom"
use "lib/control_flow.nom"
# Literals:
-compile [infinity, inf] to "math.huge"
-compile [not a number, NaN, nan] to "(0/0)"
-compile [pi, Pi, PI] to "math.pi"
-compile [tau, Tau, TAU] to "(2*math.pi)"
-compile [golden ratio] to "((1+math.sqrt(5))/2)"
-compile [e] to "math.e"
+compile [infinity, inf] to {expr:"math.huge"}
+compile [not a number, NaN, nan] to {expr:"(0/0)"}
+compile [pi, Pi, PI] to {expr:"math.pi"}
+compile [tau, Tau, TAU] to {expr:"(2*math.pi)"}
+compile [golden ratio] to {expr:"((1+math.sqrt(5))/2)"}
+compile [e] to {expr:"math.e"}
# Functions:
-compile [% as number] to "tonumber(\(% as lua))"
-compile [absolute value %, | % |, abs %] to "math.abs(\(% as lua))"
-compile [square root %, √%, sqrt %] to "math.sqrt(\(% as lua))"
-compile [sine %, sin %] to "math.sin(\(% as lua))"
-compile [cosine %, cos %] to "math.cos(\(% as lua))"
-compile [tangent %, tan %] to "math.tan(\(% as lua))"
-compile [arc sine %, asin %] to "math.asin(\(% as lua))"
-compile [arc cosine %, acos %] to "math.acos(\(% as lua))"
-compile [arc tangent %, atan %] to "math.atan(\(% as lua))"
-compile [arc tangent %y/%x, atan2 %y %x] to "math.atan2(\(%y as lua), \(%x as lua))"
-compile [hyperbolic sine %, sinh %] to "math.sinh(\(% as lua))"
-compile [hyperbolic cosine %, cosh %] to "math.cosh(\(% as lua))"
-compile [hyperbolic tangent %, tanh %] to "math.tanh(\(% as lua))"
-compile [e^%, exp %] to "math.exp(\(% as lua))"
-compile [natural log %, ln %, log %] to "math.log(\(% as lua))"
-compile [log % base %base, log_%base %, log base %base %] to "math.log(\(% as lua), \(%base as lua))"
-compile [floor %] to "math.floor(\(% as lua))"
-compile [ceiling %, ceil %] to "math.ceil(\(% as lua))"
-compile [round %, % rounded] to "math.floor(\(% as lua) + .5)"
+compile [% as number] to {expr:"tonumber(\(% as lua expr))"}
+compile [absolute value %, | % |, abs %] to {expr:"math.abs(\(% as lua expr))"}
+compile [square root %, √%, sqrt %] to {expr:"math.sqrt(\(% as lua expr))"}
+compile [sine %, sin %] to {expr:"math.sin(\(% as lua expr))"}
+compile [cosine %, cos %] to {expr:"math.cos(\(% as lua expr))"}
+compile [tangent %, tan %] to {expr:"math.tan(\(% as lua expr))"}
+compile [arc sine %, asin %] to {expr:"math.asin(\(% as lua expr))"}
+compile [arc cosine %, acos %] to {expr:"math.acos(\(% as lua expr))"}
+compile [arc tangent %, atan %] to {expr:"math.atan(\(% as lua expr))"}
+compile [arc tangent %y/%x, atan2 %y %x] to {expr:"math.atan2(\(%y as lua expr), \(%x as lua expr))"}
+compile [hyperbolic sine %, sinh %] to {expr:"math.sinh(\(% as lua expr))"}
+compile [hyperbolic cosine %, cosh %] to {expr:"math.cosh(\(% as lua expr))"}
+compile [hyperbolic tangent %, tanh %] to {expr:"math.tanh(\(% as lua expr))"}
+compile [e^%, exp %] to {expr:"math.exp(\(% as lua expr))"}
+compile [natural log %, ln %, log %] to {expr:"math.log(\(% as lua expr))"}
+compile [log % base %base, log_%base %, log base %base %] to {expr:"math.log(\(% as lua expr), \(%base as lua expr))"}
+compile [floor %] to {expr:"math.floor(\(% as lua expr))"}
+compile [ceiling %, ceil %] to {expr:"math.ceil(\(% as lua expr))"}
+compile [round %, % rounded] to {expr:"math.floor(\(% as lua expr) + .5)"}
action [%n to the nearest %rounder]
=lua "(\%rounder)*math.floor((\%n / \%rounder) + .5)"
# Any/all/none
-compile [all of %items, all %items] to
- "(\(joined ((% as lua) for all (%items' "value")) with " and "))"
- ..if ((%items' "type") is "List") else "utils.all(\(%items as lua))"
+compile [all of %items, all %items] to {..}
+ expr:
+ "(\(joined ((% as lua expr) for all (%items' "value")) with " and "))"
+ ..if ((%items' "type") is "List") else "utils.all(\(%items as lua expr))"
parse [not all of %items, not all %items] as: not (all of %items)
-compile [any of %items, any %items] to
- "(\(joined ((% as lua) for all (%items' "value")) with " or "))"
- ..if ((%items' "type") is "List") else "utils.any(\(%items as lua))"
+compile [any of %items, any %items] to {..}
+ expr:
+ "(\(joined ((% as lua expr) for all (%items' "value")) with " or "))"
+ ..if ((%items' "type") is "List") else "utils.any(\(%items as lua expr))"
parse [none of %items, none %items] as: not (any of %items)
-compile [sum of %items, sum %items] to
- "(\(joined ((% as lua) for all (%items' "value")) with " + "))"
- ..if ((%items' "type") is "List") else "utils.sum(\(%items as lua))"
-compile [product of %items, product %items] to
- "(\(joined ((% as lua) for all (%items' "value")) with " * "))"
- ..if ((%items' "type") is "List") else "utils.product(\(%items as lua))"
+compile [sum of %items, sum %items] to {..}
+ expr:
+ "(\(joined ((% as lua expr) for all (%items' "value")) with " + "))"
+ ..if ((%items' "type") is "List") else "utils.sum(\(%items as lua expr))"
+compile [product of %items, product %items] to {..}
+ expr:
+ "(\(joined ((% as lua expr) for all (%items' "value")) with " * "))"
+ ..if ((%items' "type") is "List") else "utils.product(\(%items as lua expr))"
action [avg of %items, average of %items]
=lua "(utils.sum(\%items)/#\%items)"
-compile [min of %items, smallest of %items, lowest of %items] to
- "utils.min(\(%items as lua))"
-compile [max of %items, biggest of %items, largest of %items, highest of %items] to
- "utils.max(\(%items as lua))"
-compile [min of %items by %value_expr] to ".."
- utils.min(\(%items as lua), function(\(\% as lua))
- return \(%value_expr as lua)
- end)
-compile [max of %items by %value_expr] to ".."
- utils.max(\(%items as lua), function(\(\% as lua))
- return \(%value_expr as lua)
- end)
+compile [min of %items, smallest of %items, lowest of %items] to {..}
+ expr:"utils.min(\(%items as lua expr))"
+compile [max of %items, biggest of %items, largest of %items, highest of %items] to {..}
+ expr:"utils.max(\(%items as lua expr))"
+compile [min of %items by %value_expr] to {..}
+ expr: ".."
+ utils.min(\(%items as lua expr), function(\(\% as lua expr))
+ return \(%value_expr as lua expr)
+ end)
+compile [max of %items by %value_expr] to {..}
+ expr: ".."
+ utils.max(\(%items as lua expr), function(\(\% as lua expr))
+ return \(%value_expr as lua expr)
+ end)
# Random functions
action [seed random with %]
@@ -71,9 +77,9 @@ action [seed random with %]
math.randomseed(\%);
for i=1,20 do math.random(); end
parse [seed random] as: seed random with (=lua "os.time()")
-compile [random number, random, rand] to "math.random()"
-compile [random int %n, random integer %n, randint %n] to "math.random(\(%n as lua))"
+compile [random number, random, rand] to {expr:"math.random()"}
+compile [random int %n, random integer %n, randint %n] to {expr:"math.random(\(%n as lua expr))"}
compile [random from %low to %high, random number from %low to %high, rand %low %high] to
- "math.random(\(%low as lua), \(%high as lua))"
+ "math.random(\(%low as lua expr), \(%high as lua expr))"
action [random choice from %elements, random choice %elements, random %elements]
=lua "\%elements[math.random(#\%elements)]"
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
index e7de602..49e20ee 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -2,134 +2,99 @@
This File contains actions for making actions and compile-time actions and some helper
functions to make that easier.
-# Helper function
-immediately
- lua> ".."
- nomsu.parse_spec = function(nomsu, spec)
- if spec.type == 'List' then
- local names = {};
- for i, alias in ipairs(spec.value) do
- if alias.type == "FunctionCall" then
- names[i] = alias.src;
- elseif alias.type == "Text" then
- names[i] = nomsu:tree_to_value(alias);
- end
- end
- local junk, arg_names, junk = nomsu:get_stub(names[1]);
- local args = {};
- for i, a in ipairs(arg_names) do args[i] = nomsu:var_to_lua_identifier(a); end
- return names, args;
- else
- local alias = nomsu:tree_to_value(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
- return {alias}, args;
- end
- end
-
# Compile-time action to make compile-time actions:
-# TODO: reduce code duplication here
immediately
lua> ".."
- do
- local function compile_to(name_tree, body_tree, kind)
- local names, args = nomsu:parse_spec(name_tree);
- local declared_locals = {};
- for i, arg in ipairs(args) do declared_locals[arg] = true; end
- names, args = repr(names), table.concat(args, ", ");
- local body_lua = nomsu:tree_to_lua(body_tree);
- if body_lua.expr and not body_lua.locals then
- return [[
- nomsu:define_compile_action(]]..names..[[, ]]..repr(name_tree:get_line_no())..[[, function(]]..args..[[)
- return {]]..kind..[[=]]..body_lua.expr..[[};
- end, ]]..repr(nomsu:source_code())..[[);
- ]];
- end
- local body_code = body_lua.statements or ("return "..body_lua.expr..";");
- local undeclared_locals = {};
- for i, body_local in ipairs(body_lua.locals or {}) do
- if not declared_locals[body_local] then
- table.insert(undeclared_locals, body_local);
- end
- end
- if #undeclared_locals > 0 then
- body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code;
+ nomsu:define_compile_action("compile %actions to %lua", \(!! code location !!), function(\%actions, \%lua)
+ local signature = {};
+ for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end
+ local stubs = nomsu:get_stubs_from_signature(signature);
+ local stub_args = nomsu:get_args_from_signature(signature);
+ local arg_set = {};
+ for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end
+ if \%lua.type == "Text" then
+ error("Invalid type for 'compile % to %', expected a dict with expr/statements, but got text.", 0);
+ end
+ local body_lua = nomsu:tree_to_lua(\%lua);
+ local body_code = body_lua.statements or ("return "..body_lua.expr..";");
+ local undeclared_locals = {};
+ for i, body_local in ipairs(body_lua.locals or {}) do
+ if not arg_set[body_local] then
+ table.insert(undeclared_locals, body_local);
end
- return [[
- do
- local function compile_action(]]..args..[[)
- ]]..body_code.."\\n"..[[
end
- nomsu:define_compile_action(]]..names..[[, ]]..repr(name_tree:get_line_no())..[[, function(]]..args..[[)
- return {]]..kind..[[=compile_action(]]..args..[[)};
- end, ]]..repr(nomsu:source_code())..[[);
- end]];
+ if #undeclared_locals > 0 then
+ body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code;
end
- local src = \(__src__ 1);
- nomsu:define_compile_action("compile %names to %body", \(__line_no__), function(\%names, \%body)
- return {statements=compile_to(\%names, \%body, "expr")};
- end, src);
- nomsu:define_compile_action("compile %names to code %body", \(__line_no__), function(\%names, \%body)
- return {statements=compile_to(\%names, \%body, "statements")};
- end, src);
- end
+ local lua_fn_args = table.concat(stub_args[1], ", ");
+ local def_tree = nomsu.compilestack[#nomsu.compilestack];
+ local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop);
+ return {statements=([[
+ nomsu:define_compile_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[)
+ ]]..body_code.."\\n"..[[
+ end);
+ ]])};
+ end);
# Compile-time action to make actions
immediately
- compile [action %names %body] to code
+ compile [action %actions %body] to
lua> ".."
- local names, args = nomsu:parse_spec(\%names);
- local declared_locals = {};
- for i, arg in ipairs(args) do declared_locals[arg] = true; end
- names, args = repr(names), table.concat(args, ", ");
+ local signature = {};
+ for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end
+ local stubs = nomsu:get_stubs_from_signature(signature);
+ local stub_args = nomsu:get_args_from_signature(signature);
+ local arg_set = {};
+ for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end
local body_lua = nomsu:tree_to_lua(\%body);
local body_code = body_lua.statements or ("return "..body_lua.expr..";");
local undeclared_locals = {};
for i, body_local in ipairs(body_lua.locals or {}) do
- if not declared_locals[body_local] then
+ if not arg_set[body_local] then
table.insert(undeclared_locals, body_local);
end
end
if #undeclared_locals > 0 then
body_code = "local "..table.concat(undeclared_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_code, repr(src));
- return def_lua;
+ local lua_fn_args = table.concat(stub_args[1], ", ");
+ local def_tree = nomsu.compilestack[#nomsu.compilestack];
+ local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop);
+ return {statements=[[
+ nomsu:define_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[)
+ ]]..body_code.."\\n"..[[
+ end);
+ ]]};
# Macro to make nomsu macros:
immediately
- lua> ".."
- nomsu:define_compile_action("parse %shorthand as %longhand", \(__line_no__), (function(\%shorthand, \%longhand)
- local names, args = nomsu:parse_spec(\%shorthand);
- names, args = repr(names), table.concat(args, ", ");
+ compile [parse %shorthand as %longhand] to
+ lua> ".."
+ local signature = {};
+ for i, action in ipairs(\%shorthand.value) do signature[i] = action:get_src(); end
+ local stubs = nomsu:get_stubs_from_signature(signature);
+ local stub_args = nomsu:get_args_from_signature(signature);
+ local lua_fn_args = table.concat(stub_args[1], ", ");
local template;
if \%longhand.type == "Block" then
- template = {};
- for i, line in ipairs(\%longhand.value) do
- template[i] = nomsu:dedent(line.src);
- end
- template = repr(table.concat(template, "\\n"));
+ local lines = {};
+ for i, line in ipairs(\%longhand.value) do lines[i] = nomsu:dedent(line:get_src()); end
+ template = repr(table.concat(lines, "\\n"));
else
- template = repr(nomsu:dedent(\%longhand.src));
+ template = repr(nomsu:dedent(\%longhand:get_src()));
end
- local junk, arg_names, junk = nomsu:get_stub(\%shorthand.value[1]);
local replacements = {};
- for i, a in ipairs(arg_names) do replacements[i] = "["..repr(a).."]="..nomsu:var_to_lua_identifier(a); end
+ for i, a in ipairs(stub_args[1]) do replacements[i] = a.."="..a; end
replacements = "{"..table.concat(replacements, ", ").."}";
- local lua_code = ([[
- nomsu:define_compile_action(%s, %s, (function(%s)
- local template = nomsu:parse(%s, %s);
- local replacement = nomsu:tree_with_replaced_vars(template, %s);
- return nomsu:tree_to_lua(replacement);
- end), %s)]]):format(names, repr(\%shorthand:get_line_no()), args, template,
- repr(\%shorthand:get_line_no()), replacements, repr(nomsu:source_code(0)));
- return {statements=lua_code};
- end), \(__src__ 1));
+ local def_tree = nomsu.compilestack[#nomsu.compilestack];
+ local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop);
+ return {statements=[[
+ nomsu:define_compile_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[)
+ local template = nomsu:parse(]]..template..[[, ]]..repr(def_tree.filename)..[[);
+ local replacement = nomsu:tree_with_replaced_vars(template, ]]..replacements..[[);
+ return nomsu:tree_to_lua(replacement);
+ end);
+ ]]};
action [remove action %stub]
lua> ".."
@@ -143,33 +108,41 @@ action [remove action %stub]
immediately
action [%tree as lua]
- =lua "nomsu:tree_to_lua(\%tree).expr"
+ =lua "nomsu:tree_to_lua(\%tree)"
+
+ action [%tree as lua expr]
+ lua> ".."
+ local lua = nomsu:tree_to_lua(\%tree);
+ if lua.locals or not lua.expr then
+ error("Invalid thing to convert to lua expr: "..\%tree:get_src());
+ end
+ return lua.expr;
+
action [%tree as lua statements]
lua> ".."
local lua = nomsu:tree_to_lua(\%tree);
- return lua.statements or (lua.expr..";");
+ local code = lua.statements or (lua.expr..";");
+ if lua.locals then
+ code = "local "..table.concat(lua.locals, ", ")..";\\n"..code;
+ end
+ return code;
+
action [%tree as value]
=lua "nomsu:tree_to_value(\%tree)"
- compile [repr %obj] to
- "repr(\(%obj as lua))"
- compile [type of %obj] to
- "type(\(%obj as lua))"
-
+
immediately
- parse [lua do> %block] as
- lua> "do"
- lua> %block
- lua> "end"
+ compile [%tree's source code, %tree' source code] to {expr:"\(%tree as lua expr):get_src()"}
-compile [nomsu] to "nomsu"
+ compile [repr %obj] to {expr:"repr(\(%obj as lua expr))"}
+ compile [type of %obj] to {expr:"type(\(%obj as lua expr))"}
-compile [nomsu's %key] to "nomsu[\(%key as lua)]"
-compile [nomsu %method %args] to "nomsu[\(%method as lua)](nomsu, unpack(\(%args as lua)))"
-compile [tree %tree with %replacements] to ".."
- nomsu:tree_with_replaced_vars(\(%tree as lua), \(%replacements as lua))
+compile [nomsu] to {expr:"nomsu"}
+
+compile [nomsu's %key] to {expr:"nomsu[\(%key as lua expr)]"}
+compile [nomsu %method %args] to {expr:"nomsu[\(%method as lua expr)](nomsu, unpack(\(%args as lua expr)))"}
action [action %names metadata]
- =lua "ACTION_METADATA[ACTIONS[\%names]]"
+ =lua "ACTION_METADATA[ACTIONS[\%names]]"
# Get the source code for a function
action [help %action]
@@ -186,22 +159,34 @@ parse [run %code] as: nomsu "run" [%code]
parse [enable debugging] as: lua> "nomsu.debug = true"
parse [disable debugging] as: lua> "nomsu.debug = false"
-compile [say %message] to
- lua> ".."
- if \%message.type == "Text" then
- return "nomsu:writeln("..\(%message as lua)..")";
- else
- return "nomsu:writeln(stringify("..\(%message as lua).."))";
- end
+immediately
+ compile [say %message] to
+ lua> ".."
+ if \%message.type == "Text" then
+ return {statements="nomsu:writeln("..\(%message as lua expr)..");"};
+ else
+ return {statements="nomsu:writeln(stringify("..\(%message as lua expr).."));"};
+ end
+
+# Return
+immediately
+ #.. Return statement is wrapped in a do..end block because Lua is unhappy if you
+ put code after a return statement, unless you wrap it in a block.
+ compile [return] to {statements:"do return; end"}
+ compile [return %return_value] to {statements:"do return \(%return_value as lua expr); end"}
# Error functions
-compile [barf!] to "error(nil, 0)"
-compile [barf %msg] to "error(\(%msg as lua), 0)"
-compile [assume %condition] to "assert(\(%condition as lua))"
-compile [assume %condition or barf %msg] to "assert(\(%condition as lua), \(%msg as lua))"
+immediately
+ compile [barf!] to {statements:"error(nil, 0);"}
+ compile [barf %msg] to {statements:"error(\(%msg as lua expr), 0);"}
+ compile [assume %condition] to {..}
+ statements:"if not \(%condition as lua expr) then error('Assumption failed: '..\%condition:get_src(), 0); end"
+ compile [assume %condition or barf %msg] to {..}
+ statements:"if not \(%condition as lua expr) then error(\(%msg as lua expr), 0); end"
# Literals
-compile [yes] to "true"
-compile [no] to "false"
-compile [nothing, nil, null] to "nil"
+immediately
+ compile [yes] to {expr:"true"}
+ compile [no] to {expr:"false"}
+ compile [nothing, nil, null] to {expr:"nil"}
diff --git a/lib/operators.nom b/lib/operators.nom
index 32e29db..8be0ea0 100644
--- a/lib/operators.nom
+++ b/lib/operators.nom
@@ -16,138 +16,131 @@ immediately
%key st in %obj, %key nd in %obj, %key rd in %obj, %key th in %obj,
..to
lua> ".."
- local obj_lua = \(%obj as lua);
+ local obj_lua = \(%obj as lua expr);
if not obj_lua:sub(-1,-1):match("[a-zA-Z)]") then
obj_lua = "("..obj_lua..")";
end
- local key_lua = \(%key as lua);
+ local key_lua = \(%key as lua expr);
local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'")
or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"'));
if key_attr then
- return obj_lua.."."..key_attr;
+ return {expr=obj_lua.."."..key_attr};
elseif key_lua:sub(1,1) == "[" then
key_lua = " "..key_lua.." ";
end
- return obj_lua.."["..key_lua.."]";
+ return {expr=obj_lua.."["..key_lua.."]"};
# Comparison Operators
immediately
- 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 {expr:"(\(%x as lua expr) < \(%y as lua expr))"}
+ compile [%x > %y] to {expr:"(\(%x as lua expr) > \(%y as lua expr))"}
+ compile [%x <= %y] to {expr:"(\(%x as lua expr) <= \(%y as lua expr))"}
+ compile [%x >= %y] to {expr:"(\(%x as lua expr) >= \(%y as lua expr))"}
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).expr, nomsu:tree_to_lua(\%b).expr;
if safe[\%a.type] or safe[\%b.type] then
- return "("..a_lua.." == "..b_lua..")";
+ return {expr="("..a_lua.." == "..b_lua..")"};
else
- return "utils.equivalent("..a_lua..", "..b_lua..")";
+ return {expr="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).expr, nomsu:tree_to_lua(\%b).expr;
if safe[\%a.type] or safe[\%b.type] then
- return "("..a_lua.." ~= "..b_lua..")";
+ return {expr="("..a_lua.." ~= "..b_lua..")"};
else
- return "(not utils.equivalent("..a_lua..", "..b_lua.."))";
+ return {expr="(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 "nomsu.ids[\(% as lua)]"
+ compile [%'s id, id of %] to {expr:"nomsu.ids[\(% as lua expr)]"}
# Variable assignment operator
immediately
- 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 lua;
- end, \(__src__ 1));
+ compile [%var <- %value] to
+ lua> "local \%var_lua = nomsu:tree_to_lua(\%var);"
+ assume (%var_lua's "expr") or barf "Invalid target for assignment: \(%var's source code)"
+ lua> "local \%value_lua = nomsu:tree_to_lua(\%value);"
+ assume (%value_lua's "expr") or barf "Invalid value for assignment: \(%value's source code)"
+ return {..}
+ statements:"\(%var_lua's "expr") = \(%value_lua's "expr");"
+ locals: =lua "(\%var.type == 'Var' and {\%var_lua.expr} or nil)"
- lua> ".."
- nomsu:define_compile_action("export %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));
- return lua;
- end, \(__src__ 1));
+immediately
+ compile [export %var <- %value] to
+ %var_lua <- (%var as lua)
+ assume (%var_lua's "expr") or barf "Invalid target for assignment: \(%var's source code)"
+ %value_lua <- (%value as lua)
+ assume (%value_lua's "expr") or barf "Invalid value for assignment: \(%value's source code)"
+ return {statements:"\(%var_lua's "expr") = \(%value_lua's "expr");"}
- 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);
+ compile [with %assignments %body] to
+ %body_lua <- (%body as lua)
+ %locals <- []
+ %declarations <- []
+ %leftover_locals <- (=lua "{unpack(\%body_lua.locals or {})}")
+ assume ((%assignments' "type") is "List") or barf ".."
+ Expected a List for the assignments part of 'with' statement, not \(%assignments' source code)
+ lua> ".."
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 ";
+ utils.remove_from_list(\%leftover_locals, var);
+ table.insert(\%locals, var);
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);
+ if not (item.type == "FunctionCall" and #item.value == 3 and item.value[2].value == "<-") then
+ error("'with' statement expects entries of the form: '%var <- %value', not: "..item:get_src());
+ end
local target, value = item.value[1], item.value[3];
+ local target_lua = nomsu:tree_to_lua(target);
+ if not target_lua.expr then error("Invalid target for assignment: "..target:get_src()); end
+ local value_lua = nomsu:tree_to_lua(value);
+ if not value_lua.expr then error("Invalid value for assignment: "..value:get_src()); end
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)));
+ utils.remove_from_list(\%leftover_locals, target_lua.expr);
end
+ table.insert(\%declarations, (("local %s = %s;\\n "):format(
+ target_lua.expr, value_lua.expr)));
end
end
- 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));
-
- lua> ".."
- nomsu:define_compile_action("exporting %exported %body", \(__line_no__), function(\%exported, \%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;
+ local locals_code = "";
+ if #\%locals > 0 then
+ locals_code = "\\nlocal "..table.concat(\%locals, ", ")..";";
+ end
+ local declaration_code = "";
+ if #\%declarations > 0 then
+ declaration_code = "\\n"..table.concat(\%declarations, "\\n");
end
- assert(\%exported.type == "List",
- "Expected a List for the export part of 'exporting' statement, not "..\%exported.src);
+ return {locals=\%leftover_locals, statements=([[
+ do%s%s
+ %s
+ end]]):format(locals_code, declaration_code, \%body_lua.statements or (\%body_lua.expr..";"))};
+
+ compile [exporting %exported %body] to
+ %body_lua <- (%body as lua)
+ %leftover_locals <- (=lua "{unpack(\%body_lua.locals or {})}")
+ assume ((%exported's "type") = "List") or barf ".."
+ Expected a List for the export part of 'exporting' statement, not \(%exported's source code)
+ lua> ".."
for i, item in ipairs(\%exported.value) do
- assert(item.type == "Var", "exporting statement expects Vars, not: "..item.src);
+ if item.type ~= "Var" then
+ error("'exporting' statement expects Vars, not: "..item:get_src());
+ end
local var = nomsu:tree_to_lua(item).expr;
- leftover_locals[var] = nil;
+ utils.remove_from_list(leftover_locals, var);
end
- 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));
+ return {locals:%leftover_locals, statements:=lua "\%body_lua.statements or (\%body_lua.expr..';')"}
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))"
+ compile [%x + %y] to {expr:"(\(%x as lua expr) + \(%y as lua expr))"}
+ compile [%x - %y] to {expr:"(\(%x as lua expr) - \(%y as lua expr))"}
+ compile [%x * %y] to {expr:"(\(%x as lua expr) * \(%y as lua expr))"}
+ compile [%x / %y] to {expr:"(\(%x as lua expr) / \(%y as lua expr))"}
+ compile [%x ^ %y] to {expr:"(\(%x as lua expr) ^ \(%y as lua expr))"}
+ compile [%x wrapped around %y, %x mod %y] to {expr:"(\(%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)
@@ -162,22 +155,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 "(\(%x as lua) and \(%y as lua))"
- compile [%x or %y] to "(\(%x as lua) or \(%y as lua))"
+ compile [%x and %y] to {expr:"(\(%x as lua expr) and \(%y as lua expr))"}
+ compile [%x or %y] to {expr:"(\(%x as lua expr) or \(%y as lua expr))"}
# 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))"
+ compile [%a OR %b, %a | %b] to {expr:"bit32.bor(\(%a as lua expr), \(%b as lua expr))"}
+ compile [%a XOR %b] to {expr:"bit32.bxor(\(%a as lua expr), \(%b as lua expr))"}
+ compile [%a AND %b, %a & %b] to {expr:"bit32.band(\(%a as lua expr), \(%b as lua expr))"}
+ compile [NOT %, ~ %] to {expr:"bit32.bnot(\(% as lua expr))"}
+ compile [%x LSHIFT %shift, %x << %shift] to {expr:"bit32.lshift(\(%x as lua expr), \(%shift as lua expr))"}
+ compile [%x RSHIFT %shift, %x >>> %shift] to {expr:"bit32.rshift(\(%x as lua expr), \(%shift as lua expr))"}
+ compile [%x ARSHIFT %shift, %x >> %shift] to {expr:"bit32.arshift(\(%x as lua expr), \(%shift as lua expr))"}
# TODO: implement OR, XOR, AND for multiple operands?
# Unary operators
- compile [- %] to "(- \(% as lua))"
- compile [not %] to "(not \(% as lua))"
+ compile [- %] to {expr:"(- \(% as lua expr))"}
+ compile [not %] to {expr:"(not \(% as lua expr))"}
# Update operators
immediately
@@ -188,4 +181,4 @@ immediately
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);"
+ parse [wrap %var around %] as: %var <- (%var wrapped around %)
diff --git a/lib/text.nom b/lib/text.nom
index 0bd7a8a..982db63 100644
--- a/lib/text.nom
+++ b/lib/text.nom
@@ -13,41 +13,43 @@ action [%texts joined with %glue]
parse [joined %texts, %texts joined] as: %texts joined with ""
compile [capitalized %text capitalized] to
- "(\(%text as lua)):gsub('%l', string.upper, 1)"
+ {expr:"(\(%text as lua expr)):gsub('%l', string.upper, 1)"}
compile [%text with %sub instead of %patt, %text s/%patt/%sub] to
- "((\(%text as lua)):gsub(\(%patt as lua), \(%sub as lua)))"
+ {expr:"((\(%text as lua expr)):gsub(\(%patt as lua expr), \(%sub as lua expr)))"}
-compile [indented %text, %text indented] to "\%text:gsub('\\n','\\n'..(' '))"
-compile [dedented %obj, %obj dedented] to "nomsu:dedent(\(%obj as lua))"
-compile [%text indented %n times] to "\%text:gsub('\\n','\\n'..(' '):rep(\%n))"
+compile [indented %text, %text indented] to {expr:"\%text:gsub('\\n','\\n'..(' '))"}
+compile [dedented %obj, %obj dedented] to {expr:"nomsu:dedent(\(%obj as lua expr))"}
+compile [%text indented %n times] to {expr:"\%text:gsub('\\n','\\n'..(' '):rep(\%n))"}
# Text literals
-lua do> ".."
- local escapes = {
- nl="\\\\n", newline="\\\\n", tab="\\\\t", bell="\\\\a", cr="\\\\r", ["carriage return"]="\\\\r",
- backspace="\\\\b", ["form feed"]="\\\\f", formfeed="\\\\f", ["vertical tab"]="\\\\v",
- };
- local colors = {
- ["reset color"]="\\\\27[0m", bright="\\\\27[1m", dim="\\\\27[2m", underscore="\\\\27[4m",
- blink="\\\\27[5m", inverse="\\\\27[7m", hidden="\\\\27[8m",
-
- black="\\\\27[30m", red="\\\\27[31m", green="\\\\27[32m", yellow="\\\\27[33m", blue="\\\\27[34m",
- magenta="\\\\27[35m", cyan="\\\\27[36m", white="\\\\27[37m",
-
- ["on black"]="\\\\27[40m", ["on red"]="\\\\27[41m", ["on green"]="\\\\27[42m", ["on yellow"]="\\\\27[43m",
- ["on blue"]="\\\\27[44m", ["on magenta"]="\\\\27[45m", ["on cyan"]="\\\\27[46m", ["on white"]="\\\\27[47m",
- };
- for name, e in pairs(escapes) do
- local lua = "'"..e.."'";
- nomsu:define_compile_action(name, \(__line_no__), function() return {expr=text}; end, \(__src__ 1));
- end
- for name, c in pairs(colors) do
- local color = "'"..c.."'";
- local reset = "'"..colors["reset color"].."'";
- nomsu:define_compile_action(name, \(__line_no__), function() return {expr=color}; end, \(__src__ 1));
- nomsu:define_compile_action(name.." %", \(__line_no__), function(\%)
- return {expr=color..".."..nomsu:tree_to_lua(\%).expr..".."..reset};
- end, \(__src__ 1));
+lua> ".."
+ do
+ local escapes = {
+ nl="\\\\n", newline="\\\\n", tab="\\\\t", bell="\\\\a", cr="\\\\r", ["carriage return"]="\\\\r",
+ backspace="\\\\b", ["form feed"]="\\\\f", formfeed="\\\\f", ["vertical tab"]="\\\\v",
+ };
+ for name, e in pairs(escapes) do
+ local lua = "'"..e.."'";
+ nomsu:define_compile_action(name, \(!! code location !!), function() return {expr=text}; end);
+ end
+ local colors = {
+ ["reset color"]="\\\\27[0m", bright="\\\\27[1m", dim="\\\\27[2m", underscore="\\\\27[4m",
+ blink="\\\\27[5m", inverse="\\\\27[7m", hidden="\\\\27[8m",
+
+ black="\\\\27[30m", red="\\\\27[31m", green="\\\\27[32m", yellow="\\\\27[33m", blue="\\\\27[34m",
+ magenta="\\\\27[35m", cyan="\\\\27[36m", white="\\\\27[37m",
+
+ ["on black"]="\\\\27[40m", ["on red"]="\\\\27[41m", ["on green"]="\\\\27[42m", ["on yellow"]="\\\\27[43m",
+ ["on blue"]="\\\\27[44m", ["on magenta"]="\\\\27[45m", ["on cyan"]="\\\\27[46m", ["on white"]="\\\\27[47m",
+ };
+ for name, c in pairs(colors) do
+ local color = "'"..c.."'";
+ local reset = "'"..colors["reset color"].."'";
+ nomsu:define_compile_action(name, \(!! code location !!), function() return {expr=color}; end);
+ nomsu:define_compile_action(name.." %", \(!! code location !!), function(\%)
+ return {expr=color..".."..nomsu:tree_to_lua(\%).expr..".."..reset};
+ end);
+ end
end
diff --git a/nomsu.lua b/nomsu.lua
index 282831c..5689426 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -35,9 +35,6 @@ do
return string[i]
end
end
- STRING_METATABLE.__mul = function(self, other)
- return string.rep(self, other)
- end
end
lpeg.setmaxstack(10000)
local P, R, V, S, Cg, C, Cp, B, Cmt
@@ -45,7 +42,7 @@ P, R, V, S, Cg, C, Cp, B, Cmt = lpeg.P, lpeg.R, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C,
local NOMSU_DEFS
do
local _with_0 = { }
- _with_0.nl = P("\n")
+ _with_0.nl = P("\r") ^ -1 * P("\n")
_with_0.ws = S(" \t")
_with_0.tonumber = tonumber
_with_0.print = function(src, pos, msg)
@@ -100,8 +97,8 @@ do
end
end)
_with_0.error = function(src, pos, err_msg)
- if lpeg.userdata.source_code:sub(pos, pos) == "\n" then
- pos = pos + #lpeg.userdata.source_code:match("[ \t\n]*", pos)
+ if lpeg.userdata.source_code:sub(pos, pos):match("[\r\n]") then
+ pos = pos + #lpeg.userdata.source_code:match("[ \t\n\r]*", pos)
end
local line_no = 1
while (lpeg.userdata.line_starts[line_no + 1] or math.huge) < pos do
@@ -109,14 +106,14 @@ do
end
local prev_line
if line_no > 1 then
- prev_line = lpeg.userdata.source_code:match("[^\n]*", lpeg.userdata.line_starts[line_no - 1])
+ prev_line = lpeg.userdata.source_code:match("[^\r\n]*", lpeg.userdata.line_starts[line_no - 1])
else
prev_line = ""
end
- local err_line = lpeg.userdata.source_code:match("[^\n]*", lpeg.userdata.line_starts[line_no])
+ local err_line = lpeg.userdata.source_code:match("[^\r\n]*", lpeg.userdata.line_starts[line_no])
local next_line
if line_no < #lpeg.userdata.line_starts then
- next_line = lpeg.userdata.source_code:match("[^\n]*", lpeg.userdata.line_starts[line_no + 1])
+ next_line = lpeg.userdata.source_code:match("[^\r\n]*", lpeg.userdata.line_starts[line_no + 1])
else
next_line = ""
end
@@ -138,13 +135,14 @@ do
end)(), " ")
local src = lpeg.userdata.source_code:sub(start, stop - 1)
return {
+ type = "FunctionCall",
start = start,
stop = stop,
- type = "FunctionCall",
- src = src,
- get_line_no = lpeg.userdata.get_line_no,
value = value,
- stub = stub
+ stub = stub,
+ filename = lpeg.userdata.filename,
+ get_line_no = lpeg.userdata.get_line_no,
+ get_src = lpeg.userdata.get_src
}
end
NOMSU_DEFS = _with_0
@@ -154,12 +152,13 @@ setmetatable(NOMSU_DEFS, {
local make_node
make_node = function(start, value, stop)
return {
+ type = key,
start = start,
stop = stop,
value = value,
- src = lpeg.userdata.source_code:sub(start, stop - 1),
- get_line_no = lpeg.userdata.get_line_no,
- type = key
+ filename = lpeg.userdata.filename,
+ get_src = lpeg.userdata.get_src,
+ get_line_no = lpeg.userdata.get_line_no
}
end
self[key] = make_node
@@ -183,28 +182,28 @@ end
local NomsuCompiler
do
local _class_0
+ local line_counter, stub_defs, stub_pattern, var_pattern
local _base_0 = {
writeln = function(self, ...)
self:write(...)
return self:write("\n")
end,
- errorln = function(self, ...)
- self:write_err(...)
- return self:write_err("\n")
- end,
- define_action = function(self, signature, line_no, fn, src, compile_time)
- if compile_time == nil then
- compile_time = false
+ define_action = function(self, signature, source, fn)
+ if self.debug then
+ self:writeln(tostring(colored.bright("DEFINING ACTION:")) .. " " .. tostring(colored.green(repr(signature))))
+ end
+ if type(fn) ~= 'function' then
+ error('function', "Bad fn: " .. tostring(repr(fn)))
end
if type(signature) == 'string' then
- signature = self:get_stubs({
+ signature = {
signature
- })
- elseif type(signature) == 'table' and type(signature[1]) == 'string' then
- signature = self:get_stubs(signature)
+ }
+ elseif type(signature) ~= 'table' or signature.type ~= nil then
+ error("Invalid signature, expected list of strings, but got: " .. tostring(repr(signature)), 0)
end
- assert(type(fn) == 'function', "Bad fn: " .. tostring(repr(fn)))
- local aliases = { }
+ local stubs = self:get_stubs_from_signature(signature)
+ local stub_args = self:get_args_from_signature(signature)
self.__class.def_number = self.__class.def_number + 1
local fn_info = debug.getinfo(fn, "u")
local fn_arg_positions, arg_orders
@@ -218,11 +217,10 @@ do
end
arg_orders = { }
end
- for sig_i = 1, #signature do
- local stub, arg_names = unpack(signature[sig_i])
- assert(stub, "NO STUB FOUND: " .. tostring(repr(signature)))
+ for sig_i = 1, #stubs do
+ local stub, args = stubs[sig_i], stub_args[sig_i]
if self.debug then
- self:writeln(tostring(colored.bright("DEFINING ACTION:")) .. " " .. tostring(colored.underscore(colored.magenta(repr(stub)))) .. " " .. tostring(colored.bright("WITH ARGS")) .. " " .. tostring(colored.dim(repr(arg_names))) .. " ON: " .. tostring(self.environment.ACTIONS))
+ self:writeln(tostring(colored.bright("ALIAS:")) .. " " .. tostring(colored.underscore(colored.magenta(repr(stub)))) .. " " .. tostring(colored.bright("WITH ARGS")) .. " " .. tostring(colored.dim(repr(args))) .. " ON: " .. tostring(self.environment.ACTIONS))
end
self.environment.ACTIONS[stub] = fn
if not (fn_info.isvararg) then
@@ -230,33 +228,31 @@ do
do
local _accum_0 = { }
local _len_0 = 1
- for _index_0 = 1, #arg_names do
- local a = arg_names[_index_0]
- _accum_0[_len_0] = fn_arg_positions[self:var_to_lua_identifier(a)]
+ for _index_0 = 1, #args do
+ local a = args[_index_0]
+ _accum_0[_len_0] = fn_arg_positions[a]
_len_0 = _len_0 + 1
end
arg_positions = _accum_0
end
- assert(#arg_positions == #arg_names, "Mismatch in args between lua function's " .. tostring(repr(fn_arg_positions)) .. " and stub's " .. tostring(repr(arg_names)))
+ if #arg_positions ~= #args then
+ error("Mismatch in args between lua function's " .. tostring(repr(fn_arg_positions)) .. " and stub's " .. tostring(repr(args)) .. " for " .. tostring(repr(stub)), 0)
+ end
arg_orders[stub] = arg_positions
end
end
self.action_metadata[fn] = {
fn = fn,
- src = src,
- line_no = line_no,
- aliases = aliases,
+ source = source,
+ aliases = stubs,
arg_orders = arg_orders,
arg_positions = fn_arg_positions,
def_number = self.__class.def_number
}
end,
- define_compile_action = function(self, signature, line_no, fn, src)
- self:define_action(signature, line_no, fn, src, true)
+ define_compile_action = function(self, signature, source, fn, src)
+ self:define_action(signature, source, fn)
self.action_metadata[fn].compile_time = true
- if self.debug then
- return self:writeln(tostring(colored.bright(colored.green("(it was compile time)"))))
- end
end,
serialize_defs = function(self, scope, after)
if scope == nil then
@@ -265,68 +261,7 @@ do
if after == nil then
after = nil
end
- error("Not currently functional.")
- after = after or (self.core_defs or 0)
- scope = scope or self.defs
- local defs_by_num = { }
- for stub, def in pairs(scope) do
- if def and stub:sub(1, 1) ~= "#" then
- defs_by_num[def.def_number] = def
- end
- end
- local keys
- do
- local _accum_0 = { }
- local _len_0 = 1
- for k, v in pairs(defs_by_num) do
- _accum_0[_len_0] = k
- _len_0 = _len_0 + 1
- end
- keys = _accum_0
- end
- table.sort(keys)
- local buff = { }
- local k_i = 1
- local _using = nil
- local _using_do = { }
- for k_i, i in ipairs(keys) do
- local _continue_0 = false
- repeat
- if i <= after then
- _continue_0 = true
- break
- end
- local def = defs_by_num[i]
- if def.defs == scope then
- if def.src then
- insert(buff, def.src)
- end
- _continue_0 = true
- break
- end
- if _using == def.defs then
- if def.src then
- insert(_using_do, def.src)
- end
- else
- _using = def.defs
- _using_do = {
- def.src
- }
- end
- if k_i == #keys or defs_by_num[keys[k_i + 1]].defs ~= _using then
- insert(buff, "using:\n " .. tostring(self:indent(self:serialize_defs(_using))) .. "\n..do:\n " .. tostring(self:indent(concat(_using_do, "\n"))))
- end
- _continue_0 = true
- until true
- if not _continue_0 then
- break
- end
- end
- for k, v in pairs(scope["#vars"] or { }) do
- insert(buff, "<%" .. tostring(k) .. "> = " .. tostring(self:value_to_nomsu(v)))
- end
- return concat(buff, "\n")
+ return error("Not currently functional.", 0)
end,
dedent = function(self, code)
if not (code:find("\n")) then
@@ -336,7 +271,7 @@ do
for line in code:gmatch("\n([^\n]*)") do
local _continue_0 = false
repeat
- if line:match("^%s*#.*") then
+ if line:match("^%s*#.*") or line:match("^%s*$") then
_continue_0 = true
break
else
@@ -362,8 +297,10 @@ do
end
if spaces ~= math.huge and spaces < indent_spaces then
return (code:gsub("\n" .. (" "):rep(spaces), "\n"))
- else
+ elseif indent_spaces ~= math.huge then
return (code:gsub("\n" .. (" "):rep(indent_spaces), "\n "))
+ else
+ return code
end
end,
indent = function(self, code, levels)
@@ -377,7 +314,6 @@ do
if self.debug then
self:writeln(tostring(colored.bright("PARSING:")) .. "\n" .. tostring(colored.yellow(nomsu_code)))
end
- nomsu_code = nomsu_code:gsub("\r", "")
local userdata
do
local _with_0 = {
@@ -387,11 +323,14 @@ do
0
}
}
- _with_0.line_starts = re.compile("lines <- {| line ('\n' line)* |} line <- {} [^\n]*"):match(nomsu_code)
+ _with_0.get_src = function(self)
+ return nomsu_code:sub(self.start, self.stop - 1)
+ end
+ _with_0.line_starts = line_counter:match(_with_0.source_code)
_with_0.get_line_no = function(self)
if not (self._line_no) then
local line_no = 1
- while (_with_0.line_starts[line_no + 1] or math.huge) < self.start do
+ while line_no < #_with_0.line_starts and _with_0.line_starts[line_no + 1] < self.start do
line_no = line_no + 1
end
self._line_no = tostring(_with_0.filename) .. ":" .. tostring(line_no)
@@ -425,7 +364,7 @@ do
local timeout
timeout = function()
debug.sethook()
- return error("Execution quota exceeded. Your code took too long.")
+ return error("Execution quota exceeded. Your code took too long.", 0)
end
debug.sethook(timeout, "", max_operations)
end
@@ -434,9 +373,8 @@ 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
+ if lua_code.locals and #lua_code.locals > 0 then
+ lua_code = "local " .. concat(lua_code.locals, ", ") .. ";\n" .. lua_code
end
lua_code = "-- File: " .. tostring(filename) .. "\n" .. lua_code
local ret = self:run_lua(lua_code)
@@ -466,13 +404,13 @@ do
end
local file = file or io.open(filename)
if not file then
- error("File does not exist: " .. tostring(filename))
+ error("File does not exist: " .. tostring(filename), 0)
end
local nomsu_code = file:read('*a')
file:close()
return self:run(nomsu_code, filename)
else
- return error("Invalid filetype for " .. tostring(filename))
+ return error("Invalid filetype for " .. tostring(filename), 0)
end
end,
require_file = function(self, filename)
@@ -495,18 +433,21 @@ do
return ("\n%-3d|"):format(n)
end
local code = "1 |" .. lua_code:gsub("\n", fn)
- error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(code)))) .. "\n\n" .. tostring(err))
+ error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(code)))) .. "\n\n" .. tostring(err), 0)
end
return run_lua_fn()
end,
tree_to_value = function(self, tree, filename)
+ if tree.type == 'Text' and #tree.value == 1 and type(tree.value[1]) == 'string' then
+ return tree.value[1]
+ end
local code = "return " .. tostring(self:tree_to_lua(tree).expr) .. ";"
if self.debug then
self:writeln(tostring(colored.bright("RUNNING LUA TO GET VALUE:")) .. "\n" .. tostring(colored.blue(colored.bright(code))))
end
local lua_thunk, err = load(code, nil, nil, self.environment)
if not lua_thunk then
- error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(code)))) .. "\n\n" .. tostring(colored.red(err)))
+ error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(code)))) .. "\n\n" .. tostring(colored.red(err)), 0)
end
return lua_thunk()
end,
@@ -828,7 +769,7 @@ do
for _index_0 = 1, #_list_0 do
local line = _list_0[_index_0]
nomsu = expression(line)
- assert(nomsu, "Failed to produce output for:\n" .. tostring(colored.yellow(line.src)))
+ assert(nomsu, "Failed to produce output for:\n" .. tostring(colored.yellow(line:get_src())))
insert(lines, nomsu)
end
return concat(lines, "\n")
@@ -884,13 +825,13 @@ do
return '".."\n ' .. (self:indent(value))
end
else
- return error("Unsupported value_to_nomsu type: " .. tostring(type(value)))
+ return error("Unsupported value_to_nomsu type: " .. tostring(type(value)), 0)
end
end,
tree_to_lua = function(self, tree)
assert(tree, "No tree provided.")
if not tree.type then
- error("Invalid tree: " .. tostring(repr(tree)))
+ error("Invalid tree: " .. tostring(repr(tree)), 0)
end
local _exp_0 = tree.type
if "File" == _exp_0 then
@@ -899,12 +840,13 @@ do
end
local declared_locals = { }
local lua_bits = { }
+ local line_no = 1
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
local line = _list_0[_index_0]
local lua = self:tree_to_lua(line)
if not lua then
- error("No lua produced by " .. tostring(repr(line)))
+ error("No lua produced by " .. tostring(repr(line)), 0)
end
if lua.locals then
local new_locals
@@ -944,7 +886,7 @@ do
}
elseif "Nomsu" == _exp_0 then
return {
- expr = "nomsu:parse(" .. tostring(repr(tree.value.src)) .. ", " .. tostring(repr(tree:get_line_no())) .. ").value[1]"
+ expr = "nomsu:parse(" .. tostring(repr(tree.value:get_src())) .. ", " .. tostring(repr(tree:get_line_no())) .. ").value[1]"
}
elseif "Block" == _exp_0 then
local lua_bits = { }
@@ -971,9 +913,10 @@ do
insert(lua_bits, tostring(lua.expr) .. ";")
end
end
+ utils.deduplicate(locals)
return {
statements = concat(lua_bits, "\n"),
- locals = (next(locals) and utils.keys(locals) or nil)
+ locals = (#locals > 0 and locals or nil)
}
elseif "FunctionCall" == _exp_0 then
insert(self.compilestack, tree)
@@ -1034,7 +977,7 @@ do
insert(bits, tok.value)
else
local lua = self:tree_to_lua(tok)
- assert(lua.expr, "non-expression value inside math expression: " .. tostring(tok.src))
+ assert(lua.expr, "non-expression value inside math expression: " .. tostring(tok:get_src()))
insert(bits, lua.expr)
end
end
@@ -1054,7 +997,7 @@ do
break
end
local lua = self:tree_to_lua(tok)
- 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)))
+ assert(lua.expr, tostring(tree:get_line_no()) .. ": Cannot use:\n" .. tostring(colored.yellow(tok:get_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
@@ -1104,7 +1047,7 @@ do
self:print_tree(bit)
self:writeln(tostring(colored.bright("EXPR:")) .. " " .. tostring(lua.expr) .. ", " .. tostring(colored.bright("STATEMENT:")) .. " " .. tostring(lua.statements))
end
- assert(lua.expr, "Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
+ assert(lua.expr, "Cannot use [[" .. tostring(bit:get_src()) .. "]] as a string interpolation value, since it's not an expression.")
insert(concat_parts, "stringify(" .. tostring(lua.expr) .. ")")
_continue_0 = true
until true
@@ -1134,7 +1077,7 @@ do
for _index_0 = 1, #_list_0 do
local item = _list_0[_index_0]
local lua = self:tree_to_lua(item)
- assert(lua.expr, "Cannot use [[" .. tostring(item.src) .. "]] as a list item, since it's not an expression.")
+ assert(lua.expr, "Cannot use [[" .. tostring(item:get_src()) .. "]] as a list item, since it's not an expression.")
insert(items, lua.expr)
end
return {
@@ -1153,9 +1096,9 @@ do
else
key_lua = self:tree_to_lua(entry.dict_key)
end
- assert(key_lua.expr, "Cannot use [[" .. tostring(entry.dict_key.src) .. "]] as a dict key, since it's not an expression.")
+ assert(key_lua.expr, "Cannot use [[" .. tostring(entry.dict_key:get_src()) .. "]] as a dict key, since it's not an expression.")
local value_lua = self:tree_to_lua(entry.dict_value)
- assert(value_lua.expr, "Cannot use [[" .. tostring(entry.dict_value.src) .. "]] as a dict value, since it's not an expression.")
+ assert(value_lua.expr, "Cannot use [[" .. tostring(entry.dict_value:get_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))
@@ -1177,7 +1120,7 @@ do
expr = self:var_to_lua_identifier(tree.value)
}
else
- return error("Unknown/unimplemented thingy: " .. tostring(tree.type))
+ return error("Unknown/unimplemented thingy: " .. tostring(tree.type), 0)
end
end,
walk_tree = function(self, tree, depth)
@@ -1239,8 +1182,9 @@ do
end
local _exp_0 = tree.type
if "Var" == _exp_0 then
- if replacements[tree.value] ~= nil then
- tree = replacements[tree.value]
+ local id = self:var_to_lua_identifier(tree.value)
+ if replacements[id] ~= nil then
+ tree = replacements[id]
end
elseif "File" == _exp_0 or "Nomsu" == _exp_0 or "Block" == _exp_0 or "List" == _exp_0 or "FunctionCall" == _exp_0 or "Text" == _exp_0 then
local new_value = self:tree_with_replaced_vars(tree.value, replacements)
@@ -1289,69 +1233,41 @@ do
end
return tree
end,
- get_stub = function(self, x)
- if not x then
- error("Nothing to get stub from")
- end
- if type(x) == 'string' then
- local spec = concat(self.__class.stub_patt:match(x), " ")
- local arg_names = { }
- local stub = spec:gsub("%%(%S*)", function(arg)
- insert(arg_names, arg)
- return "%"
- end)
- return stub, arg_names
- end
- if type(x) ~= 'table' then
- error("Invalid type for getting stub: " .. tostring(type(x)) .. " for:\n" .. tostring(repr(x)))
- end
- local _exp_0 = x.type
- if "Text" == _exp_0 then
- return self:get_stub(x.value)
- elseif "FunctionCall" == _exp_0 then
- return self:get_stub(x.src)
- else
- return error("Unsupported get stub type: " .. tostring(x.type) .. " for " .. tostring(repr(x)))
+ get_stubs_from_signature = function(self, signature)
+ if type(signature) ~= 'table' or signature.type then
+ error("Invalid signature: " .. tostring(repr(signature)), 0)
+ end
+ local stubs = { }
+ for i, alias in ipairs(signature) do
+ if type(alias) ~= 'string' then
+ error("Expected entries in signature to be strings, not " .. tostring(type(alias)) .. "s like: " .. tostring(repr(alias)) .. "\nsignature: " .. tostring(repr(signature)), 0)
+ end
+ stubs[i] = stub_pattern:match(alias)
+ if not (stubs[i]) then
+ error("Failed to match stub pattern on alias: " .. tostring(repr(alias)))
+ end
end
+ return stubs
end,
- get_stubs = function(self, x)
- if type(x) ~= 'table' then
- return {
- {
- self:get_stub(x)
- }
- }
- end
- local _exp_0 = x.type
- if nil == _exp_0 then
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #x do
- local i = x[_index_0]
- _accum_0[_len_0] = {
- self:get_stub(i)
- }
- _len_0 = _len_0 + 1
+ get_args_from_signature = function(self, signature)
+ if type(signature) ~= 'table' or signature.type then
+ error("Invalid signature: " .. tostring(repr(signature)), 0)
+ end
+ local stub_args = { }
+ for i, alias in ipairs(signature) do
+ if type(alias) ~= 'string' then
+ error("Invalid type for signature: " .. tostring(type(alias)) .. " for:\n" .. tostring(repr(alias)), 0)
end
- return _accum_0
- elseif "List" == _exp_0 then
- local _accum_0 = { }
- local _len_0 = 1
- local _list_0 = x.value
- for _index_0 = 1, #_list_0 do
- local i = _list_0[_index_0]
- _accum_0[_len_0] = {
- self:get_stub(i)
- }
- _len_0 = _len_0 + 1
+ local args = var_pattern:match(alias)
+ if not (args) then
+ error("Failed to match arg pattern on alias: " .. tostring(repr(alias)), 0)
end
- return _accum_0
+ for j = 1, #args do
+ args[j] = self:var_to_lua_identifier(args[j])
+ end
+ stub_args[i] = args
end
- return {
- {
- self:get_stub(x)
- }
- }
+ return stub_args
end,
var_to_lua_identifier = function(self, var)
if type(var) == 'table' and var.type == "Var" then
@@ -1369,9 +1285,13 @@ do
if level == nil then
level = 0
end
- return self:dedent(self.compilestack[#self.compilestack - level].src)
+ return self:dedent(self.compilestack[#self.compilestack - level]:get_src())
end,
initialize_core = function(self)
+ local get_line_no
+ get_line_no = function()
+ return "nomsu.moon:" .. tostring(debug.getinfo(2).currentline)
+ end
local nomsu = self
local nomsu_string_as_lua
nomsu_string_as_lua = function(code)
@@ -1383,23 +1303,24 @@ do
insert(concat_parts, bit)
else
local lua = nomsu:tree_to_lua(bit)
- assert(lua.expr, "Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
+ if not (lua.expr) then
+ error("Cannot use [[" .. tostring(bit:get_src()) .. "]] as a string interpolation value, since it's not an expression.", 0)
+ end
insert(concat_parts, lua.expr)
end
end
return concat(concat_parts)
end
- self:define_compile_action("immediately %block", "nomsu.moon", function(_block)
+ self:define_compile_action("immediately %block", get_line_no(), function(_block)
local lua = nomsu:tree_to_lua(_block)
local lua_code = lua.statements or (lua.expr .. ";")
- lua_code = "-- Immediately:\n" .. lua_code
nomsu:run_lua(lua_code)
return {
- statements = lua_code,
+ statements = "if IMMEDIATE then\n" .. tostring(lua_code) .. "\nend",
locals = lua.locals
}
end)
- self:define_compile_action("lua> %code", "nomsu.moon", function(_code)
+ self:define_compile_action("lua> %code", get_line_no(), function(_code)
if _code.type == "Text" then
local lua = nomsu_string_as_lua(_code)
return {
@@ -1411,26 +1332,22 @@ do
}
end
end)
- self:define_compile_action("=lua %code", "nomsu.moon", function(_code)
+ self:define_compile_action("=lua %code", get_line_no(), function(_code)
local lua = nomsu_string_as_lua(_code)
return {
expr = lua
}
end)
- self:define_compile_action("__line_no__", "nomsu.moon", function()
- return {
- expr = repr(nomsu.compilestack[#nomsu.compilestack]:get_line_no())
- }
- end)
- self:define_compile_action("__src__ %level", "nomsu.moon", function(_level)
+ self:define_compile_action("!! code location !!", get_line_no(), function()
+ local tree = nomsu.compilestack[#nomsu.compilestack - 1]
return {
- expr = repr(nomsu:source_code(nomsu:tree_to_value(_level)))
+ expr = repr(tostring(tree.filename) .. ":" .. tostring(tree.start) .. "," .. tostring(tree.stop))
}
end)
- self:define_action("run file %filename", "nomsu.moon", function(_filename)
+ self:define_action("run file %filename", get_line_no(), function(_filename)
return nomsu:run_file(_filename)
end)
- return self:define_compile_action("use %filename", "nomsu.moon", function(_filename)
+ return self:define_compile_action("use %filename", get_line_no(), function(_filename)
local filename = nomsu:tree_to_value(_filename)
nomsu:require_file(filename)
return {
@@ -1445,9 +1362,6 @@ do
self.write = function(self, ...)
return io.write(...)
end
- self.write_err = function(self, ...)
- return io.stderr:write(...)
- end
local NaN_surrogate = { }
local nil_surrogate = { }
self.ids = setmetatable({ }, {
@@ -1534,6 +1448,11 @@ do
_base_0.__class = _class_0
local self = _class_0
self.def_number = 0
+ line_counter = re.compile([[ lines <- {| line (%nl line)* |}
+ line <- {} (!%nl .)*
+ ]], {
+ nl = NOMSU_DEFS.nl
+ })
self.math_patt = re.compile([[ "%" (" " [*/^+-] " %")+ ]])
self.unescape_string = function(self, str)
return Cs(((P("\\\\") / "\\") + (P("\\\"") / '"') + NOMSU_DEFS.escaped_char + P(1)) ^ 0):match(str)
@@ -1557,10 +1476,13 @@ do
insert(bits, close)
return concat(bits)
end
- self.stub_patt = re.compile("{|(' '+ / '\n..' / {'%' %id*} / {%id+} / {%op})*|}", {
- id = NOMSU_DEFS.ident_char,
- op = NOMSU_DEFS.operator
- })
+ stub_defs = {
+ space = (P(' ') + P('\n..')) ^ 0,
+ word = (NOMSU_DEFS.ident_char ^ 1 + NOMSU_DEFS.operator ^ 1),
+ varname = (R('az', 'AZ', '09') + P('_') + NOMSU_DEFS.utf8_char) ^ 0
+ }
+ stub_pattern = re.compile("{~ (%space->'') (('%' (%varname->'')) / %word)? ((%space->' ') (('%' (%varname->'')) / %word))* (%space->'') ~}", stub_defs)
+ var_pattern = re.compile("{| %space ((('%' {%varname}) / %word) %space)+ |}", stub_defs)
NomsuCompiler = _class_0
end
if arg then
@@ -1608,7 +1530,8 @@ if arg then
input = io.open(args.input):read("*a")
end
local retval, code = nomsu:run(input, args.input)
- if args.output then
+ if compiled_output then
+ compiled_output:write("local IMMEDIATE = true;\n")
compiled_output:write(code)
end
end
@@ -1671,7 +1594,22 @@ if arg then
do
local metadata = nomsu.action_metadata[calling_fn.func]
if metadata then
- line = colored.yellow(metadata.line_no)
+ local filename, start, stop = metadata.source:match("([^:]*):([0-9]*),([0-9]*)")
+ if filename then
+ local file = io.open(filename):read("*a")
+ local line_no = 1
+ for _ in file:sub(1, tonumber(start)):gmatch("\n") do
+ line_no = line_no + 1
+ end
+ local offending_statement = file:sub(tonumber(start), tonumber(stop))
+ if #offending_statement > 50 then
+ offending_statement = offending_statement:sub(1, 50) .. "..."
+ end
+ offending_statement = colored.red(offending_statement)
+ line = colored.yellow(filename .. ":" .. tostring(line_no) .. "\n " .. offending_statement)
+ else
+ line = colored.yellow(metadata.source)
+ end
name = colored.bright(colored.yellow(metadata.aliases[1]))
else
if calling_fn.istailcall and not name then
diff --git a/nomsu.moon b/nomsu.moon
index 222f129..52ce1ea 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -29,7 +29,7 @@ do
if type(i) == 'number' then return string.sub(@, i, i)
elseif type(i) == 'table' then return string.sub(@, i[1], i[2])
else return string[i]
- STRING_METATABLE.__mul = (other)=> string.rep(@, other)
+ --STRING_METATABLE.__mul = (other)=> string.rep(@, other)
-- TODO:
-- consider non-linear codegen, rather than doing thunks for things like comprehensions
@@ -39,12 +39,16 @@ do
-- Add compiler options for optimization level (compile-fast vs. run-fast, etc.)
-- Do a pass on all actions to enforce parameters-are-nouns heuristic
-- Maybe do some sort of lazy definitions of actions that defer until they're used in code
+-- Remove nomsu:write and nomsu:writeln and just use print() instead.
+-- Allow ("..") as an expression, which is obviously not an indented text region, instead
+-- of having a special syntax for "\.."
lpeg.setmaxstack 10000 -- whoa
{:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg
NOMSU_DEFS = with {}
- .nl = P("\n")
+ -- Newline supports either windows-style CR+LF or unix-style LF
+ .nl = P("\r")^-1 * P("\n")
.ws = S(" \t")
.tonumber = tonumber
.print = (src,pos,msg)->
@@ -92,16 +96,16 @@ NOMSU_DEFS = with {}
return start + lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack] + 4
.error = (src,pos,err_msg)->
- if lpeg.userdata.source_code\sub(pos,pos) == "\n"
- pos += #lpeg.userdata.source_code\match("[ \t\n]*", pos)
+ if lpeg.userdata.source_code\sub(pos,pos)\match("[\r\n]")
+ pos += #lpeg.userdata.source_code\match("[ \t\n\r]*", pos)
line_no = 1
while (lpeg.userdata.line_starts[line_no+1] or math.huge) < pos do line_no += 1
prev_line = if line_no > 1
- lpeg.userdata.source_code\match("[^\n]*", lpeg.userdata.line_starts[line_no-1])
+ lpeg.userdata.source_code\match("[^\r\n]*", lpeg.userdata.line_starts[line_no-1])
else ""
- err_line = lpeg.userdata.source_code\match("[^\n]*", lpeg.userdata.line_starts[line_no])
+ err_line = lpeg.userdata.source_code\match("[^\r\n]*", lpeg.userdata.line_starts[line_no])
next_line = if line_no < #lpeg.userdata.line_starts
- lpeg.userdata.source_code\match("[^\n]*", lpeg.userdata.line_starts[line_no+1])
+ lpeg.userdata.source_code\match("[^\r\n]*", lpeg.userdata.line_starts[line_no+1])
else ""
pointer = ("-")\rep(pos-lpeg.userdata.line_starts[line_no]) .. "^"
err_msg = (err_msg or "Parse error").." in #{lpeg.userdata.filename} on line #{line_no}:\n"
@@ -111,11 +115,17 @@ NOMSU_DEFS = with {}
.FunctionCall = (start, value, stop)->
stub = concat([(t.type == "Word" and t.value or "%") for t in *value], " ")
src = lpeg.userdata.source_code\sub(start,stop-1)
- return {:start, :stop, type: "FunctionCall", :src, get_line_no:lpeg.userdata.get_line_no, :value, :stub}
+ return {
+ type: "FunctionCall", :start, :stop, :value, :stub, filename:lpeg.userdata.filename,
+ get_line_no:lpeg.userdata.get_line_no, get_src:lpeg.userdata.get_src,
+ }
setmetatable(NOMSU_DEFS, {__index:(key)=>
make_node = (start, value, stop)->
- {:start, :stop, :value, src:lpeg.userdata.source_code\sub(start,stop-1), get_line_no:lpeg.userdata.get_line_no, type: key}
+ {
+ type: key, :start, :stop, :value, filename:lpeg.userdata.filename,
+ get_src:lpeg.userdata.get_src, get_line_no:lpeg.userdata.get_line_no,
+ }
self[key] = make_node
return make_node
})
@@ -140,7 +150,6 @@ class NomsuCompiler
@def_number: 0
new:()=>
@write = (...)=> io.write(...)
- @write_err = (...)=> io.stderr\write(...)
-- Weak-key mapping from objects to randomly generated unique IDs
NaN_surrogate = {}
nil_surrogate = {}
@@ -178,17 +187,17 @@ class NomsuCompiler
@write(...)
@write("\n")
- errorln:(...)=>
- @write_err(...)
- @write_err("\n")
-
- define_action: (signature, line_no, fn, src, compile_time=false)=>
+ define_action: (signature, source, fn)=>
+ if @debug
+ @writeln "#{colored.bright "DEFINING ACTION:"} #{colored.green repr(signature)}"
+ if type(fn) != 'function'
+ error 'function', "Bad fn: #{repr fn}"
if type(signature) == 'string'
- signature = @get_stubs {signature}
- elseif type(signature) == 'table' and type(signature[1]) == 'string'
- signature = @get_stubs signature
- assert type(fn) == 'function', "Bad fn: #{repr fn}"
- aliases = {}
+ signature = {signature}
+ elseif type(signature) != 'table' or signature.type != nil
+ error("Invalid signature, expected list of strings, but got: #{repr signature}", 0)
+ stubs = @get_stubs_from_signature signature
+ stub_args = @get_args_from_signature signature
@@def_number += 1
fn_info = debug.getinfo(fn, "u")
@@ -196,98 +205,69 @@ class NomsuCompiler
unless fn_info.isvararg
fn_arg_positions = {debug.getlocal(fn, i), i for i=1,fn_info.nparams}
arg_orders = {} -- Map from stub -> index where each arg in the stub goes in the function call
- for sig_i=1,#signature
- stub, arg_names = unpack(signature[sig_i])
- assert stub, "NO STUB FOUND: #{repr signature}"
+ for sig_i=1,#stubs
+ stub, args = stubs[sig_i], stub_args[sig_i]
if @debug
- @writeln "#{colored.bright "DEFINING ACTION:"} #{colored.underscore colored.magenta repr(stub)} #{colored.bright "WITH ARGS"} #{colored.dim repr(arg_names)} ON: #{@environment.ACTIONS}"
+ @writeln "#{colored.bright "ALIAS:"} #{colored.underscore colored.magenta repr(stub)} #{colored.bright "WITH ARGS"} #{colored.dim repr(args)} ON: #{@environment.ACTIONS}"
-- TODO: use debug.getupvalue instead of @environment.ACTIONS?
@environment.ACTIONS[stub] = fn
unless fn_info.isvararg
- arg_positions = [fn_arg_positions[@var_to_lua_identifier(a)] for a in *arg_names]
+ arg_positions = [fn_arg_positions[a] for a in *args]
-- TODO: better error checking?
- assert(#arg_positions == #arg_names,
- "Mismatch in args between lua function's #{repr fn_arg_positions} and stub's #{repr arg_names}")
+ if #arg_positions != #args
+ error("Mismatch in args between lua function's #{repr fn_arg_positions} and stub's #{repr args} for #{repr stub}", 0)
arg_orders[stub] = arg_positions
@action_metadata[fn] = {
- :fn, :src, :line_no, :aliases, :arg_orders, arg_positions:fn_arg_positions, def_number:@@def_number,
+ :fn, :source, aliases:stubs, :arg_orders,
+ arg_positions:fn_arg_positions, def_number:@@def_number,
}
- define_compile_action: (signature, line_no, fn, src)=>
- @define_action(signature, line_no, fn, src, true)
+ define_compile_action: (signature, source, fn, src)=>
+ @define_action(signature, source, fn)
@action_metadata[fn].compile_time = true
- if @debug
- @writeln "#{colored.bright colored.green "(it was compile time)"}"
serialize_defs: (scope=nil, after=nil)=>
-- TODO: repair
- error("Not currently functional.")
- after or= @core_defs or 0
- scope or= @defs
- defs_by_num = {}
- for stub, def in pairs(scope)
- if def and stub\sub(1,1) != "#"
- defs_by_num[def.def_number] = def
- keys = [k for k,v in pairs(defs_by_num)]
- table.sort(keys)
-
- buff = {}
- k_i = 1
- _using = nil
- _using_do = {}
- for k_i,i in ipairs(keys)
- if i <= after then continue
- def = defs_by_num[i]
- if def.defs == scope
- if def.src
- insert buff, def.src
- continue
- if _using == def.defs
- if def.src
- insert _using_do, def.src
- else
- _using = def.defs
- _using_do = {def.src}
- if k_i == #keys or defs_by_num[keys[k_i+1]].defs != _using
- insert buff, "using:\n #{@indent @serialize_defs(_using)}\n..do:\n #{@indent concat(_using_do, "\n")}"
-
- for k,v in pairs(scope["#vars"] or {})
- insert buff, "<%#{k}> = #{@value_to_nomsu v}"
-
- return concat buff, "\n"
+ error("Not currently functional.", 0)
dedent: (code)=>
unless code\find("\n")
return code
spaces, indent_spaces = math.huge, math.huge
for line in code\gmatch("\n([^\n]*)")
- if line\match("^%s*#.*")
- continue
+ if line\match("^%s*#.*") or line\match("^%s*$")
+ continue -- skip comments and blank lines
elseif s = line\match("^(%s*)%.%..*")
spaces = math.min(spaces, #s)
elseif s = line\match("^(%s*)%S.*")
indent_spaces = math.min(indent_spaces, #s)
if spaces != math.huge and spaces < indent_spaces
return (code\gsub("\n"..(" ")\rep(spaces), "\n"))
- else
+ elseif indent_spaces != math.huge
return (code\gsub("\n"..(" ")\rep(indent_spaces), "\n "))
+ else return code
indent: (code, levels=1)=>
return code\gsub("\n","\n"..(" ")\rep(levels))
+ line_counter = re.compile([[
+ lines <- {| line (%nl line)* |}
+ line <- {} (!%nl .)*
+ ]], nl:NOMSU_DEFS.nl)
parse: (nomsu_code, filename)=>
assert type(filename) == "string", "Bad filename type: #{type filename}"
if @debug
@writeln("#{colored.bright "PARSING:"}\n#{colored.yellow nomsu_code}")
- nomsu_code = nomsu_code\gsub("\r","")
userdata = with {source_code:nomsu_code, :filename, indent_stack: {0}}
- .line_starts = re.compile("lines <- {| line ('\n' line)* |} line <- {} [^\n]*")\match(nomsu_code)
+ .get_src = => nomsu_code\sub(@start, @stop-1)
+ .line_starts = line_counter\match(.source_code)
.get_line_no = =>
unless @_line_no
line_no = 1
- while (.line_starts[line_no+1] or math.huge) < @start do line_no += 1
+ while line_no < #.line_starts and .line_starts[line_no+1] < @start
+ line_no += 1
@_line_no = "#{.filename}:#{line_no}"
return @_line_no
@@ -306,7 +286,7 @@ class NomsuCompiler
if max_operations
timeout = ->
debug.sethook!
- error "Execution quota exceeded. Your code took too long."
+ error("Execution quota exceeded. Your code took too long.", 0)
debug.sethook timeout, "", max_operations
tree = @parse(src, filename)
assert tree, "Failed to parse: #{src}"
@@ -314,9 +294,8 @@ 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
+ if lua_code.locals and #lua_code.locals > 0
+ lua_code = "local "..concat(lua_code.locals, ", ")..";\n"..lua_code
lua_code = "-- File: #{filename}\n"..lua_code
ret = @run_lua(lua_code)
if max_operations
@@ -340,12 +319,12 @@ class NomsuCompiler
return @run_lua(lua_code)
file = file or io.open(filename)
if not file
- error "File does not exist: #{filename}"
+ error("File does not exist: #{filename}", 0)
nomsu_code = file\read('*a')
file\close!
return @run(nomsu_code, filename)
else
- error "Invalid filetype for #{filename}"
+ error("Invalid filetype for #{filename}", 0)
require_file: (filename)=>
loaded = @environment.LOADED
@@ -363,16 +342,19 @@ class NomsuCompiler
n = n + 1
("\n%-3d|")\format(n)
code = "1 |"..lua_code\gsub("\n", fn)
- error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{err}")
+ error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{err}", 0)
return run_lua_fn!
tree_to_value: (tree, filename)=>
+ -- Special case for text literals
+ if tree.type == 'Text' and #tree.value == 1 and type(tree.value[1]) == 'string'
+ return tree.value[1]
code = "return #{@tree_to_lua(tree).expr};"
if @debug
@writeln "#{colored.bright "RUNNING LUA TO GET VALUE:"}\n#{colored.blue colored.bright(code)}"
lua_thunk, err = load(code, nil, nil, @environment)
if not lua_thunk
- error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{colored.red err}")
+ error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{colored.red err}", 0)
return lua_thunk!
tree_to_nomsu: (tree, indentation="", max_line=80, expr_type=nil)=>
@@ -584,7 +566,7 @@ class NomsuCompiler
lines = {}
for line in *tree.value
nomsu = expression(line)
- assert nomsu, "Failed to produce output for:\n#{colored.yellow line.src}"
+ assert nomsu, "Failed to produce output for:\n#{colored.yellow line\get_src!}"
insert lines, nomsu
return concat lines, "\n"
@@ -620,24 +602,25 @@ class NomsuCompiler
-- TODO: This might fail if it's being put inside a list or something
return '".."\n '..(@indent value)
else
- error("Unsupported value_to_nomsu type: #{type(value)}")
+ 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 tree.type
- error "Invalid tree: #{repr(tree)}"
+ error("Invalid tree: #{repr(tree)}", 0)
switch tree.type
when "File"
if #tree.value == 1
return @tree_to_lua(tree.value[1])
declared_locals = {}
lua_bits = {}
+ line_no = 1
for line in *tree.value
lua = @tree_to_lua line
if not lua
- error "No lua produced by #{repr line}"
+ error("No lua produced by #{repr line}", 0)
if lua.locals
new_locals = [l for l in *lua.locals when not declared_locals[l]]
if #new_locals > 0
@@ -651,7 +634,7 @@ class NomsuCompiler
return statements:"--"..tree.value\gsub("\n","\n--")
when "Nomsu"
- return expr:"nomsu:parse(#{repr tree.value.src}, #{repr tree\get_line_no!}).value[1]"
+ return expr:"nomsu:parse(#{repr tree.value\get_src!}, #{repr tree\get_line_no!}).value[1]"
when "Block"
lua_bits = {}
@@ -664,7 +647,8 @@ class NomsuCompiler
for l in *lua.locals do locals[l] = true
if lua.statements then insert lua_bits, lua.statements
elseif lua.expr then insert lua_bits, "#{lua.expr};"
- return statements:concat(lua_bits, "\n"), locals:(next(locals) and utils.keys(locals) or nil)
+ utils.deduplicate(locals)
+ return statements:concat(lua_bits, "\n"), locals:(#locals > 0 and locals or nil)
when "FunctionCall"
insert @compilestack, tree
@@ -693,7 +677,7 @@ class NomsuCompiler
insert bits, tok.value
else
lua = @tree_to_lua(tok)
- assert(lua.expr, "non-expression value inside math expression: #{tok.src}")
+ assert(lua.expr, "non-expression value inside math expression: #{tok\get_src!}")
insert bits, lua.expr
remove @compilestack
return expr:"(#{concat bits, " "})"
@@ -703,7 +687,7 @@ class NomsuCompiler
if tok.type == "Word" then continue
lua = @tree_to_lua(tok)
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}"
+ "#{tree\get_line_no!}: Cannot use:\n#{colored.yellow tok\get_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
@@ -729,7 +713,7 @@ class NomsuCompiler
@print_tree bit
@writeln "#{colored.bright "EXPR:"} #{lua.expr}, #{colored.bright "STATEMENT:"} #{lua.statements}"
assert lua.expr,
- "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
+ "Cannot use [[#{bit\get_src!}]] as a string interpolation value, since it's not an expression."
insert concat_parts, "stringify(#{lua.expr})"
if string_buffer ~= ""
@@ -746,7 +730,7 @@ class NomsuCompiler
for item in *tree.value
lua = @tree_to_lua item
assert lua.expr,
- "Cannot use [[#{item.src}]] as a list item, since it's not an expression."
+ "Cannot use [[#{item\get_src!}]] as a list item, since it's not an expression."
insert items, lua.expr
return expr:@@comma_separated_items("{", items, "}")
@@ -758,10 +742,10 @@ class NomsuCompiler
else
@tree_to_lua entry.dict_key
assert key_lua.expr,
- "Cannot use [[#{entry.dict_key.src}]] as a dict key, since it's not an expression."
+ "Cannot use [[#{entry.dict_key\get_src!}]] as a dict key, since it's not an expression."
value_lua = @tree_to_lua entry.dict_value
assert value_lua.expr,
- "Cannot use [[#{entry.dict_value.src}]] as a dict value, since it's not an expression."
+ "Cannot use [[#{entry.dict_value\get_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}"
@@ -778,7 +762,7 @@ class NomsuCompiler
return expr:@var_to_lua_identifier(tree.value)
else
- error("Unknown/unimplemented thingy: #{tree.type}")
+ error("Unknown/unimplemented thingy: #{tree.type}", 0)
walk_tree: (tree, depth=0)=>
coroutine.yield(tree, depth)
@@ -833,8 +817,9 @@ class NomsuCompiler
if type(tree) != 'table' then return tree
switch tree.type
when "Var"
- if replacements[tree.value] ~= nil
- tree = replacements[tree.value]
+ id = @var_to_lua_identifier tree.value
+ if replacements[id] ~= nil
+ tree = replacements[id]
when "File", "Nomsu", "Block", "List", "FunctionCall", "Text"
new_value = @tree_with_replaced_vars tree.value, replacements
if new_value != tree.value
@@ -861,37 +846,39 @@ class NomsuCompiler
tree = new_values
return tree
- @stub_patt: re.compile "{|(' '+ / '\n..' / {'%' %id*} / {%id+} / {%op})*|}",
- id:NOMSU_DEFS.ident_char, op:NOMSU_DEFS.operator
- get_stub: (x)=>
- if not x
- error "Nothing to get stub from"
- -- Returns a single stub ("say %"), list of arg names ({"msg"}), and set of arg
- -- names that should not be evaluated from a single action def
- -- (e.g. "say %msg") or function call (e.g. FunctionCall({Word("say"), Var("msg")))
- if type(x) == 'string'
- -- Standardize format to stuff separated by spaces
- spec = concat @@stub_patt\match(x), " "
- arg_names = {}
- stub = spec\gsub "%%(%S*)", (arg)->
- insert(arg_names, arg)
- return "%"
- return stub, arg_names
- if type(x) != 'table'
- error "Invalid type for getting stub: #{type(x)} for:\n#{repr x}"
- switch x.type
- when "Text" then return @get_stub(x.value)
- when "FunctionCall" then return @get_stub(x.src)
- else error "Unsupported get stub type: #{x.type} for #{repr x}"
-
- get_stubs: (x)=>
- if type(x) != 'table' then return {{@get_stub(x)}}
- switch x.type
- when nil
- return [{@get_stub(i)} for i in *x]
- when "List"
- return [{@get_stub(i)} for i in *x.value]
- return {{@get_stub(x)}}
+
+ stub_defs = {
+ space:(P(' ') + P('\n..'))^0
+ word:(NOMSU_DEFS.ident_char^1 + NOMSU_DEFS.operator^1)
+ varname:(R('az','AZ','09') + P('_') + NOMSU_DEFS.utf8_char)^0
+ }
+ stub_pattern = re.compile("{~ (%space->'') (('%' (%varname->'')) / %word)? ((%space->' ') (('%' (%varname->'')) / %word))* (%space->'') ~}", stub_defs)
+ get_stubs_from_signature: (signature)=>
+ if type(signature) != 'table' or signature.type
+ error("Invalid signature: #{repr signature}", 0)
+ stubs = {}
+ for i,alias in ipairs(signature)
+ if type(alias) != 'string'
+ error("Expected entries in signature to be strings, not #{type(alias)}s like: #{repr alias}\nsignature: #{repr signature}", 0)
+ stubs[i] = stub_pattern\match(alias)
+ unless stubs[i]
+ error("Failed to match stub pattern on alias: #{repr alias}")
+ return stubs
+
+ var_pattern = re.compile("{| %space ((('%' {%varname}) / %word) %space)+ |}", stub_defs)
+ get_args_from_signature: (signature)=>
+ if type(signature) != 'table' or signature.type
+ error("Invalid signature: #{repr signature}", 0)
+ stub_args = {}
+ for i,alias in ipairs(signature)
+ if type(alias) != 'string'
+ error("Invalid type for signature: #{type(alias)} for:\n#{repr alias}", 0)
+ args = var_pattern\match(alias)
+ unless args
+ error("Failed to match arg pattern on alias: #{repr alias}", 0)
+ for j=1,#args do args[j] = @var_to_lua_identifier(args[j])
+ stub_args[i] = args
+ return stub_args
var_to_lua_identifier: (var)=>
-- Converts arbitrary nomsu vars to valid lua identifiers by replacing illegal
@@ -902,10 +889,11 @@ class NomsuCompiler
if verboten == "_" then "__" else ("_%x")\format(verboten\byte!))
source_code: (level=0)=>
- @dedent @compilestack[#@compilestack-level].src
-
+ @dedent @compilestack[#@compilestack-level]\get_src!
+
initialize_core: =>
-- Sets up some core functionality
+ get_line_no = -> "nomsu.moon:#{debug.getinfo(2).currentline}"
nomsu = self
nomsu_string_as_lua = (code)->
concat_parts = {}
@@ -914,39 +902,37 @@ class NomsuCompiler
insert concat_parts, bit
else
lua = nomsu\tree_to_lua bit
- assert lua.expr,
- "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
+ unless lua.expr
+ error("Cannot use [[#{bit\get_src!}]] as a string interpolation value, since it's not an expression.", 0)
insert concat_parts, lua.expr
return concat(concat_parts)
-
- @define_compile_action "immediately %block", "nomsu.moon", (_block)->
+
+ -- TODO: fix how 'immediately' works with locals, e.g. "immediately: %x <- 5"
+ @define_compile_action "immediately %block", get_line_no!, (_block)->
lua = nomsu\tree_to_lua(_block)
lua_code = lua.statements or (lua.expr..";")
- lua_code = "-- Immediately:\n"..lua_code
nomsu\run_lua(lua_code)
- return statements:lua_code, locals:lua.locals
+ return statements:"if IMMEDIATE then\n#{lua_code}\nend", locals:lua.locals
- @define_compile_action "lua> %code", "nomsu.moon", (_code)->
+ @define_compile_action "lua> %code", get_line_no!, (_code)->
if _code.type == "Text"
lua = nomsu_string_as_lua(_code)
return statements:lua
else
return statements:"nomsu:run_lua(#{nomsu\tree_to_lua(_code).expr});"
- @define_compile_action "=lua %code", "nomsu.moon", (_code)->
+ @define_compile_action "=lua %code", get_line_no!, (_code)->
lua = nomsu_string_as_lua(_code)
return expr:lua
- @define_compile_action "__line_no__", "nomsu.moon", ->
- expr: repr(nomsu.compilestack[#nomsu.compilestack]\get_line_no!)
-
- @define_compile_action "__src__ %level", "nomsu.moon", (_level)->
- expr: repr(nomsu\source_code(nomsu\tree_to_value(_level)))
+ @define_compile_action "!! code location !!", get_line_no!, ->
+ tree = nomsu.compilestack[#nomsu.compilestack-1]
+ return expr: repr("#{tree.filename}:#{tree.start},#{tree.stop}")
- @define_action "run file %filename", "nomsu.moon", (_filename)->
+ @define_action "run file %filename", get_line_no!, (_filename)->
nomsu\run_file(_filename)
- @define_compile_action "use %filename", "nomsu.moon", (_filename)->
+ @define_compile_action "use %filename", get_line_no!, (_filename)->
filename = nomsu\tree_to_value(_filename)
nomsu\require_file(filename)
return statements:"nomsu:require_file(#{repr filename});"
@@ -993,7 +979,8 @@ if arg
io.read('*a')
else io.open(args.input)\read("*a")
retval, code = nomsu\run(input, args.input)
- if args.output
+ if compiled_output
+ compiled_output\write("local IMMEDIATE = true;\n")
compiled_output\write(code)
if args.flags["-p"]
@@ -1021,6 +1008,8 @@ if arg
print("#{colored.red "ERROR:"} #{colored.bright colored.yellow colored.onred (error_message or "")}")
print("stack traceback:")
+ -- TODO: properly print out the calling site of nomsu code, not just the *called* code
+
import to_lua from require "moonscript.base"
nomsu_file = io.open("nomsu.moon")
nomsu_source = nomsu_file\read("*a")
@@ -1037,7 +1026,18 @@ if arg
if name == "run_lua_fn" then continue
line = nil
if metadata = nomsu.action_metadata[calling_fn.func]
- line = colored.yellow(metadata.line_no)
+ filename, start, stop = metadata.source\match("([^:]*):([0-9]*),([0-9]*)")
+ if filename
+ file = io.open(filename)\read("*a")
+ line_no = 1
+ for _ in file\sub(1,tonumber(start))\gmatch("\n") do line_no += 1
+ offending_statement = file\sub(tonumber(start),tonumber(stop))
+ if #offending_statement > 50
+ offending_statement = offending_statement\sub(1,50).."..."
+ offending_statement = colored.red(offending_statement)
+ line = colored.yellow(filename..":"..tostring(line_no).."\n "..offending_statement)
+ else
+ line = colored.yellow(metadata.source)
name = colored.bright(colored.yellow(metadata.aliases[1]))
else
if calling_fn.istailcall and not name
diff --git a/utils.lua b/utils.lua
index 27951ea..80585a5 100644
--- a/utils.lua
+++ b/utils.lua
@@ -116,12 +116,15 @@ local function split(str, sep)
end
local function remove_from_list(list, item)
- for i=1,#list do
+ local deleted, N = 0, #list
+ for i=1,N do
if list[i] == item then
- table.remove(list, i)
- return
+ deleted = deleted + 1
+ else
+ list[i-deleted] = list[i]
end
end
+ for i=N-deleted+1,N do list[i] = nil end
end
local function accumulate(glue, co)
@@ -163,6 +166,18 @@ local function set(list)
return ret
end
+local function deduplicate(list)
+ local seen, deleted = {}, 0
+ for i, item in ipairs(list) do
+ if seen[item] then
+ deleted = deleted + 1
+ else
+ seen[item] = true
+ list[i-deleted] = list[i]
+ end
+ end
+end
+
local function sum(t)
local tot = 0
for i=1,#t do
@@ -343,6 +358,6 @@ end
return {is_list=is_list, size=size, repr=repr, stringify=stringify, split=split,
remove_from_list=remove_from_list, accumulate=accumulate, nth_to_last=nth_to_last,
- keys=keys, values=values, set=set, sum=sum, product=product, all=all, any=any,
- min=min, max=max, sort=sort, equivalent=equivalent, key_for=key_for, clamp=clamp,
- mix=mix, round=round}
+ keys=keys, values=values, set=set, deduplicate=deduplicate, sum=sum, product=product,
+ all=all, any=any, min=min, max=max, sort=sort, equivalent=equivalent, key_for=key_for,
+ clamp=clamp, mix=mix, round=round}