aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--error_handling.lua214
-rw-r--r--error_handling.moon143
-rw-r--r--nomsu.lua229
-rwxr-xr-xnomsu.moon154
-rw-r--r--parser.moon1
5 files changed, 375 insertions, 366 deletions
diff --git a/error_handling.lua b/error_handling.lua
new file mode 100644
index 0000000..f5bd10b
--- /dev/null
+++ b/error_handling.lua
@@ -0,0 +1,214 @@
+local debug_getinfo = debug.getinfo
+local ok, to_lua = pcall(function()
+ return require('moonscript.base').to_lua
+end)
+if not ok then
+ to_lua = nil
+end
+local moonscript_line_tables = setmetatable({ }, {
+ __index = function(self, filename)
+ if not (to_lua) then
+ return nil
+ end
+ local _, line_table = to_lua(FILE_CACHE[filename])
+ self[filename] = line_table
+ return line_table
+ end
+})
+debug.getinfo = function(thread, f, what)
+ if what == nil then
+ f, what, thread = thread, f, nil
+ end
+ if type(f) == 'number' then
+ f = f + 1
+ end
+ local info
+ if thread == nil then
+ info = debug_getinfo(f, what)
+ else
+ info = debug_getinfo(thread, f, what)
+ end
+ if not info or not info.func then
+ return info
+ end
+ if info.short_src or info.source or info.linedefine or info.currentline then
+ do
+ local map = SOURCE_MAP[info.source]
+ if map then
+ if info.currentline then
+ info.currentline = assert(map[info.currentline])
+ end
+ if info.linedefined then
+ info.linedefined = assert(map[info.linedefined])
+ end
+ if info.lastlinedefined then
+ info.lastlinedefined = assert(map[info.lastlinedefined])
+ end
+ info.short_src = info.source:match('@([^[]*)') or info.short_src
+ end
+ end
+ end
+ return info
+end
+local print_err_msg
+print_err_msg = function(error_message, stack_offset)
+ if stack_offset == nil then
+ stack_offset = 3
+ end
+ io.stderr:write(tostring(colored.red("ERROR:")) .. " " .. tostring(colored.bright(colored.red((error_message or "")))) .. "\n")
+ io.stderr:write("stack traceback:\n")
+ ok, to_lua = pcall(function()
+ return require('moonscript.base').to_lua
+ end)
+ if not ok then
+ to_lua = function()
+ return nil
+ end
+ end
+ local LINE_TABLES = setmetatable({ }, {
+ __index = function(self, file)
+ local _, line_table = to_lua(file)
+ self[file] = line_table or false
+ return line_table or false
+ end
+ })
+ local get_line
+ get_line = function(file, line_no)
+ local start = LINE_STARTS[file][line_no] or 1
+ local stop = (LINE_STARTS[file][line_no + 1] or 0) - 1
+ return file:sub(start, stop)
+ end
+ local level = stack_offset
+ while true do
+ local _continue_0 = false
+ repeat
+ local calling_fn = debug_getinfo(level)
+ if not calling_fn then
+ break
+ end
+ if calling_fn.func == run then
+ break
+ end
+ level = level + 1
+ local name = calling_fn.name and "function '" .. tostring(calling_fn.name) .. "'" or nil
+ if calling_fn.linedefined == 0 then
+ name = "main chunk"
+ end
+ if name == "run_lua_fn" then
+ _continue_0 = true
+ break
+ end
+ local line = nil
+ do
+ local map = SOURCE_MAP[calling_fn.source]
+ if map then
+ if calling_fn.currentline then
+ calling_fn.currentline = assert(map[calling_fn.currentline])
+ end
+ if calling_fn.linedefined then
+ calling_fn.linedefined = assert(map[calling_fn.linedefined])
+ end
+ if calling_fn.lastlinedefined then
+ calling_fn.lastlinedefined = assert(map[calling_fn.lastlinedefined])
+ end
+ local filename, start, stop = calling_fn.source:match('@([^[]*)%[([0-9]+):([0-9]+)]')
+ assert(filename)
+ local file = FILE_CACHE[filename]:sub(tonumber(start), tonumber(stop))
+ local err_line = get_line(file, calling_fn.currentline):sub(1, -2)
+ local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)")))
+ name = "action '" .. tostring(calling_fn.name) .. "'"
+ line = colored.yellow(tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. "\n " .. tostring(offending_statement))
+ else
+ local file
+ ok, file = pcall(function()
+ return FILE_CACHE[calling_fn.short_src]
+ end)
+ if not ok then
+ file = nil
+ end
+ local line_num
+ if name == nil then
+ local search_level = level
+ local _info = debug.getinfo(search_level)
+ while _info and (_info.func == pcall or _info.func == xpcall) do
+ search_level = search_level + 1
+ _info = debug.getinfo(search_level)
+ end
+ if _info then
+ for i = 1, 999 do
+ local varname, val = debug.getlocal(search_level, i)
+ if not varname then
+ break
+ end
+ if val == calling_fn.func then
+ name = "local '" .. tostring(varname) .. "'"
+ if not varname:match("%(") then
+ break
+ end
+ end
+ end
+ if not (name) then
+ for i = 1, _info.nups do
+ local varname, val = debug.getupvalue(_info.func, i)
+ if not varname then
+ break
+ end
+ if val == calling_fn.func then
+ name = "upvalue '" .. tostring(varname) .. "'"
+ if not varname:match("%(") then
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+ if file and calling_fn.short_src:match(".moon$") and LINE_TABLES[file] then
+ local char = LINE_TABLES[file][calling_fn.currentline]
+ line_num = 1
+ for _ in file:sub(1, char):gmatch("\n") do
+ line_num = line_num + 1
+ end
+ line = colored.cyan(tostring(calling_fn.short_src) .. ":" .. tostring(line_num) .. " in " .. tostring(name or '?'))
+ else
+ line_num = calling_fn.currentline
+ if calling_fn.short_src == '[C]' then
+ line = colored.green(tostring(calling_fn.short_src) .. " in " .. tostring(name or '?'))
+ else
+ line = colored.blue(tostring(calling_fn.short_src) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name or '?'))
+ end
+ end
+ if file then
+ local err_line = get_line(file, line_num):sub(1, -2)
+ local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)$")))
+ line = line .. ("\n " .. offending_statement)
+ end
+ end
+ end
+ io.stderr:write(" " .. tostring(line) .. "\n")
+ if calling_fn.istailcall then
+ io.stderr:write(" " .. tostring(colored.dim(colored.white(" (...tail calls...)"))) .. "\n")
+ end
+ _continue_0 = true
+ until true
+ if not _continue_0 then
+ break
+ end
+ end
+ return io.stderr:flush()
+end
+local err_hand
+err_hand = function(error_message)
+ print_err_msg(error_message)
+ return os.exit(false, true)
+end
+local has_ldt, ldt = pcall(require, 'ldt')
+local safe_run
+if has_ldt then
+ safe_run = ldt.guard
+else
+ safe_run = function(fn)
+ return xpcall(fn, err_hand)
+ end
+end
+return safe_run
diff --git a/error_handling.moon b/error_handling.moon
new file mode 100644
index 0000000..101c11e
--- /dev/null
+++ b/error_handling.moon
@@ -0,0 +1,143 @@
+-- This file contains the logic for making nicer error messages
+debug_getinfo = debug.getinfo
+export SOURCE_MAP
+
+ok, to_lua = pcall -> require('moonscript.base').to_lua
+if not ok then to_lua = nil
+moonscript_line_tables = setmetatable {}, {
+ __index: (filename)=>
+ return nil unless to_lua
+ _, line_table = to_lua(FILE_CACHE[filename])
+ self[filename] = line_table
+ return line_table
+}
+
+-- Make a better version of debug.getinfo that provides info about the original source
+-- where the error came from, even if that's in another language.
+debug.getinfo = (thread,f,what)->
+ if what == nil
+ f,what,thread = thread,f,nil
+ if type(f) == 'number' then f += 1 -- Account for this wrapper function
+ info = if thread == nil
+ debug_getinfo(f,what)
+ else debug_getinfo(thread,f,what)
+ if not info or not info.func then return info
+ if info.short_src or info.source or info.linedefine or info.currentline
+ -- TODO: get name properly
+ if map = SOURCE_MAP[info.source]
+ if info.currentline
+ info.currentline = assert(map[info.currentline])
+ if info.linedefined
+ info.linedefined = assert(map[info.linedefined])
+ if info.lastlinedefined
+ info.lastlinedefined = assert(map[info.lastlinedefined])
+ info.short_src = info.source\match('@([^[]*)') or info.short_src
+ return info
+
+print_err_msg = (error_message, stack_offset=3)->
+ io.stderr\write("#{colored.red "ERROR:"} #{colored.bright colored.red (error_message or "")}\n")
+ io.stderr\write("stack traceback:\n")
+
+ -- TODO: properly print out the calling site of nomsu code, not just the *called* code
+ ok, to_lua = pcall -> require('moonscript.base').to_lua
+ if not ok then to_lua = -> nil
+ LINE_TABLES = setmetatable {},
+ __index: (file)=>
+ _, line_table = to_lua(file)
+ self[file] = line_table or false
+ return line_table or false
+
+ get_line = (file, line_no)->
+ start = LINE_STARTS[file][line_no] or 1
+ stop = (LINE_STARTS[file][line_no+1] or 0) - 1
+ return file\sub(start, stop)
+
+ level = stack_offset
+ while true
+ -- TODO: reduce duplicate code
+ calling_fn = debug_getinfo(level)
+ if not calling_fn then break
+ if calling_fn.func == run then break
+ level += 1
+ name = calling_fn.name and "function '#{calling_fn.name}'" or nil
+ if calling_fn.linedefined == 0 then name = "main chunk"
+ if name == "run_lua_fn" then continue
+ line = nil
+ if map = SOURCE_MAP[calling_fn.source]
+ if calling_fn.currentline
+ calling_fn.currentline = assert(map[calling_fn.currentline])
+ if calling_fn.linedefined
+ calling_fn.linedefined = assert(map[calling_fn.linedefined])
+ if calling_fn.lastlinedefined
+ calling_fn.lastlinedefined = assert(map[calling_fn.lastlinedefined])
+ --calling_fn.short_src = calling_fn.source\match('"([^[]*)')
+ filename,start,stop = calling_fn.source\match('@([^[]*)%[([0-9]+):([0-9]+)]')
+ assert(filename)
+ file = FILE_CACHE[filename]\sub(tonumber(start),tonumber(stop))
+ err_line = get_line(file, calling_fn.currentline)\sub(1,-2)
+ offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)")))
+ -- TODO: get name properly
+ name = "action '#{calling_fn.name}'"
+ line = colored.yellow("#{filename}:#{calling_fn.currentline} in #{name}\n #{offending_statement}")
+ else
+ ok, file = pcall ->FILE_CACHE[calling_fn.short_src]
+ if not ok then file = nil
+ local line_num
+ if name == nil
+ search_level = level
+ _info = debug.getinfo(search_level)
+ while _info and (_info.func == pcall or _info.func == xpcall)
+ search_level += 1
+ _info = debug.getinfo(search_level)
+ if _info
+ for i=1,999
+ varname, val = debug.getlocal(search_level, i)
+ if not varname then break
+ if val == calling_fn.func
+ name = "local '#{varname}'"
+ if not varname\match("%(")
+ break
+ unless name
+ for i=1,_info.nups
+ varname, val = debug.getupvalue(_info.func, i)
+ if not varname then break
+ if val == calling_fn.func
+ name = "upvalue '#{varname}'"
+ if not varname\match("%(")
+ break
+ if file and calling_fn.short_src\match(".moon$") and LINE_TABLES[file]
+ char = LINE_TABLES[file][calling_fn.currentline]
+ line_num = 1
+ for _ in file\sub(1,char)\gmatch("\n") do line_num += 1
+ line = colored.cyan("#{calling_fn.short_src}:#{line_num} in #{name or '?'}")
+ else
+ line_num = calling_fn.currentline
+ if calling_fn.short_src == '[C]'
+ line = colored.green("#{calling_fn.short_src} in #{name or '?'}")
+ else
+ line = colored.blue("#{calling_fn.short_src}:#{calling_fn.currentline} in #{name or '?'}")
+
+ if file
+ err_line = get_line(file, line_num)\sub(1,-2)
+ offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)$")))
+ line ..= "\n "..offending_statement
+ io.stderr\write(" #{line}\n")
+ if calling_fn.istailcall
+ io.stderr\write(" #{colored.dim colored.white " (...tail calls...)"}\n")
+
+ io.stderr\flush!
+
+err_hand = (error_message)->
+ print_err_msg error_message
+ os.exit(false, true)
+
+-- Note: xpcall has a slightly different API in Lua <=5.1 vs. >=5.2, but this works
+-- for both APIs
+has_ldt, ldt = pcall(require,'ldt')
+safe_run = if has_ldt
+ ldt.guard
+else
+ (fn)->
+ xpcall(fn, err_hand)
+
+return safe_run
diff --git a/nomsu.lua b/nomsu.lua
index b8e5137..1f53102 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -23,7 +23,6 @@ do
local _obj_0 = string
match, sub, rep, gsub, format, byte, match, find = _obj_0.match, _obj_0.sub, _obj_0.rep, _obj_0.gsub, _obj_0.format, _obj_0.byte, _obj_0.match, _obj_0.find
end
-local debug_getinfo = debug.getinfo
local NomsuCode, LuaCode, Source
do
local _obj_0 = require("code_obj")
@@ -32,6 +31,7 @@ end
local AST = require("nomsu_tree")
local parse = require("parser")
local STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2"
+SOURCE_MAP = { }
string.as_lua_id = function(str)
local argnum = 0
str = gsub(str, "x([0-9A-F][0-9A-F])", "x\0%1")
@@ -214,7 +214,6 @@ do
NomsuCompiler.parse = function(self, ...)
return parse(...)
end
- NomsuCompiler.source_map = { }
local to_add = {
repr = repr,
stringify = stringify,
@@ -518,7 +517,7 @@ do
error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(line_numbered_lua)))) .. "\n\n" .. tostring(err), 0)
end
local source_key = tostring(source or lua.source)
- if not (self.source_map[source_key]) then
+ if not (SOURCE_MAP[source_key]) then
local map = { }
local offset = 1
source = source or lua.source
@@ -547,7 +546,7 @@ do
fn(lua)
map[lua_line] = map[lua_line] or nomsu_line
map[0] = 0
- self.source_map[source_key] = map
+ SOURCE_MAP[source_key] = map
end
return run_lua_fn()
end
@@ -1148,7 +1147,7 @@ do
end
end
end
-if arg and debug_getinfo(2).func ~= require then
+if arg and debug.getinfo(2).func ~= require then
local parser = re.compile([[ args <- {| (flag ";")* {:inputs: {| ({file} ";")* |} :} {:nomsu_args: {| ("--;" ({[^;]*} ";")*)? |} :} ";"? |} !.
flag <-
{:interactive: ("-i" -> true) :}
@@ -1187,204 +1186,6 @@ OPTIONS
end
local nomsu = NomsuCompiler
nomsu.arg = args.nomsu_args
- local ok, to_lua = pcall(function()
- return require('moonscript.base').to_lua
- end)
- if not ok then
- to_lua = nil
- end
- local moonscript_line_tables = setmetatable({ }, {
- __index = function(self, filename)
- if not (to_lua) then
- return nil
- end
- local _, line_table = to_lua(FILE_CACHE[filename])
- self[filename] = line_table
- return line_table
- end
- })
- debug.getinfo = function(thread, f, what)
- if what == nil then
- f, what, thread = thread, f, nil
- end
- if type(f) == 'number' then
- f = f + 1
- end
- local info
- if thread == nil then
- info = debug_getinfo(f, what)
- else
- info = debug_getinfo(thread, f, what)
- end
- if not info or not info.func then
- return info
- end
- if info.short_src or info.source or info.linedefine or info.currentline then
- do
- local map = nomsu.source_map[info.source]
- if map then
- if info.currentline then
- info.currentline = assert(map[info.currentline])
- end
- if info.linedefined then
- info.linedefined = assert(map[info.linedefined])
- end
- if info.lastlinedefined then
- info.lastlinedefined = assert(map[info.lastlinedefined])
- end
- end
- end
- end
- return info
- end
- local print_err_msg
- print_err_msg = function(error_message, stack_offset)
- if stack_offset == nil then
- stack_offset = 3
- end
- io.stderr:write(tostring(colored.red("ERROR:")) .. " " .. tostring(colored.bright(colored.red((error_message or "")))) .. "\n")
- io.stderr:write("stack traceback:\n")
- ok, to_lua = pcall(function()
- return require('moonscript.base').to_lua
- end)
- if not ok then
- to_lua = function()
- return nil
- end
- end
- local nomsu_source = FILE_CACHE["nomsu.moon"]
- local LINE_TABLES = setmetatable({ }, {
- __index = function(self, file)
- local _, line_table = to_lua(file)
- self[file] = line_table or false
- return line_table or false
- end
- })
- local get_line
- get_line = function(file, line_no)
- local start = LINE_STARTS[file][line_no] or 1
- local stop = (LINE_STARTS[file][line_no + 1] or 0) - 1
- return file:sub(start, stop)
- end
- local level = stack_offset
- while true do
- local _continue_0 = false
- repeat
- local calling_fn = debug_getinfo(level)
- if not calling_fn then
- break
- end
- if calling_fn.func == run then
- break
- end
- level = level + 1
- local name = calling_fn.name and "function '" .. tostring(calling_fn.name) .. "'" or nil
- if calling_fn.linedefined == 0 then
- name = "main chunk"
- end
- if name == "run_lua_fn" then
- _continue_0 = true
- break
- end
- local line = nil
- do
- local map = nomsu.source_map[calling_fn.source]
- if map then
- if calling_fn.currentline then
- calling_fn.currentline = assert(map[calling_fn.currentline])
- end
- if calling_fn.linedefined then
- calling_fn.linedefined = assert(map[calling_fn.linedefined])
- end
- if calling_fn.lastlinedefined then
- calling_fn.lastlinedefined = assert(map[calling_fn.lastlinedefined])
- end
- local filename, start, stop = calling_fn.source:match('@([^[]*)%[([0-9]+):([0-9]+)]')
- assert(filename)
- local file = FILE_CACHE[filename]:sub(tonumber(start), tonumber(stop))
- local err_line = get_line(file, calling_fn.currentline):sub(1, -2)
- local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)")))
- name = "action '" .. tostring(calling_fn.name) .. "'"
- line = colored.yellow(tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. "\n " .. tostring(offending_statement))
- else
- local file
- ok, file = pcall(function()
- return FILE_CACHE[calling_fn.short_src]
- end)
- if not ok then
- file = nil
- end
- local line_num
- if name == nil then
- local search_level = level
- local _info = debug.getinfo(search_level)
- while _info and (_info.func == pcall or _info.func == xpcall) do
- search_level = search_level + 1
- _info = debug.getinfo(search_level)
- end
- if _info then
- for i = 1, 999 do
- local varname, val = debug.getlocal(search_level, i)
- if not varname then
- break
- end
- if val == calling_fn.func then
- name = "local '" .. tostring(varname) .. "'"
- if not varname:match("%(") then
- break
- end
- end
- end
- if not (name) then
- for i = 1, _info.nups do
- local varname, val = debug.getupvalue(_info.func, i)
- if not varname then
- break
- end
- if val == calling_fn.func then
- name = "upvalue '" .. tostring(varname) .. "'"
- if not varname:match("%(") then
- break
- end
- end
- end
- end
- end
- end
- if file and calling_fn.short_src:match(".moon$") and LINE_TABLES[file] then
- local char = LINE_TABLES[file][calling_fn.currentline]
- line_num = 1
- for _ in file:sub(1, char):gmatch("\n") do
- line_num = line_num + 1
- end
- line = colored.cyan(tostring(calling_fn.short_src) .. ":" .. tostring(line_num) .. " in " .. tostring(name or '?'))
- else
- line_num = calling_fn.currentline
- if calling_fn.short_src == '[C]' then
- line = colored.green(tostring(calling_fn.short_src) .. " in " .. tostring(name or '?'))
- else
- line = colored.blue(tostring(calling_fn.short_src) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name or '?'))
- end
- end
- if file then
- local err_line = get_line(file, line_num):sub(1, -2)
- local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)$")))
- line = line .. ("\n " .. offending_statement)
- end
- end
- end
- io.stderr:write(" " .. tostring(line) .. "\n")
- if calling_fn.istailcall then
- io.stderr:write(" " .. tostring(colored.dim(colored.white(" (...tail calls...)"))) .. "\n")
- end
- _continue_0 = true
- until true
- if not _continue_0 then
- break
- end
- end
- return io.stderr:flush()
- end
local run
run = function()
for i, input in ipairs(args.inputs) do
@@ -1456,8 +1257,7 @@ OPTIONS
local filename = input_files[_index_0]
if args.syntax then
local file_contents = io.open(filename):read('*a')
- local err
- ok, err = pcall(nomsu.parse, nomsu, file_contents, Source(filename, 1, #file_contents))
+ local ok, err = pcall(nomsu.parse, nomsu, file_contents, Source(filename, 1, #file_contents))
if not ok then
insert(parse_errs, err)
elseif print_file then
@@ -1515,8 +1315,7 @@ OPTIONS
err_hand = function(error_message)
return print_err_msg(error_message)
end
- local ret
- ok, ret = xpcall(nomsu.run, err_hand, nomsu, buff, Source("REPL#" .. repl_line, 1, #buff))
+ local ok, ret = xpcall(nomsu.run, err_hand, nomsu, buff, Source("REPL#" .. repl_line, 1, #buff))
if ok and ret ~= nil then
print("= " .. repr(ret))
elseif not ok then
@@ -1525,19 +1324,7 @@ OPTIONS
end
end
end
- local err_hand
- err_hand = function(error_message)
- print_err_msg(error_message)
- return os.exit(false, true)
- end
- do
- local ldt
- ok, ldt = pcall(require, 'ldt')
- if ok then
- ldt.guard(run)
- else
- xpcall(run, err_hand)
- end
- end
+ local run_safely = require("error_handling")
+ run_safely(run)
end
return NomsuCompiler
diff --git a/nomsu.moon b/nomsu.moon
index 230b4fb..2435920 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -21,11 +21,14 @@ colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring
{:insert, :remove, :concat} = table
unpack or= table.unpack
{:match, :sub, :rep, :gsub, :format, :byte, :match, :find} = string
-debug_getinfo = debug.getinfo
{:NomsuCode, :LuaCode, :Source} = require "code_obj"
AST = require "nomsu_tree"
parse = require("parser")
STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2"
+-- Mapping from source string (e.g. "@core/metaprogramming.nom[1:100]") to a mapping
+-- from lua line number to nomsu line number
+export SOURCE_MAP
+SOURCE_MAP = {}
string.as_lua_id = (str)->
argnum = 0
@@ -147,9 +150,6 @@ with NomsuCompiler
.nomsu = NomsuCompiler
.parse = (...)=> parse(...)
- -- Mapping from source string (e.g. "@core/metaprogramming.nom[1:100]") to a mapping
- -- from lua line number to nomsu line number
- .source_map = {}
-- Discretionary/convenience stuff
to_add = {
repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re,
@@ -347,7 +347,7 @@ with NomsuCompiler
"\n")
error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack line_numbered_lua}\n\n#{err}", 0)
source_key = tostring(source or lua.source)
- unless @source_map[source_key]
+ unless SOURCE_MAP[source_key]
map = {}
offset = 1
source or= lua.source
@@ -368,7 +368,7 @@ with NomsuCompiler
map[lua_line] or= nomsu_line
map[0] = 0
-- Mapping from lua line number to nomsu line numbers
- @source_map[source_key] = map
+ SOURCE_MAP[source_key] = map
return run_lua_fn!
@@ -798,7 +798,7 @@ with NomsuCompiler
-- Command line interface:
-- Only run this code if this file was run directly with command line arguments, and not require()'d:
-if arg and debug_getinfo(2).func != require
+if arg and debug.getinfo(2).func != require
parser = re.compile([[
args <- {| (flag ";")* {:inputs: {| ({file} ";")* |} :} {:nomsu_args: {| ("--;" ({[^;]*} ";")*)? |} :} ";"? |} !.
flag <-
@@ -835,130 +835,6 @@ OPTIONS
nomsu = NomsuCompiler
nomsu.arg = args.nomsu_args
-
- ok, to_lua = pcall -> require('moonscript.base').to_lua
- if not ok then to_lua = nil
- moonscript_line_tables = setmetatable {}, {
- __index: (filename)=>
- return nil unless to_lua
- _, line_table = to_lua(FILE_CACHE[filename])
- self[filename] = line_table
- return line_table
- }
-
- debug.getinfo = (thread,f,what)->
- if what == nil
- f,what,thread = thread,f,nil
- if type(f) == 'number' then f += 1 -- Account for this wrapper function
- info = if thread == nil
- debug_getinfo(f,what)
- else debug_getinfo(thread,f,what)
- if not info or not info.func then return info
- if info.short_src or info.source or info.linedefine or info.currentline
- -- TODO: get name properly
- if map = nomsu.source_map[info.source]
- if info.currentline
- info.currentline = assert(map[info.currentline])
- if info.linedefined
- info.linedefined = assert(map[info.linedefined])
- if info.lastlinedefined
- info.lastlinedefined = assert(map[info.lastlinedefined])
- --info.short_src = info.source\match('@([^[]*)')
- return info
-
- print_err_msg = (error_message, stack_offset=3)->
- io.stderr\write("#{colored.red "ERROR:"} #{colored.bright colored.red (error_message or "")}\n")
- io.stderr\write("stack traceback:\n")
-
- -- TODO: properly print out the calling site of nomsu code, not just the *called* code
- ok, to_lua = pcall -> require('moonscript.base').to_lua
- if not ok then to_lua = -> nil
- nomsu_source = FILE_CACHE["nomsu.moon"]
- LINE_TABLES = setmetatable {},
- __index: (file)=>
- _, line_table = to_lua(file)
- self[file] = line_table or false
- return line_table or false
-
- get_line = (file, line_no)->
- start = LINE_STARTS[file][line_no] or 1
- stop = (LINE_STARTS[file][line_no+1] or 0) - 1
- return file\sub(start, stop)
-
- level = stack_offset
- while true
- -- TODO: reduce duplicate code
- calling_fn = debug_getinfo(level)
- if not calling_fn then break
- if calling_fn.func == run then break
- level += 1
- name = calling_fn.name and "function '#{calling_fn.name}'" or nil
- if calling_fn.linedefined == 0 then name = "main chunk"
- if name == "run_lua_fn" then continue
- line = nil
- if map = nomsu.source_map[calling_fn.source]
- if calling_fn.currentline
- calling_fn.currentline = assert(map[calling_fn.currentline])
- if calling_fn.linedefined
- calling_fn.linedefined = assert(map[calling_fn.linedefined])
- if calling_fn.lastlinedefined
- calling_fn.lastlinedefined = assert(map[calling_fn.lastlinedefined])
- --calling_fn.short_src = calling_fn.source\match('"([^[]*)')
- filename,start,stop = calling_fn.source\match('@([^[]*)%[([0-9]+):([0-9]+)]')
- assert(filename)
- file = FILE_CACHE[filename]\sub(tonumber(start),tonumber(stop))
- err_line = get_line(file, calling_fn.currentline)\sub(1,-2)
- offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)")))
- -- TODO: get name properly
- name = "action '#{calling_fn.name}'"
- line = colored.yellow("#{filename}:#{calling_fn.currentline} in #{name}\n #{offending_statement}")
- else
- ok, file = pcall ->FILE_CACHE[calling_fn.short_src]
- if not ok then file = nil
- local line_num
- if name == nil
- search_level = level
- _info = debug.getinfo(search_level)
- while _info and (_info.func == pcall or _info.func == xpcall)
- search_level += 1
- _info = debug.getinfo(search_level)
- if _info
- for i=1,999
- varname, val = debug.getlocal(search_level, i)
- if not varname then break
- if val == calling_fn.func
- name = "local '#{varname}'"
- if not varname\match("%(")
- break
- unless name
- for i=1,_info.nups
- varname, val = debug.getupvalue(_info.func, i)
- if not varname then break
- if val == calling_fn.func
- name = "upvalue '#{varname}'"
- if not varname\match("%(")
- break
- if file and calling_fn.short_src\match(".moon$") and LINE_TABLES[file]
- char = LINE_TABLES[file][calling_fn.currentline]
- line_num = 1
- for _ in file\sub(1,char)\gmatch("\n") do line_num += 1
- line = colored.cyan("#{calling_fn.short_src}:#{line_num} in #{name or '?'}")
- else
- line_num = calling_fn.currentline
- if calling_fn.short_src == '[C]'
- line = colored.green("#{calling_fn.short_src} in #{name or '?'}")
- else
- line = colored.blue("#{calling_fn.short_src}:#{calling_fn.currentline} in #{name or '?'}")
-
- if file
- err_line = get_line(file, line_num)\sub(1,-2)
- offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)$")))
- line ..= "\n "..offending_statement
- io.stderr\write(" #{line}\n")
- if calling_fn.istailcall
- io.stderr\write(" #{colored.dim colored.white " (...tail calls...)"}\n")
-
- io.stderr\flush!
run = ->
@@ -1069,19 +945,7 @@ OPTIONS
elseif not ok
print_err_msg ret
- err_hand = (error_message)->
- print_err_msg error_message
- os.exit(false, true)
-
- -- Note: xpcall has a slightly different API in Lua <=5.1 vs. >=5.2, but this works
- -- for both APIs
- -- TODO: revert back to old error handler
-
- --require('ProFi')\profile "scratch/profile.txt", (profi)->
- do
- ok, ldt = pcall(require,'ldt')
- if ok
- ldt.guard run
- else xpcall(run, err_hand)
+ run_safely = require "error_handling"
+ run_safely(run)
return NomsuCompiler
diff --git a/parser.moon b/parser.moon
index ad9d0d0..4de1fe2 100644
--- a/parser.moon
+++ b/parser.moon
@@ -1,3 +1,4 @@
+-- This file contains the parser, which converts Nomsu text into abstract syntax trees
lpeg = require 'lpeg'
re = require 're'
lpeg.setmaxstack 10000