aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-01-11 01:03:52 -0800
committerBruce Hill <bitbucket@bruce-hill.com>2018-01-11 01:04:17 -0800
commitc0333ca31532f14ec8ebd841a5f68b2f35e9cc80 (patch)
treecca1698b04eec2afb6adc7de55860caf12e90dca
parent53a9d4eae888d2b09c68fcd5dc14ae51f5d07c31 (diff)
Overhaul of error reporting and removing nomsu:call(stub, line_no, ...) in favor of nomsu.defs[stub].fn(...)
-rw-r--r--lib/metaprogramming.nom23
-rw-r--r--lib/operators.nom22
-rw-r--r--lib/utils.nom2
-rw-r--r--nomsu.lua372
-rwxr-xr-xnomsu.moon259
5 files changed, 316 insertions, 362 deletions
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
index 4b3c615..d396ac3 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -19,7 +19,7 @@ immediately:
immediately:
lua> ".."
- nomsu:defmacro("compile %macro_def to %body", function(nomsu, \%macro_def, \%body)
+ nomsu:defmacro("compile %macro_def to %body", \(__line_no__), 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",
@@ -33,13 +33,14 @@ immediately:
%s
end
local function macro_wrapper(...) return {expr=macro(...)}; end
- nomsu:defmacro(%s, macro_wrapper, %s);
- end]]):format(args, body_lua, signature, nomsu:repr(("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src)));
+ nomsu:defmacro(%s, %s, macro_wrapper, %s);
+ end]]):format(args, body_lua, signature, nomsu:repr(\%macro_def:get_line_no()),
+ 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, \%macro_def, \%body)
+ nomsu:defmacro("compile %macro_def to code %body", \(__line_no__), 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",
@@ -53,8 +54,9 @@ immediately:
%s
end
local function macro_wrapper(...) return {statements=macro(...)}; end
- nomsu:defmacro(%s, macro_wrapper, %s);
- end]]):format(args, body_lua, signature, nomsu:repr(("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src)));
+ nomsu:defmacro(%s, %s, macro_wrapper, %s);
+ end]]):format(args, body_lua, signature, nomsu:repr(\%macro_def:get_line_no()),
+ nomsu:repr(("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src)));
return {statements=lua};
end, \(__src__ 1));
@@ -71,7 +73,7 @@ immediately:
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(%s)
+ nomsu:def(%s, \(__line_no__), function(%s)
%s
end, %s);]]):format(signature, args, body_lua, nomsu:repr(src));
return def_lua;
@@ -79,7 +81,7 @@ immediately:
# Rule to make nomsu macros:
immediately:
lua> ".."
- nomsu:defmacro("parse %shorthand as %longhand", (function(nomsu, \%shorthand, \%longhand)
+ nomsu:defmacro("parse %shorthand as %longhand", \(__line_no__), (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",
@@ -95,11 +97,12 @@ immediately:
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)
+ nomsu:defmacro(%s, %s, (function(%s)
local template = nomsu:parse(%s, %s);
local replacement = nomsu:replaced_vars(template, %s);
return nomsu:tree_to_lua(replacement);
- end), %s)]]):format(signature, args, template, nomsu:repr(\%shorthand:get_line_no()), replacements, nomsu:repr(nomsu:source_code(0)));
+ end), %s)]]):format(signature, nomsu:repr(\%shorthand:get_line_no()), args, template,
+ nomsu:repr(\%shorthand:get_line_no()), replacements, nomsu:repr(nomsu:source_code(0)));
return {statements=lua_code};
end), \(__src__ 1));
diff --git a/lib/operators.nom b/lib/operators.nom
index 1670191..7eada51 100644
--- a/lib/operators.nom
+++ b/lib/operators.nom
@@ -19,14 +19,24 @@ compile [..]
%when_true_expr if %condition otherwise %when_false_expr
%when_false_expr unless %condition else %when_true_expr
%when_false_expr unless %condition then %when_true_expr
-..to: ".."
- (function(nomsu, condition)
- if condition then
- return \(%when_true_expr as lua);
+..to:
+ lua> ".."
+ local condition = nomsu:tree_to_lua(\%condition).expr;
+ local when_true = nomsu:tree_to_lua(\%when_true_expr).expr;
+ local when_false = nomsu:tree_to_lua(\%when_false_expr).expr;
+ local safe = {String=true, List=true, Dict=true, Number=true};
+ if safe[\%when_true_expr.type] then
+ return "("..condition.." and "..when_true.." or "..when_false..")";
else
- return \(%when_false_expr as lua);
+ return ([[
+ (function(nomsu)
+ if %s then
+ return %s;
+ else
+ return %s;
+ end
+ end)(nomsu)]]):format(condition, when_true, when_false);
end
- 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/utils.nom b/lib/utils.nom
index ce6dc9d..6af38c8 100644
--- a/lib/utils.nom
+++ b/lib/utils.nom
@@ -113,7 +113,7 @@ lua> ".."
};
for name,code in pairs(colors) do
local escape = "\\"\\\\27["..tostring(code).."m\\""
- nomsu:defmacro(name, function() return escape end, "");
+ nomsu:defmacro(name, \(__line_no__), function() return escape end, "");
end
end
diff --git a/nomsu.lua b/nomsu.lua
index 159b25f..fe15ff6 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -204,7 +204,7 @@ do
self:write_err(...)
return self:write_err("\n")
end,
- def = function(self, signature, fn, src, is_macro)
+ def = function(self, signature, line_no, fn, src, is_macro)
if is_macro == nil then
is_macro = false
end
@@ -221,6 +221,7 @@ do
local def = {
fn = fn,
src = src,
+ line_no = line_no,
is_macro = is_macro,
aliases = { },
def_number = self.__class.def_number,
@@ -275,28 +276,8 @@ do
rawset(where_defs_go, stub, stub_def)
end
end,
- defmacro = function(self, signature, fn, src)
- return self:def(signature, fn, src, true)
- end,
- scoped = function(self, thunk)
- local old_defs = self.defs
- local new_defs = {
- ["#vars"] = setmetatable({ }, {
- __index = self.defs["#vars"]
- }),
- ["#loaded_files"] = setmetatable({ }, {
- __index = self.defs["#loaded_files"]
- })
- }
- self.defs = setmetatable(new_defs, {
- __index = old_defs
- })
- local ok, ret1, ret2 = pcall(thunk, self)
- self.defs = old_defs
- if not ok then
- self:error(ret1)
- end
- return ret1, ret2
+ defmacro = function(self, signature, line_no, fn, src)
+ return self:def(signature, line_no, fn, src, true)
end,
serialize_defs = function(self, scope, after)
if scope == nil then
@@ -367,50 +348,6 @@ do
end
return concat(buff, "\n")
end,
- call = function(self, stub, line_no, ...)
- local def = self.defs[stub]
- if def and def.is_macro and self.callstack[#self.callstack] ~= "#macro" then
- self:error("Attempt to call macro at runtime: " .. tostring(stub) .. "\nThis can be caused by using a macro in a function that is defined before the macro.")
- end
- insert(self.callstack, {
- stub,
- line_no
- })
- if not (def) then
- self:error("Attempt to call undefined function: " .. tostring(stub))
- end
- if not (def.is_macro) then
- self:assert_permission(stub)
- end
- local fn, arg_positions
- fn, arg_positions = def.fn, def.arg_positions
- local args
- do
- 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 = _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 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, unpack(args))
- }
- self.defs = old_defs
- remove(self.callstack)
- return unpack(rets)
- end,
run_macro = function(self, tree)
local args
do
@@ -430,10 +367,7 @@ do
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(args))))
end
- insert(self.callstack, "#macro")
- local ret = self:call(tree.stub, tree:get_line_no(), unpack(args))
- remove(self.callstack)
- return ret
+ return self.defs[tree.stub].fn(self, unpack(args))
end,
dedent = function(self, code)
if not (code:find("\n")) then
@@ -614,11 +548,7 @@ 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)
- if not ok then
- self:errorln(debug.traceback())
- self:error(ret)
- end
+ run_lua_fn(self)
return ret
end,
tree_to_value = function(self, tree, filename)
@@ -639,7 +569,6 @@ end]]):format(lua_code))
end
self:assert(tree, "No tree provided.")
if not tree.type then
- self:errorln(debug.traceback())
self:error("Invalid tree: " .. tostring(repr(tree)))
end
local _exp_0 = tree.type
@@ -829,7 +758,6 @@ end]]):format(lua_code))
tree_to_lua = function(self, tree, filename)
self:assert(tree, "No tree provided.")
if not tree.type then
- self:errorln(debug.traceback())
self:error("Invalid tree: " .. tostring(repr(tree)))
end
local _exp_0 = tree.type
@@ -905,57 +833,45 @@ end]]):format(lua_code))
expr = "(" .. tostring(concat(bits, " ")) .. ")"
}
end
- local args = {
- repr(tree.stub),
- repr(tree:get_line_no())
- }
- local arg_names, escaped_args
- if def then
- arg_names, escaped_args = def.arg_names, def.escaped_args
- else
- arg_names, escaped_args = (function()
- local _accum_0 = { }
- local _len_0 = 1
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local w = _list_0[_index_0]
- if w.type == "Word" then
- _accum_0[_len_0] = w.value
- _len_0 = _len_0 + 1
- end
- end
- return _accum_0
- end)(), { }
- end
- local arg_num = 1
+ local arg_positions = def and def.arg_positions or { }
+ local args = { }
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
local _continue_0 = false
repeat
- local arg = _list_0[_index_0]
- if arg.type == 'Word' then
+ local tok = _list_0[_index_0]
+ if tok.type == "Word" then
_continue_0 = true
break
end
- if escaped_args[arg_names[arg_num]] then
- 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
- self:error("Cannot use [[" .. tostring(arg.src) .. "]] as a function argument to " .. tostring(tree.stub) .. ", since it's not an expression.")
- end
- insert(args, lua.expr)
- end
- arg_num = arg_num + 1
+ local lua = self:tree_to_lua(tok, filename)
+ self: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
if not _continue_0 then
break
end
end
+ if def then
+ local new_args
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_1 = def.arg_positions
+ for _index_0 = 1, #_list_1 do
+ local p = _list_1[_index_0]
+ _accum_0[_len_0] = args[p]
+ _len_0 = _len_0 + 1
+ end
+ new_args = _accum_0
+ end
+ args = new_args
+ end
+ insert(args, 1, "nomsu")
remove(self.compilestack)
return {
- expr = self.__class:comma_separated_items("nomsu:call(", args, ")")
+ expr = self.__class:comma_separated_items("nomsu.defs[" .. tostring(repr(tree.stub)) .. "].fn(", args, ")")
}
elseif "String" == _exp_0 then
local concat_parts = { }
@@ -1266,52 +1182,12 @@ end]]):format(lua_code))
msg = ''
end
if not condition then
- return self:error("Assertion failed: " .. msg)
+ self:error("Assertion failed: " .. msg)
end
+ return condition
end,
error = function(self, msg)
- local error_msg = colored.red("ERROR!")
- if msg and #msg > 0 then
- error_msg = error_msg .. ("\n" .. (colored.bright(colored.yellow(colored.onred(msg)))))
- else
- error_msg = error_msg .. "\n<no message>"
- end
- error_msg = error_msg .. "\nCallstack:"
- local maxlen = max((function()
- local _accum_0 = { }
- local _len_0 = 1
- local _list_0 = self.callstack
- for _index_0 = 1, #_list_0 do
- local c = _list_0[_index_0]
- if c ~= "#macro" then
- _accum_0[_len_0] = #c[2]
- _len_0 = _len_0 + 1
- end
- end
- return _accum_0
- end)())
- for i = #self.callstack, 1, -1 do
- if self.callstack[i] ~= "#macro" then
- local line_no = self.callstack[i][2]
- if line_no then
- local nums
- do
- local _accum_0 = { }
- local _len_0 = 1
- for n in line_no:gmatch(":([0-9]+)") do
- _accum_0[_len_0] = tonumber(n)
- _len_0 = _len_0 + 1
- end
- nums = _accum_0
- end
- line_no = line_no:gsub(":.*$", ":" .. tostring(sum(nums) - #nums + 1))
- end
- error_msg = error_msg .. "\n " .. tostring(("%-" .. tostring(maxlen) .. "s"):format(line_no)) .. "| " .. tostring(self.callstack[i][1])
- end
- end
- error_msg = error_msg .. "\n <top level>"
- self.callstack = { }
- return error(error_msg, 3)
+ return error(msg, 0)
end,
source_code = function(self, level)
if level == nil then
@@ -1338,7 +1214,7 @@ end]]):format(lua_code))
end
return concat(concat_parts)
end
- self:defmacro("do %block", function(self, _block)
+ self:defmacro("do %block", "nomsu.moon", function(self, _block)
local make_line
make_line = function(lua)
return lua.expr and (lua.expr .. ";") or lua.statements
@@ -1351,7 +1227,7 @@ end]]):format(lua_code))
}
end
end)
- self:defmacro("immediately %block", function(self, _block)
+ self:defmacro("immediately %block", "nomsu.moon", function(self, _block)
local lua = self:tree_to_lua(_block)
local lua_code = lua.statements or (lua.expr .. ";")
lua_code = "-- Immediately:\n" .. lua_code
@@ -1360,27 +1236,32 @@ end]]):format(lua_code))
statements = lua_code
}
end)
- self:defmacro("lua> %code", function(self, _code)
+ self:defmacro("lua> %code", "nomsu.moon", function(self, _code)
local lua = nomsu_string_as_lua(self, _code)
return {
statements = lua
}
end)
- self:defmacro("=lua %code", function(self, _code)
+ self:defmacro("=lua %code", "nomsu.moon", function(self, _code)
local lua = nomsu_string_as_lua(self, _code)
return {
expr = lua
}
end)
- self:defmacro("__src__ %level", function(self, _level)
+ self:defmacro("__line_no__", "nomsu.moon", function(self)
+ return {
+ expr = repr(self.compilestack[#self.compilestack]:get_line_no())
+ }
+ end)
+ self:defmacro("__src__ %level", "nomsu.moon", function(self, _level)
return {
expr = repr(self:source_code(self:tree_to_value(_level)))
}
end)
- self:def("run file %filename", function(self, _filename)
+ self:def("run file %filename", "nomsu.moon", function(self, _filename)
return self:run_file(_filename)
end)
- return self:defmacro("require %filename", function(self, _filename)
+ return self:defmacro("require %filename", "nomsu.moon", function(self, _filename)
local filename = self:tree_to_value(_filename)
self:require_file(filename)
return {
@@ -1485,63 +1366,138 @@ if arg then
print("Usage: lua nomsu.lua [-c] [-i] [-p] [-O] [--help] [input [-o output]]")
os.exit()
end
- local c = NomsuCompiler()
- if args.flags["-v"] then
- c.debug = true
- end
- c.skip_precompiled = not args.flags["-O"]
- if args.input then
- if args.flags["-c"] and not args.output then
- args.output = args.input:gsub("%.nom", ".lua")
- end
- local compiled_output = nil
- if args.flags["-p"] then
- local _write = c.write
- c.write = function() end
- compiled_output = io.output()
- elseif args.output then
- compiled_output = io.open(args.output, 'w')
+ local nomsu = NomsuCompiler()
+ local run
+ run = function()
+ if args.flags["-v"] then
+ nomsu.debug = true
end
- if args.input:match(".*%.lua") then
- local retval = dofile(args.input)(c, { })
- else
- local input
- if args.input == '-' then
- input = io.read('*a')
+ nomsu.skip_precompiled = not args.flags["-O"]
+ if args.input then
+ if args.flags["-c"] and not args.output then
+ args.output = args.input:gsub("%.nom", ".lua")
+ end
+ local compiled_output = nil
+ if args.flags["-p"] then
+ local _write = nomsu.write
+ nomsu.write = function() end
+ compiled_output = io.output()
+ elseif args.output then
+ compiled_output = io.open(args.output, 'w')
+ end
+ if args.input:match(".*%.lua") then
+ local retval = dofile(args.input)(nomsu, { })
else
- input = io.open(args.input):read("*a")
+ local input
+ if args.input == '-' then
+ input = io.read('*a')
+ else
+ input = io.open(args.input):read("*a")
+ end
+ local retval, code = nomsu:run(input, args.input)
+ if args.output then
+ compiled_output:write(code)
+ end
end
- local retval, code = c:run(input, args.input)
- if args.output then
- compiled_output:write(code)
+ if args.flags["-p"] then
+ nomsu.write = _write
end
end
- if args.flags["-p"] then
- c.write = _write
+ if args.flags["-i"] then
+ nomsu:run('require "lib/core.nom"', "stdin")
+ while true do
+ local buff = ""
+ while true do
+ io.write(">> ")
+ local line = io.read("*L")
+ if line == "\n" or not line then
+ break
+ end
+ buff = buff .. line
+ end
+ if #buff == 0 then
+ break
+ end
+ local ok, ret = pcall(function()
+ return nomsu:run(buff, "stdin")
+ end)
+ if ok and ret ~= nil then
+ print("= " .. repr(ret))
+ end
+ end
end
end
- if args.flags["-i"] then
- c:run('require "lib/core.nom"', "stdin")
+ local err_hand
+ err_hand = function(error_message)
+ print(tostring(colored.red("ERROR:")) .. " " .. tostring(colored.bright(colored.yellow(colored.onred((error_message or ""))))))
+ print("stack traceback:")
+ local to_lua
+ to_lua = require("moonscript.base").to_lua
+ local nomsu_file = io.open("nomsu.moon")
+ 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 buff = ""
- while true do
- io.write(">> ")
- local line = io.read("*L")
- if line == "\n" or not line then
+ local _continue_0 = false
+ repeat
+ local calling_fn = debug.getinfo(level)
+ if not calling_fn then
break
end
- buff = buff .. line
- end
- if #buff == 0 then
+ if calling_fn.func == run then
+ break
+ end
+ level = level + 1
+ local name = calling_fn.name
+ if name == "run_lua_fn" then
+ _continue_0 = true
+ break
+ 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]))
+ else
+ if calling_fn.istailcall and not name then
+ name = "<tail call>"
+ end
+ if calling_fn.short_src == "./nomsu.moon" then
+ local char = line_table[calling_fn.linedefined - 2]
+ local line_num = 3
+ for _ in nomsu_source:sub(1, char):gmatch("\n") do
+ line_num = line_num + 1
+ end
+ line = colored.cyan(tostring(calling_fn.short_src) .. ":" .. tostring(line_num))
+ name = colored.bright(colored.cyan(name or "???"))
+ else
+ line = colored.blue(tostring(calling_fn.short_src) .. ":" .. tostring(calling_fn.linedefined))
+ name = colored.bright(colored.blue(name or "???"))
+ end
+ end
+ end
+ local _from = colored.dim(colored.white("|"))
+ print(("%32s %s %s"):format(name, _from, line))
+ _continue_0 = true
+ until true
+ if not _continue_0 then
break
end
- local ok, ret = pcall(function()
- return c:run(buff, "stdin")
- end)
- if ok and ret ~= nil then
- print("= " .. repr(ret))
- end
end
+ return os.exit(false, true)
end
+ xpcall(run, err_hand)
end
return NomsuCompiler
diff --git a/nomsu.moon b/nomsu.moon
index 8e9e9dd..2c927ca 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -171,7 +171,7 @@ class NomsuCompiler
@write_err(...)
@write_err("\n")
- def: (signature, fn, src, is_macro=false)=>
+ def: (signature, line_no, fn, src, is_macro=false)=>
if type(signature) == 'string'
signature = @get_stubs {signature}
elseif type(signature) == 'table' and type(signature[1]) == 'string'
@@ -179,7 +179,7 @@ class NomsuCompiler
@assert type(fn) == 'function', "Bad fn: #{repr fn}"
aliases = {}
@@def_number += 1
- def = {:fn, :src, :is_macro, aliases:{}, def_number:@@def_number, defs:@defs}
+ def = {:fn, :src, :line_no, :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])
@@ -204,19 +204,8 @@ class NomsuCompiler
stub_def = setmetatable({:stub, :arg_names, :arg_positions}, {__index:def})
rawset(where_defs_go, stub, stub_def)
- defmacro: (signature, fn, src)=>
- @def(signature, fn, src, true)
-
- scoped: (thunk)=>
- old_defs = @defs
- new_defs =
- ["#vars"]: setmetatable({}, {__index:@defs["#vars"]})
- ["#loaded_files"]: setmetatable({}, {__index:@defs["#loaded_files"]})
- @defs = setmetatable(new_defs, {__index:old_defs})
- ok, ret1, ret2 = pcall thunk, @
- @defs = old_defs
- if not ok then @error(ret1)
- return ret1, ret2
+ defmacro: (signature, line_no, fn, src)=>
+ @def(signature, line_no, fn, src, true)
serialize_defs: (scope=nil, after=nil)=>
after or= @core_defs or 0
@@ -253,39 +242,12 @@ class NomsuCompiler
return concat buff, "\n"
- call: (stub,line_no,...)=>
- def = @defs[stub]
- -- This is a little bit hacky, but having this check is handy for catching mistakes
- -- I use a hash sign in "#macro" so it's guaranteed to not be a valid function name
- if def and def.is_macro and @callstack[#@callstack] != "#macro"
- @error "Attempt to call macro at runtime: #{stub}\nThis can be caused by using a macro in a function that is defined before the macro."
- insert @callstack, {stub, line_no}
- unless def
- @error "Attempt to call undefined function: #{stub}"
- unless def.is_macro
- @assert_permission(stub)
- {: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 i, value in ipairs(args)
- @writeln " #{colored.bright "* #{def.args[i]}"} = #{colored.dim repr(value)}"
- old_defs, @defs = @defs, def.defs
- rets = {fn(self,unpack(args))}
- @defs = old_defs
- remove @callstack
- return unpack(rets)
-
run_macro: (tree)=>
args = [arg for arg in *tree.value when arg.type != "Word"]
if @debug
@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\get_line_no!, unpack(args))
- remove @callstack
- return ret
+ return @defs[tree.stub].fn(self, unpack(args))
dedent: (code)=>
unless code\find("\n")
@@ -405,11 +367,7 @@ 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)
- if not ok
- --@errorln "#{colored.red "Error occurred in statement:"}\n#{colored.yellow tree.src}"
- @errorln debug.traceback!
- @error(ret)
+ run_lua_fn(self)
return ret
tree_to_value: (tree, filename)=>
@@ -426,7 +384,7 @@ end]]\format(lua_code))
-- Return <nomsu code>, <is safe for inline use>
@assert tree, "No tree provided."
if not tree.type
- @errorln debug.traceback()
+ --@errorln debug.traceback()
@error "Invalid tree: #{repr(tree)}"
switch tree.type
when "File"
@@ -558,7 +516,7 @@ end]]\format(lua_code))
-- Return <lua code for value>, <additional lua code>
@assert tree, "No tree provided."
if not tree.type
- @errorln debug.traceback()
+ --@errorln debug.traceback()
@error "Invalid tree: #{repr(tree)}"
switch tree.type
when "File"
@@ -609,26 +567,21 @@ end]]\format(lua_code))
remove @compilestack
return expr:"(#{concat bits, " "})"
- 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
- else
- arg_names, escaped_args = [w.value for w in *tree.value when w.type == "Word"], {}
- arg_num = 1
- 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\get_line_no!}).value[1]"
- else
- lua = @tree_to_lua arg, filename
- if lua.statements
- @error "Cannot use [[#{arg.src}]] as a function argument to #{tree.stub}, since it's not an expression."
- insert args, lua.expr
- arg_num += 1
+ 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, filename)
+ @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]
+ args = new_args
+
+ insert args, 1, "nomsu"
remove @compilestack
- return expr:@@comma_separated_items("nomsu:call(", args, ")")
+ return expr:@@comma_separated_items("nomsu.defs[#{repr tree.stub}].fn(", args, ")")
when "String"
concat_parts = {}
@@ -818,25 +771,10 @@ end]]\format(lua_code))
assert: (condition, msg='')=>
if not condition
@error("Assertion failed: "..msg)
+ return condition
error: (msg)=>
- error_msg = colored.red "ERROR!"
- if msg and #msg > 0
- error_msg ..= "\n" .. (colored.bright colored.yellow colored.onred msg)
- else
- error_msg ..= "\n<no message>"
- error_msg ..= "\nCallstack:"
- maxlen = max([#c[2] for c in *@callstack when c != "#macro"])
- for i=#@callstack,1,-1
- if @callstack[i] != "#macro"
- line_no = @callstack[i][2]
- if line_no
- nums = [tonumber(n) for n in line_no\gmatch(":([0-9]+)")]
- line_no = line_no\gsub(":.*$", ":#{sum(nums) - #nums + 1}")
- error_msg ..= "\n #{"%-#{maxlen}s"\format line_no}| #{@callstack[i][1]}"
- error_msg ..= "\n <top level>"
- @callstack = {}
- error error_msg, 3
+ error msg, 0
source_code: (level=0)=>
@dedent @compilestack[#@compilestack-level].src
@@ -855,35 +793,38 @@ end]]\format(lua_code))
insert concat_parts, lua.expr
return concat(concat_parts)
- @defmacro "do %block", (_block)=>
+ @defmacro "do %block", "nomsu.moon", (_block)=>
make_line = (lua)-> lua.expr and (lua.expr..";") or lua.statements
if _block.type == "Block"
return @tree_to_lua(_block)
else
return expr:"#{@tree_to_lua _block}(nomsu)"
- @defmacro "immediately %block", (_block)=>
+ @defmacro "immediately %block", "nomsu.moon", (_block)=>
lua = @tree_to_lua(_block)
lua_code = lua.statements or (lua.expr..";")
lua_code = "-- Immediately:\n"..lua_code
@run_lua(lua_code)
return statements:lua_code
- @defmacro "lua> %code", (_code)=>
+ @defmacro "lua> %code", "nomsu.moon", (_code)=>
lua = nomsu_string_as_lua(@, _code)
return statements:lua
- @defmacro "=lua %code", (_code)=>
+ @defmacro "=lua %code", "nomsu.moon", (_code)=>
lua = nomsu_string_as_lua(@, _code)
return expr:lua
- @defmacro "__src__ %level", (_level)=>
+ @defmacro "__line_no__", "nomsu.moon", ()=>
+ expr: repr(@compilestack[#@compilestack]\get_line_no!)
+
+ @defmacro "__src__ %level", "nomsu.moon", (_level)=>
expr: repr(@source_code(@tree_to_value(_level)))
- @def "run file %filename", (_filename)=>
+ @def "run file %filename", "nomsu.moon", (_filename)=>
@run_file(_filename)
- @defmacro "require %filename", (_filename)=>
+ @defmacro "require %filename", "nomsu.moon", (_filename)=>
filename = @tree_to_value(_filename)
@require_file(filename)
return statements:"nomsu:require_file(#{repr filename});"
@@ -904,52 +845,96 @@ if arg
print "Usage: lua nomsu.lua [-c] [-i] [-p] [-O] [--help] [input [-o output]]"
os.exit!
- c = NomsuCompiler()
-
- if args.flags["-v"]
- c.debug = true
-
- c.skip_precompiled = not args.flags["-O"]
- if args.input
- -- Read a file or stdin and output either the printouts or the compiled lua
- if args.flags["-c"] and not args.output
- args.output = args.input\gsub("%.nom", ".lua")
- compiled_output = nil
- if args.flags["-p"]
- _write = c.write
- c.write = ->
- compiled_output = io.output()
- elseif args.output
- compiled_output = io.open(args.output, 'w')
-
- if args.input\match(".*%.lua")
- retval = dofile(args.input)(c, {})
- else
- input = if args.input == '-'
- io.read('*a')
- else io.open(args.input)\read("*a")
- retval, code = c\run(input, args.input)
- if args.output
- compiled_output\write(code)
-
- if args.flags["-p"]
- c.write = _write
-
- if args.flags["-i"]
- -- REPL
- c\run('require "lib/core.nom"', "stdin")
- while true
- buff = ""
+ nomsu = NomsuCompiler()
+
+ run = ->
+ if args.flags["-v"]
+ nomsu.debug = true
+
+ nomsu.skip_precompiled = not args.flags["-O"]
+ if args.input
+ -- Read a file or stdin and output either the printouts or the compiled lua
+ if args.flags["-c"] and not args.output
+ args.output = args.input\gsub("%.nom", ".lua")
+ compiled_output = nil
+ if args.flags["-p"]
+ _write = nomsu.write
+ nomsu.write = ->
+ compiled_output = io.output()
+ elseif args.output
+ compiled_output = io.open(args.output, 'w')
+
+ if args.input\match(".*%.lua")
+ retval = dofile(args.input)(nomsu, {})
+ else
+ input = if args.input == '-'
+ io.read('*a')
+ else io.open(args.input)\read("*a")
+ retval, code = nomsu\run(input, args.input)
+ if args.output
+ compiled_output\write(code)
+
+ if args.flags["-p"]
+ nomsu.write = _write
+
+ if args.flags["-i"]
+ -- REPL
+ nomsu\run('require "lib/core.nom"', "stdin")
while true
- io.write(">> ")
- line = io.read("*L")
- if line == "\n" or not line
+ buff = ""
+ while true
+ io.write(">> ")
+ line = io.read("*L")
+ if line == "\n" or not line
+ break
+ buff ..= line
+ if #buff == 0
break
- buff ..= line
- if #buff == 0
- break
- ok, ret = pcall(-> c\run(buff, "stdin"))
- if ok and ret != nil
- print "= "..repr(ret)
+ ok, ret = pcall(-> nomsu\run(buff, "stdin"))
+ if ok and ret != nil
+ print "= "..repr(ret)
+
+ err_hand = (error_message)->
+ print("#{colored.red "ERROR:"} #{colored.bright colored.yellow colored.onred (error_message or "")}")
+ print("stack traceback:")
+
+ import to_lua from require "moonscript.base"
+ nomsu_file = io.open("nomsu.moon")
+ nomsu_source = nomsu_file\read("*a")
+ _, 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)
+ if not calling_fn then break
+ if calling_fn.func == run then break
+ level += 1
+ 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]))
+ else
+ if calling_fn.istailcall and not name
+ name = "<tail call>"
+ if calling_fn.short_src == "./nomsu.moon"
+ -- Ugh, magic numbers, but this works
+ char = line_table[calling_fn.linedefined-2]
+ line_num = 3
+ for _ in nomsu_source\sub(1,char)\gmatch("\n") do line_num += 1
+ line = colored.cyan("#{calling_fn.short_src}:#{line_num}")
+ name = colored.bright(colored.cyan(name or "???"))
+ else
+ line = colored.blue("#{calling_fn.short_src}:#{calling_fn.linedefined}")
+ name = colored.bright(colored.blue(name or "???"))
+ _from = colored.dim colored.white "|"
+ print(("%32s %s %s")\format(name, _from, line))
+
+ os.exit(false, true)
+
+ xpcall(run, err_hand)
return NomsuCompiler