aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-01-12 19:27:59 -0800
committerBruce Hill <bitbucket@bruce-hill.com>2018-01-12 19:28:19 -0800
commit5a526675db93ea95efc82af7fc6c8f80b0275e8a (patch)
tree1127798d9caa3512d175217edb1f96bd56205332
parent268a636157bb1fea0e90068a1c14d3c5f492134e (diff)
Better usage of ACTION_METADATA and deprecating nomsu.defs.
-rw-r--r--lib/metaprogramming.nom31
-rw-r--r--nomsu.lua272
-rwxr-xr-xnomsu.moon169
3 files changed, 202 insertions, 270 deletions
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
index 963a5cf..a0b8685 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -12,7 +12,7 @@ immediately:
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
+ 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
@@ -33,9 +33,9 @@ immediately:
local function compile_action(%s)
%s
end
- local function compile_action_wrapper(...) return {expr=compile_action(...)}; end
+ 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, names, repr(\%names:get_line_no()),
+ 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)));
return {statements=lua};
end, \(__src__ 1));
@@ -54,9 +54,9 @@ immediately:
local function compile_action(%s)
%s
end
- local function compile_action_wrapper(...) return {statements=compile_action(...)}; end
+ local function compile_action_wrapper(%s) return {statements=compile_action(%s)}; end
nomsu:define_compile_action(%s, %s, compile_action_wrapper, %s);
- end]]):format(args, body_lua, names, repr(\%names:get_line_no()),
+ 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)));
return {statements=lua};
end, \(__src__ 1));
@@ -95,7 +95,7 @@ immediately:
template = 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] = "["..repr(a).."]=_"..nomsu:var_to_lua_identifier(a); end
+ for i, a in ipairs(arg_names) do replacements[i] = "["..repr(a).."]="..nomsu:var_to_lua_identifier(a); end
replacements = "{"..table.concat(replacements, ", ").."}";
local lua_code = ([[
nomsu:define_compile_action(%s, %s, (function(%s)
@@ -109,10 +109,13 @@ immediately:
action [remove action %stub]:
lua> ".."
- local def = nomsu.defs[\%stub];
- for _, alias in ipairs(def.aliases) do
- nomsu.defs[alias] = false;
+ local fn = ACTIONS[\%stub];
+ local metadata = ACTION_METADATA[fn];
+ for i=#metadata.aliases,1,-1 do
+ metadata.arg_orders[metadata.aliases[i]] = nil;
+ table.remove(metadata.aliases, i);
end
+ ACTIONS[\%stub] = nil;
immediately:
action [%tree as lua]:
@@ -145,17 +148,17 @@ compile [nomsu %method %args] to: "nomsu[\(%method as lua)](nomsu, unpack(\(%arg
compile [tree %tree with %replacements] to: ".."
nomsu:tree_with_replaced_vars(\(%tree as lua), \(%replacements as lua))
-parse [action %names] as:
- (nomsu's "defs")->(nomsu "get_stub" [\%names])
+action [action %names metadata]:
+ =lua "ACTION_METADATA[ACTIONS[\%names]]"
# Get the source code for a function
action [help %action]:
lua> ".."
- local fn_def = nomsu.defs[nomsu:get_stub(\%action)]
- if not fn_def then
+ local metadata = \(action %action metadata);
+ if not metadata then
nomsu:writeln("Action not found: "..repr(\%action));
else
- nomsu:writeln(fn_def.src or "<unknown source code>");
+ nomsu:writeln(metadata.src or "<unknown source code>");
end
# Compiler tools
diff --git a/nomsu.lua b/nomsu.lua
index 7f0d074..587bc22 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -216,69 +216,52 @@ do
elseif type(signature) == 'table' and type(signature[1]) == 'string' then
signature = self:get_stubs(signature)
end
- self:assert(type(fn) == 'function', "Bad fn: " .. tostring(repr(fn)))
+ assert(type(fn) == 'function', "Bad fn: " .. tostring(repr(fn)))
local aliases = { }
self.__class.def_number = self.__class.def_number + 1
- local def = {
- fn = fn,
- src = src,
- line_no = line_no,
- compile_time = compile_time,
- aliases = { },
- def_number = self.__class.def_number,
- defs = self.defs
- }
- 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 ACTION:")) .. " " .. tostring(colored.underscore(colored.magenta(repr(stub)))) .. " " .. tostring(colored.bright("WITH ARGS")) .. " " .. tostring(colored.dim(repr(arg_names))))
+ 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
end
- for i = 1, #arg_names - 1 do
- for j = i + 1, #arg_names do
- if arg_names[i] == arg_names[j] then
- self:error("Duplicate argument in function " .. tostring(stub) .. ": '" .. tostring(arg_names[i]) .. "'")
- end
+ fn_arg_positions = _tbl_0
+ 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
- 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
+ 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))))
end
- insert(def.aliases, stub)
- local stub_def = setmetatable({
- stub = stub,
- arg_names = arg_names,
- arg_positions = arg_positions
- }, {
- __index = def
- })
- rawset(where_defs_go, stub, stub_def)
+ arg_orders[stub] = arg_positions
end
+ self.action_metadata[fn] = {
+ fn = fn,
+ src = src,
+ line_no = line_no,
+ aliases = aliases,
+ arg_orders = arg_orders,
+ def_number = self.__class.def_number
+ }
end,
define_compile_action = function(self, signature, line_no, fn, src)
- return self:define_action(signature, line_no, fn, src, true)
+ self:define_action(signature, line_no, fn, src, true)
+ self.action_metadata[fn].compile_time = true
end,
serialize_defs = function(self, scope, after)
if scope == nil then
@@ -287,6 +270,7 @@ do
if after == nil then
after = nil
end
+ error("Not currently functional.")
after = after or (self.core_defs or 0)
scope = scope or self.defs
local defs_by_num = { }
@@ -394,13 +378,13 @@ do
return code:gsub("\n", "\n" .. (" "):rep(levels))
end,
parse = function(self, str, filename)
- self:assert(type(filename) == "string", "Bad filename type: " .. tostring(type(filename)))
+ 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
str = str:gsub("\r", "")
local tree = parse(str, filename)
- self:assert(tree, "In file " .. tostring(colored.blue(filename)) .. " failed to parse:\n" .. tostring(colored.onyellow(colored.black(str))))
+ assert(tree, "In file " .. tostring(colored.blue(filename)) .. " failed to parse:\n" .. tostring(colored.onyellow(colored.black(str))))
if self.debug then
self:writeln("PARSE TREE:")
self:print_tree(tree, " ")
@@ -421,13 +405,13 @@ do
local timeout
timeout = function()
debug.sethook()
- return self:error("Execution quota exceeded. Your code took too long.")
+ return error("Execution quota exceeded. Your code took too long.")
end
debug.sethook(timeout, "", max_operations)
end
local tree = self:parse(src, filename)
- self:assert(tree, "Failed to parse: " .. tostring(src))
- self:assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type))
+ assert(tree, "Failed to parse: " .. tostring(src))
+ assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type))
local lua = self:tree_to_lua(tree)
local lua_code = lua.statements or (lua.expr .. ";")
lua_code = "-- File: " .. tostring(filename) .. "\n" .. lua_code
@@ -458,17 +442,17 @@ do
end
local file = file or io.open(filename)
if not file then
- self:error("File does not exist: " .. tostring(filename))
+ error("File does not exist: " .. tostring(filename))
end
local nomsu_code = file:read('*a')
file:close()
return self:run(nomsu_code, filename)
else
- return self:error("Invalid filetype for " .. tostring(filename))
+ return error("Invalid filetype for " .. tostring(filename))
end
end,
require_file = function(self, filename)
- local loaded = self.defs["#loaded_files"]
+ local loaded = self.environment.LOADED
if not loaded[filename] then
loaded[filename] = self:run_file(filename) or true
end
@@ -487,7 +471,7 @@ do
return ("\n%-3d|"):format(n)
end
local code = "1 |" .. lua_code:gsub("\n", fn)
- self:error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(code)))) .. "\n\n" .. tostring(err))
+ error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(code)))) .. "\n\n" .. tostring(err))
end
return run_lua_fn()
end,
@@ -498,7 +482,7 @@ do
end
local lua_thunk, err = load(code, nil, nil, self.environment)
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)))
+ 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()
end,
@@ -506,9 +490,9 @@ do
if force_inline == nil then
force_inline = false
end
- self:assert(tree, "No tree provided.")
+ assert(tree, "No tree provided.")
if not tree.type then
- self:error("Invalid tree: " .. tostring(repr(tree)))
+ error("Invalid tree: " .. tostring(repr(tree)))
end
local _exp_0 = tree.type
if "File" == _exp_0 then
@@ -640,7 +624,7 @@ do
return longbuff, false
end
elseif "Dict" == _exp_0 then
- return self:error("Sorry, not yet implemented.")
+ return error("Sorry, not yet implemented.")
elseif "Number" == _exp_0 then
return repr(tree.value), true
elseif "Var" == _exp_0 then
@@ -648,7 +632,7 @@ do
elseif "Word" == _exp_0 then
return tree.value, true
else
- return self:error("Unknown/unimplemented thingy: " .. tostring(tree.type))
+ return error("Unknown/unimplemented thingy: " .. tostring(tree.type))
end
end,
value_to_nomsu = function(self, value)
@@ -695,9 +679,9 @@ do
end
end,
tree_to_lua = function(self, tree)
- self:assert(tree, "No tree provided.")
+ assert(tree, "No tree provided.")
if not tree.type then
- self:error("Invalid tree: " .. tostring(repr(tree)))
+ error("Invalid tree: " .. tostring(repr(tree)))
end
local _exp_0 = tree.type
if "File" == _exp_0 then
@@ -710,7 +694,7 @@ do
local line = _list_0[_index_0]
local lua = self:tree_to_lua(line)
if not lua then
- self:error("No lua produced by " .. tostring(repr(line)))
+ error("No lua produced by " .. tostring(repr(line)))
end
if lua.statements then
insert(lua_bits, lua.statements)
@@ -749,8 +733,9 @@ do
}
elseif "FunctionCall" == _exp_0 then
insert(self.compilestack, tree)
- local def = self.defs[tree.stub]
- if def and def.compile_time then
+ local fn = self.environment.ACTIONS[tree.stub]
+ local metadata = self.environment.ACTION_METADATA[fn]
+ if metadata and metadata.compile_time then
local args
do
local _accum_0 = { }
@@ -765,6 +750,21 @@ do
end
args = _accum_0
end
+ if metadata then
+ local new_args
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_0 = metadata.arg_orders[tree.stub]
+ for _index_0 = 1, #_list_0 do
+ local p = _list_0[_index_0]
+ _accum_0[_len_0] = args[p]
+ _len_0 = _len_0 + 1
+ end
+ new_args = _accum_0
+ end
+ args = new_args
+ end
if self.debug then
self:write(tostring(colored.bright("RUNNING MACRO")) .. " " .. tostring(colored.underscore(colored.magenta(tree.stub))) .. " ")
self:writeln(tostring(colored.bright("WITH ARGS:")) .. " " .. tostring(colored.dim(repr((function()
@@ -778,10 +778,10 @@ do
return _accum_0
end)()))))
end
- local lua = self.defs[tree.stub].fn(unpack(args))
+ local lua = fn(unpack(args))
remove(self.compilestack)
return lua
- elseif not def and self.__class.math_patt:match(tree.stub) then
+ elseif not metadata and self.__class.math_patt:match(tree.stub) then
local bits = { }
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
@@ -790,7 +790,7 @@ do
insert(bits, tok.value)
else
local lua = self:tree_to_lua(tok)
- self:assert(lua.statements == nil, "non-expression value inside math expression")
+ assert(lua.statements == nil, "non-expression value inside math expression")
insert(bits, lua.expr)
end
end
@@ -799,7 +799,6 @@ do
expr = "(" .. tostring(concat(bits, " ")) .. ")"
}
end
- local arg_positions = def and def.arg_positions or { }
local args = { }
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
@@ -811,7 +810,7 @@ do
break
end
local lua = self:tree_to_lua(tok)
- self:assert(lua.expr, "Cannot use " .. tostring(tok.src) .. " as an argument, since it's not an expression.")
+ assert(lua.expr, "Cannot use " .. tostring(tok.src) .. " as an argument, since it's not an expression.")
insert(args, lua.expr)
_continue_0 = true
until true
@@ -819,12 +818,12 @@ do
break
end
end
- if def then
+ if metadata then
local new_args
do
local _accum_0 = { }
local _len_0 = 1
- local _list_1 = def.arg_positions
+ local _list_1 = metadata.arg_orders[tree.stub]
for _index_0 = 1, #_list_1 do
local p = _list_1[_index_0]
_accum_0[_len_0] = args[p]
@@ -836,7 +835,7 @@ do
end
remove(self.compilestack)
return {
- expr = self.__class:comma_separated_items("nomsu.defs[" .. tostring(repr(tree.stub)) .. "].fn(", args, ")")
+ expr = self.__class:comma_separated_items("ACTIONS[" .. tostring(repr(tree.stub)) .. "](", args, ")")
}
elseif "Text" == _exp_0 then
local concat_parts = { }
@@ -862,7 +861,7 @@ do
self:writeln(tostring(colored.bright("EXPR:")) .. " " .. tostring(lua.expr) .. ", " .. tostring(colored.bright("STATEMENT:")) .. " " .. tostring(lua.statements))
end
if lua.statements then
- self:error("Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
+ error("Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
end
insert(concat_parts, "stringify(" .. tostring(lua.expr) .. ")")
_continue_0 = true
@@ -894,7 +893,7 @@ do
local item = _list_0[_index_0]
local lua = self:tree_to_lua(item)
if lua.statements then
- self:error("Cannot use [[" .. tostring(item.src) .. "]] as a list item, since it's not an expression.")
+ error("Cannot use [[" .. tostring(item.src) .. "]] as a list item, since it's not an expression.")
end
insert(items, lua.expr)
end
@@ -915,11 +914,11 @@ do
key_lua = self:tree_to_lua(entry.dict_key)
end
if key_lua.statements then
- self:error("Cannot use [[" .. tostring(entry.dict_key.src) .. "]] as a dict key, since it's not an expression.")
+ error("Cannot use [[" .. tostring(entry.dict_key.src) .. "]] as a dict key, since it's not an expression.")
end
local value_lua = self:tree_to_lua(entry.dict_value)
if value_lua.statements then
- self:error("Cannot use [[" .. tostring(entry.dict_value.src) .. "]] as a dict value, since it's not an expression.")
+ error("Cannot use [[" .. tostring(entry.dict_value.src) .. "]] as a dict value, since it's not an expression.")
end
local key_str = key_lua.expr:match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str then
@@ -937,10 +936,10 @@ do
}
elseif "Var" == _exp_0 then
return {
- expr = ("_" .. self:var_to_lua_identifier(tree.value))
+ expr = self:var_to_lua_identifier(tree.value)
}
else
- return self:error("Unknown/unimplemented thingy: " .. tostring(tree.type))
+ return error("Unknown/unimplemented thingy: " .. tostring(tree.type))
end
end,
walk_tree = function(self, tree, depth)
@@ -1054,33 +1053,19 @@ do
end,
get_stub = function(self, x)
if not x then
- self:error("Nothing to get stub from")
+ error("Nothing to get stub from")
end
if type(x) == 'string' then
local spec = concat(self.__class.stub_patt:match(x), " ")
- local stub = spec:gsub("%%%S+", "%%"):gsub("\\", "")
- local arg_names
- do
- local _accum_0 = { }
- local _len_0 = 1
- for arg in spec:gmatch("%%(%S*)") do
- _accum_0[_len_0] = arg
- _len_0 = _len_0 + 1
- end
- arg_names = _accum_0
- end
- local escaped_args
- do
- local _tbl_0 = { }
- for arg in spec:gmatch("\\%%(%S*)") do
- _tbl_0[arg] = true
- end
- escaped_args = _tbl_0
- end
- return stub, arg_names, escaped_args
+ local arg_names = { }
+ local stub = spec:gsub("%%(%S+)", function(arg)
+ insert(arg_names, arg)
+ return "%"
+ end)
+ return stub, arg_names
end
if type(x) ~= 'table' then
- self:error("Invalid type for getting stub: " .. tostring(type(x)) .. " for:\n" .. tostring(repr(x)))
+ error("Invalid type for getting stub: " .. tostring(type(x)) .. " for:\n" .. tostring(repr(x)))
end
local _exp_0 = x.type
if "Text" == _exp_0 then
@@ -1088,7 +1073,7 @@ do
elseif "FunctionCall" == _exp_0 then
return self:get_stub(x.src)
else
- return self:error("Unsupported get stub type: " .. tostring(x.type) .. " for " .. tostring(repr(x)))
+ return error("Unsupported get stub type: " .. tostring(x.type) .. " for " .. tostring(repr(x)))
end
end,
get_stubs = function(self, x)
@@ -1134,7 +1119,7 @@ do
if type(var) == 'table' and var.type == "Var" then
var = var.value
end
- return (var:gsub("%W", function(verboten)
+ return "_" .. (var:gsub("%W", function(verboten)
if verboten == "_" then
return "__"
else
@@ -1142,18 +1127,6 @@ do
end
end))
end,
- assert = function(self, condition, msg)
- if msg == nil then
- msg = ''
- end
- if not condition then
- self:error("Assertion failed: " .. msg)
- end
- return condition
- end,
- error = function(self, msg)
- return error(msg, 0)
- end,
source_code = function(self, level)
if level == nil then
level = 0
@@ -1180,19 +1153,6 @@ do
end
return concat(concat_parts)
end
- self:define_compile_action("do %block", "nomsu.moon", function(_block)
- local make_line
- make_line = function(lua)
- return lua.expr and (lua.expr .. ";") or lua.statements
- end
- if _block.type == "Block" then
- return nomsu:tree_to_lua(_block)
- else
- return {
- expr = tostring(nomsu:tree_to_lua(_block)) .. "(nomsu)"
- }
- end
- end)
self:define_compile_action("immediately %block", "nomsu.moon", function(_block)
local lua = nomsu:tree_to_lua(_block)
local lua_code = lua.statements or (lua.expr .. ";")
@@ -1245,10 +1205,6 @@ do
self.write_err = function(self, ...)
return io.stderr:write(...)
end
- self.defs = {
- ["#vars"] = { },
- ["#loaded_files"] = { }
- }
self.ids = setmetatable({ }, {
__mode = "k",
__index = function(self, key)
@@ -1258,18 +1214,13 @@ do
end
})
if parent then
- setmetatable(self.defs, {
- __index = parent.defs
- })
- setmetatable(self.defs["#vars"], {
- __index = parent["#vars"]
- })
- setmetatable(self.defs["#loaded_files"], {
- __index = parent["#loaded_files"]
- })
+ error("Not implemented")
end
self.compilestack = { }
self.debug = false
+ self.action_metadata = setmetatable({ }, {
+ __mode = "k"
+ })
self.environment = {
nomsu = self,
repr = repr,
@@ -1277,6 +1228,9 @@ do
utils = utils,
lpeg = lpeg,
re = re,
+ ACTIONS = { },
+ ACTION_METADATA = self.action_metadata,
+ LOADED = { },
next = next,
unpack = unpack,
setmetatable = setmetatable,
@@ -1354,7 +1308,7 @@ do
insert(bits, close)
return concat(bits)
end
- self.stub_patt = re.compile("{|(' '+ / '\n..' / {'\\'? '%' %id*} / {%id+} / {%op})*|}", {
+ self.stub_patt = re.compile("{|(' '+ / '\n..' / {'%' %id*} / {%id+} / {%op})*|}", {
id = IDENT_CHAR,
op = OPERATOR_CHAR
})
@@ -1447,16 +1401,6 @@ if arg then
local nomsu_source = nomsu_file:read("*a")
local _, line_table = to_lua(nomsu_source)
nomsu_file:close()
- local function_defs
- do
- local _tbl_0 = { }
- for _, def in pairs(nomsu.defs) do
- if def.fn then
- _tbl_0[def.fn] = def
- end
- end
- function_defs = _tbl_0
- end
local level = 2
while true do
local _continue_0 = false
@@ -1476,10 +1420,10 @@ if arg then
end
local line = nil
do
- local def = function_defs[calling_fn.func]
- if def then
- line = colored.yellow(def.line_no)
- name = colored.bright(colored.yellow(def.aliases[1]))
+ local metadata = nomsu.action_metadata[calling_fn.func]
+ if metadata then
+ line = colored.yellow(metadata.line_no)
+ name = colored.bright(colored.yellow(metadata.aliases[1]))
else
if calling_fn.istailcall and not name then
name = "<tail call>"
diff --git a/nomsu.moon b/nomsu.moon
index 23ecede..054d907 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -33,7 +33,6 @@ if _VERSION == "Lua 5.1"
-- Fix compiler bug that breaks when file ends with a block comment
-- Add compiler options for optimization level (compile-fast vs. run-fast, etc.)
-- Do a pass on all actions to enforce parameters-are-nouns heuristic
--- Put function defs into a separate table so we can do nomsu.defs["foo"](nomsu, ...) directly without a ".fn"
-- Maybe do some sort of lazy definitions of actions that defer until they're used in code
-- Do automatic "local" detection of new variables and declare them as locals like moonscript does
@@ -148,7 +147,6 @@ class NomsuCompiler
@write = (...)=> io.write(...)
@write_err = (...)=> io.stderr\write(...)
-- Use # to prevent someone from defining a function that has a namespace collision.
- @defs = {["#vars"]:{}, ["#loaded_files"]:{}}
@ids = setmetatable({}, {
__mode: "k"
__index: (key)=>
@@ -157,15 +155,16 @@ class NomsuCompiler
return id
})
if parent
- setmetatable(@defs, {__index:parent.defs})
- setmetatable(@defs["#vars"], {__index:parent["#vars"]})
- setmetatable(@defs["#loaded_files"], {__index:parent["#loaded_files"]})
+ -- TODO: Implement
+ error("Not implemented")
@compilestack = {}
@debug = false
+ @action_metadata = setmetatable({}, {__mode:"k"})
@environment = {
-- Discretionary/convenience stuff
nomsu:self, repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re,
+ ACTIONS:{}, ACTION_METADATA:@action_metadata, LOADED:{},
-- Lua stuff:
:next, :unpack, :setmetatable, :coroutine, :rawequal, :getmetatable, :pcall,
:error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module,
@@ -189,38 +188,37 @@ class NomsuCompiler
signature = @get_stubs {signature}
elseif type(signature) == 'table' and type(signature[1]) == 'string'
signature = @get_stubs signature
- @assert type(fn) == 'function', "Bad fn: #{repr fn}"
+ assert type(fn) == 'function', "Bad fn: #{repr fn}"
aliases = {}
@@def_number += 1
- def = {:fn, :src, :line_no, :compile_time, aliases:{}, def_number:@@def_number, defs:@defs}
- where_defs_go = (getmetatable(@defs) or {}).__newindex or @defs
+
+ 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
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 ACTION:"} #{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 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, :arg_positions}, {__index:def})
- rawset(where_defs_go, stub, stub_def)
+ 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
+
+ @action_metadata[fn] = {
+ :fn, :src, :line_no, :aliases, :arg_orders, 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
serialize_defs: (scope=nil, after=nil)=>
+ -- TODO: repair
+ error("Not currently functional.")
after or= @core_defs or 0
scope or= @defs
defs_by_num = {}
@@ -275,12 +273,12 @@ class NomsuCompiler
return code\gsub("\n","\n"..(" ")\rep(levels))
parse: (str, filename)=>
- @assert type(filename) == "string", "Bad filename type: #{type filename}"
+ assert type(filename) == "string", "Bad filename type: #{type filename}"
if @debug
@writeln("#{colored.bright "PARSING:"}\n#{colored.yellow str}")
str = str\gsub("\r","")
tree = parse(str, filename)
- @assert tree, "In file #{colored.blue filename} failed to parse:\n#{colored.onyellow colored.black str}"
+ assert tree, "In file #{colored.blue filename} failed to parse:\n#{colored.onyellow colored.black str}"
if @debug
@writeln "PARSE TREE:"
@print_tree tree, " "
@@ -291,11 +289,11 @@ class NomsuCompiler
if max_operations
timeout = ->
debug.sethook!
- @error "Execution quota exceeded. Your code took too long."
+ error "Execution quota exceeded. Your code took too long."
debug.sethook timeout, "", max_operations
tree = @parse(src, filename)
- @assert tree, "Failed to parse: #{src}"
- @assert tree.type == "File", "Attempt to run non-file: #{tree.type}"
+ assert tree, "Failed to parse: #{src}"
+ assert tree.type == "File", "Attempt to run non-file: #{tree.type}"
lua = @tree_to_lua(tree)
lua_code = lua.statements or (lua.expr..";")
@@ -322,15 +320,15 @@ class NomsuCompiler
return @run_lua(lua_code)
file = file or io.open(filename)
if not file
- @error "File does not exist: #{filename}"
+ error "File does not exist: #{filename}"
nomsu_code = file\read('*a')
file\close!
return @run(nomsu_code, filename)
else
- @error "Invalid filetype for #{filename}"
+ error "Invalid filetype for #{filename}"
require_file: (filename)=>
- loaded = @defs["#loaded_files"]
+ loaded = @environment.LOADED
if not loaded[filename]
loaded[filename] = @run_file(filename) or true
return loaded[filename]
@@ -345,7 +343,7 @@ class NomsuCompiler
n = n + 1
("\n%-3d|")\format(n)
code = "1 |"..lua_code\gsub("\n", fn)
- @error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{err}")
+ error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{err}")
return run_lua_fn!
tree_to_value: (tree, filename)=>
@@ -354,15 +352,14 @@ class NomsuCompiler
@writeln "#{colored.bright "RUNNING LUA TO GET VALUE:"}\n#{colored.blue colored.bright(code)}"
lua_thunk, err = load(code, nil, nil, @environment)
if not lua_thunk
- @error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{colored.red err}")
+ error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{colored.red err}")
return lua_thunk!
tree_to_nomsu: (tree, force_inline=false)=>
-- Return <nomsu code>, <is safe for inline use>
- @assert tree, "No tree provided."
+ assert tree, "No tree provided."
if not tree.type
- --@errorln debug.traceback()
- @error "Invalid tree: #{repr(tree)}"
+ error "Invalid tree: #{repr(tree)}"
switch tree.type
when "File"
return concat([@tree_to_nomsu(v, force_inline) for v in *tree.value], "\n"), false
@@ -450,7 +447,7 @@ class NomsuCompiler
when "Dict"
-- TODO: Implement
- @error("Sorry, not yet implemented.")
+ error("Sorry, not yet implemented.")
when "Number"
return repr(tree.value), true
@@ -462,7 +459,7 @@ class NomsuCompiler
return tree.value, true
else
- @error("Unknown/unimplemented thingy: #{tree.type}")
+ error("Unknown/unimplemented thingy: #{tree.type}")
value_to_nomsu: (value)=>
switch type(value)
@@ -491,10 +488,9 @@ class NomsuCompiler
@math_patt: re.compile [[ "%" (" " [*/^+-] " %")+ ]]
tree_to_lua: (tree)=>
-- Return <lua code for value>, <additional lua code>
- @assert tree, "No tree provided."
+ assert tree, "No tree provided."
if not tree.type
- --@errorln debug.traceback()
- @error "Invalid tree: #{repr(tree)}"
+ error "Invalid tree: #{repr(tree)}"
switch tree.type
when "File"
if #tree.value == 1
@@ -503,7 +499,7 @@ class NomsuCompiler
for line in *tree.value
lua = @tree_to_lua line
if not lua
- @error "No lua produced by #{repr line}"
+ error "No lua produced by #{repr line}"
if lua.statements then insert lua_bits, lua.statements
if lua.expr then insert lua_bits, "#{lua.expr};"
return statements:concat(lua_bits, "\n")
@@ -524,16 +520,20 @@ class NomsuCompiler
when "FunctionCall"
insert @compilestack, tree
- def = @defs[tree.stub]
- if def and def.compile_time
+ fn = @environment.ACTIONS[tree.stub]
+ metadata = @environment.ACTION_METADATA[fn]
+ if metadata and metadata.compile_time
args = [arg for arg in *tree.value when arg.type != "Word"]
+ if metadata
+ new_args = [args[p] for p in *metadata.arg_orders[tree.stub]]
+ args = new_args
if @debug
@write "#{colored.bright "RUNNING MACRO"} #{colored.underscore colored.magenta(tree.stub)} "
@writeln "#{colored.bright "WITH ARGS:"} #{colored.dim repr [(repr a)\sub(1,50) for a in *args]}"
- lua = @defs[tree.stub].fn(unpack(args))
+ lua = fn(unpack(args))
remove @compilestack
return lua
- elseif not def and @@math_patt\match(tree.stub)
+ elseif not metadata and @@math_patt\match(tree.stub)
-- This is a bit of a hack, but this code handles arbitrarily complex
-- math expressions like 2*x + 3^2 without having to define a single
-- action for every possibility.
@@ -543,25 +543,24 @@ class NomsuCompiler
insert bits, tok.value
else
lua = @tree_to_lua(tok)
- @assert(lua.statements == nil, "non-expression value inside math expression")
+ assert(lua.statements == nil, "non-expression value inside math expression")
insert bits, lua.expr
remove @compilestack
return expr:"(#{concat bits, " "})"
- arg_positions = def and def.arg_positions or {}
args = {}
for tok in *tree.value
if tok.type == "Word" then continue
lua = @tree_to_lua(tok)
- @assert(lua.expr, "Cannot use #{tok.src} as an argument, since it's not an expression.")
+ assert(lua.expr, "Cannot use #{tok.src} as an argument, since it's not an expression.")
insert args, lua.expr
- if def
- new_args = [args[p] for p in *def.arg_positions]
+ if metadata
+ new_args = [args[p] for p in *metadata.arg_orders[tree.stub]]
args = new_args
remove @compilestack
- return expr:@@comma_separated_items("nomsu.defs[#{repr tree.stub}].fn(", args, ")")
+ return expr:@@comma_separated_items("ACTIONS[#{repr tree.stub}](", args, ")")
when "Text"
concat_parts = {}
@@ -579,7 +578,7 @@ class NomsuCompiler
@print_tree bit
@writeln "#{colored.bright "EXPR:"} #{lua.expr}, #{colored.bright "STATEMENT:"} #{lua.statements}"
if lua.statements
- @error "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
+ error "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
insert concat_parts, "stringify(#{lua.expr})"
if string_buffer ~= ""
@@ -596,7 +595,7 @@ class NomsuCompiler
for item in *tree.value
lua = @tree_to_lua item
if lua.statements
- @error "Cannot use [[#{item.src}]] as a list item, since it's not an expression."
+ error "Cannot use [[#{item.src}]] as a list item, since it's not an expression."
insert items, lua.expr
return expr:@@comma_separated_items("{", items, "}")
@@ -608,10 +607,10 @@ class NomsuCompiler
else
@tree_to_lua entry.dict_key
if key_lua.statements
- @error "Cannot use [[#{entry.dict_key.src}]] as a dict key, since it's not an expression."
+ error "Cannot use [[#{entry.dict_key.src}]] as a dict key, since it's not an expression."
value_lua = @tree_to_lua entry.dict_value
if value_lua.statements
- @error "Cannot use [[#{entry.dict_value.src}]] as a dict value, since it's not an expression."
+ error "Cannot use [[#{entry.dict_value.src}]] as a dict value, since it's not an expression."
key_str = key_lua.expr\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str
insert items, "#{key_str}=#{value_lua.expr}"
@@ -623,10 +622,10 @@ class NomsuCompiler
return expr:repr(tree.value)
when "Var"
- return expr:("_"..@var_to_lua_identifier(tree.value))
+ return expr:@var_to_lua_identifier(tree.value)
else
- @error("Unknown/unimplemented thingy: #{tree.type}")
+ error("Unknown/unimplemented thingy: #{tree.type}")
walk_tree: (tree, depth=0)=>
coroutine.yield(tree, depth)
@@ -709,27 +708,28 @@ class NomsuCompiler
tree = new_values
return tree
- @stub_patt: re.compile "{|(' '+ / '\n..' / {'\\'? '%' %id*} / {%id+} / {%op})*|}",
+ @stub_patt: re.compile "{|(' '+ / '\n..' / {'%' %id*} / {%id+} / {%op})*|}",
id:IDENT_CHAR, op:OPERATOR_CHAR
get_stub: (x)=>
if not x
- @error "Nothing to get stub from"
+ error "Nothing to get stub from"
-- Returns a single stub ("say %"), list of arg names ({"msg"}), and set of arg
-- names that should not be evaluated from a single action def
-- (e.g. "say %msg") or function call (e.g. FunctionCall({Word("say"), Var("msg")))
if type(x) == 'string'
-- Standardize format to stuff separated by spaces
spec = concat @@stub_patt\match(x), " "
- stub = spec\gsub("%%%S+","%%")\gsub("\\","")
- arg_names = [arg for arg in spec\gmatch("%%(%S*)")]
- escaped_args = {arg, true for arg in spec\gmatch("\\%%(%S*)")}
- return stub, arg_names, escaped_args
+ arg_names = {}
+ stub = spec\gsub "%%(%S+)", (arg)->
+ insert(arg_names, arg)
+ return "%"
+ return stub, arg_names
if type(x) != 'table'
- @error "Invalid type for getting stub: #{type(x)} for:\n#{repr x}"
+ error "Invalid type for getting stub: #{type(x)} for:\n#{repr x}"
switch x.type
when "Text" then return @get_stub(x.value)
when "FunctionCall" then return @get_stub(x.src)
- else @error "Unsupported get stub type: #{x.type} for #{repr x}"
+ else error "Unsupported get stub type: #{x.type} for #{repr x}"
get_stubs: (x)=>
if type(x) != 'table' then return {{@get_stub(x)}}
@@ -745,17 +745,9 @@ class NomsuCompiler
-- characters with escape sequences
if type(var) == 'table' and var.type == "Var"
var = var.value
- (var\gsub "%W", (verboten)->
+ "_"..(var\gsub "%W", (verboten)->
if verboten == "_" then "__" else ("_%x")\format(verboten\byte!))
- assert: (condition, msg='')=>
- if not condition
- @error("Assertion failed: "..msg)
- return condition
-
- error: (msg)=>
- error msg, 0
-
source_code: (level=0)=>
@dedent @compilestack[#@compilestack-level].src
@@ -773,13 +765,6 @@ class NomsuCompiler
error "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
insert concat_parts, lua.expr
return concat(concat_parts)
-
- @define_compile_action "do %block", "nomsu.moon", (_block)->
- make_line = (lua)-> lua.expr and (lua.expr..";") or lua.statements
- if _block.type == "Block"
- return nomsu\tree_to_lua(_block)
- else
- return expr:"#{nomsu\tree_to_lua _block}(nomsu)"
@define_compile_action "immediately %block", "nomsu.moon", (_block)->
lua = nomsu\tree_to_lua(_block)
@@ -876,6 +861,7 @@ if arg
print "= "..repr(ret)
err_hand = (error_message)->
+ -- TODO: write properly to stderr
print("#{colored.red "ERROR:"} #{colored.bright colored.yellow colored.onred (error_message or "")}")
print("stack traceback:")
@@ -885,7 +871,6 @@ if arg
_, line_table = to_lua(nomsu_source)
nomsu_file\close!
- function_defs = {def.fn, def for _,def in pairs(nomsu.defs) when def.fn}
level = 2
while true
calling_fn = debug.getinfo(level)
@@ -895,9 +880,9 @@ if arg
name = calling_fn.name
if name == "run_lua_fn" then continue
line = nil
- if def = function_defs[calling_fn.func]
- line = colored.yellow(def.line_no)
- name = colored.bright(colored.yellow(def.aliases[1]))
+ if metadata = nomsu.action_metadata[calling_fn.func]
+ line = colored.yellow(metadata.line_no)
+ name = colored.bright(colored.yellow(metadata.aliases[1]))
else
if calling_fn.istailcall and not name
name = "<tail call>"