diff --git a/lib/collections.nom b/lib/collections.nom index 9939c9b..697602e 100644 --- a/lib/collections.nom +++ b/lib/collections.nom @@ -105,14 +105,13 @@ compile [%expression for %item in %iterable] to: assert ((%item's "type") == "Var") ".." List comprehension has the wrong type for the loop variable. Expected Var, but got: \(%item's "type") return ".." - (function(nomsu, vars); + (function(nomsu); local comprehension = {}; - for i,item in ipairs(\(%iterable as lua)) do; - \(%item as lua) = item; + for i,\(%item as lua) in ipairs(\(%iterable as lua)) do; comprehension[i] = \(%expression as lua); end; return comprehension; - end)(nomsu, setmetatable({}, {__index=vars})) + end)(nomsu) parse [%expression for all %iterable] as: %expression for % in %iterable compile [%expression for %key = %value in %iterable] to: @@ -121,14 +120,13 @@ compile [%expression for %key = %value in %iterable] to: assert ((%value's "type") == "Var") ".." List comprehension has the wrong type for the value loop variable. Expected Var, but got: \(%value's "type") return ".." - (function(nomsu, vars); + (function(nomsu); local comprehension = {}; - for key,value in pairs(\(%iterable as lua)) do; - \(%key as lua), \(%value as lua) = key, value; + for \(%key as lua), \(%value as lua) in pairs(\(%iterable as lua)) do; comprehension[i] = \(%expression as lua); end; return comprehension; - end)(nomsu, setmetatable({}, {__index=vars})) + end)(nomsu) rule [%items sorted] =: %copy = (% for all %items) @@ -166,14 +164,13 @@ compile [%key = %value for %item in %iterable] to: assert ((%item's "type") == "Var") ".." Dict comprehension has the wrong type for the loop variable. Expected Var, but got: \(%item's "type") return ".." - (function(nomsu, vars); + (function(nomsu); local comprehension = {}; - for i,value in ipairs(\(%iterable as lua)) do; - \(%item as lua) = value; + for i,\(%item as lua) in ipairs(\(%iterable as lua)) do; comprehension[\(%key as lua)] = \(%value as lua); end; return comprehension; - end)(nomsu, setmetatable({}, {__index=vars})) + end)(nomsu) parse [%key = %value for all %iterable] as: %key = %value for % in %iterable compile [%key = %value for %src_key = %src_value in %iterable] to: @@ -182,12 +179,11 @@ compile [%key = %value for %src_key = %src_value in %iterable] to: assert ((%src_value's "type") == "Var") ".." Dict comprehension has the wrong type for the value loop variable. Expected Var, but got: \(%src_value's "type") return ".." - (function(nomsu, vars); + (function(nomsu); local comprehension = {}; - for key,value in pairs(\(%iterable as lua)) do; - \(%src_key as lua), \(%src_value as lua) = key, value; + for \(%src_key as lua), \(%src_value as lua) in pairs(\(%iterable as lua)) do; comprehension[\(%key as lua)] = \(%value as lua); end; return comprehension; - end)(nomsu, setmetatable({}, {__index=vars})) + end)(nomsu) diff --git a/lib/control_flow.nom b/lib/control_flow.nom index 922338f..254d81d 100644 --- a/lib/control_flow.nom +++ b/lib/control_flow.nom @@ -91,6 +91,7 @@ immediately: for %var from %start to %stop by %step %body for %var from %start to %stop via %step %body ..to code: + lua> "local \%continue_labels, \%code, \%stop_labels" %continue_labels = "" if (tree %body has function call \(do next for-loop)): %continue_labels join= "\n::continue_for::;" @@ -126,6 +127,7 @@ immediately: immediately: compile [for %var in %iterable %body] to code: + lua> "local \%continue_labels, \%stop_labels, \%code, \%stop_labels" %continue_labels = "" if (tree %body has function call \(do next for-loop)): %continue_labels join= "\n::continue_for::;" @@ -143,12 +145,12 @@ immediately: %stop_labels join= "\n::stop_for::;" if (tree %body has function call (tree \(stop %) with {""=%var})): %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;" - return (..) - ".." + if %stop_labels != "": + %code = ".." do --for-loop label scope \%code\%stop_labels end --for-loop label scope - ..if %stop_labels != "" else %code + return %code parse [for all %iterable %body] as: for % in %iterable %body # Dict iteration (lua's "pairs()") @@ -186,10 +188,12 @@ immediately: # Switch statement/multi-branch if immediately: compile [when %body] to code: + lua> "local \%result, \%fallthroughs, \%first" %result = "" %fallthroughs = [] %first = (yes) for %func_call in (%body's "value"): + lua> "local \%tokens, \%star, \%condition, \%action" assert ((%func_call's "type") == "FunctionCall") ".." Invalid format for 'when' statement. Only '*' blocks are allowed. %tokens = (%func_call's "value") @@ -286,10 +290,10 @@ immediately: ..to code: ".." do local fell_through = false; - local ok, ret1, ret2 = pcall(function(nomsu, vars) + local ok, ret1, ret2 = pcall(function(nomsu) \(%action as lua statements) fell_through = true; - end, nomsu, vars); + end, nomsu); if ok then \(%success as lua statements) end @@ -313,13 +317,13 @@ immediately: compile [do %action then always %final_action] to code: ".." do local fell_through = false; - local ok, ret1, ret2 = pcall(function(nomsu, vars) + local ok, ret1, ret2 = pcall(function(nomsu) \(%action as lua statements) fell_through = true; - end, nomsu, vars); - local ok2, _ = pcall(function(nomsu, vars) + end, nomsu); + local ok2, _ = pcall(function(nomsu) \(%final_action as lua statements) - end, nomsu, vars); + end, nomsu); if not ok then nomsu:error(ret1); end if not ok2 then nomsu:error(ret2); end if not fell_through then diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom index 1477175..4b3c615 100644 --- a/lib/metaprogramming.nom +++ b/lib/metaprogramming.nom @@ -5,53 +5,59 @@ # Rule to make macros: immediately: lua> ".." - nomsu:defmacro("compile %macro_def to %body", function(nomsu, vars) + nomsu.parse_spec = function(nomsu, spec) + local signature = {}; + for i, alias in ipairs(spec.value) do + signature[i] = alias.src; + end + local _, arg_names, _ = nomsu:get_stub(spec.value[1]); + local args = {"nomsu"}; + for i, a in ipairs(arg_names) do args[i+1] = "_"..nomsu:var_to_lua_identifier(a); end + signature, args = nomsu:repr(signature), table.concat(args, ", "); + return signature, args; + end + +immediately: + lua> ".." + nomsu:defmacro("compile %macro_def to %body", function(nomsu, \%macro_def, \%body) nomsu:assert(\%macro_def.type == "List", "Invalid type for compile definition signature. Expected List, but got: "..tostring(\%macro_def.type)); nomsu:assert(\%body.type == "Block", "Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type)); - local signature = {}; - for i, alias in ipairs(\%macro_def.value) do - signature[i] = alias.src; - end + local signature, args = nomsu:parse_spec(\%macro_def); local body_lua = nomsu:tree_to_lua(\%body); body_lua = body_lua.statements or ("return "..body_lua.expr..";"); local lua = ([[ do - local function macro(nomsu, vars) + local function macro(%s) %s end local function macro_wrapper(...) return {expr=macro(...)}; end nomsu:defmacro(%s, macro_wrapper, %s); - end]]):format(body_lua, nomsu:repr(signature), nomsu:repr(("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src))); + end]]):format(args, body_lua, signature, nomsu:repr(("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src))); return {statements=lua}; end, \(__src__ 1)); lua> ".." - nomsu:defmacro("compile %macro_def to code %body", function(nomsu, vars) + nomsu:defmacro("compile %macro_def to code %body", function(nomsu, \%macro_def, \%body) nomsu:assert(\%macro_def.type == "List", "Invalid type for compile definition signature. Expected List, but got: "..tostring(\%macro_def.type)); nomsu:assert(\%body.type == "Block", "Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type)); - local signature = {}; - for i, alias in ipairs(\%macro_def.value) do - signature[i] = alias.src; - end + local signature, args = nomsu:parse_spec(\%macro_def); local body_lua = nomsu:tree_to_lua(\%body); body_lua = body_lua.statements or ("return "..body_lua.expr..";"); local lua = ([[ do - local function macro(nomsu, vars) + local function macro(%s) %s end local function macro_wrapper(...) return {statements=macro(...)}; end nomsu:defmacro(%s, macro_wrapper, %s); - end]]):format(body_lua, nomsu:repr(signature), nomsu:repr(("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src))); + end]]):format(args, body_lua, signature, nomsu:repr(("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src))); return {statements=lua}; end, \(__src__ 1)); -compile [rand] to: "math.random()" - # Rule to make rules: immediately: compile [rule %signature = %body] to code: @@ -60,43 +66,41 @@ immediately: "Invalid type for rule definition signature. Expected List, but got: "..tostring(\%signature.type)); nomsu:assert(\%body.type == "Block", "Invalid type for rule definition body. Expected Block, but got: "..tostring(\%body.type)); - local signature = {}; - for i, alias in ipairs(\%signature.value) do - signature[i] = alias.src; - end + local signature, args = nomsu:parse_spec(\%signature); local body_lua = nomsu:tree_to_lua(\%body); body_lua = body_lua.statements or ("return "..body_lua.expr..";"); local src = nomsu:dedent(nomsu:source_code(0)); local def_lua = ([[ - nomsu:def(%s, function(nomsu, vars) + nomsu:def(%s, function(%s) %s - end, %s);]]):format(nomsu:repr(signature), body_lua, nomsu:repr(src)); + end, %s);]]):format(signature, args, body_lua, nomsu:repr(src)); return def_lua; # Rule to make nomsu macros: immediately: lua> ".." - nomsu:defmacro("parse %shorthand as %longhand", (function(nomsu, vars) + nomsu:defmacro("parse %shorthand as %longhand", (function(nomsu, \%shorthand, \%longhand) nomsu:assert(\%shorthand.type == "List", "Invalid type for parse definition signature. Expected List, but got: "..tostring(\%shorthand.type)); nomsu:assert(\%longhand.type == "Block", "Invalid type for parse definition body. Expected Block, but got: "..tostring(\%longhand.type)); - local signature = {}; - for i, alias in ipairs(\%shorthand.value) do - signature[i] = alias.src; - end + local signature, args = nomsu:parse_spec(\%shorthand); local template = {}; for i, line in ipairs(\%longhand.value) do template[i] = nomsu:dedent(line.src); end - signature, template = nomsu:repr(signature), nomsu:repr(table.concat(template, "\\n")); - return {expr=([[ - nomsu:defmacro(%s, (function(nomsu, vars) + template = nomsu:repr(table.concat(template, "\\n")); + local _, arg_names, _ = nomsu:get_stub(\%shorthand.value[1]); + local replacements = {}; + for i, a in ipairs(arg_names) do replacements[i] = "["..nomsu:repr(a).."]=_"..nomsu:var_to_lua_identifier(a); end + replacements = "{"..table.concat(replacements, ", ").."}"; + local lua_code = ([[ + nomsu:defmacro(%s, (function(%s) local template = nomsu:parse(%s, %s); - if #template.value == 1 then template = template.value[1]; end - local replacement = nomsu:replaced_vars(template, vars); + local replacement = nomsu:replaced_vars(template, %s); return nomsu:tree_to_lua(replacement); - end), %s)]]):format(signature, template, nomsu:repr(\%shorthand.line_no), nomsu:repr(nomsu:source_code(0)))}; + end), %s)]]):format(signature, args, template, nomsu:repr(\%shorthand:get_line_no()), replacements, nomsu:repr(nomsu:source_code(0))); + return {statements=lua_code}; end), \(__src__ 1)); rule [remove rule %stub] =: @@ -114,7 +118,7 @@ immediately: local lua = nomsu:tree_to_lua(\%tree); return lua.statements or (lua.expr..";"); rule [%tree as value] =: - =lua "nomsu:tree_to_value(\%tree, vars)" + =lua "nomsu:tree_to_value(\%tree)" compile [repr %obj] to: "nomsu:repr(\(%obj as lua))" compile [indented %obj] to: @@ -124,12 +128,14 @@ immediately: compile [type %obj, type of %obj] to: "type(\(%obj as lua))" -parse [lua do> %block] as: - lua> "do" - lua> %block - lua> "end" +immediately: + parse [lua do> %block] as: + lua> "do" + lua> %block + lua> "end" 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: ".." diff --git a/lib/operators.nom b/lib/operators.nom index 2f08089..1670191 100644 --- a/lib/operators.nom +++ b/lib/operators.nom @@ -20,13 +20,13 @@ compile [..] %when_false_expr unless %condition else %when_true_expr %when_false_expr unless %condition then %when_true_expr ..to: ".." - (function(nomsu, vars) - if \(%condition as lua) then + (function(nomsu, condition) + if condition then return \(%when_true_expr as lua); else return \(%when_false_expr as lua); end - end)(nomsu, vars) + end)(nomsu, \(%condition as lua)) parse [..] %true if %x == %y else %false, %true if %x == %y otherwise %false %false unless %x == %y else %true, %false unless %x == %y otherwise %true diff --git a/lib/scopes.nom b/lib/scopes.nom index 910b30e..3f6eff1 100644 --- a/lib/scopes.nom +++ b/lib/scopes.nom @@ -13,6 +13,8 @@ compile [str %] to: "tostring(\(% as lua))" compile [scope] to: "nomsu.defs" compile [parent scope] to: "getmetatable(nomsu.defs).__index" +# TODO: fix this file +return compile [using %scoped do %actions] to code: ".." do local old_scope, old_vars = nomsu.defs, vars; diff --git a/lib/utils.nom b/lib/utils.nom index ddaac8d..ce6dc9d 100644 --- a/lib/utils.nom +++ b/lib/utils.nom @@ -7,7 +7,7 @@ rule [error %msg] =: nomsu "error"[%msg] compile [assert %condition %msg] to code: ".." if not (\(%condition as lua)) then - nomsu:error(\(%msg as lua)) + nomsu:error(\(%msg as lua)); end parse [assert %condition] as: assert %condition (nil) @@ -80,19 +80,17 @@ compile [min of %items, smallest of %items, lowest of %items] to: compile [max of %items, biggest of %items, largest of %items, highest of %items] to: "nomsu.utils.max(\(%items as lua))" compile [min of %items by %value_expr] to: ".." - nomsu.utils.min(\(%items as lua), function(item) - local vars = setmetatable({['']=item}, {__index=vars}) + nomsu.utils.min(\(%items as lua), function(\(\% as lua)) return \(%value_expr as lua) end) compile [max of %items by %value_expr] to: ".." - nomsu.utils.max(\(%items as lua), function(item) - local vars = setmetatable({['']=item}, {__index=vars}) + nomsu.utils.max(\(%items as lua), function(\(\% as lua)) return \(%value_expr as lua) end) compile [sort %items] to: "table.sort(\(%items as lua))" -rule [sort %items by %key] =: =lua ".." - nomsu.utils.sort(\%items, function(x) - return (\%key)(nomsu, {['']=x}); +compile [sort %items by %key_expr] to: ".." + nomsu.utils.sort(\(%items as lua), function(\(\% as lua)) + return \(%key_expr as lua); end) # String utilities diff --git a/lib/utils2.nom b/lib/utils2.nom index 2fa29f7..8077203 100644 --- a/lib/utils2.nom +++ b/lib/utils2.nom @@ -11,7 +11,7 @@ compile [say %str] to: compile [do %action] to code: (%action as lua statements) if ((%action's "type") == "Thunk") - ..else "(\(%action as lua))(nomsu, vars);" + ..else "(\(%action as lua))(nomsu);" # With statement compile [with %assignments %action] to code: @@ -33,10 +33,10 @@ compile [with %assignments %action] to code: do \%setup local fell_through = false; - local ok, ret1, ret2 = pcall(function(nomsu, vars) + local ok, ret1, ret2 = pcall(function(nomsu) \(%action as lua statements); fell_through = true; - end, nomsu, vars); + end, nomsu); \(join ("\((%->"var") as lua) = old_value\(%->"i");" for all %data) with glue "\n ") if not ok then nomsu:error(ret1); end if not fell_through then diff --git a/nomsu.lua b/nomsu.lua index c103777..159b25f 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -126,15 +126,13 @@ do end return _accum_0 end)(), " ") - local line_no = 1 - while (ctx.line_starts[line_no + 1] or math.huge) < start do - line_no = line_no + 1 - end local src = ctx.source_code:sub(start, stop - 1) return { + start = start, + stop = stop, type = "FunctionCall", src = src, - line_no = tostring(ctx.filename) .. ":" .. tostring(line_no), + get_line_no = ctx.get_line_no, value = value, stub = stub } @@ -149,6 +147,7 @@ do stop = stop, value = value, src = ctx.source_code:sub(start, stop - 1), + get_line_no = ctx.get_line_no, type = key } end @@ -168,15 +167,26 @@ do local nomsu = peg_tidier:match(io.open("nomsu.peg"):read("*a")) nomsu = re.compile(nomsu, defs) parse = function(source_code, filename) - local old_ctx = ctx - ctx = { + local _ctx = { source_code = source_code, filename = filename, indent_stack = { 0 } } - ctx.line_starts = re.compile("lines <- {| line ('\n' line)* |} line <- {} [^\n]*"):match(source_code) + _ctx.line_starts = re.compile("lines <- {| line ('\n' line)* |} line <- {} [^\n]*"):match(source_code) + _ctx.get_line_no = function(self) + if not (self._line_no) then + local line_no = 1 + while (_ctx.line_starts[line_no + 1] or math.huge) < self.start do + line_no = line_no + 1 + end + self._line_no = tostring(_ctx.filename) .. ":" .. tostring(line_no) + end + return self._line_no + end + local old_ctx = ctx + ctx = _ctx local tree = nomsu:match(source_code) ctx = old_ctx return tree @@ -206,8 +216,6 @@ do signature = self:get_stubs(signature) end self:assert(type(fn) == 'function', "Bad fn: " .. tostring(repr(fn))) - local canonical_args = nil - local canonical_escaped_args = nil local aliases = { } self.__class.def_number = self.__class.def_number + 1 local def = { @@ -221,6 +229,7 @@ do local where_defs_go = (getmetatable(self.defs) or { }).__newindex or self.defs for sig_i = 1, #signature do local stub, arg_names, escaped_args = unpack(signature[sig_i]) + local arg_positions = { } self:assert(stub, "NO STUB FOUND: " .. tostring(repr(signature))) if self.debug then self:writeln(tostring(colored.bright("DEFINING RULE:")) .. " " .. tostring(colored.underscore(colored.magenta(repr(stub)))) .. " " .. tostring(colored.bright("WITH ARGS")) .. " " .. tostring(colored.dim(repr(arg_names)))) @@ -232,22 +241,34 @@ do end end end - if canonical_args then - self:assert(equivalent(set(arg_names), canonical_args), "Mismatched args") - else - canonical_args = set(arg_names) - end - if canonical_escaped_args then - self:assert(equivalent(escaped_args, canonical_escaped_args), "Mismatched escaped args") - else - canonical_escaped_args = escaped_args + if sig_i == 1 then + do + local _accum_0 = { } + local _len_0 = 1 + for i = 1, #arg_names do + _accum_0[_len_0] = i + _len_0 = _len_0 + 1 + end + arg_positions = _accum_0 + end + def.args = arg_names def.escaped_args = escaped_args + else + self:assert(equivalent(set(def.args), set(arg_names)), "Mismatched args") + self:assert(equivalent(def.escaped_args, escaped_args), "Mismatched escaped args") + for j, a in ipairs(arg_names) do + for i, c_a in ipairs(def.args) do + if a == c_a then + arg_positions[j] = i + end + end + end end insert(def.aliases, stub) local stub_def = setmetatable({ stub = stub, arg_names = arg_names, - escaped_args = escaped_args + arg_positions = arg_positions }, { __index = def }) @@ -361,27 +382,30 @@ do if not (def.is_macro) then self:assert_permission(stub) end - local fn, arg_names - fn, arg_names = def.fn, def.arg_names + local fn, arg_positions + fn, arg_positions = def.fn, def.arg_positions local args do - local _tbl_0 = { } - for i, name in ipairs(arg_names) do - _tbl_0[name] = select(i, ...) + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #arg_positions do + local p = arg_positions[_index_0] + _accum_0[_len_0] = select(p, ...) + _len_0 = _len_0 + 1 end - args = _tbl_0 + args = _accum_0 end if self.debug then self:write(tostring(colored.bright("CALLING")) .. " " .. tostring(colored.magenta(colored.underscore(stub))) .. " ") self:writeln(tostring(colored.bright("WITH ARGS:"))) - for name, value in pairs(args) do - self:writeln(" " .. tostring(colored.bright("* " .. tostring(name))) .. " = " .. tostring(colored.dim(repr(value)))) + for i, value in ipairs(args) do + self:writeln(" " .. tostring(colored.bright("* " .. tostring(def.args[i]))) .. " = " .. tostring(colored.dim(repr(value)))) end end local old_defs old_defs, self.defs = self.defs, def.defs local rets = { - fn(self, args) + fn(self, unpack(args)) } self.defs = old_defs remove(self.callstack) @@ -407,7 +431,7 @@ do self:writeln(tostring(colored.bright("WITH ARGS:")) .. " " .. tostring(colored.dim(repr(args)))) end insert(self.callstack, "#macro") - local ret = self:call(tree.stub, tree.line_no, unpack(args)) + local ret = self:call(tree.stub, tree:get_line_no(), unpack(args)) remove(self.callstack) return ret end, @@ -495,6 +519,7 @@ do return false end, parse = function(self, str, filename) + self:assert(type(filename) == "string", "Bad filename type: " .. tostring(type(filename))) if self.debug then self:writeln(tostring(colored.bright("PARSING:")) .. "\n" .. tostring(colored.yellow(str))) end @@ -507,10 +532,7 @@ do end return tree end, - run = function(self, src, filename, vars, max_operations, output_file) - if vars == nil then - vars = { } - end + run = function(self, src, filename, max_operations, output_file) if max_operations == nil then max_operations = nil end @@ -518,7 +540,7 @@ do output_file = nil end if src == "" then - return nil, "", vars + return nil, "" end if max_operations then local timeout @@ -534,21 +556,18 @@ do local lua = self:tree_to_lua(tree, filename) local lua_code = lua.statements or (lua.expr .. ";") lua_code = "-- File: " .. tostring(filename) .. "\n" .. lua_code - local ret = self:run_lua(lua_code, vars) + local ret = self:run_lua(lua_code) if max_operations then debug.sethook() end if output_file then output_file:write(lua_code) end - return ret, lua_code, vars + return ret, lua_code end, - run_file = function(self, filename, vars) - if vars == nil then - vars = { } - end + run_file = function(self, filename) if filename:match(".*%.lua") then - return dofile(filename)(self, vars) + return dofile(filename)(self) end if filename:match(".*%.nom") then if not self.skip_precompiled then @@ -556,7 +575,7 @@ do if file then local lua_code = file:read("*a") file:close() - return self:run_lua(lua_code, vars) + return self:run_lua(lua_code) end end local file = file or io.open(filename) @@ -570,23 +589,20 @@ do return self:error("Invalid filetype for " .. tostring(filename)) end end, - require_file = function(self, filename, vars) - if vars == nil then - vars = { } - end + require_file = function(self, filename) local loaded = self.defs["#loaded_files"] if not loaded[filename] then - loaded[filename] = self:run_file(filename, vars) or true + loaded[filename] = self:run_file(filename) or true end return loaded[filename] end, - run_lua = function(self, lua_code, vars) - if vars == nil then - vars = { } - end - local load_lua_fn, err = load(([[return function(nomsu, vars) + run_lua = function(self, lua_code) + local load_lua_fn, err = load(([[return function(nomsu) %s end]]):format(lua_code)) + if self.debug then + self:writeln(tostring(colored.bright("RUNNING LUA:")) .. "\n" .. tostring(colored.blue(colored.bright(lua_code)))) + end if not load_lua_fn then local n = 1 local fn @@ -598,15 +614,15 @@ end]]):format(lua_code)) self:error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(code)))) .. "\n\n" .. tostring(err)) end local run_lua_fn = load_lua_fn() - local ok, ret = pcall(run_lua_fn, self, vars) + local ok, ret = pcall(run_lua_fn, self) if not ok then self:errorln(debug.traceback()) self:error(ret) end return ret end, - tree_to_value = function(self, tree, vars, filename) - local code = "return (function(nomsu, vars)\nreturn " .. tostring(self:tree_to_lua(tree, filename).expr) .. ";\nend);" + tree_to_value = function(self, tree, filename) + local code = "return (function(nomsu)\nreturn " .. tostring(self:tree_to_lua(tree, filename).expr) .. ";\nend);" code = "-- Tree to value: " .. tostring(filename) .. "\n" .. code if self.debug then self:writeln(tostring(colored.bright("RUNNING LUA TO GET VALUE:")) .. "\n" .. tostring(colored.blue(colored.bright(code)))) @@ -615,7 +631,7 @@ end]]):format(lua_code)) if not lua_thunk then self:error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(code)))) .. "\n\n" .. tostring(colored.red(err))) end - return (lua_thunk())(self, vars or { }) + return (lua_thunk())(self) end, tree_to_nomsu = function(self, tree, force_inline) if force_inline == nil then @@ -841,7 +857,7 @@ end]]):format(lua_code)) } elseif "Nomsu" == _exp_0 then return { - expr = "nomsu:parse(" .. tostring(repr(tree.value.src)) .. ", " .. tostring(repr(tree.line_no)) .. ").value[1]" + expr = "nomsu:parse(" .. tostring(repr(tree.value.src)) .. ", " .. tostring(repr(tree:get_line_no())) .. ").value[1]" } elseif "Block" == _exp_0 then local lua_bits = { } @@ -891,7 +907,7 @@ end]]):format(lua_code)) end local args = { repr(tree.stub), - repr(tree.line_no) + repr(tree:get_line_no()) } local arg_names, escaped_args if def then @@ -922,7 +938,7 @@ end]]):format(lua_code)) break end if escaped_args[arg_names[arg_num]] then - insert(args, "nomsu:parse(" .. tostring(repr(arg.src)) .. ", " .. tostring(repr(tree.line_no)) .. ").value[1]") + insert(args, "nomsu:parse(" .. tostring(repr(arg.src)) .. ", " .. tostring(repr(tree:get_line_no())) .. ").value[1]") else local lua = self:tree_to_lua(arg, filename) if lua.statements then @@ -1039,15 +1055,9 @@ end]]):format(lua_code)) expr = repr(tree.value) } elseif "Var" == _exp_0 then - if tree.value:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then - return { - expr = "vars." .. tostring(tree.value) - } - else - return { - expr = "vars[" .. tostring(repr(tree.value)) .. "]" - } - end + return { + expr = ("_" .. self:var_to_lua_identifier(tree.value)) + } else return self:error("Unknown/unimplemented thingy: " .. tostring(tree.type)) end @@ -1256,13 +1266,15 @@ end]]):format(lua_code)) msg = '' end if not condition then - return self:error(msg) + return self:error("Assertion failed: " .. msg) end end, error = function(self, msg) local error_msg = colored.red("ERROR!") - if msg then + if msg and #msg > 0 then error_msg = error_msg .. ("\n" .. (colored.bright(colored.yellow(colored.onred(msg))))) + else + error_msg = error_msg .. "\n" end error_msg = error_msg .. "\nCallstack:" local maxlen = max((function() @@ -1301,20 +1313,6 @@ end]]):format(lua_code)) self.callstack = { } return error(error_msg, 3) end, - typecheck = function(self, vars, varname, desired_type) - local x = vars[varname] - local x_type = type(x) - if x_type == desired_type then - return x - end - if x_type == 'table' then - x_type = x.type or x_type - if x_type == desired_type then - return x - end - end - return self:error("Invalid type for %" .. tostring(varname) .. ". Expected " .. tostring(desired_type) .. ", but got " .. tostring(x_type) .. ":\n" .. tostring(repr(x))) - end, source_code = function(self, level) if level == nil then level = 0 @@ -1340,51 +1338,51 @@ end]]):format(lua_code)) end return concat(concat_parts) end - self:defmacro("do %block", function(self, vars) + self:defmacro("do %block", function(self, _block) local make_line make_line = function(lua) return lua.expr and (lua.expr .. ";") or lua.statements end - if vars.block.type == "Block" then - return self:tree_to_lua(vars.block) + if _block.type == "Block" then + return self:tree_to_lua(_block) else return { - expr = tostring(self:tree_to_lua(vars.block)) .. "(nomsu, vars)" + expr = tostring(self:tree_to_lua(_block)) .. "(nomsu)" } end end) - self:defmacro("immediately %block", function(self, vars) - local lua = self:tree_to_lua(vars.block) + self:defmacro("immediately %block", function(self, _block) + local lua = self:tree_to_lua(_block) local lua_code = lua.statements or (lua.expr .. ";") lua_code = "-- Immediately:\n" .. lua_code - self:run_lua(lua_code, vars) + self:run_lua(lua_code) return { statements = lua_code } end) - self:defmacro("lua> %code", function(self, vars) - local lua = nomsu_string_as_lua(self, vars.code) + self:defmacro("lua> %code", function(self, _code) + local lua = nomsu_string_as_lua(self, _code) return { statements = lua } end) - self:defmacro("=lua %code", function(self, vars) - local lua = nomsu_string_as_lua(self, vars.code) + self:defmacro("=lua %code", function(self, _code) + local lua = nomsu_string_as_lua(self, _code) return { expr = lua } end) - self:defmacro("__src__ %level", function(self, vars) + self:defmacro("__src__ %level", function(self, _level) return { - expr = repr(self:source_code(self:tree_to_value(vars.level))) + expr = repr(self:source_code(self:tree_to_value(_level))) } end) - self:def("run file %filename", function(self, vars) - return self:run_file(vars.filename, vars) + self:def("run file %filename", function(self, _filename) + return self:run_file(_filename) end) - return self:defmacro("require %filename", function(self, vars) - local filename = self:tree_to_value(vars.filename) - self:require_file(filename, vars) + return self:defmacro("require %filename", function(self, _filename) + local filename = self:tree_to_value(_filename) + self:require_file(filename) return { statements = "nomsu:require_file(" .. tostring(repr(filename)) .. ");" } @@ -1513,8 +1511,7 @@ if arg then else input = io.open(args.input):read("*a") end - local vars = { } - local retval, code = c:run(input, args.input, vars) + local retval, code = c:run(input, args.input) if args.output then compiled_output:write(code) end @@ -1524,7 +1521,6 @@ if arg then end end if args.flags["-i"] then - local vars = { } c:run('require "lib/core.nom"', "stdin") while true do local buff = "" @@ -1540,7 +1536,7 @@ if arg then break end local ok, ret = pcall(function() - return c:run(buff, "stdin", vars) + return c:run(buff, "stdin") end) if ok and ret ~= nil then print("= " .. repr(ret)) diff --git a/nomsu.moon b/nomsu.moon index 999c4ca..8e9e9dd 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -100,14 +100,12 @@ do error(err_msg) FunctionCall: (start, value, stop)-> stub = concat([(t.type == "Word" and t.value or "%") for t in *value], " ") - line_no = 1 - while (ctx.line_starts[line_no+1] or math.huge) < start do line_no += 1 src = ctx.source_code\sub(start,stop-1) - return {type: "FunctionCall", :src, line_no: "#{ctx.filename}:#{line_no}", :value, :stub} + 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), type: key} + {: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 }) @@ -129,10 +127,18 @@ do 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 = {:source_code, :filename, indent_stack: {0}} - ctx.line_starts = re.compile("lines <- {| line ('\n' line)* |} line <- {} [^\n]*")\match(source_code) + ctx = _ctx tree = nomsu\match(source_code) ctx = old_ctx return tree @@ -171,28 +177,31 @@ class NomsuCompiler elseif type(signature) == 'table' and type(signature[1]) == 'string' signature = @get_stubs signature @assert type(fn) == 'function', "Bad fn: #{repr fn}" - canonical_args = nil - canonical_escaped_args = nil aliases = {} @@def_number += 1 def = {:fn, :src, :is_macro, aliases:{}, def_number:@@def_number, defs:@defs} where_defs_go = (getmetatable(@defs) or {}).__newindex or @defs for sig_i=1,#signature stub, arg_names, escaped_args = unpack(signature[sig_i]) + arg_positions = {} @assert stub, "NO STUB FOUND: #{repr signature}" if @debug then @writeln "#{colored.bright "DEFINING RULE:"} #{colored.underscore colored.magenta repr(stub)} #{colored.bright "WITH ARGS"} #{colored.dim repr(arg_names)}" for i=1,#arg_names-1 do for j=i+1,#arg_names if arg_names[i] == arg_names[j] then @error "Duplicate argument in function #{stub}: '#{arg_names[i]}'" - if canonical_args - @assert equivalent(set(arg_names), canonical_args), "Mismatched args" - else canonical_args = set(arg_names) - if canonical_escaped_args - @assert equivalent(escaped_args, canonical_escaped_args), "Mismatched escaped args" - else - canonical_escaped_args = escaped_args + + if sig_i == 1 + arg_positions = [i for i=1,#arg_names] + def.args = arg_names def.escaped_args = escaped_args + else + @assert equivalent(set(def.args), set(arg_names)), "Mismatched args" + @assert equivalent(def.escaped_args, escaped_args), "Mismatched escaped args" + for j,a in ipairs(arg_names) + for i,c_a in ipairs(def.args) + if a == c_a + arg_positions[j] = i insert def.aliases, stub - stub_def = setmetatable({:stub, :arg_names, :escaped_args}, {__index:def}) + stub_def = setmetatable({:stub, :arg_names, :arg_positions}, {__index:def}) rawset(where_defs_go, stub, stub_def) defmacro: (signature, fn, src)=> @@ -255,15 +264,15 @@ class NomsuCompiler @error "Attempt to call undefined function: #{stub}" unless def.is_macro @assert_permission(stub) - {:fn, :arg_names} = def - args = {name, select(i,...) for i,name in ipairs(arg_names)} + {:fn, :arg_positions} = def + args = [select(p, ...) for p in *arg_positions] if @debug @write "#{colored.bright "CALLING"} #{colored.magenta(colored.underscore stub)} " @writeln "#{colored.bright "WITH ARGS:"}" - for name, value in pairs(args) - @writeln " #{colored.bright "* #{name}"} = #{colored.dim repr(value)}" + for i, value in ipairs(args) + @writeln " #{colored.bright "* #{def.args[i]}"} = #{colored.dim repr(value)}" old_defs, @defs = @defs, def.defs - rets = {fn(self,args)} + rets = {fn(self,unpack(args))} @defs = old_defs remove @callstack return unpack(rets) @@ -274,7 +283,7 @@ class NomsuCompiler @write "#{colored.bright "RUNNING MACRO"} #{colored.underscore colored.magenta(tree.stub)} " @writeln "#{colored.bright "WITH ARGS:"} #{colored.dim repr args}" insert @callstack, "#macro" - ret = @call(tree.stub, tree.line_no, unpack(args)) + ret = @call(tree.stub, tree\get_line_no!, unpack(args)) remove @callstack return ret @@ -324,6 +333,7 @@ class NomsuCompiler return false parse: (str, filename)=> + @assert type(filename) == "string", "Bad filename type: #{type filename}" if @debug @writeln("#{colored.bright "PARSING:"}\n#{colored.yellow str}") str = str\gsub("\r","") @@ -334,8 +344,8 @@ class NomsuCompiler @print_tree tree, " " return tree - run: (src, filename, vars={}, max_operations=nil, output_file=nil)=> - if src == "" then return nil, "", vars + run: (src, filename, max_operations=nil, output_file=nil)=> + if src == "" then return nil, "" if max_operations timeout = -> debug.sethook! @@ -348,23 +358,23 @@ class NomsuCompiler lua = @tree_to_lua(tree, filename) lua_code = lua.statements or (lua.expr..";") lua_code = "-- File: #{filename}\n"..lua_code - ret = @run_lua(lua_code, vars) + ret = @run_lua(lua_code) if max_operations debug.sethook! if output_file output_file\write(lua_code) - return ret, lua_code, vars + return ret, lua_code - run_file: (filename, vars={})=> + run_file: (filename)=> if filename\match(".*%.lua") - return dofile(filename)(@, vars) + return dofile(filename)(@) if filename\match(".*%.nom") if not @skip_precompiled -- Look for precompiled version file = io.open(filename\gsub("%.nom", ".lua"), "r") if file lua_code = file\read("*a") file\close! - return @run_lua(lua_code, vars) + return @run_lua(lua_code) file = file or io.open(filename) if not file @error "File does not exist: #{filename}" @@ -374,17 +384,19 @@ class NomsuCompiler else @error "Invalid filetype for #{filename}" - require_file: (filename, vars={})=> + require_file: (filename)=> loaded = @defs["#loaded_files"] if not loaded[filename] - loaded[filename] = @run_file(filename, vars) or true + loaded[filename] = @run_file(filename) or true return loaded[filename] - run_lua: (lua_code, vars={})=> + run_lua: (lua_code)=> load_lua_fn, err = load([[ -return function(nomsu, vars) +return function(nomsu) %s end]]\format(lua_code)) + if @debug + @writeln "#{colored.bright "RUNNING LUA:"}\n#{colored.blue colored.bright(lua_code)}" if not load_lua_fn n = 1 fn = -> @@ -393,22 +405,22 @@ end]]\format(lua_code)) code = "1 |"..lua_code\gsub("\n", fn) @error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{err}") run_lua_fn = load_lua_fn! - ok,ret = pcall(run_lua_fn, self, vars) + ok,ret = pcall(run_lua_fn, self) if not ok --@errorln "#{colored.red "Error occurred in statement:"}\n#{colored.yellow tree.src}" @errorln debug.traceback! @error(ret) return ret - tree_to_value: (tree, vars, filename)=> - code = "return (function(nomsu, vars)\nreturn #{@tree_to_lua(tree, filename).expr};\nend);" + tree_to_value: (tree, filename)=> + code = "return (function(nomsu)\nreturn #{@tree_to_lua(tree, filename).expr};\nend);" code = "-- Tree to value: #{filename}\n"..code if @debug @writeln "#{colored.bright "RUNNING LUA TO GET VALUE:"}\n#{colored.blue colored.bright(code)}" lua_thunk, err = load(code) if not lua_thunk @error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{colored.red err}") - return (lua_thunk!)(self, vars or {}) + return (lua_thunk!)(self) tree_to_nomsu: (tree, force_inline=false)=> -- Return , @@ -562,7 +574,7 @@ end]]\format(lua_code)) return statements:concat(lua_bits, "\n") when "Nomsu" - return expr:"nomsu:parse(#{repr tree.value.src}, #{repr tree.line_no}).value[1]" + return expr:"nomsu:parse(#{repr tree.value.src}, #{repr tree\get_line_no!}).value[1]" when "Block" lua_bits = {} @@ -597,7 +609,7 @@ end]]\format(lua_code)) remove @compilestack return expr:"(#{concat bits, " "})" - args = {repr(tree.stub), repr(tree.line_no)} + args = {repr(tree.stub), repr(tree\get_line_no!)} local arg_names, escaped_args if def arg_names, escaped_args = def.arg_names, def.escaped_args @@ -607,7 +619,7 @@ end]]\format(lua_code)) for arg in *tree.value if arg.type == 'Word' then continue if escaped_args[arg_names[arg_num]] - insert args, "nomsu:parse(#{repr arg.src}, #{repr tree.line_no}).value[1]" + insert args, "nomsu:parse(#{repr arg.src}, #{repr tree\get_line_no!}).value[1]" else lua = @tree_to_lua arg, filename if lua.statements @@ -678,10 +690,7 @@ end]]\format(lua_code)) return expr:repr(tree.value) when "Var" - if tree.value\match("^[a-zA-Z_][a-zA-Z0-9_]*$") - return expr:"vars.#{tree.value}" - else - return expr:"vars[#{repr tree.value}]" + return expr:("_"..@var_to_lua_identifier(tree.value)) else @error("Unknown/unimplemented thingy: #{tree.type}") @@ -808,12 +817,14 @@ end]]\format(lua_code)) assert: (condition, msg='')=> if not condition - @error(msg) + @error("Assertion failed: "..msg) error: (msg)=> error_msg = colored.red "ERROR!" - if msg + if msg and #msg > 0 error_msg ..= "\n" .. (colored.bright colored.yellow colored.onred msg) + else + error_msg ..= "\n" error_msg ..= "\nCallstack:" maxlen = max([#c[2] for c in *@callstack when c != "#macro"]) for i=#@callstack,1,-1 @@ -827,15 +838,6 @@ end]]\format(lua_code)) @callstack = {} error error_msg, 3 - typecheck: (vars, varname, desired_type)=> - x = vars[varname] - x_type = type(x) - if x_type == desired_type then return x - if x_type == 'table' - x_type = x.type or x_type - if x_type == desired_type then return x - @error "Invalid type for %#{varname}. Expected #{desired_type}, but got #{x_type}:\n#{repr x}" - source_code: (level=0)=> @dedent @compilestack[#@compilestack-level].src @@ -853,37 +855,37 @@ end]]\format(lua_code)) insert concat_parts, lua.expr return concat(concat_parts) - @defmacro "do %block", (vars)=> + @defmacro "do %block", (_block)=> make_line = (lua)-> lua.expr and (lua.expr..";") or lua.statements - if vars.block.type == "Block" - return @tree_to_lua(vars.block) + if _block.type == "Block" + return @tree_to_lua(_block) else - return expr:"#{@tree_to_lua vars.block}(nomsu, vars)" + return expr:"#{@tree_to_lua _block}(nomsu)" - @defmacro "immediately %block", (vars)=> - lua = @tree_to_lua(vars.block) + @defmacro "immediately %block", (_block)=> + lua = @tree_to_lua(_block) lua_code = lua.statements or (lua.expr..";") lua_code = "-- Immediately:\n"..lua_code - @run_lua(lua_code, vars) + @run_lua(lua_code) return statements:lua_code - @defmacro "lua> %code", (vars)=> - lua = nomsu_string_as_lua(@, vars.code) + @defmacro "lua> %code", (_code)=> + lua = nomsu_string_as_lua(@, _code) return statements:lua - @defmacro "=lua %code", (vars)=> - lua = nomsu_string_as_lua(@, vars.code) + @defmacro "=lua %code", (_code)=> + lua = nomsu_string_as_lua(@, _code) return expr:lua - @defmacro "__src__ %level", (vars)=> - expr: repr(@source_code(@tree_to_value(vars.level))) + @defmacro "__src__ %level", (_level)=> + expr: repr(@source_code(@tree_to_value(_level))) - @def "run file %filename", (vars)=> - @run_file(vars.filename, vars) + @def "run file %filename", (_filename)=> + @run_file(_filename) - @defmacro "require %filename", (vars)=> - filename = @tree_to_value(vars.filename) - @require_file(filename, vars) + @defmacro "require %filename", (_filename)=> + filename = @tree_to_value(_filename) + @require_file(filename) return statements:"nomsu:require_file(#{repr filename});" if arg @@ -926,8 +928,7 @@ if arg input = if args.input == '-' io.read('*a') else io.open(args.input)\read("*a") - vars = {} - retval, code = c\run(input, args.input, vars) + retval, code = c\run(input, args.input) if args.output compiled_output\write(code) @@ -936,7 +937,6 @@ if arg if args.flags["-i"] -- REPL - vars = {} c\run('require "lib/core.nom"', "stdin") while true buff = "" @@ -948,7 +948,7 @@ if arg buff ..= line if #buff == 0 break - ok, ret = pcall(-> c\run(buff, "stdin", vars)) + ok, ret = pcall(-> c\run(buff, "stdin")) if ok and ret != nil print "= "..repr(ret)