aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/collections.nom8
-rw-r--r--lib/control_flow.nom198
-rw-r--r--lib/metaprogramming.nom127
-rw-r--r--lib/operators.nom2
-rw-r--r--lib/testing.nom69
-rw-r--r--lib/utils.nom20
6 files changed, 241 insertions, 183 deletions
diff --git a/lib/collections.nom b/lib/collections.nom
index 09fff10..c7cc10d 100644
--- a/lib/collections.nom
+++ b/lib/collections.nom
@@ -57,8 +57,8 @@ macro [..]
%list doesn't have index %index, %list does not have index %index
..=: ".."|(\%list as lua\[\%index as lua\] ~= nil)
-macro [length of %list, size of %list, number of %list, len %list] =:
- ".."|#(\%list as lua\)
+macro [length of %list, size of %list, size %list, number of %list, len %list] =:
+ ".."|nomsu.utils.size(\%list as lua\)
# Chained lookup
macro [%list ->* %indices] =:
@@ -143,7 +143,7 @@ macro [%expression for all %iterable] =:
".."|(function(game, vars)
| local comprehension = {}
| for i,value in ipairs(\%iterable as lua\) do
- | vars.it = value
+ | vars[''] = value
| comprehension[i] = \%expression as lua\
| end
| return comprehension
@@ -152,7 +152,7 @@ macro [%expression for all %iterable] =:
# TODO: maybe make a generator/coroutine?
#.. Dict comprehensions can be accomplished okay by doing:
- dict ([new_key using (%it's "key"), new_value using (%it's "value")] for all (entries in %dict))
+ dict ([new_key using (%'s "key"), new_value using (%'s "value")] for all (entries in %dict))
or something similar
# TODO: fix compiler bugs
pass
diff --git a/lib/control_flow.nom b/lib/control_flow.nom
index 8d5f8ab..b1df621 100644
--- a/lib/control_flow.nom
+++ b/lib/control_flow.nom
@@ -31,21 +31,15 @@ macro statement [go to %label] =: ".."
|goto label_\nomsu "var_to_lua_identifier" [%label]\
# Loop control flow
-macro statement [break] =: "break"
-macro statement [break for] =: "goto break_for"
-macro statement [break for-all] =: "goto break_for_all"
-macro statement [break repeat] =: "goto break_repeat"
-macro statement [break repeat-until] =: "goto break_repeat_until"
-macro statement [break repeat-while] =: "goto break_repeat_while"
-macro statement [break %var, stop getting %var, no more %var] =: ".."
+macro statement [stop, stop loop, break] =: "break"
+macro statement [stop for, stop for-loop, break for] =: "goto break_for"
+macro statement [stop repeat, stop repeat-loop, break repeat] =: "goto break_repeat"
+macro statement [stop %var, break %var] =: ".."
|goto break_\nomsu "var_to_lua_identifier" [%var]\
-macro statement [continue] =: "continue"
-macro statement [continue for] =: "goto continue_for"
-macro statement [continue for-all] =: "goto continue_for_all"
-macro statement [continue repeat] =: "goto continue_repeat"
-macro statement [continue repeat-until] =: "goto continue_repeat_until"
-macro statement [continue repeat-while] =: "goto continue_repeat_while"
+macro statement [continue, continue loop] =: "continue"
+macro statement [continue for, continue for-loop] =: "goto continue_for"
+macro statement [continue repeat, continue repeat-loop] =: "goto continue_repeat"
macro statement [continue %var, go to next %var, on to the next %var] =: ".."
|goto continue_\nomsu "var_to_lua_identifier" [%var]\
@@ -59,17 +53,58 @@ macro block [repeat %body] =:
macro block [repeat while %condition %body] =:
".."|while \%condition as lua\ do
| \(lua expr "vars.body.value") as lua\
- | ::continue_repeat_while::
+ | ::continue_repeat::
|end
- |::break_repeat_while::
+ |::break_repeat::
macro block [repeat until %condition %body] =:
".."|while not (\%condition as lua\) do
| \(lua expr "vars.body.value") as lua\
- | ::continue_repeat_until::
+ | ::continue_repeat::
+ |end
+ |::break_repeat::
+
+# Numeric range for loops
+macro block [for %var from %start to %stop by %step %body] =:
+ %var-type =: lua expr "vars.var.type"
+ assert (%var-type == "Var") ".."
+ |For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\
+ # This trashes the loop variables, just like in Python.
+ ".."
+ |for i=\%start as lua\,\%stop as lua\,\%step as lua\ do
+ | \%var as lua\ = i
+ | \(lua expr "vars.body.value") as lua\
+ | ::continue_for::
+ | ::continue_\nomsu "var_to_lua_identifier" [%var]\::
+ |end
+ |::break_for::
+ |::break_\nomsu "var_to_lua_identifier" [%var]\::
+macro block [for %var from %start to %stop %body] =:
+ %thunk =: :for %var from %start to %stop by 1 %body
+ lua block ".."
+ |for i,x in ipairs(vars.thunk.value) do
+ | if x.type == 'Var' then vars.thunk.type == vars[x.value] end
|end
- |::break_repeat_until::
+ |return compiler:run_macro(vars.thunk, 'Statement')
+
+macro block [for all %start to %stop by %step %body] =:
+ # This trashes the loop variables, just like in Python.
+ ".."
+ |for i=\%start as lua\,\%stop as lua\,\%step as lua\ do
+ | vars[''] = i
+ | \(lua expr "vars.body.value") as lua\
+ | ::continue_for::
+ | ::continue_\nomsu "var_to_lua_identifier" [%]\::
+ |end
+ |::break_for::
+ |::break_\nomsu "var_to_lua_identifier" [%]\::
+macro block [for %var from %start to %stop %body] =:
+ %thunk =: :for %var from %start to %stop by 1 %body
+ lua block ".."
+ |for i,x in ipairs(vars.thunk.value) do
+ | if x.type == 'Var' then vars.thunk.type == vars[x.value] end
+ |end
+ |return compiler:run_macro(vars.thunk, 'Statement')
-# For loops
macro block [for %var in %iterable %body] =:
%var-type =: lua expr "vars.var.type"
assert (%var-type == "Var") ".."
@@ -89,92 +124,93 @@ macro block [for all %iterable %body] =:
pass # TODO: fix compiler bug
# This trashes the loop variables, just like in Python.
".."|for i,value in ipairs(\%iterable as lua\) do
- | vars.it = value
+ | vars[''] = value
| \(lua expr "vars.body.value") as lua\
- | ::continue_for_all::
+ | ::continue_for::
+ | ::continue_\nomsu "var_to_lua_identifier" [%]\::
|end
- |::break_for_all::
+ |::break_for::
+ |::break_\nomsu "var_to_lua_identifier" [%]\::
# Switch statement/multi-branch if
macro block [when %body] =:
%result =: ""
+ %fallthroughs =: []
for %statement in (lua expr "vars.body.value.value"):
%func-call =: lua expr "vars.statement.value"
assert ((lua expr "vars['func-call'].type") == "FunctionCall") ".."
- |Invalid format for 'when' statement. Only '?' blocks are allowed.
+ |Invalid format for 'when' statement. Only '*' blocks are allowed.
%tokens =: lua expr "vars['func-call'].value"
- %q =: lua expr "vars.tokens[1]"
- assert (((lua expr "vars.q.type") == "Word") and ((lua expr "vars.q.value") == "?")) ".."
- |Invalid format for 'when' statement. Lines must begin with '?'
- %thunk =: lua expr "vars.tokens[#vars.tokens]"
- assert ((lua expr "vars.thunk.type") == "Thunk") ".."
- |Invalid format for 'when' statement. Lines must have a body.
- %condition_bits =: []
- for %i in (2 up to (lua expr "#vars.tokens")):
- lua block "table.insert(vars['condition_bits'], vars.tokens[vars.i])"
-
- if (lua expr "#vars.condition_bits == 0"):
- %result join=: ".."
- |
+
+ %star =: lua expr "vars.tokens[1]"
+ assert (lua expr "vars.star and vars.star.type == 'Word' and vars.star.value == '*'") ".."
+ |Invalid format for 'when' statement. Lines must begin with '*'
+
+ %condition =: lua expr "vars.tokens[2]"
+ assert %condition ".."
+ |Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
+
+ %thunk =: lua expr "vars.tokens[3]"
+ if (%thunk == (nil)):
+ lua block "table.insert(vars.fallthroughs, vars.condition)"
+ go to next %statement
+
+ if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"):
+ %result join=: ".."|
|do
- | \(lua expr "vars.thunk.value") as lua\
- | goto finished_when
- |end
..else:
- if (lua expr "#vars.condition_bits == 1 and vars.condition_bits[1].type ~= 'Word'"):
- %condition =: lua expr "vars.condition_bits[1]"
- ..else:
- %condition =: dict [..]
- ["type",lua expr "vars['func-call'].type"]
- ["src",lua expr "vars['func-call'].src"]
- ["value", %condition_bits]
- %result join=: ".."
- |
- |if \%condition as lua\ then
- | \(lua expr "vars.thunk.value") as lua\
- | goto finished_when
- |end
+ %condition =: %condition as lua
+ for all %fallthroughs: %condition join=: ".."| or \% as lua\
+ %result join=: ".."|
+ |if \%condition\ then
+ %result join=: ".."|
+ | \(lua expr "vars.thunk.value") as lua\
+ | goto finished_when
+ |end
+
+ %fallthroughs =: []
+
%result join=: "\n::finished_when::"
%result
# Switch statement
-macro block [when %branch-value %body] =:
+macro block [when %branch-value == ? %body] =:
%result =: ".."|local branch_value = \%branch-value as lua\
+ %fallthroughs =: []
for %statement in (lua expr "vars.body.value.value"):
%func-call =: lua expr "vars.statement.value"
assert ((lua expr "vars['func-call'].type") == "FunctionCall") ".."
- |Invalid format for 'when' statement. Only == blocks are allowed.
+ |Invalid format for 'when' statement. Only '*' blocks are allowed.
%tokens =: lua expr "vars['func-call'].value"
- %eq =: lua expr "vars.tokens[1]"
- assert (((lua expr "vars.eq.type") == "Word") and ((lua expr "vars.eq.value") == "==")) ".."
- |Invalid format for 'when' statement. Lines must begin with '=='
- %thunk =: lua expr "vars.tokens[#vars.tokens]"
- assert ((lua expr "vars.thunk.type") == "Thunk") ".."
- |Invalid format for 'when' statement. Lines must have a body.
- %condition_bits =: []
- for %i in (2 up to (lua expr "#vars.tokens")):
- lua block "table.insert(vars.condition_bits, vars.tokens[vars.i])"
- if (lua expr "#vars.condition_bits == 0"):
- %result join=: ".."
- |
+
+ %star =: lua expr "vars.tokens[1]"
+ assert (lua expr "vars.star and vars.star.type == 'Word' and vars.star.value == '*'") ".."
+ |Invalid format for 'when' statement. Lines must begin with '*'
+
+ %condition =: lua expr "vars.tokens[2]"
+ assert %condition ".."
+ |Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
+
+ %thunk =: lua expr "vars.tokens[3]"
+ if (%thunk == (nil)):
+ lua block "table.insert(vars.fallthroughs, vars.condition)"
+ go to next %statement
+
+ if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"):
+ %result join=: ".."|
|do
- | \(lua expr "vars.thunk.value") as lua\
- | goto finished_when
- |end
..else:
- if (lua expr "#vars.condition_bits == 1 and vars.condition_bits[1].type ~= 'Word'"):
- %condition =: (lua expr "vars.condition_bits[1]")
- ..else:
- %condition =: dict [..]
- ["type",lua expr "vars['func-call'].type"]
- ["src",lua expr "vars['func-call'].src"]
- ["value", %condition_bits]
- %result join=: ".."
- |
- |if nomsu.utils.equivalent(branch_condition, \%condition as lua\) then
- | \(lua expr "vars.thunk.value") as lua\
- | goto finished_when
- |end
+ %condition =: ".."|branch_value == (\%condition as lua\)
+ for all %fallthroughs: %condition join=: ".."| or (branch_value == \% as lua\)
+ %result join=: ".."|
+ |if \%condition\ then
+ %result join=: ".."|
+ | \(lua expr "vars.thunk.value") as lua\
+ | goto finished_when
+ |end
+
+ %fallthroughs =: []
+
%result join=: "\n::finished_when::"
%result
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
index 72c50b5..9dcc9df 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -2,88 +2,55 @@
This File contains rules for making rules and macros and some helper functions to make
that easier.
-# Macros:
-
-lua block ".."
- |local function make_fn(wrap_in_block)
- | return function(nomsu, vars, kind)
- # Do a minimal amount of pre-processing (get the aliases and the source)
- | local aliases = nomsu:repr(nomsu:get_aliases(vars.macro_def))
- | local src = nomsu:repr(vars.user_macro.src)
- | local user_macro = nomsu:tree_to_lua(vars.user_macro)
- # Then produce a block of code that creates the macro at runtime
- | local lua = [[
- |nomsu:defmacro(%s, (function(nomsu, vars, kind)
- | if kind == "Expression" then
- | nomsu:error("Macro "..%s.." was defined to be a block, but is being used as an expression")
- | end
- | local user_macro = %s
- | local lua = user_macro(nomsu, vars)
- | %s
- | return lua, true
- |end), %s)]]
- | lua = lua:format(aliases, nomsu:repr(aliases), user_macro,
- | wrap_in_block and [[lua = "do\\n "..lua.."\\nend"]] or "", src)
- | return lua, true
- | end
- |end
- |nomsu:defmacro("macro statement %macro_def = %user_macro", make_fn(false), "see:lib/metaprogramming.nom")
- |nomsu:defmacro("macro block %macro_def = %user_macro", make_fn(true), "see:lib/metaprogramming.nom")
+# Nil
+lua code ".."
+ |nomsu:defmacro("nil", function(nomsu, vars) return "nil", nil end)
+..with value 0
-macro statement [macro %macro_def = %user_macro] =:
- ".."|nomsu:defmacro(
- | \lua expr "nomsu:get_aliases(vars.macro_def)"\,
- | \lua expr "nomsu:tree_to_lua(vars.user_macro)"\,
- | \lua expr "nomsu:repr(vars.user_macro.src)"\)
+# Macros:
+lua code ".."
+ |nomsu:def("parse %shorthand as %longhand", function(nomsu, vars)
+ | local alias = nomsu:get_alias(vars.shorthand)
+ | local template = vars.longhand
+ | nomsu:defmacro(alias, function(nomsu, vars)
+ | return nomsu:tree_to_lua(nomsu:replaced_vars(template, vars))
+ | end)
+ |end)
+..with value (nil)
+
+parse \(lua code %code) as\: lua code %code with value (nil)
+parse \(lua block %block) as\:
+ lua code ".."
+ |do
+ | \(%block)
+ |end
+ ..with value (nil)
+parse \(lua value %value) as\: lua code (nil) with value %value
-macro [nomsu] =: "nomsu"
-macro [nomsu's %key] =: ".."|nomsu[\%key as lua\]
-macro [nomsu %method %args] =:
+parse \(nomsu) as\: lua value "nomsu"
+parse \(nomsu's %key) as\:
+ lua value "nomsu[\(%key as lua)]"
+parse \(nomsu %method %args) as\:
lua block ".."
|local args = {"nomsu"}
|for _,arg in ipairs(vars.args.value) do
| table.insert(args, nomsu:tree_to_lua(arg))
|end
- |return "nomsu["..nomsu:repr(nomsu:tree_to_value(vars.method, vars)).."]("..table.concat(args, ", ")..")"
-macro [nomsu utils %method %args] =:
- lua block ".."
- |local args = {}
- |for i,arg in ipairs(vars.args.value) do
- | args[i] = nomsu:tree_to_lua(arg)
- |end
- |return "nomsu.utils["..nomsu:repr(nomsu:tree_to_value(vars.method, vars)).."]("..table.concat(args, ", ")..")"
-
-# Macro that lets you make new rules
-#..
- This is a macro instead of a rule because it needs to pre-empt processing the list of
- function calls and convert it into a list of strings (rather than call a function that
- is currently in the middle of being defined). Being a macro also allows us to snatch
- the source code and store that
-macro statement [rule %rule_def = %body] =: ".."
- |nomsu:def(
- | \nomsu "repr" [nomsu "get_aliases" [%rule_def]]\,
- | \nomsu "tree_to_lua" [%body]\,
- | \nomsu "repr" [lua expr "vars.body.src"]\)
-
-rule [fart]=: lua block "print('poot')"
+ |local method_name = nomsu:repr(nomsu:tree_to_value(vars.method, vars))
+ ..with value ".."
+ |("nomsu[%s](%s)"):format(method_name, table.concat(args, ", "))
+parse \(repr %) as\: nomsu "repr" [%]
-macro block [alias %aliases = %aliased] =:
- lua block ".."
- |local aliases = nomsu:get_aliases(vars.aliases)
- |aliases = nomsu:repr(aliases)
- |if vars.aliased.type ~= "Thunk" then
- | nomsu:error("Right hand side of alias % = % should be a Thunk, but got "..vars.aliased.type
- | ..". Maybe you used = instead of =: by mistake?")
- |end
- |local aliased = next(nomsu:get_aliases(vars.aliased.value))
- |aliased = nomsu:repr(aliased)
- |local lua = ([[
- |nomsu:add_aliases(%s, nomsu.defs[%s])
- |]]):format(aliases, aliased)
- |return lua
+# Rule that lets you make new rules
+lua block ".."
+ |nomsu:def("rule helper %rule_def = %body", function(nomsu, vars)
+ | nomsu:def(nomsu:get_alias(vars.rule_def), vars.body)
+ |end)
+parse \(rule %rule_def = %body) as\: rule helper \(%rule_def) = \(%body)
+parse \(rule %rule_name) as\: lua value "nomsu.defs[nomsu:get_alias(\(%rule_name))]"
# Get the source code for a function
-rule [help %rule] =:
+rule (help %rule) =:
lua block ".."
|local fn_def = nomsu:get_fn_def(vars.rule)
|if not fn_def then
@@ -93,20 +60,16 @@ rule [help %rule] =:
| .." ="..(fn_def.src or ":\\n <unknown source code>"))
|end
-
# Macro helper functions
-rule [%tree as value] =:
+rule (%tree as value) =:
lua expr "nomsu:tree_to_value(vars.tree, vars)"
-rule [%tree as lua] =:
+rule (%tree as lua) =:
lua expr "nomsu:tree_to_lua(vars.tree)"
-rule [test, foobar] =: lua expr "print('yup')"
-
# Compiler tools
-rule [eval %code, run %code] =: nomsu "run" [%code]
-
-rule [source code from tree %tree] =:
+rule (eval %code; run %code) =: nomsu "run" [%code]
+rule (source code from tree %tree) =:
lua block ".."
|local _,_,leading_space = vars.tree.src:find("\\n(%s*)%S")
|if leading_space then
@@ -117,9 +80,9 @@ rule [source code from tree %tree] =:
| return vars.tree.src:match(":%s*(%S.*)").."\\n"
|end
-macro [source code %body] =:
+macro (source code %body) =:
nomsu "repr" [nomsu "call" ["source code from tree %", %body]]
-macro [parse tree %code] =:
+macro (parse tree %code) =:
nomsu "repr" [nomsu "stringify_tree" [lua expr "vars.code.value"]]
diff --git a/lib/operators.nom b/lib/operators.nom
index 6dadaf3..f2bb7c2 100644
--- a/lib/operators.nom
+++ b/lib/operators.nom
@@ -4,6 +4,8 @@ require "lib/metaprogramming.nom"
macro [true, yes] =: "true"
macro [false, no] =: "false"
macro [nil, null] =: "nil"
+macro [inf, infinity] =: "math.huge"
+macro [nan, NaN, not a number] =: "(0/0)"
macro statement [nop, pass] =: ""
# Ternary operator
diff --git a/lib/testing.nom b/lib/testing.nom
index 87a7711..83d0108 100644
--- a/lib/testing.nom
+++ b/lib/testing.nom
@@ -2,6 +2,75 @@ require "lib/metaprogramming.nom"
# For unit testing
macro block [test %code yields %expected] =:
+
+
+ _yield_tree: (tree, indent_level=0)=>
+ ind = (s) -> INDENT\rep(indent_level)..s
+ switch tree.type
+ when "File"
+ coroutine.yield(ind"File:")
+ @_yield_tree(tree.value.body, indent_level+1)
+ when "Errors" then coroutine.yield(ind"Error:\n#{tree.value}")
+ when "Block"
+ for chunk in *tree.value
+ @_yield_tree(chunk, indent_level)
+ when "Thunk"
+ coroutine.yield(ind"Thunk:")
+ @_yield_tree(tree.value, indent_level+1)
+ when "Statement" then @_yield_tree(tree.value, indent_level)
+ when "FunctionCall"
+ alias = @get_alias tree
+ args = [a for a in *tree.value when a.type != "Word"]
+ if #args == 0
+ coroutine.yield(ind"Call [#{alias}]!")
+ else
+ coroutine.yield(ind"Call [#{alias}]:")
+ for a in *args
+ @_yield_tree(a, indent_level+1)
+ when "String" then coroutine.yield(ind(repr(tree.value)))
+ when "Longstring" then coroutine.yield(ind(repr(tree.value)))
+ when "Number" then coroutine.yield(ind(tree.value))
+ when "Var" then coroutine.yield ind"Var[#{repr(tree.value)}]"
+ when "List"
+ if #tree.value == 0
+ coroutine.yield(ind("<Empty List>"))
+ else
+ coroutine.yield(ind"List:")
+ for item in *tree.value
+ @_yield_tree(item, indent_level+1)
+ else error("Unknown/unimplemented thingy: #{tree.type}")
+
+ print_tree:(tree)=>
+ for line in coroutine.wrap(-> @_yield_tree(tree))
+ @writeln(line)
+
+ stringify_tree:(tree)=>
+ result = {}
+ for line in coroutine.wrap(-> @_yield_tree(tree))
+ insert(result, line)
+ return concat result, "\n"
+
+ test: (src, filename, expected)=>
+ i = 1
+ while i != nil
+ start,stop = src\find("\n\n", i)
+
+ test = src\sub(i,start)
+ i = stop
+ start,stop = test\find"==="
+ if not start or not stop then
+ @error("WHERE'S THE ===? in:\n#{test}")
+ test_src, expected = test\sub(1,start-1), test\sub(stop+1,-1)
+ expected = expected\match'[\n]*(.*[^\n])'
+ tree = @parse(test_src, filename)
+ got = @stringify_tree(tree.value.body)
+ if got != expected
+ @error"TEST FAILED!\nSource:\n#{test_src}\nExpected:\n#{expected}\n\nGot:\n#{got}"
+
+
+
+
+
%generated =: repr (nomsu "stringify_tree" [%code's "value"])
%expected =: %expected as lua
if (%generated != %expected):
diff --git a/lib/utils.nom b/lib/utils.nom
index b1e104b..1afc33a 100644
--- a/lib/utils.nom
+++ b/lib/utils.nom
@@ -44,22 +44,10 @@ macro [say %str] =:
".."|nomsu:writeln(nomsu.utils.repr_if_not_string(\%str as lua\))
# Number ranges
-macro [%start up to %stop] =: ".."
- |nomsu.utils.range(\%start as lua\, \%stop as lua\-1)
-macro [%start thru %stop, %start through %stop] =: ".."
- |nomsu.utils.range(\%start as lua\, \%stop as lua\)
-macro [%start down to %stop] =: ".."
- |nomsu.utils.range(\%start as lua\, \%stop as lua\+1,-1)
-macro [%start down thru %stop, %start down through %stop] =: ".."
- |nomsu.utils.range(\%start as lua\, \%stop as lua\,-1)
-macro [%start up to %stop via %step] =: ".."
- |nomsu.utils.range(\%start as lua\,\%stop as lua\-1,vars.step)
-macro [%start thru %stop via %step, %start through %stop via %step] =: ".."
- |nomsu.utils.range(\%start as lua\,\%stop as lua\,vars.step)
-macro [%start down to %stop via %step] =: ".."
- |nomsu.utils.range(\%start as lua\,\%stop as lua\+1,-vars.step)
-macro [%start down thru %stop via %step, %start down through %stop via %step] =: ".."
- |nomsu.utils.range(\%start as lua\,\%stop as lua\,-vars.step)
+macro [%start to %stop] =: ".."
+ |nomsu.utils.range(\%start as lua\, \%stop as lua\)
+macro [%start to %stop by %step, %start to %stop via %step] =: ".."
+ |nomsu.utils.range(\%start as lua\, \%stop as lua\, \%step as lua\)
# Common utility functions
macro [random number, random, rand] =: "math.random()"