diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2017-09-24 20:20:27 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2017-09-24 20:20:27 -0700 |
| commit | af3274ca9237a08093009e87955e30ab5f473f12 (patch) | |
| tree | 46c249d6b42ac51111c7e198a10b8dad09b17923 /lib | |
| parent | e4660b169c14d24c3ec373b197e8b9469d200d50 (diff) | |
massive overhaul, compiler kinda works.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/collections.nom | 8 | ||||
| -rw-r--r-- | lib/control_flow.nom | 198 | ||||
| -rw-r--r-- | lib/metaprogramming.nom | 127 | ||||
| -rw-r--r-- | lib/operators.nom | 2 | ||||
| -rw-r--r-- | lib/testing.nom | 69 | ||||
| -rw-r--r-- | lib/utils.nom | 20 |
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()" |
