diff options
Diffstat (limited to 'core')
| -rw-r--r-- | core/collections.nom | 294 | ||||
| -rw-r--r-- | core/control_flow.nom | 668 | ||||
| -rw-r--r-- | core/errors.nom | 82 | ||||
| -rw-r--r-- | core/io.nom | 8 | ||||
| -rw-r--r-- | core/metaprogramming.nom | 328 | ||||
| -rw-r--r-- | core/operators.nom | 303 | ||||
| -rw-r--r-- | core/text.nom | 8 |
7 files changed, 864 insertions, 827 deletions
diff --git a/core/collections.nom b/core/collections.nom index 8a59852..69a51c7 100644 --- a/core/collections.nom +++ b/core/collections.nom @@ -9,162 +9,158 @@ use "core/operators.nom" # List/dict functions: # Indexing -immediately - 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: Lua value "utils.nth_to_last(\(%list as lua expr), \(%index as lua expr))" +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: Lua value "utils.nth_to_last(\(%list as lua expr), \(%index as lua expr))" -immediately - parse [last in %list] as: 1st to last in %list - parse [first in %list] as: %list.1 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +parse [last in %list] as: 1st to last in %list +parse [first in %list] as: %list.1 # Membership testing -immediately - action [%item is in %list, %list contains %item, %list has %item] - for %key = %value in %list - if (%key is %item): return (yes) - return (no) - - action [..] - %item isn't in %list, %item is not in %list - %list doesn't contain %item, %list does not contain %item - %list doesn't have %item, %list does not have %item - .. - for %key = %value in %list - if (%key is %item): return (no) - return (yes) - -immediately - parse [%list has key %index, %list has index %index] as - %list.%index != (nil) - - parse [..] - %list doesn't have key %index, %list does not have key %index - %list doesn't have index %index, %list does not have index %index - ..as - %list.%index = (nil) - - compile [number of keys in %list] to - Lua value "utils.size(\(%list as lua expr))" - - compile [append %item to %list, add %item to %list, to %list add %item, to %list append %item] to - Lua "table.insert(\(%list as lua expr), \(%item as lua expr))" - - compile [pop from %list, remove last from %list] to - Lua "table.remove(\(%list as lua expr))" - - compile [remove index %index from %list] to - Lua "table.remove(\(%list as lua expr), \(%index as lua expr))" +action [%item is in %list, %list contains %item, %list has %item] + for %key = %value in %list + if (%key is %item): return (yes) + return (no) + +action [..] + %item isn't in %list, %item is not in %list + %list doesn't contain %item, %list does not contain %item + %list doesn't have %item, %list does not have %item +.. + for %key = %value in %list + if (%key is %item): return (no) + return (yes) + +parse [%list has key %index, %list has index %index] as + %list.%index != (nil) + +parse [..] + %list doesn't have key %index, %list does not have key %index + %list doesn't have index %index, %list does not have index %index +..as + %list.%index = (nil) + +compile [number of keys in %list] to + Lua value "utils.size(\(%list as lua expr))" + +compile [append %item to %list, add %item to %list, to %list add %item, to %list append %item] to + Lua "table.insert(\(%list as lua expr), \(%item as lua expr))" + +compile [pop from %list, remove last from %list] to + Lua "table.remove(\(%list as lua expr))" + +compile [remove index %index from %list] to + Lua "table.remove(\(%list as lua expr), \(%index as lua expr))" # List Comprehension -immediately - parse [%expression for %item in %iterable] as - result of - %comprehension <- [] - for %item in %iterable - add %expression to %comprehension - return %comprehension - - parse [..] - %expression for %index in %start to %stop via %step - %expression for %index in %start to %stop by %step - ..as - result of - %comprehension <- [] - for %index in %start to %stop via %step - add %expression to %comprehension - return %comprehension - - parse [%expression for %var in %start to %stop] as - %expression for %var in %start to %stop via 1 - - parse [..] - %expression for %key = %value in %iterable - %expression for (%key,%value) in %iterable - ..as - result of - %comprehension <- [] - for %key = %value in %iterable - add %expression to %comprehension - return %comprehension - - # Dict comprehensions - parse [..] - %key = %value for %item in %iterable - (%key,%value) for %item in %iterable - ..as - result of - %comprehension <- {} - for %item in %iterable - %comprehension.%key <- %value - return %comprehension - - parse [..] - %key = %value for %src_key = %src_value in %iterable - (%key,%value) for (%src_key,%src_value) in %iterable - ..as - result of - %comprehension <- {} - for %src_key = %src_value in %iterable - %comprehension.%key <- %value - return %comprehension - -immediately - action [%lists flattened] - %flat <- [] - for %list in %lists - for %item in %list - add %item to %flat - return %flat - - parse [entries in %dict] as: {key:%k, value:%v} for %k = %v in %dict - parse [keys in %dict, keys of %dict] as: %k for %k = %v in %dict - parse [values in %dict, values of %dict] as: %v for %k = %v in %dict +parse [%expression for %item in %iterable] as + result of + %comprehension <- [] + for %item in %iterable + add %expression to %comprehension + return %comprehension + +parse [..] + %expression for %index in %start to %stop via %step + %expression for %index in %start to %stop by %step +..as + result of + %comprehension <- [] + for %index in %start to %stop via %step + add %expression to %comprehension + return %comprehension + +parse [%expression for %var in %start to %stop] as + %expression for %var in %start to %stop via 1 + +parse [..] + %expression for %key = %value in %iterable + %expression for (%key,%value) in %iterable +..as + result of + %comprehension <- [] + for %key = %value in %iterable + add %expression to %comprehension + return %comprehension + +# Dict comprehensions +parse [..] + %key = %value for %item in %iterable + (%key,%value) for %item in %iterable +..as + result of + %comprehension <- {} + for %item in %iterable + %comprehension.%key <- %value + return %comprehension + +parse [..] + %key = %value for %src_key = %src_value in %iterable + (%key,%value) for (%src_key,%src_value) in %iterable +..as + result of + %comprehension <- {} + for %src_key = %src_value in %iterable + %comprehension.%key <- %value + return %comprehension + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +action [%lists flattened] + %flat <- [] + for %list in %lists + for %item in %list + add %item to %flat + return %flat + +parse [entries in %dict] as: {key:%k, value:%v} for %k = %v in %dict +parse [keys in %dict, keys of %dict] as: %k for %k = %v in %dict +parse [values in %dict, values of %dict] as: %v for %k = %v in %dict # Metatable stuff -immediately - compile [set %dict's metatable to %metatable] to - Lua "setmetatable(\(%dict as lua expr), \(%metatable as lua expr));" - - compile [%dict with fallback %key -> %value] to - Lua value ".." - setmetatable(\(%dict as lua expr), {__index=function(self, \(%key as lua expr)) - local value = \(%value as lua expr) - self[\(%key as lua expr)] = value - return value - end}) +compile [set %dict's metatable to %metatable] to + Lua "setmetatable(\(%dict as lua expr), \(%metatable as lua expr));" + +compile [%dict with fallback %key -> %value] to + Lua value ".." + setmetatable(\(%dict as lua expr), {__index=function(self, \(%key as lua expr)) + local value = \(%value as lua expr) + self[\(%key as lua expr)] = value + return value + end}) # Sorting -immediately - compile [sort %items] to: Lua "table.sort(\(%items as lua expr));" - parse [..] - sort %items by %item = %key_expr - sort %items by %item -> %key_expr - ..as - do - %keys <- ({} with fallback %item -> %key_expr) - lua> "table.sort(\%items, function(x,y) return \%keys[x] < \%keys[y] end)" - -immediately - action [%items sorted, sorted %items] - %copy <- (% for % in %items) - sort %copy - return %copy - action [..] - %items sorted by %item = %key - %items sorted by %item -> %key - .. - %copy <- (% for % in %items) - sort %copy by %item = %key - return %copy - - action [unique %items] - %unique <- [] - %seen <- {} - for % in %items - unless: %seen.% - add % to %unique - %seen.% <- (yes) - return %unique +compile [sort %items] to: Lua "table.sort(\(%items as lua expr));" +parse [..] + sort %items by %item = %key_expr + sort %items by %item -> %key_expr +..as + do + %keys <- ({} with fallback %item -> %key_expr) + lua> "table.sort(\%items, function(x,y) return \%keys[x] < \%keys[y] end)" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +action [%items sorted, sorted %items] + %copy <- (% for % in %items) + sort %copy + return %copy +action [..] + %items sorted by %item = %key + %items sorted by %item -> %key +.. + %copy <- (% for % in %items) + sort %copy by %item = %key + return %copy + +action [unique %items] + %unique <- [] + %seen <- {} + for % in %items + unless: %seen.% + add % to %unique + %seen.% <- (yes) + return %unique diff --git a/core/control_flow.nom b/core/control_flow.nom index 15c1ca1..fccdfca 100644 --- a/core/control_flow.nom +++ b/core/control_flow.nom @@ -8,387 +8,381 @@ use "core/operators.nom" use "core/errors.nom" # No-Op -immediately - compile [do nothing] to: Lua "" +compile [do nothing] to: Lua "" # Conditionals -immediately - compile [if %condition %if_body] to - Lua ".." - if \(%condition as lua expr) then - \(%if_body as lua statements) - end - parse [unless %condition %unless_body] as: if (not %condition) %unless_body - - compile [if %condition %if_body else %else_body, unless %condition %else_body else %if_body] to - Lua ".." - if \(%condition as lua expr) then - \(%if_body as lua statements) - else - \(%else_body as lua statements) - end +compile [if %condition %if_body] to + Lua ".." + if \(%condition as lua expr) then + \(%if_body as lua statements) + end +parse [unless %condition %unless_body] as: if (not %condition) %unless_body + +compile [if %condition %if_body else %else_body, unless %condition %else_body else %if_body] to + Lua ".." + if \(%condition as lua expr) then + \(%if_body as lua statements) + else + \(%else_body as lua statements) + end + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Conditional expression (ternary operator) # Note: this uses a function instead of "(condition and if_expr or else_expr)" because that breaks if %if_expr is falsey, e.g. "x < 5 and false or 99" -immediately - compile [..] - %when_true_expr if %condition else %when_false_expr - %when_true_expr if %condition otherwise %when_false_expr - %when_false_expr unless %condition else %when_true_expr - %when_false_expr unless %condition then %when_true_expr - ..to - # If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic - equivalent of a conditional expression: (cond and if_true or if_false) - if: {Text:yes, List:yes, Dict:yes, Number:yes}.(%when_true_expr.type) - return - Lua value ".." - (\(%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 - Lua value ".." - ((function() - if \(%condition as lua expr) then - return \(%when_true_expr as lua expr) - else - return \(%when_false_expr as lua expr) - end - end)()) +compile [..] + %when_true_expr if %condition else %when_false_expr + %when_true_expr if %condition otherwise %when_false_expr + %when_false_expr unless %condition else %when_true_expr + %when_false_expr unless %condition then %when_true_expr +..to + # If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic + equivalent of a conditional expression: (cond and if_true or if_false) + if: {Text:yes, List:yes, Dict:yes, Number:yes}.(%when_true_expr.type) + return + Lua value ".." + (\(%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 + Lua value ".." + ((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 ---, *** %label ***] to - Lua "::label_\(%label as lua identifier)::" - compile [go to %label] to - Lua "goto label_\(%label as lua identifier)" +compile [=== %label ===, --- %label ---, *** %label ***] to + Lua "::label_\(%label as lua identifier)::" +compile [go to %label] to + Lua "goto label_\(%label as lua identifier)" # Basic loop control -immediately - compile [do next] to: Lua "continue" - compile [stop] to: Lua "break" +compile [do next] to: Lua "continue" +compile [stop] to: Lua "break" # Helper function -immediately - compile [%tree has subtree %subtree where %condition] to - Lua value ".." - (function() - for \(%subtree as lua expr) in coroutine.wrap(function() \(%tree as lua expr):map(coroutine.yield) end) do - if \(%condition as lua expr) then - return true - end +#TODO: do "using % compile %" instead so it's actually a helper function +compile [%tree has subtree %subtree where %condition] to + Lua value ".." + (function() + for \(%subtree as lua expr) in coroutine.wrap(function() \(%tree as lua expr):map(coroutine.yield) end) do + if \(%condition as lua expr) then + return true end - return false - end)() + end + return false + end)() + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # While loops -immediately - compile [do next repeat] to: Lua "goto continue_repeat" - compile [stop repeating] to: Lua "goto stop_repeat" - compile [repeat while %condition %body] to +compile [do next repeat] to: Lua "goto continue_repeat" +compile [stop repeating] to: Lua "goto stop_repeat" +compile [repeat while %condition %body] to + %lua <- + Lua ".." + while \(%condition as lua expr) do + \(%body as lua statements) + if + %body has subtree % where: (%.type = "Action") and (%.stub is "do next repeat") + ..: to %lua write "\n ::continue_repeat::" + to %lua write "\nend --while-loop" + if + %body has subtree % where: (%.type = "Action") and (%.stub is "stop repeating") + .. %lua <- Lua ".." - while \(%condition as lua expr) do - \(%body as lua statements) - if - %body has subtree % where: (%.type = "Action") and (%.stub is "do next repeat") - ..: to %lua write "\n ::continue_repeat::" - to %lua write "\nend --while-loop" - if - %body has subtree % where: (%.type = "Action") and (%.stub is "stop repeating") - .. - %lua <- - Lua ".." - do -- scope of "stop repeating" label - \%lua - ::stop_repeat:: - end -- end of "stop repeating" label scope - return %lua - parse [repeat %body] as: repeat while (yes) %body - parse [repeat until %condition %body] as: repeat while (not %condition) %body - - compile [..] - repeat %n times %body - ..to + do -- scope of "stop repeating" label + \%lua + ::stop_repeat:: + end -- end of "stop repeating" label scope + return %lua +parse [repeat %body] as: repeat while (yes) %body +parse [repeat until %condition %body] as: repeat while (not %condition) %body + +compile [..] + repeat %n times %body +..to + %lua <- + Lua ".." + for i=1,\(%n as lua expr) do + \(%body as lua statements) + if + %body has subtree % where: (%.type = "Action") and (%.stub is "do next repeat") + ..: to %lua write "\n ::continue_repeat::" + to %lua write "\nend --numeric for-loop" + if + %body has subtree % where: (%.type = "Action") and (%.stub is "stop repeating") + .. %lua <- Lua ".." - for i=1,\(%n as lua expr) do - \(%body as lua statements) - if - %body has subtree % where: (%.type = "Action") and (%.stub is "do next repeat") - ..: to %lua write "\n ::continue_repeat::" - to %lua write "\nend --numeric for-loop" - if - %body has subtree % where: (%.type = "Action") and (%.stub is "stop repeating") - .. - %lua <- - Lua ".." - do -- scope of "stop repeating" label - \%lua - ::stop_repeat:: - end -- end of "stop repeating" label scope - return %lua + do -- scope of "stop repeating" label + \%lua + ::stop_repeat:: + end -- end of "stop repeating" label scope + return %lua # For loop control flow -immediately - compile [stop %var] to - Lua "goto stop_\(%var as lua identifier)" - compile [do next %var] to - Lua "goto continue_\(%var as lua identifier)" +compile [stop %var] to + Lua "goto stop_\(%var as lua identifier)" +compile [do next %var] to + Lua "goto continue_\(%var as lua identifier)" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Numeric range for loops -immediately - compile [..] - for %var in %start to %stop by %step %body - for %var in %start to %stop via %step %body - ..to - # This uses Lua's approach of only allowing loop-scoped variables in a loop - assume (%var.type is "Var") or barf "Loop expected variable, not: \%var" +compile [..] + for %var in %start to %stop by %step %body + for %var in %start to %stop via %step %body +..to + # This uses Lua's approach of only allowing loop-scoped variables in a loop + assume (%var.type is "Var") or barf "Loop expected variable, not: \%var" + %lua <- + Lua ".." + for \(%var as lua expr)=\(%start as lua expr),\(%stop as lua expr),\(%step as lua expr) do + \(%body as lua statements) + if + %body has subtree % where + (%.type = "Action") and + (%.stub is "do next %") and + %.3 = %var + ..: to %lua write "\n ::continue_\(%var as lua identifier)::" + to %lua write "\nend --numeric for-loop" + + if + %body has subtree % where + (%.type = "Action") and + (%.stub is "stop %") and + %.2 = %var + .. %lua <- Lua ".." - for \(%var as lua expr)=\(%start as lua expr),\(%stop as lua expr),\(%step as lua expr) do - \(%body as lua statements) - if - %body has subtree % where - (%.type = "Action") and - (%.stub is "do next %") and - %.3 = %var - ..: to %lua write "\n ::continue_\(%var as lua identifier)::" - to %lua write "\nend --numeric for-loop" - - if - %body has subtree % where - (%.type = "Action") and - (%.stub is "stop %") and - %.2 = %var - .. - %lua <- - Lua ".." - do -- scope for stopping for-loop - \%lua - ::stop_\(%var as lua identifier):: - end -- end of scope for stopping for-loop + do -- scope for stopping for-loop + \%lua + ::stop_\(%var as lua identifier):: + end -- end of scope for stopping for-loop + + return %lua - return %lua +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -immediately - parse [for %var in %start to %stop %body] as: for %var in %start to %stop via 1 %body +parse [for %var in %start to %stop %body] as: for %var in %start to %stop via 1 %body # For-each loop (lua's "ipairs()") -immediately - compile [for %var in %iterable %body] to - # This uses Lua's approach of only allowing loop-scoped variables in a loop - assume (%var.type is "Var") or barf "Loop expected variable, not: \%var" +compile [for %var in %iterable %body] to + # This uses Lua's approach of only allowing loop-scoped variables in a loop + assume (%var.type is "Var") or barf "Loop expected variable, not: \%var" + %lua <- + Lua ".." + for i,\(%var as lua identifier) in ipairs(\(%iterable as lua expr)) do + \(%body as lua statements) + if + %body has subtree % where + (%.type = "Action") and + (%.stub is "do next %") and + %.3.(1) = %var.(1) + ..: to %lua write (Lua "\n ::continue_\(%var as lua identifier)::") + to %lua write "\nend --foreach-loop" + if + %body has subtree % where + (%.type = "Action") and + (%.stub is "stop %") and + %.2.(1) = %var.(1) + .. %lua <- Lua ".." - for i,\(%var as lua identifier) in ipairs(\(%iterable as lua expr)) do - \(%body as lua statements) - if - %body has subtree % where - (%.type = "Action") and - (%.stub is "do next %") and - %.3.(1) = %var.(1) - ..: to %lua write (Lua "\n ::continue_\(%var as lua identifier)::") - to %lua write "\nend --foreach-loop" - if - %body has subtree % where - (%.type = "Action") and - (%.stub is "stop %") and - %.2.(1) = %var.(1) - .. - %lua <- - Lua ".." - do -- scope for stopping for-loop - \%lua - ::stop_\(%var as lua identifier):: - end -- end of scope for stopping for-loop - return %lua + do -- scope for stopping for-loop + \%lua + ::stop_\(%var as lua identifier):: + end -- end of scope for stopping for-loop + return %lua # Dict iteration (lua's "pairs()") -immediately - compile [..] - for %key = %value in %iterable %body - for (%key,%value) in %iterable %body - ..to - # This uses Lua's approach of only allowing loop-scoped variables in a loop - assume (%key.type is "Var") or barf "Loop expected variable, not: \%key" - assume (%value.type is "Var") or barf "Loop expected variable, not: \%value" +compile [..] + for %key = %value in %iterable %body + for (%key,%value) in %iterable %body +..to + # This uses Lua's approach of only allowing loop-scoped variables in a loop + assume (%key.type is "Var") or barf "Loop expected variable, not: \%key" + assume (%value.type is "Var") or barf "Loop expected variable, not: \%value" + %lua <- + Lua ".." + for \(%key as lua identifier),\(%value as lua identifier) in pairs(\(%iterable as lua expr)) do + \(%body as lua statements) + if + %body has subtree % where + (%.type = "Action") and + (%.stub is "do next %") and + %.3.(1) = %key.(1) + ..: to %lua write (Lua "\n ::continue_\(%key as lua identifier)::") + + if + %body has subtree % where + (%.type = "Action") and + (%.stub is "do next %") and + %.3.(1) = %value.(1) + ..: to %lua write (Lua "\n ::continue_\(%value as lua identifier)::") + to %lua write "\nend --foreach-loop" + + %stop_labels <- (Lua "") + if + %body has subtree % where + (%.type = "Action") and + (%.stub is "stop %") and + %.2.(1) = %key.(1) + ..: to %stop_labels write "\n::stop_\(%key as lua identifier)::" + + if + %body has subtree % where + (%.type = "Action") and + (%.stub is "stop %") and + %.2.(1) = %value.(1) + ..: to %stop_labels write "\n::stop_\(%value as lua identifier)::" + + if: (length of %stop_labels) > 0 %lua <- Lua ".." - for \(%key as lua identifier),\(%value as lua identifier) in pairs(\(%iterable as lua expr)) do - \(%body as lua statements) - if - %body has subtree % where - (%.type = "Action") and - (%.stub is "do next %") and - %.3.(1) = %key.(1) - ..: to %lua write (Lua "\n ::continue_\(%key as lua identifier)::") - - if - %body has subtree % where - (%.type = "Action") and - (%.stub is "do next %") and - %.3.(1) = %value.(1) - ..: to %lua write (Lua "\n ::continue_\(%value as lua identifier)::") - to %lua write "\nend --foreach-loop" - - %stop_labels <- (Lua "") - if - %body has subtree % where - (%.type = "Action") and - (%.stub is "stop %") and - %.2.(1) = %key.(1) - ..: to %stop_labels write "\n::stop_\(%key as lua identifier)::" - - if - %body has subtree % where - (%.type = "Action") and - (%.stub is "stop %") and - %.2.(1) = %value.(1) - ..: to %stop_labels write "\n::stop_\(%value as lua identifier)::" - - if: (length of %stop_labels) > 0 - %lua <- - Lua ".." - do -- scope for stopping for % = % loop - \%lua - \%stop_labels - end - return %lua + do -- scope for stopping for % = % loop + \%lua + \%stop_labels + end + return %lua -# Switch statement/multi-branch if -immediately - compile [when %body] to - %code <- (Lua "") - %fallthroughs <- [] - %is_first <- (yes) - %seen_else <- (no) - %branches <- - %body if (%body.type = "Block") else [%body] - for %func_call in %branches - assume (%func_call.type is "Action") or barf ".." - Invalid format for 'when' statement. Only '*' blocks are allowed. - with {..} - %star: %func_call.1 - %condition: %func_call.2 - %action: %func_call.3 - .. - assume (%star = "*") or barf ".." - Invalid format for 'when' statement. Lines must begin with '*' - assume %condition or barf ".." - Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else" - if: %action is (nil) - lua> "table.insert(\%fallthroughs, \(%condition as lua expr))" - do next %func_call - - if: %condition = "else" - assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block" - to %code write "\nelse\n " - to %code write: %action as lua statements - %seen_else <- (yes) - ..else - assume (not %seen_else) or barf "'else' clause needs to be last in 'when' block" - lua> "table.insert(\%fallthroughs, \(%condition as lua expr))" - to %code write "\("if" if %is_first else "\nelseif") " - for %i = %condition in %fallthroughs - if (%i > 1): to %code write " or " - to %code write %condition - to %code write " then\n " - to %code write (%action as lua statements) - - %fallthroughs <- [] - %is_first <- (no) - - assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block" - assume ((length of %code) > 0) or barf "Empty body for 'when' block" - to %code write "\nend --when" - return %code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Switch statement -immediately - compile [when %branch_value = ? %body, when %branch_value is ? %body] to - %code <- (Lua "") - %fallthroughs <- [] - %is_first <- (yes) - %seen_else <- (no) - %branches <- - %body if (%body.type = "Block") else [%body] - for %func_call in %branches - assume (%func_call.type is "Action") or barf ".." - Invalid format for 'when' statement. Only '*' blocks are allowed. - with {%star:%func_call.1, %condition:%func_call.2, %action:%func_call.3} - assume (%star = "*") or barf ".." - Invalid format for 'when' statement. Lines must begin with '*' - assume %condition or barf ".." - Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else" - if: %action is (nil) - lua> "table.insert(\%fallthroughs, \(%condition as lua expr))" - do next %func_call - - if: %condition = "else" - assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block" - to %code write "\nelse\n " - to %code write: %action as lua statements - ..else - assume (not %seen_else) or barf "'else' clause needs to be last in 'when % = ?' block" - to %code write "\("if" if %is_first else "\nelseif") " - lua> "table.insert(\%fallthroughs, \(%condition as lua expr))" - for %i = % in %fallthroughs - if: %i > 1 - to %code write " or " - if: (%.type is "Text") or (%.type is "Number") - to %code write "branch_value == \%" - ..else - to %code write "utils.equivalent(branch_value, \%)" - to %code write "then\n " - to %code write (%action as lua statements) +# Switch statement/multi-branch if +compile [when %body] to + %code <- (Lua "") + %fallthroughs <- [] + %is_first <- (yes) + %seen_else <- (no) + %branches <- + %body if (%body.type = "Block") else [%body] + for %func_call in %branches + assume (%func_call.type is "Action") or barf ".." + Invalid format for 'when' statement. Only '*' blocks are allowed. + with {..} + %star: %func_call.1 + %condition: %func_call.2 + %action: %func_call.3 + .. + assume (%star = "*") or barf ".." + Invalid format for 'when' statement. Lines must begin with '*' + assume %condition or barf ".." + Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else" + if: %action is (nil) + lua> "table.insert(\%fallthroughs, \(%condition as lua expr))" + do next %func_call + + if: %condition = "else" + assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block" + to %code write "\nelse\n " + to %code write: %action as lua statements + %seen_else <- (yes) + ..else + assume (not %seen_else) or barf "'else' clause needs to be last in 'when' block" + lua> "table.insert(\%fallthroughs, \(%condition as lua expr))" + to %code write "\("if" if %is_first else "\nelseif") " + for %i = %condition in %fallthroughs + if (%i > 1): to %code write " or " + to %code write %condition + to %code write " then\n " + to %code write (%action as lua statements) %fallthroughs <- [] %is_first <- (no) - - assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block" - assume ((length of %code) > 0) or barf "No body for 'when % = ?' block!" - to %code write "\nend" - %code <- - Lua ".." - do --when % = ? - local branch_value = \(%branch_value as lua expr) - \%code - end --when % = ? - return %code + assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block" + assume ((length of %code) > 0) or barf "Empty body for 'when' block" + to %code write "\nend --when" + return %code -# Do/finally -immediately - compile [do %action] to - Lua ".." - do - \(%action as lua statements) - end --do +# Switch statement +compile [when %branch_value = ? %body, when %branch_value is ? %body] to + %code <- (Lua "") + %fallthroughs <- [] + %is_first <- (yes) + %seen_else <- (no) + %branches <- + %body if (%body.type = "Block") else [%body] + for %func_call in %branches + assume (%func_call.type is "Action") or barf ".." + Invalid format for 'when' statement. Only '*' blocks are allowed. + with {%star:%func_call.1, %condition:%func_call.2, %action:%func_call.3} + assume (%star = "*") or barf ".." + Invalid format for 'when' statement. Lines must begin with '*' + assume %condition or barf ".." + Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else" + if: %action is (nil) + lua> "table.insert(\%fallthroughs, \(%condition as lua expr))" + do next %func_call + + if: %condition = "else" + assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block" + to %code write "\nelse\n " + to %code write: %action as lua statements + ..else + assume (not %seen_else) or barf "'else' clause needs to be last in 'when % = ?' block" + to %code write "\("if" if %is_first else "\nelseif") " + lua> "table.insert(\%fallthroughs, \(%condition as lua expr))" + for %i = % in %fallthroughs + if: %i > 1 + to %code write " or " + if: (%.type is "Text") or (%.type is "Number") + to %code write "branch_value == \%" + ..else + to %code write "utils.equivalent(branch_value, \%)" + to %code write "then\n " + to %code write (%action as lua statements) - compile [do %action then always %final_action] to + %fallthroughs <- [] + %is_first <- (no) + + assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block" + assume ((length of %code) > 0) or barf "No body for 'when % = ?' block!" + to %code write "\nend" + %code <- Lua ".." - do - local fell_through = false - local ok, ret = pcall(function() - \(%action as lua statements) - fell_through = true - end) - \(%final_action as lua statements) - if not ok then error(ret, 0) end - if not fell_through then return ret end - end + do --when % = ? + local branch_value = \(%branch_value as lua expr) + \%code + end --when % = ? + return %code + +# Do/finally +compile [do %action] to + Lua ".." + do + \(%action as lua statements) + end --do + +compile [do %action then always %final_action] to + Lua ".." + do + local fell_through = false + local ok, ret = pcall(function() + \(%action as lua statements) + fell_through = true + end) + \(%final_action as lua statements) + if not ok then error(ret, 0) end + if not fell_through then return ret end + end # Inline thunk: -immediately - compile [result of %body] to - %body <- (%body as lua statements) - declare locals in %body - return - Lua value ".." - (function() - \%body - end)() +compile [result of %body] to + %body <- (%body as lua statements) + declare locals in %body + return + Lua value ".." + (function() + \%body + end)() diff --git a/core/errors.nom b/core/errors.nom index 8c6261a..812052e 100644 --- a/core/errors.nom +++ b/core/errors.nom @@ -22,47 +22,47 @@ compile [assume %condition or barf %message] to end # Try/except -immediately - compile [..] - try %action and if it succeeds %success or if it barfs %msg %fallback - try %action and if it barfs %msg %fallback or if it succeeds %success - ..to - Lua ".." - do - local fell_through = false - local err, erred = nil, false - local ok, ret = xpcall(function() - \(%action as lua statements) - fell_through = true - end, function(\(%msg as lua expr)) - local ok, ret = pcall(function() - \(%fallback as lua statements) - end) - if not ok then err, erred = ret, true end +compile [..] + try %action and if it succeeds %success or if it barfs %msg %fallback + try %action and if it barfs %msg %fallback or if it succeeds %success +..to + Lua ".." + do + local fell_through = false + local err, erred = nil, false + local ok, ret = xpcall(function() + \(%action as lua statements) + fell_through = true + end, function(\(%msg as lua expr)) + local ok, ret = pcall(function() + \(%fallback as lua statements) end) - if ok then - \(%success as lua statements) - if not fell_through then - return ret - end - elseif erred then - error(err, 0) + if not ok then err, erred = ret, true end + end) + if ok then + \(%success as lua statements) + if not fell_through then + return ret end + elseif erred then + error(err, 0) end -immediately - parse [..] - try %action and if it succeeds %success or if it barfs %fallback - try %action and if it barfs %fallback or if it succeeds %success - ..as: try %action and if it succeeds %success or if it barfs (=lua "") %fallback -immediately - parse [try %action] as - try %action and if it succeeds: do nothing - ..or if it barfs: do nothing - parse [try %action and if it barfs %fallback] as - try %action and if it succeeds: do nothing - ..or if it barfs %fallback - parse [try %action and if it barfs %msg %fallback] as - try %action and if it succeeds: do nothing - ..or if it barfs %msg %fallback - parse [try %action and if it succeeds %success] as - try %action and if it succeeds %success or if it barfs: do nothing + end + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +parse [..] + try %action and if it succeeds %success or if it barfs %fallback + try %action and if it barfs %fallback or if it succeeds %success +..as: try %action and if it succeeds %success or if it barfs (=lua "") %fallback +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +parse [try %action] as + try %action and if it succeeds: do nothing + ..or if it barfs: do nothing +parse [try %action and if it barfs %fallback] as + try %action and if it succeeds: do nothing + ..or if it barfs %fallback +parse [try %action and if it barfs %msg %fallback] as + try %action and if it succeeds: do nothing + ..or if it barfs %msg %fallback +parse [try %action and if it succeeds %success] as + try %action and if it succeeds %success or if it barfs: do nothing diff --git a/core/io.nom b/core/io.nom index 4f12e57..751650d 100644 --- a/core/io.nom +++ b/core/io.nom @@ -6,15 +6,15 @@ use "core/metaprogramming.nom" compile [say %message] to lua> ".." if \%message.type == "Text" then - return Lua(tree.source, "io.write(", \(%message as lua expr), ", '\\\\n');"); + return LuaCode(tree.source, "io.write(", \(%message as lua expr), ", '\\\\n');"); else - return Lua(tree.source, "io.write(tostring(", \(%message as lua expr), "), '\\\\n');"); + return LuaCode(tree.source, "io.write(tostring(", \(%message as lua expr), "), '\\\\n');"); end compile [ask %prompt] to lua> ".." if \%prompt.type == "Text" then - return Lua.Value(tree.source, "(io.write(", \(%prompt as lua expr), ") and io.read())"); + return LuaCode.Value(tree.source, "(io.write(", \(%prompt as lua expr), ") and io.read())"); else - return Lua.Value(tree.source, "(io.write(tostring(", \(%prompt as lua expr), ")) and io.read())"); + return LuaCode.Value(tree.source, "(io.write(tostring(", \(%prompt as lua expr), ")) and io.read())"); end diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom index 82447b3..d04ced3 100644 --- a/core/metaprogramming.nom +++ b/core/metaprogramming.nom @@ -3,89 +3,122 @@ functions to make that easier. # Compile-time action to make compile-time actions: -immediately - lua> ".." - A_give_1_nickname_2 = compile_time(function(tree, \%action, \%nickname, is_compile_time) - local function arg_to_string(a) return tostring(nomsu:tree_to_lua(a)) end - local action_args = table.map(\%action:get_args(), arg_to_string) - local nickname_args = table.map(\%nickname:get_args(), arg_to_string) - if utils.equivalent(action_args, nickname_args) then - return Lua(tree.source, "A", string.as_lua_id(\%nickname.stub), " = A", string.as_lua_id(\%action.stub)) - end - local lua = Lua(tree.source, "A", string.as_lua_id(\%nickname.stub), " = ") - if is_compile_time or COMPILE_TIME[_ENV["A"..string.as_lua_id(\%action.stub)]] then - lua:append("compile_time") - table.insert(action_args, 1, "tree") - table.insert(nickname_args, 1, "tree") +lua> ".." + nomsu.COMPILE_ACTIONS["give % nickname %"] = (function(nomsu, tree, \%action, \%nickname, is_compile_time) + local function arg_to_string(a) return tostring(nomsu:compile(a)) end + local action_args = table.map(\%action:get_args(), arg_to_string) + local nickname_args = table.map(\%nickname:get_args(), arg_to_string) + if utils.equivalent(action_args, nickname_args) then + if is_compile_time then + return LuaCode(tree.source, "nomsu.COMPILE_ACTIONS[", repr(\%nickname.stub), "] = nomsu.COMPILE_ACTIONS[", repr(\%action.stub), "]") + else + return LuaCode(tree.source, "A", string.as_lua_id(\%nickname.stub), " = A", string.as_lua_id(\%action.stub)) end - lua:append("(function(") - lua:concat_append(nickname_args, ", ") + end + local lua = LuaCode(tree.source) + if is_compile_time then + lua:append("nomsu.COMPILE_ACTIONS[", repr(\%nickname.stub), "] = ") + table.insert(action_args, 1, "nomsu") + table.insert(nickname_args, 1, "nomsu") + table.insert(action_args, 2, "tree") + table.insert(nickname_args, 2, "tree") + else + lua:append("A", string.as_lua_id(\%nickname.stub), " = ") + end + lua:append("(function(") + lua:concat_append(nickname_args, ", ") + if is_compile_time then + lua:append(")\n return nomsu.COMPILE_ACTIONS[", repr(\%action.stub), "](") + else lua:append(")\n return A", string.as_lua_id(\%action.stub), "(") - lua:concat_append(action_args, ", ") - lua:append(")\nend)") - return lua - end) - - __MANGLE_INDEX = 0 - A_parse_1_as_2 = compile_time(function(tree, \%actions, \%body) - local replacements = {} - for i,arg in ipairs(\%actions[1]:get_args()) do - replacements[arg[1]] = tostring(nomsu:tree_to_lua(arg)) - end - local function make_tree(t) - if not AST.is_syntax_tree(t) then - return repr(t) - elseif t.type ~= 'Var' then - local args = table.map(t, make_tree) - table.insert(args, 1, repr(tostring(t.source))) - return t.type.."("..table.concat(args, ", ")..")" - elseif replacements[t[1]] then - return replacements[t[1]] - else - return t.type.."("..repr(tostring(t.source))..", "..repr(t[1].." \\0").."..('%X'):format(__MANGLE_INDEX))" - end - end - local lua = Lua(tree.source, "A", string.as_lua_id(\%actions[1].stub), " = compile_time(function(tree") - for _,arg in ipairs(\%actions[1]:get_args()) do - lua:append(", ", nomsu:tree_to_lua(arg)) - end - lua:append(")\n __MANGLE_INDEX = __MANGLE_INDEX + 1", - "\n local tree = ", make_tree(\%body), - "\n local lua = nomsu:tree_to_lua(tree)", - "\n lua:remove_free_vars({") - local vars = table.map(\%actions[1]:get_args(), function(a) - return "Var("..repr(tostring(a.source))..", "..repr(a[1])..")" - end) - lua:concat_append(vars, ", ") - lua:append("})\n return lua\nend)") - - for i=2,#\%actions do - lua:append("\n", A_give_1_nickname_2(\%actions[i], \%actions[1], \%actions[i], true)) + end + lua:concat_append(action_args, ", ") + lua:append(")\nend)") + if not is_compile_time then + lua:add_free_vars({"A"..string.as_lua_id(\%nickname.stub)}) + end + return lua + end) + + __MANGLE_INDEX = 0 + nomsu.COMPILE_ACTIONS["parse % as %"] = (function(nomsu, tree, \%actions, \%body) + local replacements = {} + for i,arg in ipairs(\%actions[1]:get_args()) do + replacements[arg[1]] = tostring(nomsu:compile(arg)) + end + local function make_tree(t) + if not AST.is_syntax_tree(t) then + return repr(t) + elseif t.type ~= 'Var' then + local args = table.map(t, make_tree) + table.insert(args, 1, repr(tostring(t.source))) + return t.type.."("..table.concat(args, ", ")..")" + elseif replacements[t[1]] then + return replacements[t[1]] + else + return t.type.."("..repr(tostring(t.source))..", "..repr(t[1].." \\0").."..('%X'):format(__MANGLE_INDEX))" end - return lua - end) - - A_action_1_2 = compile_time(function(tree, \%actions, \%body, is_compile_time) - local lua = Lua(tree.source, "A", string.as_lua_id(\%actions[1].stub), " = ") - if is_compile_time then lua:append("compile_time") end - lua:append("(function(") - local args = \%actions[1]:get_args() - local lua_args = table.map(args, function(a) return nomsu:tree_to_lua(a) end) - if is_compile_time then table.insert(lua_args, 1, "tree") end - lua:concat_append(lua_args, ", ") - local body_lua = nomsu:tree_to_lua(\%body):as_statements("return ") - body_lua:remove_free_vars(args) - body_lua:declare_locals() - lua:append(")\n ", body_lua, "\nend)") - for i=2,#\%actions do - lua:append("\n", A_give_1_nickname_2(\%actions[i], \%actions[1], \%actions[i], is_compile_time)) - end - return lua - end) - - A_compile_1_to_2 = compile_time(function(tree, \%actions, \%body) - return A_action_1_2(tree, \%actions, \%body, true) + end + local lua = LuaCode(tree.source, "nomsu.COMPILE_ACTIONS[", repr(\%actions[1].stub), "] = (function(nomsu, tree") + lua:add_free_vars({"A"..string.as_lua_id(\%actions[1].stub)}) + for _,arg in ipairs(\%actions[1]:get_args()) do + lua:append(", ", nomsu:compile(arg)) + end + lua:append(")\n __MANGLE_INDEX = __MANGLE_INDEX + 1", + "\n local tree = ", make_tree(\%body), + "\n local lua = nomsu:compile(tree)", + "\n lua:remove_free_vars({") + local vars = table.map(\%actions[1]:get_args(), function(a) + return "Var("..repr(tostring(a.source))..", "..repr(a[1])..")" end) + lua:concat_append(vars, ", ") + lua:append("})\n return lua\nend)") + + for i=2,#\%actions do + lua:append("\n", nomsu.COMPILE_ACTIONS["give % nickname %"](nomsu, \%actions[i], \%actions[1], \%actions[i], true)) + end + return lua + end) + + nomsu.COMPILE_ACTIONS["local action % %"] = (function(nomsu, tree, \%actions, \%body, is_compile_time) + local lua = LuaCode(tree.source) + if is_compile_time then + lua:append("nomsu.COMPILE_ACTIONS[", repr(\%actions[1].stub), "] = ") + else + lua:append("A", string.as_lua_id(\%actions[1].stub), " = ") + lua:add_free_vars({"A"..string.as_lua_id(\%actions[1].stub)}) + end + lua:append("(function(") + local args = \%actions[1]:get_args() + local lua_args = table.map(args, function(a) return nomsu:compile(a) end) + if is_compile_time then + table.insert(lua_args, 1, "nomsu") + table.insert(lua_args, 2, "tree") + end + lua:concat_append(lua_args, ", ") + local body_lua = nomsu:compile(\%body):as_statements("return ") + body_lua:remove_free_vars(args) + body_lua:declare_locals() + lua:append(")\n ", body_lua, "\nend)") + for i=2,#\%actions do + lua:append("\n", nomsu.COMPILE_ACTIONS["give % nickname %"](nomsu, \%actions[i], \%actions[1], \%actions[i], is_compile_time)) + end + return lua + end) + + -- Compile-time actions are always global, since they affect the state of the compiler + nomsu.COMPILE_ACTIONS["compile % to %"] = (function(nomsu, tree, \%actions, \%body) + local lua = nomsu.COMPILE_ACTIONS["local action % %"](nomsu, tree, \%actions, \%body, true) + return lua + end) + + nomsu.COMPILE_ACTIONS["action % %"] = (function(nomsu, tree, \%actions, \%body) + local lua = nomsu.COMPILE_ACTIONS["local action % %"](nomsu, tree, \%actions, \%body) + lua:remove_free_vars(table.map(\%actions, function(a) return "A"..a.stub:as_lua_id() end)) + return lua + end) + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ compile [remove action %action] to Lua ".." @@ -93,90 +126,87 @@ compile [remove action %action] to ARG_ORDERS[fn] = nil COMPILE_TIME[fn] = nil -immediately - action [%tree as nomsu] - =lua "nomsu:tree_to_nomsu(\%tree)" +action [%tree as nomsu] + =lua "nomsu:tree_to_nomsu(\%tree)" - action [%tree as inline nomsu] - =lua "nomsu:tree_to_nomsu(\%tree, true)" +action [%tree as inline nomsu] + =lua "nomsu:tree_to_nomsu(\%tree, true)" - action [%tree as lua] - =lua "nomsu:tree_to_lua(\%tree)" +action [%tree as lua] + =lua "nomsu:compile(\%tree)" - action [%tree as lua expr] - lua> ".." - local lua = nomsu:tree_to_lua(\%tree) - if not lua.is_value then - compile_error(\%tree, "Invalid thing to convert to lua expr:\n%s") - end - return lua +action [%tree as lua expr] + lua> ".." + local lua = nomsu:compile(\%tree) + if not lua.is_value then + nomsu:compile_error(\%tree, "Invalid thing to convert to lua expr:\n%s") + end + return lua - action [%tree as lua statements] - =lua "nomsu:tree_to_lua(\%tree):as_statements()" +action [%tree as lua statements] + =lua "nomsu:compile(\%tree):as_statements()" - action [%tree as lua return] - =lua "nomsu:tree_to_lua(\%tree):as_statements('return ')" +action [%tree as lua return] + =lua "nomsu:compile(\%tree):as_statements('return ')" - action [%var as lua identifier, %var as lua id] - lua> ".." - if type(\%var) == 'string' then return string.as_lua_id(\%var) - elseif \%var.type == 'Var' then return string.as_lua_id(\%var[1]) - elseif \%var.type == 'Action' then return "A"..string.as_lua_id(\%var.stub) - end - -immediately - compile [%tree with %t -> %replacement] to - Lua value ".." - \(%tree as lua expr):map(function(\(%t as lua expr)) - \(%replacement as lua return) - end) - - compile [declare locals in %code] to - Lua value "\(%code as lua expr):declare_locals()" - - compile [declare locals %locals in %code] to - Lua value "\(%code as lua expr):declare_locals(\(%locals as lua expr))" +action [%var as lua identifier, %var as lua id] + lua> ".." + if type(\%var) == 'string' then return string.as_lua_id(\%var) + elseif \%var.type == 'Var' then return string.as_lua_id(\%var[1]) + elseif \%var.type == 'Action' then return "A"..string.as_lua_id(\%var.stub) + end + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - compile [remove free vars %vars from %code] to - Lua "\(%code as lua expr):remove_free_vars(\(%vars as lua expr));" +compile [%tree with %t -> %replacement] to + Lua value ".." + \(%tree as lua expr):map(function(\(%t as lua expr)) + \(%replacement as lua return) + end) - parse [%lua <-write %code, to %lua write %code] as: lua> "\%lua:append(\%code);" +compile [declare locals in %code] to + Lua value "\(%code as lua expr):declare_locals()" -immediately - compile [quote %s] to - Lua value ".." - ('"'..\(%s as lua expr):gsub("\\\\", "\\\\\\\\"):gsub("\n","\\\\n"):gsub('"', '\\\\"')..'"') - compile [type of %obj] to: Lua value "type(\(%obj as lua expr))" +compile [declare locals %locals in %code] to + Lua value "\(%code as lua expr):declare_locals(\(%locals as lua expr))" -immediately - compile [parse %text] to - Lua value ".." - nomsu:parse(Nomsu("\("\(%text.source)")", \(%text as lua expr))) +compile [remove free vars %vars from %code] to + Lua "\(%code as lua expr):remove_free_vars(\(%vars as lua expr));" - compile [run %nomsu_code] to - Lua value "nomsu:run(Nomsu(\(quote "\(%nomsu_code.source)"), \(%nomsu_code as lua expr)))" +parse [%lua <-write %code, to %lua write %code] as: lua> "\%lua:append(\%code);" - action [run tree %tree, %tree as value] - lua> ".." - if \%tree.type == 'Text' and #\%tree == 1 and type(\%tree[1]) == 'string' then - return \%tree[1] - end - local lua = Lua(\%tree.source, "return ",nomsu:tree_to_lua(\%tree)) - return nomsu:run_lua(lua) +compile [quote %s] to + Lua value ".." + ('"'..\(%s as lua expr):gsub("\\\\", "\\\\\\\\"):gsub("\n","\\\\n"):gsub('"', '\\\\"')..'"') +compile [type of %obj] to: Lua value "type(\(%obj as lua expr))" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +compile [parse %text] to + Lua value ".." + nomsu:parse(NomsuCode("\("\(%text.source)")", \(%text as lua expr))) + +compile [run %nomsu_code] to + Lua value "nomsu:run(NomsuCode(\(quote "\(%nomsu_code.source)"), \(%nomsu_code as lua expr)))" + +action [run tree %tree, %tree as value] + lua> ".." + if \%tree.type == 'Text' and #\%tree == 1 and type(\%tree[1]) == 'string' then + return \%tree[1] + end + local lua = LuaCode(\%tree.source, "return ",nomsu:compile(\%tree)) + return nomsu:run_lua(lua) - compile [compile %block, compiled %block, %block compiled] to - Lua value "nomsu:tree_to_lua(\(%block as lua))" +compile [compile %block, compiled %block, %block compiled] to + Lua value "nomsu:compile(\(%block as lua))" -# 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: Lua "do return; end" - compile [return %return_value] to: Lua "do return \(%return_value as lua expr); end" +# 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: Lua "do return; end" +compile [return %return_value] to: Lua "do return \(%return_value as lua expr); end" # Literals -immediately - compile [yes] to: Lua value "true" - compile [no] to: Lua value "false" - compile [nothing, nil, null] to: Lua value "nil" +compile [yes] to: Lua value "true" +compile [no] to: Lua value "false" +compile [nothing, nil, null] to: Lua value "nil" diff --git a/core/operators.nom b/core/operators.nom index f07ab3f..b4567b1 100644 --- a/core/operators.nom +++ b/core/operators.nom @@ -5,158 +5,175 @@ use "core/metaprogramming.nom" use "core/errors.nom" # Comparison Operators -immediately - compile [%x < %y] to: Lua value "(\(%x as lua expr) < \(%y as lua expr))" - compile [%x > %y] to: Lua value "(\(%x as lua expr) > \(%y as lua expr))" - compile [%x <= %y] to: Lua value "(\(%x as lua expr) <= \(%y as lua expr))" - compile [%x >= %y] to: Lua value "(\(%x as lua expr) >= \(%y as lua expr))" - compile [%a is %b, %a = %b, %a == %b] to - Lua value "(\(%a as lua expr) == \(%b as lua expr))" - compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to - Lua value "(\(%a as lua expr) ~= \(%b as lua expr))" - # For strict identity checking, use (%x's id) is (%y's id) - compile [%'s id, id of %] to: Lua value "nomsu.ids[\(% as lua expr)]" +compile [%x < %y] to: Lua value "(\(%x as lua expr) < \(%y as lua expr))" +compile [%x > %y] to: Lua value "(\(%x as lua expr) > \(%y as lua expr))" +compile [%x <= %y] to: Lua value "(\(%x as lua expr) <= \(%y as lua expr))" +compile [%x >= %y] to: Lua value "(\(%x as lua expr) >= \(%y as lua expr))" +compile [%a is %b, %a = %b, %a == %b] to + Lua value "(\(%a as lua expr) == \(%b as lua expr))" +compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to + Lua value "(\(%a as lua expr) ~= \(%b as lua expr))" +# For strict identity checking, use (%x's id) is (%y's id) +lua> ".." + do + local new_uuid = require('uuid') + local NaN_surrogate = {} + local nil_surrogate = {} + IDS = setmetatable({}, { + __mode = "k", + __index = function(self, key) + if key == nil then return self[nil_surrogate] + elseif key ~= key then return self[NaN_surrogate] end + local id = new_uuid() + self[key] = id + return id + end + }) + end +compile [%'s id, id of %] to: Lua value "IDS[\(% as lua expr)]" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Variable assignment operator -immediately - compile [%var <- %value] to - lua> "local \%var_lua = \(%var as lua)" - assume %var_lua.is_value or barf "Invalid target for assignment: \%var" - lua> ".." +compile [%var <- %value] to + lua> "local \%var_lua = \(%var as lua)" + assume %var_lua.is_value or barf "Invalid target for assignment: \%var" + lua> ".." + \%value = \%value:map(function(t) + if Action:is_instance(t) and t.stub == "?" then + return \%var + end + end) + local \%value_lua = \(%value as lua) + + assume %value_lua.is_value or barf "Invalid value for assignment: \%value" + lua> ".." + local lua = LuaCode(tree.source, \%var_lua, ' = ', \%value_lua, ';') + if \%var.type == 'Var' then + lua:add_free_vars({\%var}) + end + return lua + +# Simultaneous mutli-assignments like: x,y,z = 1,x,3; +compile [<- %assignments, assign %assignments] to + assume (%assignments.type is "Dict") or barf ".." + Expected a Dict for the assignments part of '<- %' statement, not \%assignments + lua> ".." + local lhs, rhs = LuaCode(tree.source), LuaCode(tree.source) + for i, item in ipairs(\%assignments) do + local \%target, \%value = item[1], item[2] \%value = \%value:map(function(t) if Action:is_instance(t) and t.stub == "?" then - return \%var + return \%target end end) - local \%value_lua = \(%value as lua) + local target_lua = \(%target as lua) + if not target_lua.is_value then error("Invalid target for assignment: "..\(%target as text)) end + local value_lua = \(%value as lua) + if not value_lua.is_value then error("Invalid value for assignment: "..\(%value as text)) end + if \%target.type == "Var" then + lhs:add_free_vars({\%target}) + end + if i > 1 then + lhs:append(", ") + rhs:append(", ") + end + lhs:append(target_lua) + rhs:append(value_lua) + end + return LuaCode(tree.source, lhs, " = ", rhs, ";") + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +compile [external %var <- %value] to + %var_lua <- (%var as lua) + assume %var_lua.is_value or barf "Invalid target for assignment: \%var" + %value_lua <- (%value as lua) + assume %value_lua.is_value or barf "Invalid value for assignment: \%value" + return: Lua "\%var_lua = \%value_lua;" - assume %value_lua.is_value or barf "Invalid value for assignment: \%value" - lua> ".." - local lua = Lua(tree.source, \%var_lua, ' = ', \%value_lua, ';') - if \%var.type == 'Var' then - lua:add_free_vars({\%var}) +compile [with external %externs %body] to + %body_lua <- (%body as lua statements) + lua> "\%body_lua:remove_free_vars(\%externs);" + return %body_lua + +compile [with %assignments %body] to + %lua <- (%body as lua statements) + lua> ".." + local lhs, rhs = LuaCode(tree.source), LuaCode(tree.source) + local vars = {} + for i, item in ipairs(\%assignments) do + local \%target, \%value = item[1], item[2] + if not \%target.type == "Var" then + error("Invalid target for 'with' assignment: "..tostring(\%target)) end - return lua; - -immediately - # Simultaneous mutli-assignments like: x,y,z = 1,x,3; - compile [<- %assignments, assign %assignments] to - assume (%assignments.type is "Dict") or barf ".." - Expected a Dict for the assignments part of '<- %' statement, not \%assignments - lua> ".." - local lhs, rhs = Lua(tree.source), Lua(tree.source) - for i, item in ipairs(\%assignments) do - local \%target, \%value = item[1], item[2] - \%value = \%value:map(function(t) - if Action:is_instance(t) and t.stub == "?" then - return \%target - end - end) - local target_lua = \(%target as lua) - if not target_lua.is_value then error("Invalid target for assignment: "..\(%target as text)) end - local value_lua = \(%value as lua) - if not value_lua.is_value then error("Invalid value for assignment: "..\(%value as text)) end - if \%target.type == "Var" then - lhs:add_free_vars({\%target}) - end - if i > 1 then - lhs:append(", ") - rhs:append(", ") - end - lhs:append(target_lua) - rhs:append(value_lua) + local target_lua = \(%target as lua) + local value_lua = \(%value as lua) + if not value_lua.is_value then + error("Invalid value for assignment: "..tostring(\%value)) end - return Lua(tree.source, lhs, " = ", rhs, ";") - -immediately - compile [external %var <- %value] to - %var_lua <- (%var as lua) - assume %var_lua.is_value or barf "Invalid target for assignment: \%var" - %value_lua <- (%value as lua) - assume %value_lua.is_value or barf "Invalid value for assignment: \%value" - return: Lua "\%var_lua = \%value_lua;" - - compile [with external %externs %body] to - %body_lua <- (%body as lua statements) - lua> "\%body_lua:remove_free_vars(\%externs);" - return %body_lua - - compile [with %assignments %body] to - %lua <- (%body as lua statements) - lua> ".." - local lhs, rhs = Lua(tree.source), Lua(tree.source) - local vars = {} - for i, item in ipairs(\%assignments) do - local \%target, \%value = item[1], item[2] - if not \%target.type == "Var" then - error("Invalid target for 'with' assignment: "..tostring(\%target)) - end - local target_lua = \(%target as lua) - local value_lua = \(%value as lua) - if not value_lua.is_value then - error("Invalid value for assignment: "..tostring(\%value)) - end - if \%target.type == "Var" then - lhs:add_free_vars({\%target}) - end - if i > 1 then - lhs:append(", ") - rhs:append(", ") - end - lhs:append(target_lua) - rhs:append(value_lua) - vars[i] = \%target + if \%target.type == "Var" then + lhs:add_free_vars({\%target}) + end + if i > 1 then + lhs:append(", ") + rhs:append(", ") end - \%lua:remove_free_vars(vars) - \%lua:prepend("local ", lhs, " = ", rhs, ";\n") - return - Lua ".." - do - \%lua - end -- 'with' block - -immediately - # Math Operators - compile [%x wrapped around %y, %x mod %y] to: Lua value "(\(%x as lua expr) % \(%y as lua expr))" - - # 3-part chained comparisons - # (uses a lambda to avoid re-evaluating middle value, while still being an expression) - parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)" - parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)" - parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)" - parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)" - parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)" - parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)" - parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)" - parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)" - # TODO: optimize for common case where x,y,z are all either variables or number literals - - # Boolean Operators - compile [%x and %y] to: Lua value "(\(%x as lua expr) and \(%y as lua expr))" - compile [%x or %y] to: Lua value "(\(%x as lua expr) or \(%y as lua expr))" - - # Bitwise Operators - compile [%a OR %b, %a | %b] to: Lua value "bit32.bor(\(%a as lua expr), \(%b as lua expr))" - compile [%a XOR %b] to: Lua value "bit32.bxor(\(%a as lua expr), \(%b as lua expr))" - compile [%a AND %b, %a & %b] to: Lua value "bit32.band(\(%a as lua expr), \(%b as lua expr))" - compile [NOT %, ~ %] to: Lua value "bit32.bnot(\(% as lua expr))" - compile [%x LSHIFT %shift, %x << %shift] to: Lua value "bit32.lshift(\(%x as lua expr), \(%shift as lua expr))" - compile [%x RSHIFT %shift, %x >>> %shift] to: Lua value "bit32.rshift(\(%x as lua expr), \(%shift as lua expr))" - compile [%x ARSHIFT %shift, %x >> %shift] to: Lua value "bit32.arshift(\(%x as lua expr), \(%shift as lua expr))" - # TODO: implement OR, XOR, AND for multiple operands? - - # Unary operators - compile [- %] to: Lua value "(- \(% as lua expr))" - compile [not %] to: Lua value "(not \(% as lua expr))" - compile [length of %list] to: Lua value "(#\(%list as lua expr))" + lhs:append(target_lua) + rhs:append(value_lua) + vars[i] = \%target + end + \%lua:remove_free_vars(vars) + \%lua:prepend("local ", lhs, " = ", rhs, ";\n") + return + Lua ".." + do + \%lua + end -- 'with' block + +# Math Operators +compile [%x wrapped around %y, %x mod %y] to: Lua value "(\(%x as lua expr) % \(%y as lua expr))" + +# 3-part chained comparisons +# (uses a lambda to avoid re-evaluating middle value, while still being an expression) +parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)" +parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)" +parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)" +parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)" +parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)" +parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)" +parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)" +parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)" +# TODO: optimize for common case where x,y,z are all either variables or number literals + +# Boolean Operators +compile [%x and %y] to: Lua value "(\(%x as lua expr) and \(%y as lua expr))" +compile [%x or %y] to: Lua value "(\(%x as lua expr) or \(%y as lua expr))" + +# Bitwise Operators +# TODO: support bit.???() for luajit and bit32.??? for lua 5.2 +compile [%a OR %b, %a | %b] to: Lua value "(\(%a as lua expr) | \(%b as lua expr))" +compile [%a XOR %b] to: Lua value "(\(%a as lua expr) ^ \(%b as lua expr))" +compile [%a AND %b, %a & %b] to: Lua value "(\(%a as lua expr) & \(%b as lua expr))" +compile [NOT %, ~ %] to: Lua value "~(\(% as lua expr))" +compile [%x LSHIFT %shift, %x << %shift] to: Lua value "(\(%x as lua expr) << \(%shift as lua expr))" +compile [%x RSHIFT %shift, %x >>> %shift] to: Lua value "(\(%x as lua expr) >>> \(%shift as lua expr))" +compile [%x ARSHIFT %shift, %x >> %shift] to: Lua value "(\(%x as lua expr) >> \(%shift as lua expr))" +# TODO: implement OR, XOR, AND for multiple operands? + +# Unary operators +compile [- %] to: Lua value "(- \(% as lua expr))" +compile [not %] to: Lua value "(not \(% as lua expr))" +compile [length of %list] to: Lua value "(#\(%list as lua expr))" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Update operators -immediately - parse [%var + <- %, %var +<- %] as: %var <- (%var + %) - parse [%var - <- %, %var -<- %] as: %var <- (%var - %) - parse [%var * <- %, %var *<- %] as: %var <- (%var * %) - parse [%var / <- %, %var /<- %] as: %var <- (%var / %) - parse [%var ^ <- %, %var ^<- %] as: %var <- (%var ^ %) - parse [%var and <- %] as: %var <- (%var and %) - parse [%var or <- %] as: %var <- (%var or %) - parse [wrap %var around %] as: %var <- (%var wrapped around %) +parse [%var + <- %, %var +<- %] as: %var <- (%var + %) +parse [%var - <- %, %var -<- %] as: %var <- (%var - %) +parse [%var * <- %, %var *<- %] as: %var <- (%var * %) +parse [%var / <- %, %var /<- %] as: %var <- (%var / %) +parse [%var ^ <- %, %var ^<- %] as: %var <- (%var ^ %) +parse [%var and <- %] as: %var <- (%var and %) +parse [%var or <- %] as: %var <- (%var or %) +parse [wrap %var around %] as: %var <- (%var wrapped around %) diff --git a/core/text.nom b/core/text.nom index 8b26ccb..b576127 100644 --- a/core/text.nom +++ b/core/text.nom @@ -35,7 +35,7 @@ lua> ".." }; for name, e in pairs(escapes) do local lua = "'"..e.."'" - _ENV["A"..name:as_lua_id()] = compile_time(function(tree) return Lua.Value(tree.source, lua) end) + nomsu.COMPILE_ACTIONS[name] = (function(nomsu, tree) return LuaCode.Value(tree.source, lua) end) end local colors = { ["reset color"]="\\\\27[0m", bright="\\\\27[1m", dim="\\\\27[2m", underscore="\\\\27[4m", @@ -50,9 +50,9 @@ lua> ".." for name, c in pairs(colors) do local color = "'"..c.."'"; local reset = "'"..colors["reset color"].."'"; - _ENV["A"..name:as_lua_id()] = compile_time(function(tree) return Lua.Value(tree.source, color) end) - _ENV["A"..name:as_lua_id().."_1"] = compile_time(function(tree, text) - return Lua.Value(tree.source, color, "..", nomsu:tree_to_lua(text), "..", reset); + nomsu.COMPILE_ACTIONS[name] = (function(nomsu, tree) return LuaCode.Value(tree.source, color) end) + nomsu.COMPILE_ACTIONS[name.." %"] = (function(nomsu, tree, text) + return LuaCode.Value(tree.source, color, "..", nomsu:tree_to_lua(text), "..", reset); end) end end |
