aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/collections.nom84
-rw-r--r--lib/control_flow.nom192
-rw-r--r--lib/math.nom80
-rw-r--r--lib/metaprogramming.nom91
-rw-r--r--lib/operators.nom80
-rw-r--r--lib/text.nom49
-rwxr-xr-xnomsu.moon219
-rw-r--r--nomsu.peg59
8 files changed, 402 insertions, 452 deletions
diff --git a/lib/collections.nom b/lib/collections.nom
index d0d1f02..90bd08c 100644
--- a/lib/collections.nom
+++ b/lib/collections.nom
@@ -16,14 +16,14 @@ parse [..]
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 "utils.nth_to_last(\(%list as lua), \(%index as lua))"
parse [first in %list, first %list] as: 1 st in %list
parse [last in %list, last %list] as: 1 st to last in %list
# Membership testing
-action [%item is in %list, %list contains %item, %list has %item]:
- for %key = %value in %list:
+action [%item is in %list, %list contains %item, %list has %item]
+ for %key = %value in %list
if (%key is %item): return (yes)
return (no)
@@ -31,28 +31,28 @@ 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:
+..
+ for %key = %value in %list
if (%key is %item): return (no)
return (yes)
-compile [%list has key %index, %list has index %index] to: ".."
+compile [%list has key %index, %list has index %index] to ".."
((\(%list as lua))[\(%index as lua)] ~= nil)
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 "((\(%list as lua))[\(%index as lua)] ~= nil)"
-compile [length of %list, size of %list, size %list, number of %list, len %list] to:
+compile [length of %list, size of %list, size %list, number of %list, len %list] to
"utils.size(\(%list as lua))"
# Chained lookup
-compile [%list ->* %indices] to:
+compile [%list ->* %indices] to
assume ((%indices's "type") is "List") or barf ".."
Expected List for chained lookup, not \(%indices's "type")
set %ret = "\(%list as lua)"
- for %index in (%indices's "value"):
+ for %index in (%indices's "value")
%ret join= "[\(%index as lua)]"
return "\%ret"
@@ -61,41 +61,41 @@ compile [..]
%list's %index = %new_value, %index st in %list = %new_value, %index nd in %list = %new_value
%index rd in %list = %new_value, %index th in %list = %new_value, %index in %list = %new_value
%list -> %index = %new_value
-..to code:
+..to code
"(\(%list as lua))[\(%index as lua)] = \(%new_value as lua);"
-compile [append %item to %list, add %item to %list] to:
+compile [append %item to %list, add %item to %list] to
"table.insert(\(%list as lua), \(%item as lua))"
-compile [pop from %list, remove last from %list] to:
+compile [pop from %list, remove last from %list] to
"table.remove(\(%list as lua))"
-compile [remove index %index from %list] to:
+compile [remove index %index from %list] to
"table.remove(\(%list as lua), \(%index as lua))"
-action [flatten %lists]:
+action [flatten %lists]
set %flat = []
- for %list in %lists:
- for %item in %list:
+ for %list in %lists
+ for %item in %list
add %item to %flat
return %flat
-action [dict %items]:
- {(%->1)=(%->2) for all %items}
+action [dict %items]
+ (%->1)=(%->2) for all %items
-action [entries in %dict]:
- [{key=%k, value=%v} for %k=%v in %dict]
+action [entries in %dict]
+ [{key:%k, value:%v} for %k=%v in %dict]
-action [keys in %dict]:
+action [keys in %dict]
[%k for %k=%v in %dict]
-action [values in %dict]:
+action [values in %dict]
[%v for %k=%v in %dict]
# List Comprehension
-immediately:
- compile [%expression for %item in %iterable] to:
+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 ".."
@@ -108,7 +108,7 @@ immediately:
end)()
parse [%expression for all %iterable] as: %expression for % in %iterable
- compile [%expression for %key = %value in %iterable] to:
+ compile [%expression for %key = %value in %iterable] to
assume ((%key's "type") is "Var") or barf ".."
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 ".."
@@ -123,8 +123,8 @@ immediately:
end)()
# Dict comprehensions
-immediately:
- compile [%key = %value for %item in %iterable] to:
+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 ".."
@@ -137,7 +137,7 @@ immediately:
end)()
parse [%key = %value for all %iterable] as: %key = %value for % in %iterable
- compile [%key = %value for %src_key = %src_value in %iterable] to:
+ compile [%key = %value for %src_key = %src_value in %iterable] to
assume ((%src_key's "type") is "Var") or barf ".."
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 ".."
@@ -152,39 +152,39 @@ immediately:
end)()
# Sorting:
-compile [sort %items] to: "table.sort(\(%items as lua))"
-compile [sort %items by %key_expr] to: ".."
+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)
-action [%items sorted]:
+action [%items sorted]
%copy = (% for all %items)
sort %copy
return %copy
-action [%items sorted by %key]:
+action [%items sorted by %key]
%copy = (% for all %items)
sort %copy by %key
return %copy
-action [unique %items]:
- [%k for %k=%v in {%=(yes) for all %items}]
+action [unique %items]
+ [%k for %k=%v in (%=(yes) for all %items)]
# Metatable stuff
-compile [counter] to: "setmetatable({}, {__index=function() return 0; end})"
-compile [default dict] to: ".."
+compile [counter] to "setmetatable({}, {__index=function() return 0; end})"
+compile [default dict] to ".."
setmetatable({}, {__index=function(self, key)
t = {};
self[key] = t;
return t;
end})"
-action [chain %dict to %fallback]:
- when (type of %fallback) is ?:
- * "table":
+action [chain %dict to %fallback]
+ when (type of %fallback) is ?
+ * "table"
=lua "setmetatable(\%dict, \%fallback)"
- * "function":
+ * "function"
=lua "setmetatable(\%dict, {__index=function(self, key) return (\%fallback)(nomsu, {['']=key, self=self}); end})"
- * else:
+ * else
=lua "setmetatable(\%dict, {__index=function(self, key) return (\%fallback); end})"
diff --git a/lib/control_flow.nom b/lib/control_flow.nom
index dd602ff..269a2a5 100644
--- a/lib/control_flow.nom
+++ b/lib/control_flow.nom
@@ -7,23 +7,23 @@ use "lib/text.nom"
use "lib/operators.nom"
# No-Op
-immediately:
- compile [do nothing] to code: ""
+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"
+immediately
+ compile [return] to code "do return; end"
+ compile [return %return_value] to code "do return \(%return_value as lua); end"
# Conditionals
-immediately:
- compile [if %condition %if_body] to code: ".."
+immediately
+ compile [if %condition %if_body] to code ".."
if \(%condition as lua) then
\(%if_body as lua statements)
end --end if
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: ".."
+ 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
@@ -33,19 +33,19 @@ immediately:
# 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:
+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:
+ ..to
local %safe
#.. If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic
equivalent of a conditional expression: (cond and if_true or if_false)
- if ({Text=yes, List=yes, Dict=yes, Number=yes}->(%when_true_expr's "type")):
+ if: {Text:yes, List:yes, Dict:yes, Number:yes}->(%when_true_expr's "type")
return "(\(%condition as lua) and \(%when_true_expr as lua) or \(%when_false_expr as lua))"
- ..else:
+ ..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)
@@ -59,20 +59,20 @@ immediately:
end)()
# GOTOs
-immediately:
- compile [-> %label] to code: ".."
+immediately
+ compile [-> %label] to code ".."
::label_\(nomsu "var_to_lua_identifier" [%label])::;
- compile [go to %label] to code: ".."
+ compile [go to %label] to code ".."
goto label_\(nomsu "var_to_lua_identifier" [%label]);
# Basic loop control
-immediately:
- compile [do next] to code: "continue;"
- compile [stop] to code: "break;"
+immediately
+ compile [do next] to code "continue;"
+ compile [stop] to code "break;"
# Helper function
-immediately:
- action [tree %tree has function call %call]:
+immediately
+ action [tree %tree has function call %call]
lua> ".."
local target = (\%call).stub;
for subtree,depth in coroutine.wrap(function() nomsu:walk_tree(\%tree); end) do
@@ -84,11 +84,11 @@ immediately:
return false;
# While loops
-immediately:
- compile [do next repeat-loop] to code: "goto continue_repeat;"
- compile [stop repeat-loop] to code: "goto stop_repeat;"
- compile [repeat while %condition %body] to code:
- set %continue_labels = (..)
+immediately
+ compile [do next repeat-loop] to code "goto continue_repeat;"
+ compile [stop repeat-loop] to code "goto stop_repeat;"
+ compile [repeat while %condition %body] to code
+ set %continue_labels =
"\n::continue_repeat::;"
..if (tree %body has function call \(do next repeat-loop)) else ""
set %code = ".."
@@ -96,7 +96,7 @@ immediately:
\(%body as lua statements)\
..\%continue_labels
end --while-loop
- if (tree %body has function call \(stop repeat-loop)):
+ if: tree %body has function call \(stop repeat-loop)
return ".."
do --while-loop label scope
\%code
@@ -107,30 +107,30 @@ immediately:
parse [repeat until %condition %body] as: repeat while (not %condition) %body
# For loop control flow:
-immediately:
- compile [stop for-loop] to code: "goto stop_for;"
- compile [stop %var] to code: ".."
+immediately
+ compile [stop for-loop] to code "goto stop_for;"
+ compile [stop %var] to code ".."
goto stop_\(nomsu "var_to_lua_identifier" [%var]);
- compile [do next for-loop] to code: "goto continue_for;"
- compile [do next %var] to code: ".."
+ compile [do next for-loop] to code "goto continue_for;"
+ compile [do next %var] to code ".."
goto continue_\(nomsu "var_to_lua_identifier" [%var]);
# Numeric range for loops
-immediately:
+immediately
compile [..]
for %var from %start to %stop by %step %body
for %var from %start to %stop via %step %body
- ..to code:
+ ..to code
local [%continue_labels, %code, %stop_labels, %loop_var, %loop_var_shim]
set %continue_labels = ""
- if (tree %body has function call \(do next for-loop)):
+ if: tree %body has function call \(do next for-loop)
%continue_labels join= "\n::continue_for::;"
- if (tree %body has function call (tree \(do next %) with {""=%var})):
+ if: tree %body has function call (tree \(do next %) with {"":%var})
%continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
- if ((%var's "type") is "Var"):
+ if: (%var's "type") is "Var"
set %loop_var = (%var as lua)
set %loop_var_shim = ""
- ..else:
+ ..else
set %loop_var = "i"
set %loop_var_shim = "\n\(%var as lua) = i;"
# This trashes the loop variables, just like in Python.
@@ -141,11 +141,11 @@ immediately:
..\%continue_labels
end --numeric for-loop
set %stop_labels = ""
- if (tree %body has function call \(stop for-loop)):
+ if: tree %body has function call \(stop for-loop)
%stop_labels join= "\n::stop_for::;"
- if (tree %body has function call (tree \(stop %) with {""=%var})):
+ if: tree %body has function call (tree \(stop %) with {"":%var})
%stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;"
- return (..)
+ return
".."
do --for-loop label scope
\%code\
@@ -153,7 +153,7 @@ immediately:
end --for-loop label scope
..if (%stop_labels is not "") else %code
-immediately:
+immediately
parse [for %var from %start to %stop %body] as: for %var from %start to %stop via 1 %body
parse [..]
for all %start to %stop by %step %body
@@ -161,18 +161,18 @@ 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
-immediately:
- compile [for %var in %iterable %body] to code:
+immediately
+ compile [for %var in %iterable %body] to code
local [%continue_labels, %code, %stop_labels, %loop_var, %loop_var_shim]
set %continue_labels = ""
- if (tree %body has function call \(do next for-loop)):
+ if: tree %body has function call \(do next for-loop)
%continue_labels join= "\n::continue_for::;"
- if (tree %body has function call (tree \(do next %) with {""=%var})):
+ if: tree %body has function call (tree \(do next %) with {"":%var})
%continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
- if ((%var's "type") is "Var"):
+ if: (%var's "type") is "Var"
set %loop_var = (%var as lua)
set %loop_var_shim = ""
- ..else:
+ ..else
set %loop_var = "value"
set %loop_var_shim = "\n\(%var as lua) = value;"
# This trashes the loop variables, just like in Python.
@@ -183,11 +183,11 @@ immediately:
..\%continue_labels
end --foreach-loop
set %stop_labels = ""
- if (tree %body has function call \(stop for-loop)):
+ if: tree %body has function call \(stop for-loop)
%stop_labels join= "\n::stop_for::;"
- if (tree %body has function call (tree \(stop %) with {""=%var})):
+ if: tree %body has function call (tree \(stop %) with {"":%var})
%stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;"
- if (%stop_labels is not ""):
+ if: %stop_labels is not ""
set %code = ".."
do --for-loop label scope
\%code\%stop_labels
@@ -195,13 +195,13 @@ immediately:
return %code
parse [for all %iterable %body] as: for % in %iterable %body
-immediately:
+immediately
compile [..]
repeat %n times %body, repeat %n x %body
- ..to code:
+ ..to code
local [%continue_labels, %code, %stop_labels]
set %continue_labels = ""
- if (tree %body has function call \(do next repeat-loop)):
+ if: tree %body has function call \(do next repeat-loop)
%continue_labels join= "\n::continue_repeat::;"
# This trashes the loop variables, just like in Python.
set %code = ".."
@@ -210,9 +210,9 @@ immediately:
..\%continue_labels
end --numeric for-loop
set %stop_labels = ""
- if (tree %body has function call \(stop repeat-loop)):
+ if: tree %body has function call \(stop repeat-loop)
%stop_labels join= "\n::stop_repeat::;"
- return (..)
+ return
".."
do --repeat-loop label scope
\%code\
@@ -221,28 +221,28 @@ immediately:
..if (%stop_labels is not "") else %code
# Dict iteration (lua's "pairs()")
-immediately:
- compile [for %key = %value in %iterable %body] to code:
+immediately
+ compile [for %key = %value in %iterable %body] to code
local [..]
%continue_labels, %code, %stop_labels, %key_loop_var, %key_loop_var_shim,
%value_loop_var, %value_loop_var_shim
set %continue_labels = ""
- if (tree %body has function call \(do next for-loop)):
+ if: tree %body has function call \(do next for-loop)
%continue_labels join= "\n::continue_for::;"
- if (tree %body has function call (tree \(do next %x) with {x=%key})):
+ if: tree %body has function call (tree \(do next %) with {"":%key})
%continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%key])::;"
- if (tree %body has function call (tree \(do next %) with {""=%value})):
+ if: tree %body has function call (tree \(do next %) with {"":%value})
%continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%value])::;"
- if ((%key's "type") is "Var"):
+ if: (%key's "type") is "Var"
set %key_loop_var = (%key as lua)
set %key_loop_var_shim = ""
- ..else:
+ ..else
set %key_loop_var = "key"
set %key_loop_var_shim = "\n\(%key as lua) = key;"
- if ((%value's "type") is "Var"):
+ if: (%value's "type") is "Var"
set %value_loop_var = (%value as lua)
set %value_loop_var_shim = ""
- ..else:
+ ..else
set %value_loop_var = "value"
set %value_loop_var_shim = "\n\(%value as lua) = value;"
# This trashes the loop variables, just like in Python.
@@ -253,13 +253,13 @@ immediately:
..\%continue_labels
end --foreach-loop
set %stop_labels = ""
- if (tree %body has function call \(stop for-loop)):
+ if: tree %body has function call \(stop for-loop)
%stop_labels join= "\n::stop_for::;"
- if (tree %body has function call (tree \(stop %) with {""=%key})):
+ if: tree %body has function call (tree \(stop %) with {"":%key})
%stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%key])::;"
- if (tree %body has function call (tree \(stop %) with {""=%value})):
+ if: tree %body has function call (tree \(stop %) with {"":%value})
%stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%value])::;"
- return (..)
+ return
".."
do --for-loop label scope
\%code\
@@ -268,13 +268,13 @@ immediately:
..if (%stop_labels is not "") else %code
# Switch statement/multi-branch if
-immediately:
- compile [when %body] to code:
+immediately
+ compile [when %body] to code
local [%result, %fallthroughs, %first]
set %result = ""
set %fallthroughs = []
set %first = (yes)
- for %func_call in (%body's "value"):
+ for %func_call in (%body's "value")
local [%tokens, %star, %condition, %action]
assume ((%func_call's "type") is "FunctionCall") or barf ".."
Invalid format for 'when' statement. Only '*' blocks are allowed.
@@ -288,19 +288,19 @@ immediately:
Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
set %action = (%tokens -> 3)
- if (%action is (nil)):
+ if: %action is (nil)
lua do> "table.insert(\%fallthroughs, \%condition)"
do next %func_call
- if (=lua "\%condition.type == 'Word' and \%condition.value == 'else'"):
+ if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
%result join= ".."
else
\(%action as lua statements)
stop for-loop
- ..else:
+ ..else
set %condition = (%condition as lua)
- for all %fallthroughs:
+ for all %fallthroughs
%condition join= " or \(% as lua)"
%result join= ".."
@@ -310,17 +310,17 @@ immediately:
set %fallthroughs = []
set %first = (no)
- if (%result != ""):
+ if: %result is not ""
%result join= "\nend"
return %result
# Switch statement
-immediately:
- compile [when %branch_value = ? %body, when %branch_value is ? %body] to code:
+immediately
+ compile [when %branch_value = ? %body, when %branch_value is ? %body] to code
set %result = ""
set %fallthroughs = []
set %first = (yes)
- for %func_call in (%body's "value"):
+ for %func_call in (%body's "value")
assume ((%func_call's "type") is "FunctionCall") or barf ".."
Invalid format for 'when' statement. Only '*' blocks are allowed.
set %tokens = (%func_call's "value")
@@ -333,19 +333,19 @@ immediately:
Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
set %action = (%tokens -> 3)
- if (%action is (nil)):
+ if: %action is (nil)
lua> "table.insert(\%fallthroughs, \%condition)"
do next %func_call
- if (=lua "\%condition.type == 'Word' and \%condition.value == 'else'"):
+ if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
%result join= ".."
else
\(%action as lua statements)
stop for-loop
- ..else:
+ ..else
set %condition = "branch_value == (\(%condition as lua))"
- for all %fallthroughs:
+ for all %fallthroughs
%condition join= " or (branch_value == \(% as lua))"
%result join= ".."
@@ -355,7 +355,7 @@ immediately:
set %fallthroughs = []
set %first = (no)
- if (%result != ""):
+ if: %result is not ""
set %result = ".."
do --when % = ?
local branch_value = \(%branch_value as lua);\
@@ -365,11 +365,11 @@ immediately:
return %result
# Try/except
-immediately:
+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: ".."
+ ..to code ".."
do
local fell_through = false;
local ok, ret = pcall(function()
@@ -385,22 +385,22 @@ immediately:
return ret;
end
end
- parse [try %action] as:
+ parse [try %action] as
try %action and if it succeeds: do nothing
..or if it fails: do nothing
- parse [try %action and if it fails %fallback] as:
+ parse [try %action and if it fails %fallback] as
try %action and if it succeeds: do nothing
..or if it fails %fallback
- parse [try %action and if it succeeds %success] as:
+ parse [try %action and if it succeeds %success] as
try %action and if it succeeds %success or if it fails: do nothing
# Do/finally:
-immediately:
- compile [do %action] to code:
+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: ".."
+ compile [do %action then always %final_action] to code ".."
do
local fell_through = false;
local ok, ret1 = pcall(function()
@@ -417,11 +417,11 @@ immediately:
end
end
-immediately:
- compile [with %assignments %action] to code:
+immediately
+ compile [with %assignments %action] to code
local [%lua, %olds, %old_vals, %new_vals]
- set {%temp_vars=[], %old_vals=[], %new_vals=[]}
- for %i=%assignment in (%assignments' "value"):
+ set {%temp_vars:[], %old_vals:[], %new_vals:[]}
+ for %i=%assignment in (%assignments' "value")
set (%temp_vars->%i) = "temp\%i"
set (%old_vals->%i) = ((%assignment's "dict_key") as lua)
set (%new_vals->%i) = ((%assignment's "dict_value") as lua)
diff --git a/lib/math.nom b/lib/math.nom
index 4e78b7e..2af59eb 100644
--- a/lib/math.nom
+++ b/lib/math.nom
@@ -5,75 +5,75 @@ 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 "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"
# 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)"
-action [%n to the nearest %rounder]:
+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)"
+action [%n to the nearest %rounder]
=lua "(\%rounder)*math.floor((\%n / \%rounder) + .5)"
# Any/all/none
-compile [all of %items, all %items] to:
+compile [all of %items, all %items] to
"(\(join ((% as lua) for all (%items' "value")) with " and "))"
..if ((%items' "type") is "List") else "utils.all(\(%items as lua))"
parse [not all of %items, not all %items] as: not (all of %items)
-compile [any of %items, any %items] to:
+compile [any of %items, any %items] to
"(\(join ((% as lua) for all (%items' "value")) with " or "))"
..if ((%items' "type") is "List") else "utils.any(\(%items as lua))"
parse [none of %items, none %items] as: not (any of %items)
-compile [sum of %items, sum %items] to:
+compile [sum of %items, sum %items] to
"(\(join ((% 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:
+compile [product of %items, product %items] to
"(\(join ((% as lua) for all (%items' "value")) with " * "))"
..if ((%items' "type") is "List") else "utils.product(\(%items as lua))"
-action [avg of %items, average of %items]:
+action [avg of %items, average of %items]
=lua "(utils.sum(\%items)/#\%items)"
-compile [min of %items, smallest of %items, lowest of %items] to:
+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:
+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: ".."
+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: ".."
+compile [max of %items by %value_expr] to ".."
utils.max(\(%items as lua), function(\(\% as lua))
return \(%value_expr as lua)
end)
# Random functions
-action [seed random with %]:
+action [seed random with %]
lua> ".."
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 from %low to %high, random number from %low to %high, rand %low %high] to:
+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 from %low to %high, random number from %low to %high, rand %low %high] to
"math.random(\(%low as lua), \(%high as lua))"
-action [random choice from %elements, random choice %elements, random %elements]:
+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 ba63c25..f07e2d1 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -3,7 +3,7 @@
functions to make that easier.
# Helper function
-immediately:
+immediately
lua> ".."
nomsu.parse_spec = function(nomsu, spec)
if spec.type == 'List' then
@@ -31,13 +31,9 @@ immediately:
# Compile-time action to make compile-time actions:
# TODO: reduce code duplication here
-immediately:
+immediately
lua> ".."
nomsu:define_compile_action("compile %names to %body", \(__line_no__), function(\%names, \%body)
- --assert(\%names.type == "List",
- -- "Invalid type for compile definition names. Expected List, but got: "..tostring(\%names.type));
- assert(\%body.type == "Block",
- "Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type));
local names, args = nomsu:parse_spec(\%names);
names, args = repr(names), table.concat(args, ", ");
local body_lua = nomsu:tree_to_lua(\%body);
@@ -56,10 +52,6 @@ immediately:
lua> ".."
nomsu:define_compile_action("compile %names to code %body", \(__line_no__), function(\%names, \%body)
- --assert(\%names.type == "List",
- -- "Invalid type for compile definition names. Expected List, but got: "..tostring(\%names.type));
- assert(\%body.type == "Block",
- "Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type));
local names, args = nomsu:parse_spec(\%names);
names, args = repr(names), table.concat(args, ", ");
local body_lua = nomsu:tree_to_lua(\%body);
@@ -77,13 +69,9 @@ immediately:
end, \(__src__ 1));
# Compile-time action to make actions
-immediately:
- compile [action %names %body] to code:
+immediately
+ compile [action %names %body] to code
lua> ".."
- --assert(\%names.type == "List",
- -- "Invalid type for action definition names. Expected List, but got: "..tostring(\%names.type));
- assert(\%body.type == "Block",
- "Invalid type for action definition body. Expected Block, but got: "..tostring(\%body.type));
local names, args = nomsu:parse_spec(\%names);
names, args = repr(names), table.concat(args, ", ");
local body_lua = nomsu:tree_to_lua(\%body);
@@ -96,20 +84,21 @@ immediately:
return def_lua;
# Macro to make nomsu macros:
-immediately:
+immediately
lua> ".."
nomsu:define_compile_action("parse %shorthand as %longhand", \(__line_no__), (function(\%shorthand, \%longhand)
- --assert(\%shorthand.type == "List",
- -- "Invalid type for parse definition shorthand. Expected List, but got: "..tostring(\%shorthand.type));
- assert(\%longhand.type == "Block",
- "Invalid type for parse definition body. Expected Block, but got: "..tostring(\%longhand.type));
local names, args = nomsu:parse_spec(\%shorthand);
names, args = repr(names), table.concat(args, ", ");
- local template = {};
- for i, line in ipairs(\%longhand.value) do
- template[i] = nomsu:dedent(line.src);
+ 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"));
+ else
+ template = repr(\%longhand.src);
end
- template = repr(table.concat(template, "\\n"));
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
@@ -124,7 +113,7 @@ immediately:
return {statements=lua_code};
end), \(__src__ 1));
-action [remove action %stub]:
+action [remove action %stub]
lua> ".."
local fn = ACTIONS[\%stub];
local metadata = ACTION_METADATA[fn];
@@ -134,42 +123,42 @@ action [remove action %stub]:
end
ACTIONS[\%stub] = nil;
-immediately:
- action [%tree as lua]:
+immediately
+ action [%tree as lua]
=lua "nomsu:tree_to_lua(\%tree).expr"
- action [%tree as lua statements]:
+ action [%tree as lua statements]
lua> ".."
local lua = nomsu:tree_to_lua(\%tree);
return lua.statements or (lua.expr..";");
- action [%tree as value]:
+ action [%tree as value]
=lua "nomsu:tree_to_value(\%tree)"
- compile [repr %obj] to:
+ compile [repr %obj] to
"repr(\(%obj as lua))"
- compile [indented %obj] to:
+ compile [indented %obj] to
"nomsu:indent(\(%obj as lua))"
- compile [dedented %obj] to:
+ compile [dedented %obj] to
"nomsu:dedent(\(%obj as lua))"
- compile [type %obj, type of %obj] to:
+ compile [type %obj, type of %obj] to
"type(\(%obj as lua))"
-immediately:
- parse [lua do> %block] as:
+immediately
+ parse [lua do> %block] as
lua> "do"
lua> %block
lua> "end"
-compile [nomsu] to: "nomsu"
+compile [nomsu] to "nomsu"
-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: ".."
+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))
-action [action %names metadata]:
+action [action %names metadata]
=lua "ACTION_METADATA[ACTIONS[\%names]]"
# Get the source code for a function
-action [help %action]:
+action [help %action]
lua> ".."
local metadata = \(action %action metadata);
if not metadata then
@@ -180,7 +169,7 @@ action [help %action]:
# Compiler tools
parse [eval %code, run %code] as: nomsu "run" [%code]
-action [source code from tree %tree]:
+action [source code from tree %tree]
lua> ".."
local junk,junk,leading_space = \%tree.src:find("\\n(%s*)%S");
if leading_space then
@@ -197,7 +186,7 @@ parse [parse tree %code] as: nomsu "tree_to_str" [\%code]
parse [enable debugging] as: lua> "nomsu.debug = true"
parse [disable debugging] as: lua> "nomsu.debug = false"
-compile [say %str] to:
+compile [say %str] to
lua> ".."
if \%str.type == "Text" then
return "nomsu:writeln("..\(%str as lua)..")";
@@ -206,13 +195,13 @@ compile [say %str] to:
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))"
+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))"
# Literals
-compile [yes] to: "true"
-compile [no] to: "false"
-compile [nothing, nil, null] to: "nil"
+compile [yes] to "true"
+compile [no] to "false"
+compile [nothing, nil, null] to "nil"
diff --git a/lib/operators.nom b/lib/operators.nom
index 2bbeadf..91410be 100644
--- a/lib/operators.nom
+++ b/lib/operators.nom
@@ -4,14 +4,14 @@
use "lib/metaprogramming.nom"
# Indexing:
-immediately:
+immediately
#.. NOTE!!! It's critical that there are spaces around %key if it's a string,
otherwise, Lua will get confused and interpret %obj[[[foo]]] as %obj("[foo]")
instead of %obj[ "foo" ].
It's also critical to have parens around %obj, otherwise Lua is too dumb to
realize that {x=1}["x"] is the same as ({x=1})["x"] or that
{x=1}.x is the same as ({x=1}).x
- compile [%obj'%key, %obj's %key, %obj -> %key] to:
+ compile [%obj'%key, %obj's %key, %obj -> %key] to
lua> ".."
local obj_lua = \(%obj as lua);
if not obj_lua:sub(-1,-1):match("[a-zA-Z)]") then
@@ -28,12 +28,12 @@ immediately:
return 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 [%a is %b, %a = %b, %a == %b] to:
+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 [%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;
@@ -42,7 +42,7 @@ immediately:
else
return "utils.equivalent("..a_lua..", "..b_lua..")";
end
- compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to:
+ compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to
lua> ".."
local safe = {Text=true, Number=true};
local a_lua, b_lua = nomsu:tree_to_lua(\%a).expr, nomsu:tree_to_lua(\%b).expr;
@@ -52,11 +52,11 @@ immediately:
return "(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 "nomsu.ids[\(% as lua)]"
# Variable assignment operator, and += type versions
-immediately:
- compile [local %vars] to code:
+immediately
+ compile [local %vars] to code
lua> ".."
local locals = \%vars.type == "List" and \%vars.value or {\%vars};
local identifiers = {};
@@ -64,8 +64,8 @@ immediately:
identifiers[i] = nomsu:tree_to_lua(x).expr;
end
return "local "..table.concat(identifiers, ", ");
- compile [set %var = %val] to code: "\(%var as lua) = \(%val as lua);"
- compile [set %assignments] to code:
+ compile [set %var = %val] to code "\(%var as lua) = \(%val as lua);"
+ compile [set %assignments] to code
assume ((%assignments' "type") is "Dict") or barf "Expected Dict, but got \(%assignments' "type")"
lua> ".."
local lhs, rhs = {}, {};
@@ -76,23 +76,23 @@ immediately:
return table.concat(lhs, ", ").." = "..table.concat(rhs, ", ")..";";
# Update assignment operators
-compile [%var += %val] to code: "\(%var as lua) = \(%var as lua) + \(%val as lua);"
-compile [%var -= %val] to code: "\(%var as lua) = \(%var as lua) - \(%val as lua);"
-compile [%var *= %val] to code: "\(%var as lua) = \(%var as lua) * \(%val as lua);"
-compile [%var /= %val] to code: "\(%var as lua) = \(%var as lua) / \(%val as lua);"
-compile [%var ^= %val] to code: "\(%var as lua) = \(%var as lua) ^ \(%val as lua);"
-compile [%var and= %val] to code: "\(%var as lua) = \(%var as lua) and\(%val as lua);"
-compile [%var or= %val] to code: "\(%var as lua) = \(%var as lua) or \(%val as lua);"
-compile [%var join= %val] to code: "\(%var as lua) = \(%var as lua) .. \(%val as lua);"
-compile [wrap %var around %val] to code: "\(%var as lua) = \(%var as lua) % \(%val as lua);"
+compile [%var += %val] to code "\(%var as lua) = \(%var as lua) + \(%val as lua);"
+compile [%var -= %val] to code "\(%var as lua) = \(%var as lua) - \(%val as lua);"
+compile [%var *= %val] to code "\(%var as lua) = \(%var as lua) * \(%val as lua);"
+compile [%var /= %val] to code "\(%var as lua) = \(%var as lua) / \(%val as lua);"
+compile [%var ^= %val] to code "\(%var as lua) = \(%var as lua) ^ \(%val as lua);"
+compile [%var and= %val] to code "\(%var as lua) = \(%var as lua) and\(%val as lua);"
+compile [%var or= %val] to code "\(%var as lua) = \(%var as lua) or \(%val as lua);"
+compile [%var join= %val] to code "\(%var as lua) = \(%var as lua) .. \(%val as lua);"
+compile [wrap %var around %val] to code "\(%var as lua) = \(%var as lua) % \(%val as lua);"
# 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 "(\(%x as lua) + \(%y as lua))"
+compile [%x - %y] to "(\(%x as lua) - \(%y as lua))"
+compile [%x * %y] to "(\(%x as lua) * \(%y as lua))"
+compile [%x / %y] to "(\(%x as lua) / \(%y as lua))"
+compile [%x ^ %y] to "(\(%x as lua) ^ \(%y as lua))"
+compile [%x wrapped around %y, %x mod %y] to "(\(%x as lua) % \(%y as lua))"
# 3-part chained comparisons
# (uses a lambda to avoid re-evaluating middle value, while still being an expression)
@@ -107,19 +107,19 @@ parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)
# 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 "(\(%x as lua) and \(%y as lua))"
+compile [%x or %y] to "(\(%x as lua) or \(%y as lua))"
# Bitwise Operators
-compile [%a OR %b, %a | %b] to: "bit32.bor(\(%a as lua), \(%b as lua))"
-compile [%a XOR %b] to: "bit32.bxor(\(%a as lua), \(%b as lua))"
-compile [%a AND %b, %a & %b] to: "bit32.band(\(%a as lua), \(%b as lua))"
-compile [NOT %, ~ %] to: "bit32.bnot(\(% as lua))"
-compile [%x LSHIFT %shift, %x << %shift] to: "bit32.lshift(\(%x as lua), \(%shift as lua))"
-compile [%x RSHIFT %shift, %x >>> %shift] to: "bit32.rshift(\(%x as lua), \(%shift as lua))"
-compile [%x ARSHIFT %shift, %x >> %shift] to: "bit32.arshift(\(%x as lua), \(%shift as lua))"
+compile [%a OR %b, %a | %b] to "bit32.bor(\(%a as lua), \(%b as lua))"
+compile [%a XOR %b] to "bit32.bxor(\(%a as lua), \(%b as lua))"
+compile [%a AND %b, %a & %b] to "bit32.band(\(%a as lua), \(%b as lua))"
+compile [NOT %, ~ %] to "bit32.bnot(\(% as lua))"
+compile [%x LSHIFT %shift, %x << %shift] to "bit32.lshift(\(%x as lua), \(%shift as lua))"
+compile [%x RSHIFT %shift, %x >>> %shift] to "bit32.rshift(\(%x as lua), \(%shift as lua))"
+compile [%x ARSHIFT %shift, %x >> %shift] to "bit32.arshift(\(%x as lua), \(%shift as lua))"
# TODO: implement OR, XOR, AND for multiple operands?
# Unary operators
-compile [- %] to: "-(\(% as lua))"
-compile [not %] to: "not (\(% as lua))"
+compile [- %] to "-(\(% as lua))"
+compile [not %] to "not (\(% as lua))"
diff --git a/lib/text.nom b/lib/text.nom
index 5cb268d..2eb6b67 100644
--- a/lib/text.nom
+++ b/lib/text.nom
@@ -5,28 +5,28 @@
use "lib/metaprogramming.nom"
# Text functions
-action [join %strs with %glue]:
+action [join %strs with %glue]
lua> ".."
local str_bits = {}
for i,bit in ipairs(\%strs) do str_bits[i] = stringify(bit) end
return table.concat(str_bits, \%glue)
parse [join %strs] as: join %strs with ""
-compile [capitalize %str, %str capitalized] to:
+compile [capitalize %str, %str capitalized] to
"(\(%str as lua)):gsub('%l', string.upper, 1)"
-compile [%str with %patt replaced with %sub, %str s/%patt/%sub] to:
+compile [%str with %patt replaced with %sub, %str s/%patt/%sub] to
"((\(%str as lua)):gsub(\(%patt as lua), \(%sub as lua)))"
-compile [%str with %patt replaced with %sub %n times, %str s/%patt/%sub/%n] to:
+compile [%str with %patt replaced with %sub %n times, %str s/%patt/%sub/%n] to
"((\(%str as lua)):gsub(\(%patt as lua), \(%sub as lua), \(%n as lua)))"
-compile [indent %str] to: "\%str:gsub('\\n','\\n'..(' '))"
-compile [indent %str %n times, indent %str %n x] to: "\%str:gsub('\\n','\\n'..(' '):rep(\%n))"
+compile [indent %str] to "\%str:gsub('\\n','\\n'..(' '))"
+compile [indent %str %n times, indent %str %n x] to "\%str:gsub('\\n','\\n'..(' '):rep(\%n))"
# Substring
# TODO: improve this syntax
-compile [%str |%start|] to: "\(%str as lua):sub(\(%start as lua), \(%start as lua))"
-compile [%str |%start - %stop|] to: "\(%str as lua):sub(\(%start as lua), \(%stop as lua))"
+compile [%str |%start|] to "\(%str as lua):sub(\(%start as lua), \(%start as lua))"
+compile [%str |%start - %stop|] to "\(%str as lua):sub(\(%start as lua), \(%stop as lua))"
# Text literals
lua do> ".."
@@ -57,36 +57,3 @@ lua do> ".."
end, \(__src__ 1));
end
-#..
- use "lib/control_flow.nom"
- use "lib/collections.nom"
- local [%ansi, %colors]
- set %ansi = {..}
- nl="\\n", newline="\\n", tab="\\t", bell="\\a", cr="\\r", "carriage return"="\\r"
- backspace="\\b", "form feed"="\\f", formfeed="\\f", "vertical tab"="\\v"
-
- set %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=%str in %ansi:
- lua> ".."
- local e = "'"..\%str.."'";
- nomsu:define_compile_action(\%name, \(__line_no__), function(nomsu) return {expr=e}; end, \(__src__ 1));
-
- for %name=%str in %colors:
- lua> ".."
- local e = "'"..\%str.."'";
- local reset = "'"..\(%colors->"reset color").."'";
- nomsu:define_compile_action(\%name, \(__line_no__), function(nomsu) return {expr=e}; end, \(__src__ 1));
- nomsu:define_compile_action(\%name.." %", \(__line_no__), function(nomsu, \%)
- return {expr=e..".."..nomsu:tree_to_lua(\%).expr..".."..reset};
- end, \(__src__ 1));
-
-# FIXME
diff --git a/nomsu.moon b/nomsu.moon
index dfe3c7b..40ca9bb 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -18,11 +18,6 @@ new_uuid = require 'uuid'
colors = setmetatable({}, {__index:->""})
colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..(msg or '')..colors.reset)})
{:insert, :remove, :concat} = table
-if _VERSION == "Lua 5.1"
- xp = xpcall
- xpcall = (f, errhandler, ...)->
- args = {n:select("#", ...), ...}
- return xp((...)-> f(unpack(args,1,args.n))), errhandler
-- TODO:
-- consider non-linear codegen, rather than doing thunks for things like comprehensions
@@ -39,75 +34,84 @@ if _VERSION == "Lua 5.1"
lpeg.setmaxstack 10000 -- whoa
{:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg
-STRING_ESCAPES = n:"\n", t:"\t", b:"\b", a:"\a", v:"\v", f:"\f", r:"\r"
-DIGIT, HEX = R('09'), R('09','af','AF')
-ESCAPE_CHAR = (P("\\")*S("xX")*C(HEX*HEX)) / => string.char(tonumber(@, 16))
-ESCAPE_CHAR += (P("\\")*C(DIGIT*(DIGIT^-2))) / => string.char(tonumber @)
-ESCAPE_CHAR += (P("\\")*C(S("ntbavfr"))) / STRING_ESCAPES
-OPERATOR_CHAR = S("'~`!@$^&*-+=|<>?/")
-UTF8_CHAR = (
- R("\194\223")*R("\128\191") +
- R("\224\239")*R("\128\191")*R("\128\191") +
- R("\240\244")*R("\128\191")*R("\128\191")*R("\128\191"))
-IDENT_CHAR = R("az","AZ","09") + P("_") + UTF8_CHAR
-
-local parse
-do
- export parse
- ctx = {}
- indent_patt = P (start)=>
+NOMSU_DEFS = with {}
+ .nl = P("\n")
+ .ws = S(" \t")
+ .tonumber = tonumber
+ .print = (src,pos,msg)->
+ print(msg, pos, repr(src\sub(math.max(0,pos-16),math.max(0,pos-1)).."|"..src\sub(pos,pos+16)))
+ return true
+ string_escapes = n:"\n", t:"\t", b:"\b", a:"\a", v:"\v", f:"\f", r:"\r"
+ digit, hex = R('09'), R('09','af','AF')
+ .escaped_char = (P("\\")*S("xX")*C(hex*hex)) / => string.char(tonumber(@, 16))
+ .escaped_char += (P("\\")*C(digit*(digit^-2))) / => string.char(tonumber @)
+ .escaped_char += (P("\\")*C(S("ntbavfr"))) / string_escapes
+ .operator_char = S("'~`!@$^&*-+=|<>?/")
+ .operator = .operator_char^1
+ .utf8_char = (
+ R("\194\223")*R("\128\191") +
+ R("\224\239")*R("\128\191")*R("\128\191") +
+ R("\240\244")*R("\128\191")*R("\128\191")*R("\128\191"))
+ .ident_char = R("az","AZ","09") + P("_") + .utf8_char
+
+ -- If the number of leading space characters is greater than number in the top of the
+ -- stack, this pattern matches and pushes the number onto the stack.
+ .indent = P (start)=>
spaces = @match("[ \t]*", start)
- if #spaces > ctx.indent_stack[#ctx.indent_stack]
- insert(ctx.indent_stack, #spaces)
+ if #spaces > lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack]
+ insert(lpeg.userdata.indent_stack, #spaces)
return start + #spaces
- dedent_patt = P (start)=>
+ -- If the number of leading space characters is less than number in the top of the
+ -- stack, this pattern matches and pops off the top of the stack exactly once.
+ .dedent = P (start)=>
spaces = @match("[ \t]*", start)
- if #spaces < ctx.indent_stack[#ctx.indent_stack]
- remove(ctx.indent_stack)
+ if #spaces < lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack]
+ remove(lpeg.userdata.indent_stack)
return start
- nodent_patt = P (start)=>
+ -- If the number of leading space characters is equal to the number on the top of the
+ -- stack, this pattern matches and does not modify the stack.
+ .nodent = P (start)=>
spaces = @match("[ \t]*", start)
- if #spaces == ctx.indent_stack[#ctx.indent_stack]
+ if #spaces == lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack]
return start + #spaces
- gt_nodent_patt = P (start)=>
- -- Note! This assumes indent is 4 spaces!!!
+ -- If the number of leading space characters is 4+ more than the number on the top of the
+ -- stack, this pattern matches the first n+4 spaces and does not modify the stack.
+ .gt_nodent = P (start)=>
+ -- Note! This assumes indent is exactly 4 spaces!!!
spaces = @match("[ \t]*", start)
- if #spaces >= ctx.indent_stack[#ctx.indent_stack] + 4
- return start + ctx.indent_stack[#ctx.indent_stack] + 4
-
- defs =
- nl: P("\n"), ws: S(" \t"), :tonumber, operator: OPERATOR_CHAR
- print: (src,pos,msg)-> print(msg, pos, repr(src\sub(math.max(0,pos-16),math.max(0,pos-1)).."|"..src\sub(pos,pos+16))) or true
- utf8_char: (
- R("\194\223")*R("\128\191") +
- R("\224\239")*R("\128\191")*R("\128\191") +
- R("\240\244")*R("\128\191")*R("\128\191")*R("\128\191"))
- indented: indent_patt, nodented: nodent_patt, dedented: dedent_patt
- gt_nodented: gt_nodent_patt, escape_char:ESCAPE_CHAR
- error: (src,pos,err_msg)->
- if ctx.source_code\sub(pos,pos) == "\n"
- pos += #ctx.source_code\match("[ \t\n]*", pos)
- line_no = 1
- while (ctx.line_starts[line_no+1] or math.huge) < pos do line_no += 1
- prev_line = line_no > 1 and ctx.source_code\match("[^\n]*", ctx.line_starts[line_no-1]) or ""
- err_line = ctx.source_code\match("[^\n]*", ctx.line_starts[line_no])
- next_line = line_no < #ctx.line_starts and ctx.source_code\match("[^\n]*", ctx.line_starts[line_no+1]) or ""
- pointer = ("-")\rep(pos-ctx.line_starts[line_no]) .. "^"
- err_msg = (err_msg or "Parse error").." in #{ctx.filename} on line #{line_no}:\n"
- err_msg ..="\n#{prev_line}\n#{err_line}\n#{pointer}\n#{next_line}\n"
- error(err_msg)
- FunctionCall: (start, value, stop)->
- stub = concat([(t.type == "Word" and t.value or "%") for t in *value], " ")
- src = ctx.source_code\sub(start,stop-1)
- return {:start, :stop, type: "FunctionCall", :src, get_line_no:ctx.get_line_no, :value, :stub}
-
- setmetatable(defs, {__index:(key)=>
- make_node = (start, value, stop)->
- {:start, :stop, :value, src:ctx.source_code\sub(start,stop-1), get_line_no:ctx.get_line_no, type: key}
- self[key] = make_node
- return make_node
- })
-
+ if #spaces >= lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack] + 4
+ 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)
+ 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])
+ else ""
+ err_line = lpeg.userdata.source_code\match("[^\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])
+ 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"
+ err_msg ..="\n#{prev_line}\n#{err_line}\n#{pointer}\n#{next_line}\n"
+ error(err_msg)
+
+ .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}
+
+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}
+ self[key] = make_node
+ return make_node
+})
+
+NOMSU = do
-- Just for cleanliness, I put the language spec in its own file using a slightly modified
-- version of the lpeg.re syntax.
peg_tidier = re.compile [[
@@ -120,33 +124,15 @@ do
ident <- [a-zA-Z_][a-zA-Z0-9_]*
comment <- "--" [^%nl]*
]]
-
- nomsu = peg_tidier\match(io.open("nomsu.peg")\read("*a"))
- nomsu = re.compile(nomsu, defs)
-
- parse = (source_code, filename)->
- _ctx = {:source_code, :filename, indent_stack: {0}}
- _ctx.line_starts = re.compile("lines <- {| line ('\n' line)* |} line <- {} [^\n]*")\match(source_code)
- _ctx.get_line_no = =>
- unless @_line_no
- line_no = 1
- while (_ctx.line_starts[line_no+1] or math.huge) < @start do line_no += 1
- @_line_no = "#{_ctx.filename}:#{line_no}"
- return @_line_no
-
- old_ctx = ctx
- export ctx
- ctx = _ctx
- tree = nomsu\match(source_code)
- ctx = old_ctx
- return tree
+ nomsu_peg = peg_tidier\match(io.open("nomsu.peg")\read("*a"))
+ re.compile(nomsu_peg, NOMSU_DEFS)
class NomsuCompiler
@def_number: 0
- new:(parent)=>
+ new:()=>
@write = (...)=> io.write(...)
@write_err = (...)=> io.stderr\write(...)
- -- Use # to prevent someone from defining a function that has a namespace collision.
+ -- Weak-key mapping from objects to randomly generated unique IDs
@ids = setmetatable({}, {
__mode: "k"
__index: (key)=>
@@ -154,17 +140,12 @@ class NomsuCompiler
@[key] = id
return id
})
- if parent
- -- TODO: Implement
- error("Not implemented")
@compilestack = {}
@debug = false
- @action_metadata = setmetatable({}, {__mode:"k"})
@environment = {
-- Discretionary/convenience stuff
nomsu:self, repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re,
- ACTIONS:{}, ACTION_METADATA:@action_metadata, LOADED:{},
-- Lua stuff:
:next, :unpack, :setmetatable, :coroutine, :rawequal, :getmetatable, :pcall,
:error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module,
@@ -172,8 +153,13 @@ class NomsuCompiler
:table, :assert, :dofile, :loadstring, :type, :select, :debug, :math, :io, :pairs,
:load, :ipairs,
}
- if not parent
- @initialize_core!
+ @environment.ACTIONS = setmetatable({}, {__index:(key)=>
+ error("Attempt to run undefined action: #{key}", 0)
+ })
+ @action_metadata = setmetatable({}, {__mode:"k"})
+ @environment.ACTION_METADATA = @action_metadata
+ @environment.LOADED = {}
+ @initialize_core!
writeln:(...)=>
@write(...)
@@ -277,13 +263,26 @@ class NomsuCompiler
indent: (code, levels=1)=>
return code\gsub("\n","\n"..(" ")\rep(levels))
- parse: (str, filename)=>
+ parse: (nomsu_code, filename)=>
assert type(filename) == "string", "Bad filename type: #{type filename}"
if @debug
- @writeln("#{colored.bright "PARSING:"}\n#{colored.yellow str}")
- str = str\gsub("\r","")
- tree = parse(str, filename)
- assert tree, "In file #{colored.blue filename} failed to parse:\n#{colored.onyellow colored.black str}"
+ @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_line_no = =>
+ unless @_line_no
+ line_no = 1
+ while (.line_starts[line_no+1] or math.huge) < @start do line_no += 1
+ @_line_no = "#{.filename}:#{line_no}"
+ return @_line_no
+
+ old_userdata, lpeg.userdata = lpeg.userdata, userdata
+ tree = NOMSU\match(nomsu_code)
+ lpeg.userdata = old_userdata
+
+ assert tree, "In file #{colored.blue filename} failed to parse:\n#{colored.onyellow colored.black nomsu_code}"
if @debug
@writeln "PARSE TREE:"
@print_tree tree, " "
@@ -377,7 +376,7 @@ class NomsuCompiler
return nil
return concat(lines, "\n"..indentation)
- is_operator = (tok)-> tok and tok.type == "Word" and OPERATOR_CHAR\match(tok.value)
+ is_operator = (tok)-> tok and tok.type == "Word" and NOMSU_DEFS.operator\match(tok.value)
local inline_expression, noeol_expression, expression
inline_expression = (tok)->
@@ -645,7 +644,8 @@ class NomsuCompiler
when "FunctionCall"
insert @compilestack, tree
- fn = @environment.ACTIONS[tree.stub]
+ -- Rawget here to avoid triggering an error for accessing an undefined action
+ fn = rawget(@environment.ACTIONS, tree.stub)
metadata = @environment.ACTION_METADATA[fn]
if metadata and metadata.compile_time
args = [arg for arg in *tree.value when arg.type != "Word"]
@@ -788,7 +788,7 @@ class NomsuCompiler
return concat(bits, "\n")
@unescape_string: (str)=>
- Cs(((P("\\\\")/"\\") + (P("\\\"")/'"') + ESCAPE_CHAR + P(1))^0)\match(str)
+ Cs(((P("\\\\")/"\\") + (P("\\\"")/'"') + NOMSU_DEFS.escaped_char + P(1))^0)\match(str)
@comma_separated_items: (open, items, close)=>
bits = {open}
@@ -836,7 +836,7 @@ class NomsuCompiler
return tree
@stub_patt: re.compile "{|(' '+ / '\n..' / {'%' %id*} / {%id+} / {%op})*|}",
- id:IDENT_CHAR, op:OPERATOR_CHAR
+ id:NOMSU_DEFS.ident_char, op:NOMSU_DEFS.operator
get_stub: (x)=>
if not x
error "Nothing to get stub from"
@@ -1014,20 +1014,21 @@ if arg
if calling_fn.istailcall and not name
name = "<tail call>"
if calling_fn.short_src == "./nomsu.moon"
- -- Ugh, magic numbers, but this works
- char = line_table[calling_fn.linedefined-2]
- line_num = 3
+ char = line_table[calling_fn.currentline]
+ line_num = 1
for _ in nomsu_source\sub(1,char)\gmatch("\n") do line_num += 1
line = colored.cyan("#{calling_fn.short_src}:#{line_num}")
name = colored.bright(colored.cyan(name or "???"))
else
- line = colored.blue("#{calling_fn.short_src}:#{calling_fn.linedefined}")
+ line = colored.blue("#{calling_fn.short_src}:#{calling_fn.currentline}")
name = colored.bright(colored.blue(name or "???"))
_from = colored.dim colored.white "|"
print(("%32s %s %s")\format(name, _from, line))
os.exit(false, true)
+ -- Note: xpcall has a slightly different API in Lua <=5.1 vs. >=5.2, but this works
+ -- for both APIs
xpcall(run, err_hand)
return NomsuCompiler
diff --git a/nomsu.peg b/nomsu.peg
index 0011924..046abbd 100644
--- a/nomsu.peg
+++ b/nomsu.peg
@@ -8,56 +8,49 @@ file (File):
shebang: "#!" [^%nl]* %nl
statement: functioncall / expression
-noeol_statement: noeol_functioncall / noeol_expression
inline_statement: inline_functioncall / inline_expression
-inline_block (Block): {| ":" %ws* inline_statement |}
-eol_block (Block): {| ":" %ws* noeol_statement eol |}
indented_block (Block):
- {| ":" indent
+ {| indent
statement (nodent statement)*
(dedent / (("" -> "Error while parsing block") => error))
|}
inline_nomsu (Nomsu): "\" inline_expression
-eol_nomsu (Nomsu): "\" noeol_expression
indented_nomsu (Nomsu): "\" expression
inline_expression:
number / variable / inline_text / inline_list / inline_dict / inline_nomsu
- / ("(" %ws* (inline_block / inline_statement) %ws* ")")
-noeol_expression:
+ / ("(" %ws* (inline_functioncall / inline_expression) %ws* ")")
+indented_expression:
indented_text / indented_nomsu / indented_list / indented_dict / indented_block
- / ("(..)" indent
- statement
- (dedent / (("" -> "Error while parsing indented expression") => error))
- ) / inline_expression
-expression: eol_block / eol_nomsu / noeol_expression
+expression:
+ inline_expression / (":" %ws* (inline_functioncall / inline_expression)) / indented_expression
-- Function calls need at least one word in them
inline_functioncall (FunctionCall):
- {| (inline_expression %ws*)* word (%ws* (inline_expression / word))* (%ws* inline_block)?|}
-noeol_functioncall (FunctionCall):
- {| (noeol_expression %ws*)* word (%ws* (noeol_expression / word))* |}
+ {| (inline_expression %ws*)* word (%ws* (inline_expression / word))*
+ (%ws* ":" %ws* (inline_functioncall / inline_expression))?|}
functioncall (FunctionCall):
{| (expression (dotdot / %ws*))* word ((dotdot / %ws*) (expression / word))* |}
word (Word): { %operator / (!number plain_word) }
inline_text (Text):
+ !('".."')
'"' {|
- ({~ (('\"' -> '"') / ('\\' -> '\') / ('\..' -> '..') / %escape_char / [^%nl\"])+ ~}
+ ({~ (('\"' -> '"') / ('\\' -> '\') / ('\..' -> '..') / %escaped_char / [^%nl\"])+ ~}
/ text_interpolation)*
|} '"'
indented_text (Text):
- '".."' %ws* line_comment? %nl %gt_nodented? {|
+ '".."' %ws* line_comment? %nl %gt_nodent? {|
({~
- (("\\" -> "\") / (("\" eol %nl %gt_nodented "..") -> "")
- / (%nl+ {~ %gt_nodented -> "" ~}) / [^%nl\])+
+ (("\\" -> "\") / (("\" eol %nl %gt_nodent "..") -> "")
+ / (%nl+ {~ %gt_nodent -> "" ~}) / [^%nl\])+
~} / text_interpolation)*
- |} ((!.) / (&(%nl+ !%gt_nodented)) / (("" -> "Error while parsing Text") => error))
+ |} ((!.) / (&(%nl+ !%gt_nodent)) / (("" -> "Error while parsing Text") => error))
text_interpolation:
- "\" (variable / inline_list / inline_dict / inline_text / ("(" %ws* (inline_block / inline_statement) %ws* ")"))
+ "\" (variable / inline_list / inline_dict / inline_text / ("(" %ws* (inline_functioncall / inline_expression) %ws* ")"))
number (Number): (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+)))-> tonumber)
@@ -66,6 +59,7 @@ number (Number): (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+)))-> tonum
variable (Var): "%" { plain_word? }
inline_list (List):
+ !('[..]')
"[" %ws* {| (inline_list_item (comma inline_list_item)* comma?)? |} %ws* "]"
indented_list (List):
"[..]" indent {|
@@ -73,11 +67,12 @@ indented_list (List):
|}
(dedent / (("" -> "Error while parsing list") => error))
list_line:
- (inline_list_item (comma inline_list_item)* (comma (functioncall / expression)?)?)
- / (functioncall / expression)
+ ((functioncall / expression) !comma)
+ / (inline_list_item (comma list_line?)?)
inline_list_item: inline_functioncall / inline_expression
inline_dict (Dict):
+ !('{..}')
"{" %ws* {| (inline_dict_item (comma inline_dict_item)* comma?)? |} %ws* "}"
indented_dict (Dict):
"{..}" indent {|
@@ -85,21 +80,19 @@ indented_dict (Dict):
|}
(dedent / (("" -> "Error while parsing dict") => error))
dict_line:
- (inline_dict_item comma)* (
- (inline_dict_item comma)
- /{| {:dict_key: inline_expression / word :} %ws* "=" %ws* {:dict_value: functioncall / expression :} |}
- )
+ ({| {:dict_key: inline_expression / word :} %ws* ":" %ws* {:dict_value: functioncall / expression :} |} !comma)
+ / (inline_dict_item (comma dict_line?)?)
inline_dict_item:
- {| {:dict_key: inline_expression / word :} %ws* "=" %ws* {:dict_value: inline_functioncall / inline_expression :} |}
+ {| {:dict_key: inline_expression / word :} %ws* ":" %ws* {:dict_value: inline_functioncall / inline_expression :} |}
-block_comment(Comment): "#.." { [^%nl]* (%nl (%ws* &%nl))* %nl %indented [^%nl]+ (%nl ((%ws* ((!.) / &%nl)) / (!%dedented [^%nl]+)))* }
+block_comment(Comment): "#.." { [^%nl]* (%nl %gt_nodent [^%nl]*)* }
line_comment(Comment): "#" { [^%nl]* }
eol: %ws* line_comment? (!. / &%nl)
-ignored_line: (%nodented (block_comment / line_comment)) / (%ws* (!. / &%nl))
-indent: eol (%nl ignored_line)* %nl %indented ((block_comment/line_comment) (%nl ignored_line)* nodent)?
-nodent: eol (%nl ignored_line)* %nl %nodented
-dedent: eol (%nl ignored_line)* (((!.) &%dedented) / (&(%nl %dedented)))
+ignored_line: (%nodent (block_comment / line_comment)) / (%ws* (!. / &%nl))
+indent: eol (%nl ignored_line)* %nl %indent ((block_comment/line_comment) (%nl ignored_line)* nodent)?
+nodent: eol (%nl ignored_line)* %nl %nodent
+dedent: eol (%nl ignored_line)* (((!.) &%dedent) / (&(%nl %dedent)))
comma: %ws* "," %ws*
dotdot: nodent ".." %ws*
plain_word: ([a-zA-Z0-9_] / %utf8_char)+