From 3b10a608267ce00cc140b63a19c9433f261f9794 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 19 Sep 2017 00:30:47 -0700 Subject: Moved core into lib/ and recompiled. --- core.nom | 626 ---------------------------------------------- examples/parser_tests.nom | 2 +- examples/sample_code.nom | 2 +- examples/sample_game.nom | 2 +- lib/core.nom | 626 ++++++++++++++++++++++++++++++++++++++++++++++ nomsu.lua | 2 +- 6 files changed, 630 insertions(+), 630 deletions(-) delete mode 100644 core.nom create mode 100644 lib/core.nom diff --git a/core.nom b/core.nom deleted file mode 100644 index 54f728d..0000000 --- a/core.nom +++ /dev/null @@ -1,626 +0,0 @@ -# Rule for making rules -lua block ".." - |compiler:defmacro("rule %spec = %body", function(compiler, vars, kind) - | if kind == "Expression" then - | compiler:error("Cannot use rule definitions as an expression.") - | end - | local spec = compiler:get_invocations_from_definition(vars.spec.value, vars) - | return ("compiler:def("..compiler.utils.repr(spec,true)..", "..compiler:tree_to_lua(vars.body, vars)..")"), true - |end) - -rule: help %invocation ..=: - lua block ".." - |local fn_info = compiler.defs[vars.invocation] - |if not fn_info then - | compiler:writeln("Function not found: "..compiler.utils.repr(vars.invocation, true)) - |else - | compiler:writeln("rule "..compiler.utils.repr(fn_info.invocations).." ="..(fn_info.src or ":\\n ")) - |end - -# Macros -lua block ".." - |local add_macro = function(compiler, vars, kind) - | local spec = compiler:get_invocations_from_definition(vars.spec.value, vars) - | local fn = compiler:tree_to_value(vars.body, vars) - | compiler:defmacro(spec, fn, vars.body.src) - | return "", true - |end - |compiler:defmacro("macro %spec = %body", add_macro) - | - |local add_macro_block = function(compiler, vars, kind) - | local spec = compiler:get_invocations_from_definition(vars.spec.value, vars) - | local fn = compiler:tree_to_value(vars.body, vars) - | local wrapper = function(compiler, vars, kind) - | if kind == "Expression" then - | compiler:error("Macro: "..spec.." was defined to be a block, but is being used as an expression.") - | end - | return ("do\\n"..fn(compiler, vars, kind).."\\nend"), true - | end - | compiler:defmacro(spec, wrapper, vars.body.src) - | return "", true - |end - |compiler:defmacro("macro block %spec = %body", add_macro_block) - -# Compiler tools -rule: - eval %code - run %code -..=: - lua expr "compiler:run(vars.code)" - -macro: source code %code ..=: - lua block ".." - |if vars.code.value.type ~= "Thunk" then - | compiler:error("'source code %' only takes code blocks, not "..vars.code.value.type) - |end - lua expr "compiler.utils.repr(vars.code.value.value.src, true)" - -rule: run file %filename ..=: - lua block ".." - |local file = io.open(vars.filename) - |return compiler:run(file:read("*a")) - -# Error functions -rule: error! ..=: - lua block ".." - |table.remove(compiler.callstack) - |compiler:error() - -rule: error %msg ..=: - lua block ".." - |table.remove(compiler.callstack) - |compiler:error(vars.msg) - -macro: as lua %block ..=: - lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value.value, vars), true)" - -macro block: show generated lua %block ..=: - ".."|compiler:writeln(\lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value.value, vars), true)"\) - -# Macro helper functions -rule: %tree as value ..=: - lua expr ".." - |compiler:tree_to_value(vars.tree, vars) - -rule: %tree as lua block ..=: - lua block ".." - |return compiler:tree_to_lua(vars.tree, 'Statement'), true - -rule: %tree as lua expr ..=: - lua expr ".." - |compiler:tree_to_lua(vars.tree, 'Expression') - -# Moonscript! -macro block: moonscript block %moonscript_code ..=: - lua block ".." - |local parse, compile = require('moonscript.parse'), require('moonscript.compile') - |local moon_code = compiler:tree_to_value(vars.moonscript_code, vars) - |local tree, err = parse.string(moon_code) - |if not tree then - | compiler:error("Failed to parse moonscript: "..err) - |end - |local lua_code, err, pos = compile.tree(tree) - |if not lua_code then - | compiler:error(compile.format_error(err, pos, moon_code)) - |end - |return "do\\n"..lua_code.."\\nend" - -macro: moonscript %moonscript_code ..=: - lua block ".." - |local parse, compile = require('moonscript.parse'), require('moonscript.compile') - |local moon_code = compiler:tree_to_value(vars.moonscript_code, vars) - |local tree, err = parse.string(moon_code) - |if not tree then - | compiler:error("Failed to parse moonscript: "..err) - |end - |local lua_code, err, pos = compile.tree(tree) - |if not lua_code then - | compiler:error(compile.format_error(err, pos, moon_code)) - |end - |return "(function(compiler, vars)\\n"..lua_code.."\\nend)(compiler, vars)" - -# String functions -rule: join %strs ..=: - lua block ".." - |local str_bits = {} - |for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end - |return table.concat(str_bits) - -rule: join %strs with glue %glue ..=: - lua block ".." - |local str_bits = {} - |for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end - |return table.concat(str_bits, vars.glue) - -rule: - capitalize %str - %str capitalized -..=: - lua expr ".."|vars.str:gsub("%l", string.upper, 1) - -# Variable assignment -#.. - macro block: %var = %value ..=: - lua block ".." - |if vars.var.value.type ~= "Var" then - | compiler:error("Assignment operation has the wrong type for the left hand side. " - | .."Expected Var, but got: "..vars.var.value.type) - |end - ".."|\%var as lua expr\ = \%value as lua expr\ - -lua block ".." - |local function helper(callback) - | return function(compiler, vars, kind) - | if kind == "Expression" then - | compiler:error("Cannot use an assignment operation as an expression value.") - | end - | if vars.var.value.type ~= "Var" then - | compiler:error("Assignment operation has the wrong type for the left hand side. " - | .."Expected Var, but got: "..vars.var.value.type) - | end - | if vars.value.value.type ~= "Thunk" then - | compiler:error("Assignment operation has the wrong type for the right hand side. " - | .."Expected Thunk, but got: "..vars.value.value.type.."\\nMaybe you used '=' instead of '=:'?") - | end - | local ret = "do\\n local ret" - | ret = ret .. "\\n "..compiler:tree_to_lua(vars.value.value.value, "Statement") - | ret = ret .. "\\n "..callback(compiler:tree_to_lua(vars.var, "Expression")) - | return (ret.."\\nend"), true - | end - |end - |compiler:defmacro("%var = %value", helper(function(var) return var.." = ret" end)) - |compiler:defmacro("%var += %value", helper(function(var) return var.." = "..var.." + ret" end)) - |compiler:defmacro("%var -= %value", helper(function(var) return var.." = "..var.." - ret" end)) - |compiler:defmacro("%var *= %value", helper(function(var) return var.." = "..var.." * ret" end)) - |compiler:defmacro("%var /= %value", helper(function(var) return var.." = "..var.." / ret" end)) - |compiler:defmacro("%var ^= %value", helper(function(var) return var.." = "..var.." ^ ret" end)) - |compiler:defmacro("%var and= %value", helper(function(var) return var.." = "..var.." and ret" end)) - |compiler:defmacro("%var or= %value", helper(function(var) return var.." = "..var.." or ret" end)) - |compiler:defmacro("%var concat= %value", helper(function(var) return var.." = "..var.." .. ret" end)) - |compiler:defmacro("%var mod= %value", helper(function(var) return var.." = "..var.." % ret" end)) - -# Operators -macro: - true - yes -..=: "true" -macro: - false - no -..=: "false" -macro: - nil - null -..=: "nil" -macro block: - nop - pass -..=: "" -macro: %a + %b ..=: ".."|(\%a as lua expr\ + \%b as lua expr\) -macro: %a + %b + %c ..=: ".."|(\%a as lua expr\ + \%b as lua expr\ + \%c as lua expr\) -macro: %a + %b + %c + %d ..=: ".."|(\%a as lua expr\ + \%b as lua expr\ + \%c as lua expr\ + \%d as lua expr\) -macro: %a - %b ..=: ".."|(\%a as lua expr\ - \%b as lua expr\) -macro: %a * %b ..=: ".."|(\%a as lua expr\ * \%b as lua expr\) -macro: %a * %b * %c ..=: ".."|(\%a as lua expr\ * \%b as lua expr\ * \%c as lua expr\) -macro: %a * %b * %c * %d ..=: ".."|(\%a as lua expr\ * \%b as lua expr\ * \%c as lua expr\ * \%d as lua expr\) -macro: %a / %b ..=: ".."|(\%a as lua expr\ / \%b as lua expr\) -macro: %a === %b ..=: ".."|(\%a as lua expr\ == \%b as lua expr\) -macro: %a !== %b ..=: ".."|(\%a as lua expr\ ~= \%b as lua expr\) -macro: %a < %b ..=: ".."|(\%a as lua expr\ < \%b as lua expr\) -macro: %a < %b < %c ..=: ".."|((\%a as lua expr\ < \%b as lua expr\) and (\%b as lua expr\ < \%c as lua expr\)) -macro: %a <= %b < %c ..=: ".."|((\%a as lua expr\ <= \%b as lua expr\) and (\%b as lua expr\ < \%c as lua expr\)) -macro: %a <= %b <= %c ..=: ".."|((\%a as lua expr\ <= \%b as lua expr\) and (\%b as lua expr\ <= \%c as lua expr\)) -macro: %a < %b <= %c ..=: ".."|((\%a as lua expr\ < \%b as lua expr\) and (\%b as lua expr\ <= \%c as lua expr\)) -macro: %a > %b > %c ..=: ".."|((\%a as lua expr\ > \%b as lua expr\) and (\%b as lua expr\ > \%c as lua expr\)) -macro: %a >= %b > %c ..=: ".."|((\%a as lua expr\ >= \%b as lua expr\) and (\%b as lua expr\ > \%c as lua expr\)) -macro: %a >= %b >= %c ..=: ".."|((\%a as lua expr\ >= \%b as lua expr\) and (\%b as lua expr\ >= \%c as lua expr\)) -macro: %a > %b >= %c ..=: ".."|((\%a as lua expr\ > \%b as lua expr\) and (\%b as lua expr\ >= \%c as lua expr\)) -macro: %a <= %b ..=: ".."|(\%a as lua expr\ <= \%b as lua expr\) -macro: %a > %b ..=: ".."|(\%a as lua expr\ > \%b as lua expr\) -macro: %a >= %b ..=: ".."|(\%a as lua expr\ >= \%b as lua expr\) -macro: %a ^ %b ..=: ".."|(\%a as lua expr\ ^ \%b as lua expr\) -macro: %a and %b ..=: ".."|(\%a as lua expr\ and \%b as lua expr\) -macro: %a and %b and %c ..=: - ".."|(\%a as lua expr\ and \%b as lua expr\ and \%c as lua expr\) -macro: %a and %b and %c and %d ..=: - ".."|(\%a as lua expr\ and \%b as lua expr\ and \%c as lua expr\ and \%d as lua expr\) -macro: %a or %b ..=: ".."|(\%a as lua expr\ or \%b as lua expr\) -macro: %a or %b or %c ..=: - ".."|(\%a as lua expr\ or \%b as lua expr\ or \%c as lua expr\) -macro: %a or %b or %c or %d ..=: - ".."|(\%a as lua expr\ or \%b as lua expr\ or \%c as lua expr\ or \%d as lua expr\) -macro: %a mod %b ..=: ".."|(\%a as lua expr\ mod \%b as lua expr\) -macro: - %a ..=: ".."|-(\%a as lua expr\) -macro: not %a ..=: ".."|not (\%a as lua expr\) - -rule: %a == %b ..=: - lua expr "((vars.a == vars.b) or compiler.utils.equivalent(vars.a, vars.b))" -rule: %a != %b ..=: - lua expr "((vars.a ~= vars.b) or not compiler.utils.equivalent(vars.a, vars.b))" - -macro: repr %obj ..=: - ".."|compiler.utils.repr(\%obj as lua expr\, true) - -macro: say %str ..=: - ".."|compiler:writeln(compiler.utils.repr(\%str as lua expr\)) - -# Control flow -rule: do %action ..=: - lua expr "vars.action(compiler, setmetatable({}, {__index=vars}))" - -macro block: return %return_value ..=: - lua block ".." - |if vars.return_value.value.type ~= "Thunk" then - | compiler:error("Assignment operation has the wrong type for the right hand side. " - | .."Expected Thunk, but got: "..vars.return_value.value.type.."\\nMaybe you used '=' instead of '=:'?") - |end - ".."|do - | local ret - | \lua expr "compiler:tree_to_lua(vars.return_value.value.value, 'Statement')"\ - | return ret - |end - -macro block: return ..=: - "return nil" - -# Conditionals -macro block: if %condition %if_body ..=: - ".."|if \%condition as lua expr\ then - | \(lua expr "vars.if_body.value.value") as lua block\ - |end - -macro block: if %condition %if_body else %else_body ..=: - ".."|if \%condition as lua expr\ then - | \(lua expr "vars.if_body.value.value") as lua block\ - |else - | \(lua expr "vars.else_body.value.value") as lua block\ - |end - -# Ternary operator -macro: %if_expr if %condition else %else_expr ..=: - ".."|(function(compiler, vars) - | if \%condition as lua expr\ then - | return \%if_expr as lua expr\ - | else - | return \%else_expr as lua expr\ - | end - |end)(compiler, vars) - -# Loop control flow -macro block: break ..=: "break" -macro block: continue ..=: "continue" -# TODO: add labeled break/continue? - -# GOTOs -lua block ".." - |local function lua_label(label) - | if label.type ~= "Var" then - | compiler:error("Goto label has the wrong type for the label. Expected Var, but got: "..label.type) - | end - | local bits = "abcdefghijklmnop" - | local lua_identifier = "label_" - | for i=1,#label.value do - | local byte = string.byte(label.value, i) - | local low = byte % 16 - | local high = (byte - low) / 16 - | lua_identifier = lua_identifier .. bits:sub(low+1,low+1) .. bits:sub(high+1,high+1) - | end - | return lua_identifier - |end - | - |compiler:defmacro("-> %label", function(compiler, vars, kind) - | return "::"..lua_label(vars.label.value).."::", true - |end) - |compiler:defmacro("go to %label", function(compiler, vars, kind) - | return "goto "..lua_label(vars.label.value), true - |end) - -# While loops -macro block: repeat %body ..=: - ".."|while true do - | \(lua expr "vars.body.value.value") as lua block\ - |end -macro block: repeat while %condition %body ..=: - ".."|while \%condition as lua expr\ do - | \(lua expr "vars.body.value.value") as lua block\ - |end -macro block: repeat until %condition %body ..=: - ".."|while not (\%condition as lua expr\) do - | \(lua expr "vars.body.value.value") as lua block\ - |end - -# For loops -macro block: for %var in %iterable %body ..=: - %var-type =: lua expr "vars.var.value.type" - if (%var-type != "Var"): - error ".." - |For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\ - %var-code =: %var as lua expr - ".."|local old_loopval = \%var-code\ - |for i,value in ipairs(\%iterable as lua expr\) do - | \%var-code\ = value - | \(lua expr "vars.body.value.value") as lua block\ - |end - |\%var-code\ = old_loopval - -macro block: for all %iterable %body ..=: - ".."|local old_loopval = vars.it - |for i,value in ipairs(\%iterable as lua expr\) do - | vars.it = value - | \(lua expr "vars.body.value.value") as lua block\ - |end - |vars.it = old_loopval - -# List Comprehension -# TODO: maybe make this lazy, or a lazy version? -macro: %expression for %var in %iterable ..=: - %var-type =: lua expr "vars.var.value.type" - if (%var-type != "Var"): - error ".." - |List comprehension has the wrong type for the loop variable. Expected Var, but got: \%var-type\ - %var-code =: %var as lua expr - ".."|(function(game, vars) - | local comprehension = {} - | for i,value in ipairs(\%iterable as lua expr\) do - | \%var-code\ = value - | comprehension[i] = \%expression as lua expr\ - | end - | return comprehension - |end)(game, setmetatable({}, {__index=vars})) - -macro: %expression for all %iterable ..=: - ".."|(function(game, vars) - | local comprehension = {} - | for i,value in ipairs(\%iterable as lua expr\) do - | vars.it = value - | comprehension[i] = \%expression as lua expr\ - | end - | return comprehension - |end)(game, setmetatable({}, {__index=vars})) - -# Dict comprehension -macro: %key -> %value for %var in %iterable ..=: - %var-type =: lua expr "vars.var.value.type" - if (%var-type != "Var"): - error ".." - |Dict comprehension has the wrong type for the loop variable. Expected Var, but got: \%var-type\ - %var-code =: %var as lua expr - ".."|(function(game, vars) - | local comprehension = {} - | for i,value in ipairs(\%iterable as lua expr\) do - | \%var-code\ = value - | comprehension[\%key as lua expr\] = \%value as lua expr\ - | end - | return comprehension - |end)(game, setmetatable({}, {__index=vars})) - -macro: %key -> %value for all %iterable ..=: - ".."|(function(game, vars) - | local comprehension = {} - | for i,value in ipairs(\%iterable as lua expr\) do - | vars.it = value - | comprehension[\%key as lua expr\] = \%value as lua expr\ - | end - | return comprehension - |end)(game, setmetatable({}, {__index=vars})) - -# Number ranges -rule: %start up to %stop ..=: - lua expr "compiler.utils.range(vars.start,vars.stop-1)" - -rule: - %start thru %stop - %start through %stop -..=: - lua expr "compiler.utils.range(vars.start,vars.stop)" - -rule: %start down to %stop ..=: - lua expr "compiler.utils.range(vars.start,vars.stop+1,-1)" - -rule: - %start down thru %stop - %start down through %stop -..=: - lua expr "compiler.utils.range(vars.start,vars.stop,-1)" - -rule: %start up to %stop via %step ..=: - lua expr "compiler.utils.range(vars.start,vars.stop-1,vars.step)" - -rule: - %start thru %stop via %step - %start through %stop via %step -..=: - lua expr "compiler.utils.range(vars.start,vars.stop,vars.step)" - -rule: %start down to %stop via %step ..=: - lua expr "compiler.utils.range(vars.start,vars.stop+1,-vars.step)" - -rule: - %start down thru %stop via %step - %start down through %stop via %step -..=: - lua expr "compiler.utils.range(vars.start,vars.stop,-vars.step)" - -# Common utility functions -rule: random number ..=: lua expr "math.random()" -rule: sum of %items ..=: lua expr "compiler.utils.sum(vars.items)" -rule: product of %items ..=: lua expr "compiler.utils.product(vars.items)" -rule: all of %items ..=: lua expr "compiler.utils.all(vars.items)" -rule: any of %items ..=: lua expr "compiler.utils.any(vars.items)" -rule: - avg of %items - average of %items -..=: lua expr "(compiler.utils.sum(vars.items)/#vars.items)" -rule: - min of %items - smallest of %items - lowest of %items -..=: - lua expr "compiler.utils.min(vars.items)" -rule: - max of %items - biggest of %items - largest of %items - highest of %items -..=: - lua expr "compiler.utils.min(vars.items)" - -rule: min of %items with respect to %keys ..=: - lua expr "compiler.utils.min(vars.items, vars.keys)" -rule: max of %items with respect to %keys ..=: - lua expr "compiler.utils.max(vars.items, vars.keys)" - -# List/dict functions -macro: - %list's %index - %index st in %list - %index nd in %list - %index rd in %list - %index th in %list - %index in %list - %list -> %index -..=: - ".."|\%list as lua expr\[\%index as lua expr\] - -macro block: - %list's %index = %value - %index st in %list = %value - %index nd in %list = %value - %index rd in %list = %value - %index th in %list = %value - %index in %list = %value - %list -> %index = %value -..=: - lua block ".." - |if vars.value.value.type ~= "Thunk" then - | compiler:error("Dict assignment operation has the wrong type for the right hand side. " - | .."Expected Thunk, but got: "..vars.value.value.type.."\\nMaybe you used '=' instead of '=:'?") - |end - ".."|do - | local ret - | \lua expr "compiler:tree_to_lua(vars.value.value.value, 'Statement')"\ - | \%list as lua expr\[\%index as lua expr\] = ret - |end - -macro: - append %item to %list - add %item to %list -..=: - ".."|table.insert(\%list as lua expr\, \%item as lua expr\) - -rule: flatten %lists ..=: - %flat =: [] - for %list in %lists: - for %item in %list: - add %item to %flat - %flat - -macro: - %item is in %list - %list contains %item -..=: - ".."|(\%list as lua expr\[\%index as lua expr\] ~= nil) - -macro: - length of %list - size of %list - number of %list -..=: - ".."|#(\%list as lua expr\) - -rule: dict %items ..=: - %dict =: [] - for %pair in %items: - lua block "vars.dict[vars.pair[1]] = vars.pair[2]" - return: %dict - -rule: entries in %dict ..=: - lua block ".." - |local items = {} - |for k,v in pairs(vars.dict) do - | table.insert(items, {key=k,value=v}) - |end - |return items - -# Permission functions -rule: restrict %fn to within %whitelist ..=: - lua block ".." - |local fns = compiler:get_invocations(vars.fn) - |local whitelist = compiler:get_invocations(vars.whitelist) - |local whiteset = {} - |for _,w in ipairs(whitelist) do - | if not compiler.defs[w] then - | compiler:error("Undefined function: "..tostring(w)) - | else whiteset[w] = true - | end - |end - |for _,fn in ipairs(fns) do - | local fn_info = compiler.defs[fn] - | if fn_info == nil then - | compiler:error("Undefined function: "..tostring(fn)) - | elseif not compiler:check_permission(fn) then - | compiler:writeln("You do not have permission to restrict function: "..tostring(fn)) - | else - | compiler.defs[fn].whiteset = whiteset - | end - |end - -rule: allow %whitelist to use %fn ..=: - lua block ".." - |local fns = compiler:get_invocations(vars.fn) - |local whitelist = compiler:get_invocations(vars.whitelist) - |for _,w in ipairs(whitelist) do - | if not compiler.defs[w] then - | compiler:error("Undefined function: "..tostring(w)) - | end - |end - |for _,fn in ipairs(fns) do - | local fn_info = compiler.defs[fn] - | if fn_info == nil then - | compiler:error("Undefined function: "..tostring(fn)) - | elseif fn_info.whiteset == nil then - | compiler:writeln("Function is already allowed by everyone: "..tostring(fn)) - | elseif not compiler:check_permission(fn) then - | compiler:writeln("You do not have permission to grant permissions for function: "..tostring(fn)) - | else - | for _,w in ipairs(whitelist) do - | fn_info.whiteset[w] = true - | end - | end - |end - -rule: forbid %blacklist to use %fn ..=: - lua block ".." - |local fns = compiler:get_invocations(vars.fn) - |local blacklist = compiler:get_invocations(vars.blacklist) - |for _,b in ipairs(blacklist) do - | if not compiler.defs[b] then - | compiler:error("Undefined function: "..tostring(b)) - | end - |end - |for _,fn in ipairs(fns) do - | local fn_info = compiler.defs[fn] - | if fn_info == nil then - | compiler:error("Undefined function: "..tostring(fn)) - | elseif fn_info.whiteset == nil then - | compiler:writeln("Cannot remove items from a whitelist when there is no whitelist on function: "..tostring(fn)) - | elseif not compiler:check_permission(fn) then - | compiler:writeln("You do not have permission to restrict function: "..tostring(fn)) - | else - | for _,b in ipairs(blacklist) do fn_info.whiteset[b] = nil end - | end - |end - -# For unit testing -macro block: test %code yields %expected ..=: - %generated =: lua expr "compiler.utils.repr(compiler:stringify_tree(vars.code.value.value), true)" - %expected =: %expected as lua expr - if (%generated != %expected): - say "Test failed!" - say "Expected:" - say %expected - say "But got:" - say %generated - error! - return: "" - diff --git a/examples/parser_tests.nom b/examples/parser_tests.nom index 81608b7..47cb893 100644 --- a/examples/parser_tests.nom +++ b/examples/parser_tests.nom @@ -1,4 +1,4 @@ -run file "core.nom" +run file "lib/core.nom" test: say "foo" ..yields ".." |Call [say %]: diff --git a/examples/sample_code.nom b/examples/sample_code.nom index 4f4d2dd..4aa8cde 100644 --- a/examples/sample_code.nom +++ b/examples/sample_code.nom @@ -3,7 +3,7 @@ start with a #.. and continue until dedent -run file "core.nom" +run file "lib/core.nom" say "foo" diff --git a/examples/sample_game.nom b/examples/sample_game.nom index e366243..e393747 100644 --- a/examples/sample_game.nom +++ b/examples/sample_game.nom @@ -1,4 +1,4 @@ -run file "core.nom" +run file "lib/core.nom" run file "lib/secrets.nom" run file "lib/plurals.nom" diff --git a/lib/core.nom b/lib/core.nom new file mode 100644 index 0000000..54f728d --- /dev/null +++ b/lib/core.nom @@ -0,0 +1,626 @@ +# Rule for making rules +lua block ".." + |compiler:defmacro("rule %spec = %body", function(compiler, vars, kind) + | if kind == "Expression" then + | compiler:error("Cannot use rule definitions as an expression.") + | end + | local spec = compiler:get_invocations_from_definition(vars.spec.value, vars) + | return ("compiler:def("..compiler.utils.repr(spec,true)..", "..compiler:tree_to_lua(vars.body, vars)..")"), true + |end) + +rule: help %invocation ..=: + lua block ".." + |local fn_info = compiler.defs[vars.invocation] + |if not fn_info then + | compiler:writeln("Function not found: "..compiler.utils.repr(vars.invocation, true)) + |else + | compiler:writeln("rule "..compiler.utils.repr(fn_info.invocations).." ="..(fn_info.src or ":\\n ")) + |end + +# Macros +lua block ".." + |local add_macro = function(compiler, vars, kind) + | local spec = compiler:get_invocations_from_definition(vars.spec.value, vars) + | local fn = compiler:tree_to_value(vars.body, vars) + | compiler:defmacro(spec, fn, vars.body.src) + | return "", true + |end + |compiler:defmacro("macro %spec = %body", add_macro) + | + |local add_macro_block = function(compiler, vars, kind) + | local spec = compiler:get_invocations_from_definition(vars.spec.value, vars) + | local fn = compiler:tree_to_value(vars.body, vars) + | local wrapper = function(compiler, vars, kind) + | if kind == "Expression" then + | compiler:error("Macro: "..spec.." was defined to be a block, but is being used as an expression.") + | end + | return ("do\\n"..fn(compiler, vars, kind).."\\nend"), true + | end + | compiler:defmacro(spec, wrapper, vars.body.src) + | return "", true + |end + |compiler:defmacro("macro block %spec = %body", add_macro_block) + +# Compiler tools +rule: + eval %code + run %code +..=: + lua expr "compiler:run(vars.code)" + +macro: source code %code ..=: + lua block ".." + |if vars.code.value.type ~= "Thunk" then + | compiler:error("'source code %' only takes code blocks, not "..vars.code.value.type) + |end + lua expr "compiler.utils.repr(vars.code.value.value.src, true)" + +rule: run file %filename ..=: + lua block ".." + |local file = io.open(vars.filename) + |return compiler:run(file:read("*a")) + +# Error functions +rule: error! ..=: + lua block ".." + |table.remove(compiler.callstack) + |compiler:error() + +rule: error %msg ..=: + lua block ".." + |table.remove(compiler.callstack) + |compiler:error(vars.msg) + +macro: as lua %block ..=: + lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value.value, vars), true)" + +macro block: show generated lua %block ..=: + ".."|compiler:writeln(\lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value.value, vars), true)"\) + +# Macro helper functions +rule: %tree as value ..=: + lua expr ".." + |compiler:tree_to_value(vars.tree, vars) + +rule: %tree as lua block ..=: + lua block ".." + |return compiler:tree_to_lua(vars.tree, 'Statement'), true + +rule: %tree as lua expr ..=: + lua expr ".." + |compiler:tree_to_lua(vars.tree, 'Expression') + +# Moonscript! +macro block: moonscript block %moonscript_code ..=: + lua block ".." + |local parse, compile = require('moonscript.parse'), require('moonscript.compile') + |local moon_code = compiler:tree_to_value(vars.moonscript_code, vars) + |local tree, err = parse.string(moon_code) + |if not tree then + | compiler:error("Failed to parse moonscript: "..err) + |end + |local lua_code, err, pos = compile.tree(tree) + |if not lua_code then + | compiler:error(compile.format_error(err, pos, moon_code)) + |end + |return "do\\n"..lua_code.."\\nend" + +macro: moonscript %moonscript_code ..=: + lua block ".." + |local parse, compile = require('moonscript.parse'), require('moonscript.compile') + |local moon_code = compiler:tree_to_value(vars.moonscript_code, vars) + |local tree, err = parse.string(moon_code) + |if not tree then + | compiler:error("Failed to parse moonscript: "..err) + |end + |local lua_code, err, pos = compile.tree(tree) + |if not lua_code then + | compiler:error(compile.format_error(err, pos, moon_code)) + |end + |return "(function(compiler, vars)\\n"..lua_code.."\\nend)(compiler, vars)" + +# String functions +rule: join %strs ..=: + lua block ".." + |local str_bits = {} + |for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end + |return table.concat(str_bits) + +rule: join %strs with glue %glue ..=: + lua block ".." + |local str_bits = {} + |for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end + |return table.concat(str_bits, vars.glue) + +rule: + capitalize %str + %str capitalized +..=: + lua expr ".."|vars.str:gsub("%l", string.upper, 1) + +# Variable assignment +#.. + macro block: %var = %value ..=: + lua block ".." + |if vars.var.value.type ~= "Var" then + | compiler:error("Assignment operation has the wrong type for the left hand side. " + | .."Expected Var, but got: "..vars.var.value.type) + |end + ".."|\%var as lua expr\ = \%value as lua expr\ + +lua block ".." + |local function helper(callback) + | return function(compiler, vars, kind) + | if kind == "Expression" then + | compiler:error("Cannot use an assignment operation as an expression value.") + | end + | if vars.var.value.type ~= "Var" then + | compiler:error("Assignment operation has the wrong type for the left hand side. " + | .."Expected Var, but got: "..vars.var.value.type) + | end + | if vars.value.value.type ~= "Thunk" then + | compiler:error("Assignment operation has the wrong type for the right hand side. " + | .."Expected Thunk, but got: "..vars.value.value.type.."\\nMaybe you used '=' instead of '=:'?") + | end + | local ret = "do\\n local ret" + | ret = ret .. "\\n "..compiler:tree_to_lua(vars.value.value.value, "Statement") + | ret = ret .. "\\n "..callback(compiler:tree_to_lua(vars.var, "Expression")) + | return (ret.."\\nend"), true + | end + |end + |compiler:defmacro("%var = %value", helper(function(var) return var.." = ret" end)) + |compiler:defmacro("%var += %value", helper(function(var) return var.." = "..var.." + ret" end)) + |compiler:defmacro("%var -= %value", helper(function(var) return var.." = "..var.." - ret" end)) + |compiler:defmacro("%var *= %value", helper(function(var) return var.." = "..var.." * ret" end)) + |compiler:defmacro("%var /= %value", helper(function(var) return var.." = "..var.." / ret" end)) + |compiler:defmacro("%var ^= %value", helper(function(var) return var.." = "..var.." ^ ret" end)) + |compiler:defmacro("%var and= %value", helper(function(var) return var.." = "..var.." and ret" end)) + |compiler:defmacro("%var or= %value", helper(function(var) return var.." = "..var.." or ret" end)) + |compiler:defmacro("%var concat= %value", helper(function(var) return var.." = "..var.." .. ret" end)) + |compiler:defmacro("%var mod= %value", helper(function(var) return var.." = "..var.." % ret" end)) + +# Operators +macro: + true + yes +..=: "true" +macro: + false + no +..=: "false" +macro: + nil + null +..=: "nil" +macro block: + nop + pass +..=: "" +macro: %a + %b ..=: ".."|(\%a as lua expr\ + \%b as lua expr\) +macro: %a + %b + %c ..=: ".."|(\%a as lua expr\ + \%b as lua expr\ + \%c as lua expr\) +macro: %a + %b + %c + %d ..=: ".."|(\%a as lua expr\ + \%b as lua expr\ + \%c as lua expr\ + \%d as lua expr\) +macro: %a - %b ..=: ".."|(\%a as lua expr\ - \%b as lua expr\) +macro: %a * %b ..=: ".."|(\%a as lua expr\ * \%b as lua expr\) +macro: %a * %b * %c ..=: ".."|(\%a as lua expr\ * \%b as lua expr\ * \%c as lua expr\) +macro: %a * %b * %c * %d ..=: ".."|(\%a as lua expr\ * \%b as lua expr\ * \%c as lua expr\ * \%d as lua expr\) +macro: %a / %b ..=: ".."|(\%a as lua expr\ / \%b as lua expr\) +macro: %a === %b ..=: ".."|(\%a as lua expr\ == \%b as lua expr\) +macro: %a !== %b ..=: ".."|(\%a as lua expr\ ~= \%b as lua expr\) +macro: %a < %b ..=: ".."|(\%a as lua expr\ < \%b as lua expr\) +macro: %a < %b < %c ..=: ".."|((\%a as lua expr\ < \%b as lua expr\) and (\%b as lua expr\ < \%c as lua expr\)) +macro: %a <= %b < %c ..=: ".."|((\%a as lua expr\ <= \%b as lua expr\) and (\%b as lua expr\ < \%c as lua expr\)) +macro: %a <= %b <= %c ..=: ".."|((\%a as lua expr\ <= \%b as lua expr\) and (\%b as lua expr\ <= \%c as lua expr\)) +macro: %a < %b <= %c ..=: ".."|((\%a as lua expr\ < \%b as lua expr\) and (\%b as lua expr\ <= \%c as lua expr\)) +macro: %a > %b > %c ..=: ".."|((\%a as lua expr\ > \%b as lua expr\) and (\%b as lua expr\ > \%c as lua expr\)) +macro: %a >= %b > %c ..=: ".."|((\%a as lua expr\ >= \%b as lua expr\) and (\%b as lua expr\ > \%c as lua expr\)) +macro: %a >= %b >= %c ..=: ".."|((\%a as lua expr\ >= \%b as lua expr\) and (\%b as lua expr\ >= \%c as lua expr\)) +macro: %a > %b >= %c ..=: ".."|((\%a as lua expr\ > \%b as lua expr\) and (\%b as lua expr\ >= \%c as lua expr\)) +macro: %a <= %b ..=: ".."|(\%a as lua expr\ <= \%b as lua expr\) +macro: %a > %b ..=: ".."|(\%a as lua expr\ > \%b as lua expr\) +macro: %a >= %b ..=: ".."|(\%a as lua expr\ >= \%b as lua expr\) +macro: %a ^ %b ..=: ".."|(\%a as lua expr\ ^ \%b as lua expr\) +macro: %a and %b ..=: ".."|(\%a as lua expr\ and \%b as lua expr\) +macro: %a and %b and %c ..=: + ".."|(\%a as lua expr\ and \%b as lua expr\ and \%c as lua expr\) +macro: %a and %b and %c and %d ..=: + ".."|(\%a as lua expr\ and \%b as lua expr\ and \%c as lua expr\ and \%d as lua expr\) +macro: %a or %b ..=: ".."|(\%a as lua expr\ or \%b as lua expr\) +macro: %a or %b or %c ..=: + ".."|(\%a as lua expr\ or \%b as lua expr\ or \%c as lua expr\) +macro: %a or %b or %c or %d ..=: + ".."|(\%a as lua expr\ or \%b as lua expr\ or \%c as lua expr\ or \%d as lua expr\) +macro: %a mod %b ..=: ".."|(\%a as lua expr\ mod \%b as lua expr\) +macro: - %a ..=: ".."|-(\%a as lua expr\) +macro: not %a ..=: ".."|not (\%a as lua expr\) + +rule: %a == %b ..=: + lua expr "((vars.a == vars.b) or compiler.utils.equivalent(vars.a, vars.b))" +rule: %a != %b ..=: + lua expr "((vars.a ~= vars.b) or not compiler.utils.equivalent(vars.a, vars.b))" + +macro: repr %obj ..=: + ".."|compiler.utils.repr(\%obj as lua expr\, true) + +macro: say %str ..=: + ".."|compiler:writeln(compiler.utils.repr(\%str as lua expr\)) + +# Control flow +rule: do %action ..=: + lua expr "vars.action(compiler, setmetatable({}, {__index=vars}))" + +macro block: return %return_value ..=: + lua block ".." + |if vars.return_value.value.type ~= "Thunk" then + | compiler:error("Assignment operation has the wrong type for the right hand side. " + | .."Expected Thunk, but got: "..vars.return_value.value.type.."\\nMaybe you used '=' instead of '=:'?") + |end + ".."|do + | local ret + | \lua expr "compiler:tree_to_lua(vars.return_value.value.value, 'Statement')"\ + | return ret + |end + +macro block: return ..=: + "return nil" + +# Conditionals +macro block: if %condition %if_body ..=: + ".."|if \%condition as lua expr\ then + | \(lua expr "vars.if_body.value.value") as lua block\ + |end + +macro block: if %condition %if_body else %else_body ..=: + ".."|if \%condition as lua expr\ then + | \(lua expr "vars.if_body.value.value") as lua block\ + |else + | \(lua expr "vars.else_body.value.value") as lua block\ + |end + +# Ternary operator +macro: %if_expr if %condition else %else_expr ..=: + ".."|(function(compiler, vars) + | if \%condition as lua expr\ then + | return \%if_expr as lua expr\ + | else + | return \%else_expr as lua expr\ + | end + |end)(compiler, vars) + +# Loop control flow +macro block: break ..=: "break" +macro block: continue ..=: "continue" +# TODO: add labeled break/continue? + +# GOTOs +lua block ".." + |local function lua_label(label) + | if label.type ~= "Var" then + | compiler:error("Goto label has the wrong type for the label. Expected Var, but got: "..label.type) + | end + | local bits = "abcdefghijklmnop" + | local lua_identifier = "label_" + | for i=1,#label.value do + | local byte = string.byte(label.value, i) + | local low = byte % 16 + | local high = (byte - low) / 16 + | lua_identifier = lua_identifier .. bits:sub(low+1,low+1) .. bits:sub(high+1,high+1) + | end + | return lua_identifier + |end + | + |compiler:defmacro("-> %label", function(compiler, vars, kind) + | return "::"..lua_label(vars.label.value).."::", true + |end) + |compiler:defmacro("go to %label", function(compiler, vars, kind) + | return "goto "..lua_label(vars.label.value), true + |end) + +# While loops +macro block: repeat %body ..=: + ".."|while true do + | \(lua expr "vars.body.value.value") as lua block\ + |end +macro block: repeat while %condition %body ..=: + ".."|while \%condition as lua expr\ do + | \(lua expr "vars.body.value.value") as lua block\ + |end +macro block: repeat until %condition %body ..=: + ".."|while not (\%condition as lua expr\) do + | \(lua expr "vars.body.value.value") as lua block\ + |end + +# For loops +macro block: for %var in %iterable %body ..=: + %var-type =: lua expr "vars.var.value.type" + if (%var-type != "Var"): + error ".." + |For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\ + %var-code =: %var as lua expr + ".."|local old_loopval = \%var-code\ + |for i,value in ipairs(\%iterable as lua expr\) do + | \%var-code\ = value + | \(lua expr "vars.body.value.value") as lua block\ + |end + |\%var-code\ = old_loopval + +macro block: for all %iterable %body ..=: + ".."|local old_loopval = vars.it + |for i,value in ipairs(\%iterable as lua expr\) do + | vars.it = value + | \(lua expr "vars.body.value.value") as lua block\ + |end + |vars.it = old_loopval + +# List Comprehension +# TODO: maybe make this lazy, or a lazy version? +macro: %expression for %var in %iterable ..=: + %var-type =: lua expr "vars.var.value.type" + if (%var-type != "Var"): + error ".." + |List comprehension has the wrong type for the loop variable. Expected Var, but got: \%var-type\ + %var-code =: %var as lua expr + ".."|(function(game, vars) + | local comprehension = {} + | for i,value in ipairs(\%iterable as lua expr\) do + | \%var-code\ = value + | comprehension[i] = \%expression as lua expr\ + | end + | return comprehension + |end)(game, setmetatable({}, {__index=vars})) + +macro: %expression for all %iterable ..=: + ".."|(function(game, vars) + | local comprehension = {} + | for i,value in ipairs(\%iterable as lua expr\) do + | vars.it = value + | comprehension[i] = \%expression as lua expr\ + | end + | return comprehension + |end)(game, setmetatable({}, {__index=vars})) + +# Dict comprehension +macro: %key -> %value for %var in %iterable ..=: + %var-type =: lua expr "vars.var.value.type" + if (%var-type != "Var"): + error ".." + |Dict comprehension has the wrong type for the loop variable. Expected Var, but got: \%var-type\ + %var-code =: %var as lua expr + ".."|(function(game, vars) + | local comprehension = {} + | for i,value in ipairs(\%iterable as lua expr\) do + | \%var-code\ = value + | comprehension[\%key as lua expr\] = \%value as lua expr\ + | end + | return comprehension + |end)(game, setmetatable({}, {__index=vars})) + +macro: %key -> %value for all %iterable ..=: + ".."|(function(game, vars) + | local comprehension = {} + | for i,value in ipairs(\%iterable as lua expr\) do + | vars.it = value + | comprehension[\%key as lua expr\] = \%value as lua expr\ + | end + | return comprehension + |end)(game, setmetatable({}, {__index=vars})) + +# Number ranges +rule: %start up to %stop ..=: + lua expr "compiler.utils.range(vars.start,vars.stop-1)" + +rule: + %start thru %stop + %start through %stop +..=: + lua expr "compiler.utils.range(vars.start,vars.stop)" + +rule: %start down to %stop ..=: + lua expr "compiler.utils.range(vars.start,vars.stop+1,-1)" + +rule: + %start down thru %stop + %start down through %stop +..=: + lua expr "compiler.utils.range(vars.start,vars.stop,-1)" + +rule: %start up to %stop via %step ..=: + lua expr "compiler.utils.range(vars.start,vars.stop-1,vars.step)" + +rule: + %start thru %stop via %step + %start through %stop via %step +..=: + lua expr "compiler.utils.range(vars.start,vars.stop,vars.step)" + +rule: %start down to %stop via %step ..=: + lua expr "compiler.utils.range(vars.start,vars.stop+1,-vars.step)" + +rule: + %start down thru %stop via %step + %start down through %stop via %step +..=: + lua expr "compiler.utils.range(vars.start,vars.stop,-vars.step)" + +# Common utility functions +rule: random number ..=: lua expr "math.random()" +rule: sum of %items ..=: lua expr "compiler.utils.sum(vars.items)" +rule: product of %items ..=: lua expr "compiler.utils.product(vars.items)" +rule: all of %items ..=: lua expr "compiler.utils.all(vars.items)" +rule: any of %items ..=: lua expr "compiler.utils.any(vars.items)" +rule: + avg of %items + average of %items +..=: lua expr "(compiler.utils.sum(vars.items)/#vars.items)" +rule: + min of %items + smallest of %items + lowest of %items +..=: + lua expr "compiler.utils.min(vars.items)" +rule: + max of %items + biggest of %items + largest of %items + highest of %items +..=: + lua expr "compiler.utils.min(vars.items)" + +rule: min of %items with respect to %keys ..=: + lua expr "compiler.utils.min(vars.items, vars.keys)" +rule: max of %items with respect to %keys ..=: + lua expr "compiler.utils.max(vars.items, vars.keys)" + +# List/dict functions +macro: + %list's %index + %index st in %list + %index nd in %list + %index rd in %list + %index th in %list + %index in %list + %list -> %index +..=: + ".."|\%list as lua expr\[\%index as lua expr\] + +macro block: + %list's %index = %value + %index st in %list = %value + %index nd in %list = %value + %index rd in %list = %value + %index th in %list = %value + %index in %list = %value + %list -> %index = %value +..=: + lua block ".." + |if vars.value.value.type ~= "Thunk" then + | compiler:error("Dict assignment operation has the wrong type for the right hand side. " + | .."Expected Thunk, but got: "..vars.value.value.type.."\\nMaybe you used '=' instead of '=:'?") + |end + ".."|do + | local ret + | \lua expr "compiler:tree_to_lua(vars.value.value.value, 'Statement')"\ + | \%list as lua expr\[\%index as lua expr\] = ret + |end + +macro: + append %item to %list + add %item to %list +..=: + ".."|table.insert(\%list as lua expr\, \%item as lua expr\) + +rule: flatten %lists ..=: + %flat =: [] + for %list in %lists: + for %item in %list: + add %item to %flat + %flat + +macro: + %item is in %list + %list contains %item +..=: + ".."|(\%list as lua expr\[\%index as lua expr\] ~= nil) + +macro: + length of %list + size of %list + number of %list +..=: + ".."|#(\%list as lua expr\) + +rule: dict %items ..=: + %dict =: [] + for %pair in %items: + lua block "vars.dict[vars.pair[1]] = vars.pair[2]" + return: %dict + +rule: entries in %dict ..=: + lua block ".." + |local items = {} + |for k,v in pairs(vars.dict) do + | table.insert(items, {key=k,value=v}) + |end + |return items + +# Permission functions +rule: restrict %fn to within %whitelist ..=: + lua block ".." + |local fns = compiler:get_invocations(vars.fn) + |local whitelist = compiler:get_invocations(vars.whitelist) + |local whiteset = {} + |for _,w in ipairs(whitelist) do + | if not compiler.defs[w] then + | compiler:error("Undefined function: "..tostring(w)) + | else whiteset[w] = true + | end + |end + |for _,fn in ipairs(fns) do + | local fn_info = compiler.defs[fn] + | if fn_info == nil then + | compiler:error("Undefined function: "..tostring(fn)) + | elseif not compiler:check_permission(fn) then + | compiler:writeln("You do not have permission to restrict function: "..tostring(fn)) + | else + | compiler.defs[fn].whiteset = whiteset + | end + |end + +rule: allow %whitelist to use %fn ..=: + lua block ".." + |local fns = compiler:get_invocations(vars.fn) + |local whitelist = compiler:get_invocations(vars.whitelist) + |for _,w in ipairs(whitelist) do + | if not compiler.defs[w] then + | compiler:error("Undefined function: "..tostring(w)) + | end + |end + |for _,fn in ipairs(fns) do + | local fn_info = compiler.defs[fn] + | if fn_info == nil then + | compiler:error("Undefined function: "..tostring(fn)) + | elseif fn_info.whiteset == nil then + | compiler:writeln("Function is already allowed by everyone: "..tostring(fn)) + | elseif not compiler:check_permission(fn) then + | compiler:writeln("You do not have permission to grant permissions for function: "..tostring(fn)) + | else + | for _,w in ipairs(whitelist) do + | fn_info.whiteset[w] = true + | end + | end + |end + +rule: forbid %blacklist to use %fn ..=: + lua block ".." + |local fns = compiler:get_invocations(vars.fn) + |local blacklist = compiler:get_invocations(vars.blacklist) + |for _,b in ipairs(blacklist) do + | if not compiler.defs[b] then + | compiler:error("Undefined function: "..tostring(b)) + | end + |end + |for _,fn in ipairs(fns) do + | local fn_info = compiler.defs[fn] + | if fn_info == nil then + | compiler:error("Undefined function: "..tostring(fn)) + | elseif fn_info.whiteset == nil then + | compiler:writeln("Cannot remove items from a whitelist when there is no whitelist on function: "..tostring(fn)) + | elseif not compiler:check_permission(fn) then + | compiler:writeln("You do not have permission to restrict function: "..tostring(fn)) + | else + | for _,b in ipairs(blacklist) do fn_info.whiteset[b] = nil end + | end + |end + +# For unit testing +macro block: test %code yields %expected ..=: + %generated =: lua expr "compiler.utils.repr(compiler:stringify_tree(vars.code.value.value), true)" + %expected =: %expected as lua expr + if (%generated != %expected): + say "Test failed!" + say "Expected:" + say %expected + say "But got:" + say %generated + error! + return: "" + diff --git a/nomsu.lua b/nomsu.lua index 6a4793d..3267fc9 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -576,7 +576,7 @@ do elseif "Var" == _exp_0 then add("vars[" .. tostring(utils.repr(tree.value, true)) .. "]") else - error("Unknown/unimplemented thingy: " .. tostring(tree.type)) + self:error("Unknown/unimplemented thingy: " .. tostring(tree.type)) end buffer = table.concat(buffer, "\n") return buffer, return_value -- cgit v1.2.3