diff options
| -rw-r--r-- | lib/class.nom | 116 | ||||
| -rw-r--r-- | lib/control_flow.nom | 4 | ||||
| -rw-r--r-- | lib/metaprogramming.nom | 55 | ||||
| -rw-r--r-- | lib/operators.nom | 22 | ||||
| -rw-r--r-- | lib/text.nom | 8 | ||||
| -rw-r--r-- | nomsu.lua | 58 | ||||
| -rwxr-xr-x | nomsu.moon | 33 | ||||
| -rw-r--r-- | utils.lua | 13 |
8 files changed, 238 insertions, 71 deletions
diff --git a/lib/class.nom b/lib/class.nom new file mode 100644 index 0000000..7099d48 --- /dev/null +++ b/lib/class.nom @@ -0,0 +1,116 @@ +use "lib/core.nom" + +compile [@%var] to: + lua> ".." + local key_lua = repr(\%var.value); + local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'") + or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"')); + if key_attr then + return "_me."..key_attr; + elseif key_lua:sub(1,1) == "[" then + key_lua = " "..key_lua.." "; + end + return "_me["..key_lua.."]"; + +compile [set @%var = %val] to code: + lua> ".." + local val_lua = \(%val as lua); + local key_lua = repr(\%var.value); + local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'") + or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"')); + if key_attr then + return "_me."..key_attr.." = "..val_lua..";"; + elseif key_lua:sub(1,1) == "[" then + key_lua = " "..key_lua.." "; + end + return "_me["..key_lua.."] = "..val_lua..";"; + +compile [object %classname %class_body] to: + local [%methods] + set %methods = [] + for %line in (%class_body's "value"): + if ((%line's "type") == "Comment"): do next %line + assume (((%line's "type") == "FunctionCall") and ((%line's "stub") == "action % %")) + ..or barf "Only action definitions are supported inside 'object % %', not \(%line's "src")" + lua> ".." + assert(\%line.value[3].type == "Block", + "Invalid type for action definition body. Expected Block, but got: "..tostring(\%line.value[3].type)); + local names, args = nomsu:parse_spec(\%line.value[2]); + local stub = nomsu:get_stub(names[1]); + local body_lua = nomsu:tree_to_lua(\%line.value[3]); + body_lua = body_lua.statements or ("return "..body_lua.expr..";"); + table.insert(\%methods, ([[ + { aliases=%s, + line=%s, + source=%s, + action=function(%s) + %s + end, + compile_to=function(%s) + return {expr="("..nomsu:tree_to_lua(_me).expr..")[ "..%s.."]("..nomsu:tree_to_lua(%s).expr..")"}; + end, + }]]):format(repr(names), repr(\%line:get_line_no()), repr(nomsu:dedent(\%line.src)), + table.concat(args, ", "), body_lua, + table.concat(args, ", "), repr(repr(stub)), table.concat(args, ").expr..nomsu:tree_to_lua("))); + + local %class_identifier + set %class_identifier = (=lua "nomsu:var_to_lua_identifier(\(%classname as value)):sub(2,-1)") + if (%class_identifier == ""): + set %class_identifier = "class" + + return ".." + do -- \%class_identifier + -- Create the class object: + local \%class_identifier = setmetatable({ + name=\(%classname as lua), instances=setmetatable({}, {__mode="k"}), + }, { + __tostring=function(c) return c.name; end, + __call=function(cls, inst) + inst = inst or {}; + inst.id = tostring(inst):match('table: (.*)'); + setmetatable(inst, cls.instance_metatable); + cls.instances[inst] = true; + if inst['set % up'] then + inst['set % up'](inst); + end + return inst; + end, + }); + \%class_identifier.class = \%class_identifier; + + -- Define the methods: + \%class_identifier.methods = { + \(join %methods with ",\n ") + } + + -- Define nomsu compile time actions that rewrite "%foo baz %x" to %foo['% baz %'](%foo, %x) + -- so classes can have overlapping method names: + for i,method in ipairs(\%class_identifier.methods) do + for a_i, alias in ipairs(method.aliases) do + \%class_identifier[nomsu:get_stub(alias)] = method.action; + end + ACTION_METADATA[method.action] = { + fn=method.action, src=method.source, line_no=method.line, aliases=method.aliases, + }; + nomsu:define_compile_action(method.aliases, method.line, method.compile_to, method.source); + end + + -- Define class methods for instantiating and accessing instances: + \%class_identifier.instance_metatable = { + __index=\%class_identifier, + __tostring=\%class_identifier['% as text'] or function(inst) + return "<"..inst.class.name..": "..inst.id..">"; + end, + }; + nomsu:define_action("instances of "..\%class_identifier.name, "lib/class.nom", function() + return utils.keys(\%class_identifier.instances); + end, ""); + nomsu:define_action("new "..\%class_identifier.name.." %instance", "lib/class.nom", function(_instance) + return \%class_identifier(_instance); + end, ""); + nomsu:define_action("new "..\%class_identifier.name, "lib/class.nom", function() + return \%class_identifier({}); + end, ""); + end -- End of definition of \%class_identifier + \("\n\n") + diff --git a/lib/control_flow.nom b/lib/control_flow.nom index 7455ee9..dd602ff 100644 --- a/lib/control_flow.nom +++ b/lib/control_flow.nom @@ -75,7 +75,7 @@ immediately: action [tree %tree has function call %call]: lua> ".." local target = (\%call).stub; - for subtree,_ in coroutine.wrap(function() nomsu:walk_tree(\%tree); end) do + for subtree,depth in coroutine.wrap(function() nomsu:walk_tree(\%tree); end) do if type(subtree) == 'table' and subtree.type == "FunctionCall" and subtree.stub == target then return true; @@ -397,7 +397,7 @@ immediately: # Do/finally: immediately: compile [do %action] to code: - (%action as lua statements) if ((%action's "type") is "Block") + "do\n \(%action as lua statements)\nend" if ((%action's "type") is "Block") ..else "(\(%action as lua))(nomsu);" compile [do %action then always %final_action] to code: ".." diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom index a0b8685..ba63c25 100644 --- a/lib/metaprogramming.nom +++ b/lib/metaprogramming.nom @@ -6,26 +6,40 @@ immediately: lua> ".." nomsu.parse_spec = function(nomsu, spec) - local names = {}; - for i, alias in ipairs(spec.value) do - names[i] = alias.src; + if spec.type == 'List' then + local names = {}; + for i, alias in ipairs(spec.value) do + if alias.type == "FunctionCall" then + names[i] = alias.src; + elseif alias.type == "Text" then + names[i] = nomsu:tree_to_value(alias); + end + end + local junk, arg_names, junk = nomsu:get_stub(names[1]); + local args = {}; + for i, a in ipairs(arg_names) do args[i] = nomsu:var_to_lua_identifier(a); end + return names, args; + else + local alias = nomsu:tree_to_value(spec); + print("ALIAS!!! "..repr(alias).." from "..repr(spec)); + local junk, arg_names, junk = nomsu:get_stub(alias); + local args = {}; + for i, a in ipairs(arg_names) do args[i] = nomsu:var_to_lua_identifier(a); end + return {alias}, args; end - local _, arg_names, _ = nomsu:get_stub(spec.value[1]); - local args = {}; - for i, a in ipairs(arg_names) do args[i] = nomsu:var_to_lua_identifier(a); end - names, args = repr(names), table.concat(args, ", "); - return names, args; end # Compile-time action to make compile-time actions: +# TODO: reduce code duplication here immediately: lua> ".." nomsu:define_compile_action("compile %names to %body", \(__line_no__), function(\%names, \%body) - assert(\%names.type == "List", - "Invalid type for compile definition names. Expected List, but got: "..tostring(\%names.type)); + --assert(\%names.type == "List", + -- "Invalid type for compile definition names. Expected List, but got: "..tostring(\%names.type)); assert(\%body.type == "Block", "Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type)); local names, args = nomsu:parse_spec(\%names); + names, args = repr(names), table.concat(args, ", "); local body_lua = nomsu:tree_to_lua(\%body); body_lua = body_lua.statements or ("return "..body_lua.expr..";"); local lua = ([[ @@ -36,17 +50,18 @@ immediately: local function compile_action_wrapper(%s) return {expr=compile_action(%s)}; end nomsu:define_compile_action(%s, %s, compile_action_wrapper, %s); end]]):format(args, body_lua, args, args, names, repr(\%names:get_line_no()), - repr(("compile %s\\n..to code %s"):format(\%names.src, \%body.src))); + repr(("compile %s\\n..to %s"):format(\%names.src, \%body.src))); return {statements=lua}; end, \(__src__ 1)); lua> ".." nomsu:define_compile_action("compile %names to code %body", \(__line_no__), function(\%names, \%body) - assert(\%names.type == "List", - "Invalid type for compile definition names. Expected List, but got: "..tostring(\%names.type)); + --assert(\%names.type == "List", + -- "Invalid type for compile definition names. Expected List, but got: "..tostring(\%names.type)); assert(\%body.type == "Block", "Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type)); local names, args = nomsu:parse_spec(\%names); + names, args = repr(names), table.concat(args, ", "); local body_lua = nomsu:tree_to_lua(\%body); body_lua = body_lua.statements or ("return "..body_lua.expr..";"); local lua = ([[ @@ -65,11 +80,12 @@ immediately: immediately: compile [action %names %body] to code: lua> ".." - assert(\%names.type == "List", - "Invalid type for action definition names. Expected List, but got: "..tostring(\%names.type)); + --assert(\%names.type == "List", + -- "Invalid type for action definition names. Expected List, but got: "..tostring(\%names.type)); assert(\%body.type == "Block", "Invalid type for action definition body. Expected Block, but got: "..tostring(\%body.type)); local names, args = nomsu:parse_spec(\%names); + names, args = repr(names), table.concat(args, ", "); local body_lua = nomsu:tree_to_lua(\%body); body_lua = body_lua.statements or ("return "..body_lua.expr..";"); local src = nomsu:dedent(nomsu:source_code(0)); @@ -83,17 +99,18 @@ immediately: immediately: lua> ".." nomsu:define_compile_action("parse %shorthand as %longhand", \(__line_no__), (function(\%shorthand, \%longhand) - assert(\%shorthand.type == "List", - "Invalid type for parse definition shorthand. Expected List, but got: "..tostring(\%shorthand.type)); + --assert(\%shorthand.type == "List", + -- "Invalid type for parse definition shorthand. Expected List, but got: "..tostring(\%shorthand.type)); assert(\%longhand.type == "Block", "Invalid type for parse definition body. Expected Block, but got: "..tostring(\%longhand.type)); local names, args = nomsu:parse_spec(\%shorthand); + names, args = repr(names), table.concat(args, ", "); local template = {}; for i, line in ipairs(\%longhand.value) do template[i] = nomsu:dedent(line.src); end template = repr(table.concat(template, "\\n")); - local _, arg_names, _ = nomsu:get_stub(\%shorthand.value[1]); + local junk, arg_names, junk = nomsu:get_stub(\%shorthand.value[1]); local replacements = {}; for i, a in ipairs(arg_names) do replacements[i] = "["..repr(a).."]="..nomsu:var_to_lua_identifier(a); end replacements = "{"..table.concat(replacements, ", ").."}"; @@ -165,7 +182,7 @@ action [help %action]: parse [eval %code, run %code] as: nomsu "run" [%code] action [source code from tree %tree]: lua> ".." - local _,_,leading_space = \%tree.src:find("\\n(%s*)%S"); + local junk,junk,leading_space = \%tree.src:find("\\n(%s*)%S"); if leading_space then local chunk1, chunk2 = \%tree.src:match(":%s*([^\\n]*)(\\n.*)"); chunk2 = chunk2:gsub("\\n"..leading_space, "\\n"); diff --git a/lib/operators.nom b/lib/operators.nom index b697aad..2bbeadf 100644 --- a/lib/operators.nom +++ b/lib/operators.nom @@ -5,7 +5,27 @@ use "lib/metaprogramming.nom" # Indexing: immediately: - compile [%obj'%key, %obj's %key, %obj -> %key] to: "(\(%obj as lua))[\(%key as lua)]" + #.. NOTE!!! It's critical that there are spaces around %key if it's a string, + otherwise, Lua will get confused and interpret %obj[[[foo]]] as %obj("[foo]") + instead of %obj[ "foo" ]. + It's also critical to have parens around %obj, otherwise Lua is too dumb to + realize that {x=1}["x"] is the same as ({x=1})["x"] or that + {x=1}.x is the same as ({x=1}).x + compile [%obj'%key, %obj's %key, %obj -> %key] to: + lua> ".." + local obj_lua = \(%obj as lua); + if not obj_lua:sub(-1,-1):match("[a-zA-Z)]") then + obj_lua = "("..obj_lua..")"; + end + local key_lua = \(%key as lua); + local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'") + or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"')); + if key_attr then + return obj_lua.."."..key_attr; + elseif key_lua:sub(1,1) == "[" then + key_lua = " "..key_lua.." "; + end + return obj_lua.."["..key_lua.."]"; # Comparison Operators immediately: diff --git a/lib/text.nom b/lib/text.nom index 9db8a07..5cb268d 100644 --- a/lib/text.nom +++ b/lib/text.nom @@ -52,8 +52,8 @@ lua do> ".." local color = "'"..c.."'"; local reset = "'"..colors["reset color"].."'"; nomsu:define_compile_action(name, \(__line_no__), function() return {expr=color}; end, \(__src__ 1)); - nomsu:define_compile_action(name.." %", \(__line_no__), function(_) - return {expr=color..".."..nomsu:tree_to_lua(_).expr..".."..reset}; + nomsu:define_compile_action(name.." %", \(__line_no__), function(\%) + return {expr=color..".."..nomsu:tree_to_lua(\%).expr..".."..reset}; end, \(__src__ 1)); end @@ -85,8 +85,8 @@ lua do> ".." local e = "'"..\%str.."'"; local reset = "'"..\(%colors->"reset color").."'"; nomsu:define_compile_action(\%name, \(__line_no__), function(nomsu) return {expr=e}; end, \(__src__ 1)); - nomsu:define_compile_action(\%name.." %", \(__line_no__), function(nomsu, _) - return {expr=e..".."..nomsu:tree_to_lua(_).expr..".."..reset}; + nomsu:define_compile_action(\%name.." %", \(__line_no__), function(nomsu, \%) + return {expr=e..".."..nomsu:tree_to_lua(\%).expr..".."..reset}; end, \(__src__ 1)); # FIXME @@ -220,35 +220,39 @@ do local aliases = { } self.__class.def_number = self.__class.def_number + 1 local fn_info = debug.getinfo(fn, "u") - local fn_arg_positions - do - local _tbl_0 = { } - for i = 1, fn_info.nparams do - _tbl_0[debug.getlocal(fn, i)] = i + local fn_arg_positions, arg_orders + if not (fn_info.isvararg) then + do + local _tbl_0 = { } + for i = 1, fn_info.nparams do + _tbl_0[debug.getlocal(fn, i)] = i + end + fn_arg_positions = _tbl_0 end - fn_arg_positions = _tbl_0 + arg_orders = { } end - local arg_orders = { } for sig_i = 1, #signature do local stub, arg_names = unpack(signature[sig_i]) - local arg_positions - do - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #arg_names do - local a = arg_names[_index_0] - _accum_0[_len_0] = fn_arg_positions[self:var_to_lua_identifier(a)] - _len_0 = _len_0 + 1 - end - arg_positions = _accum_0 - end - assert(#arg_positions == #arg_names, "Mismatch in args between lua function's " .. tostring(repr(fn_arg_positions)) .. " and stub's " .. tostring(repr(arg_names))) - self.environment.ACTIONS[stub] = fn assert(stub, "NO STUB FOUND: " .. tostring(repr(signature))) if self.debug then - self:writeln(tostring(colored.bright("DEFINING ACTION:")) .. " " .. tostring(colored.underscore(colored.magenta(repr(stub)))) .. " " .. tostring(colored.bright("WITH ARGS")) .. " " .. tostring(colored.dim(repr(arg_names)))) + self:writeln(tostring(colored.bright("DEFINING ACTION:")) .. " " .. tostring(colored.underscore(colored.magenta(repr(stub)))) .. " " .. tostring(colored.bright("WITH ARGS")) .. " " .. tostring(colored.dim(repr(arg_names))) .. " ON: " .. tostring(self.environment.ACTIONS)) + end + self.environment.ACTIONS[stub] = fn + if not (fn_info.isvararg) then + local arg_positions + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #arg_names do + local a = arg_names[_index_0] + _accum_0[_len_0] = fn_arg_positions[self:var_to_lua_identifier(a)] + _len_0 = _len_0 + 1 + end + arg_positions = _accum_0 + end + assert(#arg_positions == #arg_names, "Mismatch in args between lua function's " .. tostring(repr(fn_arg_positions)) .. " and stub's " .. tostring(repr(arg_names))) + arg_orders[stub] = arg_positions end - arg_orders[stub] = arg_positions end self.action_metadata[fn] = { fn = fn, @@ -256,12 +260,16 @@ do line_no = line_no, aliases = aliases, arg_orders = arg_orders, + arg_positions = fn_arg_positions, def_number = self.__class.def_number } end, define_compile_action = function(self, signature, line_no, fn, src) self:define_action(signature, line_no, fn, src, true) self.action_metadata[fn].compile_time = true + if self.debug then + return self:writeln(tostring(colored.bright(colored.green("(it was compile time)")))) + end end, serialize_defs = function(self, scope, after) if scope == nil then @@ -939,7 +947,7 @@ do end args = _accum_0 end - if metadata then + if metadata and metadata.arg_orders then local new_args do local _accum_0 = { } @@ -1007,7 +1015,7 @@ do break end end - if metadata then + if metadata and metadata.arg_orders then local new_args do local _accum_0 = { } @@ -1247,7 +1255,7 @@ do if type(x) == 'string' then local spec = concat(self.__class.stub_patt:match(x), " ") local arg_names = { } - local stub = spec:gsub("%%(%S+)", function(arg) + local stub = spec:gsub("%%(%S*)", function(arg) insert(arg_names, arg) return "%" end) @@ -193,28 +193,33 @@ class NomsuCompiler @@def_number += 1 fn_info = debug.getinfo(fn, "u") - fn_arg_positions = {debug.getlocal(fn, i), i for i=1,fn_info.nparams} - arg_orders = {} -- Map from stub -> index where each arg in the stub goes in the function call + local fn_arg_positions, arg_orders + unless fn_info.isvararg + fn_arg_positions = {debug.getlocal(fn, i), i for i=1,fn_info.nparams} + arg_orders = {} -- Map from stub -> index where each arg in the stub goes in the function call for sig_i=1,#signature stub, arg_names = unpack(signature[sig_i]) - arg_positions = [fn_arg_positions[@var_to_lua_identifier(a)] for a in *arg_names] - -- TODO: better error checking? - assert(#arg_positions == #arg_names, - "Mismatch in args between lua function's #{repr fn_arg_positions} and stub's #{repr arg_names}") - -- TODO: use debug.getupvalue instead of @environment.ACTIONS? - @environment.ACTIONS[stub] = fn assert stub, "NO STUB FOUND: #{repr signature}" if @debug - @writeln "#{colored.bright "DEFINING ACTION:"} #{colored.underscore colored.magenta repr(stub)} #{colored.bright "WITH ARGS"} #{colored.dim repr(arg_names)}" - arg_orders[stub] = arg_positions + @writeln "#{colored.bright "DEFINING ACTION:"} #{colored.underscore colored.magenta repr(stub)} #{colored.bright "WITH ARGS"} #{colored.dim repr(arg_names)} ON: #{@environment.ACTIONS}" + -- TODO: use debug.getupvalue instead of @environment.ACTIONS? + @environment.ACTIONS[stub] = fn + unless fn_info.isvararg + arg_positions = [fn_arg_positions[@var_to_lua_identifier(a)] for a in *arg_names] + -- TODO: better error checking? + assert(#arg_positions == #arg_names, + "Mismatch in args between lua function's #{repr fn_arg_positions} and stub's #{repr arg_names}") + arg_orders[stub] = arg_positions @action_metadata[fn] = { - :fn, :src, :line_no, :aliases, :arg_orders, def_number:@@def_number, + :fn, :src, :line_no, :aliases, :arg_orders, arg_positions:fn_arg_positions, def_number:@@def_number, } define_compile_action: (signature, line_no, fn, src)=> @define_action(signature, line_no, fn, src, true) @action_metadata[fn].compile_time = true + if @debug + @writeln "#{colored.bright colored.green "(it was compile time)"}" serialize_defs: (scope=nil, after=nil)=> -- TODO: repair @@ -644,7 +649,7 @@ class NomsuCompiler metadata = @environment.ACTION_METADATA[fn] if metadata and metadata.compile_time args = [arg for arg in *tree.value when arg.type != "Word"] - if metadata + if metadata and metadata.arg_orders new_args = [args[p] for p in *metadata.arg_orders[tree.stub]] args = new_args if @debug @@ -675,7 +680,7 @@ class NomsuCompiler assert(lua.expr, "Cannot use #{tok.src} as an argument, since it's not an expression.") insert args, lua.expr - if metadata + if metadata and metadata.arg_orders new_args = [args[p] for p in *metadata.arg_orders[tree.stub]] args = new_args @@ -840,7 +845,7 @@ class NomsuCompiler -- Standardize format to stuff separated by spaces spec = concat @@stub_patt\match(x), " " arg_names = {} - stub = spec\gsub "%%(%S+)", (arg)-> + stub = spec\gsub "%%(%S*)", (arg)-> insert(arg_names, arg) return "%" return stub, arg_names @@ -80,14 +80,15 @@ local function repr(x, depth) return "\"" .. x .. "\"" else local eq = ("="):rep(_quote_state.min_eq or 0) - -- Need to add parens around ([=[...]=]) so lua's parser doesn't get confused - -- by stuff like x[[=[...]=]], which should obviously parse as x[ ([=[...]=]) ], - -- but instead parses as x( [[=[...]=]] ), i.e. a function call whose argument - -- is a string that starts with "=[" and ends with "]=" + -- BEWARE!!! + -- Lua's parser and syntax are dumb, so Lua interprets x[[=[asdf]=]] as + -- a function call to x (i.e. x("=[asdf]=")), instead of indexing x + -- (i.e. x["asdf"]), which it obviously should be. This can be fixed by + -- slapping spaces or parens around the [=[asdf]=]. if x:sub(1, 1) == "\n" then - return "(["..eq.."[\n"..x.."]"..eq.."])" + return "["..eq.."[\n"..x.."]"..eq.."]" else - return "(["..eq.."["..x.."]"..eq.."])" + return "["..eq.."["..x.."]"..eq.."]" end end else |
