Added *much* better filename and line number error reporting.
This commit is contained in:
parent
06d05ebeee
commit
1822df2b1a
50
nomsu.moon
50
nomsu.moon
@ -55,7 +55,7 @@ check_nodent = (subject,end_pos,spaces)->
|
|||||||
-- Number 1, "String", %Var, [List], (expression), {Thunk}, \Nomsu, FunctionCall, File
|
-- Number 1, "String", %Var, [List], (expression), {Thunk}, \Nomsu, FunctionCall, File
|
||||||
|
|
||||||
nomsu = [=[
|
nomsu = [=[
|
||||||
file <- ({ {| shebang?
|
file <- ({{| shebang?
|
||||||
(ignored_line %nl)*
|
(ignored_line %nl)*
|
||||||
statements (nodent statements)*
|
statements (nodent statements)*
|
||||||
(%nl ignored_line)* %nl?
|
(%nl ignored_line)* %nl?
|
||||||
@ -78,9 +78,9 @@ nomsu = [=[
|
|||||||
(dedent / (({.+} ("" -> "Error while parsing thunk")) => error))
|
(dedent / (({.+} ("" -> "Error while parsing thunk")) => error))
|
||||||
|} }) -> Thunk
|
|} }) -> Thunk
|
||||||
|
|
||||||
inline_nomsu <- ({ ("\" inline_expression) }) -> Nomsu
|
inline_nomsu <- ({("\" inline_expression) }) -> Nomsu
|
||||||
eol_nomsu <- ({ ("\" noeol_expression) }) -> Nomsu
|
eol_nomsu <- ({("\" noeol_expression) }) -> Nomsu
|
||||||
indented_nomsu <- ({ ("\" expression) }) -> Nomsu
|
indented_nomsu <- ({("\" expression) }) -> Nomsu
|
||||||
|
|
||||||
inline_expression <- number / variable / inline_string / inline_list / inline_nomsu
|
inline_expression <- number / variable / inline_string / inline_list / inline_nomsu
|
||||||
/ inline_thunk / ("(" inline_statement ")")
|
/ inline_thunk / ("(" inline_statement ")")
|
||||||
@ -92,13 +92,13 @@ nomsu = [=[
|
|||||||
expression <- eol_thunk / eol_nomsu / noeol_expression
|
expression <- eol_thunk / eol_nomsu / noeol_expression
|
||||||
|
|
||||||
-- Function calls need at least one word in them
|
-- Function calls need at least one word in them
|
||||||
inline_functioncall <- ({ {|
|
inline_functioncall <- ({(''=>line_no) {|
|
||||||
(inline_expression tok_gap)* word (tok_gap (inline_expression / word))*
|
(inline_expression tok_gap)* word (tok_gap (inline_expression / word))*
|
||||||
|} }) -> FunctionCall
|
|} }) -> FunctionCall
|
||||||
noeol_functioncall <- ({ {|
|
noeol_functioncall <- ({(''=>line_no) {|
|
||||||
(noeol_expression tok_gap)* word (tok_gap (noeol_expression / word))*
|
(noeol_expression tok_gap)* word (tok_gap (noeol_expression / word))*
|
||||||
|} }) -> FunctionCall
|
|} }) -> FunctionCall
|
||||||
functioncall <- ({ {|
|
functioncall <- ({(''=>line_no) {|
|
||||||
(expression (dotdot / tok_gap))* word ((dotdot / tok_gap) (expression / word))*
|
(expression (dotdot / tok_gap))* word ((dotdot / tok_gap) (expression / word))*
|
||||||
|} }) -> FunctionCall
|
|} }) -> FunctionCall
|
||||||
|
|
||||||
@ -147,6 +147,7 @@ nomsu = [=[
|
|||||||
dotdot <- nodent ".." %ws?
|
dotdot <- nodent ".." %ws?
|
||||||
]=]
|
]=]
|
||||||
|
|
||||||
|
CURRENT_FILE = nil
|
||||||
whitespace = S(" \t")^1
|
whitespace = S(" \t")^1
|
||||||
defs =
|
defs =
|
||||||
ws:whitespace, nl: P("\n"), :tonumber
|
ws:whitespace, nl: P("\n"), :tonumber
|
||||||
@ -155,6 +156,12 @@ defs =
|
|||||||
nodented: Cmt(S(" \t")^0 * (#(P(1)-S(" \t\n") + (-P(1)))), check_nodent)
|
nodented: Cmt(S(" \t")^0 * (#(P(1)-S(" \t\n") + (-P(1)))), check_nodent)
|
||||||
dedented: Cmt(S(" \t")^0 * (#(P(1)-S(" \t\n") + (-P(1)))), check_dedent)
|
dedented: Cmt(S(" \t")^0 * (#(P(1)-S(" \t\n") + (-P(1)))), check_dedent)
|
||||||
prev_edge: B(S(" \t\n.,:;}])\"\\"))
|
prev_edge: B(S(" \t\n.,:;}])\"\\"))
|
||||||
|
line_no: (src, pos)->
|
||||||
|
line_no = 1
|
||||||
|
for _ in src\sub(1,pos)\gmatch("\n") do line_no += 1
|
||||||
|
return pos, "#{CURRENT_FILE}:#{line_no}"
|
||||||
|
FunctionCall: (src, line_no, value, errors)->
|
||||||
|
{type: "FunctionCall", :src, :line_no, :value, :errors}
|
||||||
error: (src,pos,errors,err_msg)->
|
error: (src,pos,errors,err_msg)->
|
||||||
line_no = 1
|
line_no = 1
|
||||||
for _ in src\sub(1,-#errors)\gmatch("\n") do line_no += 1
|
for _ in src\sub(1,-#errors)\gmatch("\n") do line_no += 1
|
||||||
@ -173,7 +180,7 @@ defs =
|
|||||||
prev_line,err_line,next_line = src\match("([^\n]*)\n([^\n]*)\n([^\n]*)", start_of_prev_line+1)
|
prev_line,err_line,next_line = src\match("([^\n]*)\n([^\n]*)\n([^\n]*)", start_of_prev_line+1)
|
||||||
|
|
||||||
pointer = ("-")\rep(err_pos - start_of_err_line + 0) .. "^"
|
pointer = ("-")\rep(err_pos - start_of_err_line + 0) .. "^"
|
||||||
error("\n#{err_msg or "Parse error"} in #{filename} on line #{line_no}:\n\n#{prev_line}\n#{err_line}\n#{pointer}\n#{next_line}\n")
|
error("\n#{err_msg or "Parse error"} in #{CURRENT_FILE} on line #{line_no}:\n\n#{prev_line}\n#{err_line}\n#{pointer}\n#{next_line}\n")
|
||||||
|
|
||||||
setmetatable(defs, {
|
setmetatable(defs, {
|
||||||
__index: (t,key)->
|
__index: (t,key)->
|
||||||
@ -220,14 +227,15 @@ class NomsuCompiler
|
|||||||
defmacro: (signature, thunk, src)=>
|
defmacro: (signature, thunk, src)=>
|
||||||
@def(signature, thunk, src, true)
|
@def(signature, thunk, src, true)
|
||||||
|
|
||||||
call: (stub,...)=>
|
call: (stub,line_no,...)=>
|
||||||
def = @defs[stub]
|
def = @defs[stub]
|
||||||
if def == nil
|
|
||||||
@error "Attempt to call undefined function: #{stub}"
|
|
||||||
-- This is a little bit hacky, but having this check is handy for catching mistakes
|
-- 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
|
-- I use a hash sign in "#macro" so it's guaranteed to not be a valid function name
|
||||||
if def.is_macro and @callstack[#@callstack] != "#macro"
|
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."
|
@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}
|
||||||
|
if def == nil
|
||||||
|
@error "Attempt to call undefined function: #{stub}"
|
||||||
unless def.is_macro
|
unless def.is_macro
|
||||||
@assert_permission(stub)
|
@assert_permission(stub)
|
||||||
{:thunk, :arg_names} = def
|
{:thunk, :arg_names} = def
|
||||||
@ -235,7 +243,6 @@ class NomsuCompiler
|
|||||||
if @debug
|
if @debug
|
||||||
@write "#{colored.bright "CALLING"} #{colored.magenta(colored.underscore stub)} "
|
@write "#{colored.bright "CALLING"} #{colored.magenta(colored.underscore stub)} "
|
||||||
@writeln "#{colored.bright "WITH ARGS:"} #{colored.dim repr(args)}"
|
@writeln "#{colored.bright "WITH ARGS:"} #{colored.dim repr(args)}"
|
||||||
insert @callstack, stub
|
|
||||||
-- TODO: optimize, but still allow multiple return values?
|
-- TODO: optimize, but still allow multiple return values?
|
||||||
rets = {thunk(self,args)}
|
rets = {thunk(self,args)}
|
||||||
remove @callstack
|
remove @callstack
|
||||||
@ -247,7 +254,7 @@ class NomsuCompiler
|
|||||||
@write "#{colored.bright "RUNNING MACRO"} #{colored.underscore colored.magenta(stub)} "
|
@write "#{colored.bright "RUNNING MACRO"} #{colored.underscore colored.magenta(stub)} "
|
||||||
@writeln "#{colored.bright "WITH ARGS:"} #{colored.dim repr args}"
|
@writeln "#{colored.bright "WITH ARGS:"} #{colored.dim repr args}"
|
||||||
insert @callstack, "#macro"
|
insert @callstack, "#macro"
|
||||||
expr, statement = @call(stub, unpack(args))
|
expr, statement = @call(stub, tree.line_no, unpack(args))
|
||||||
remove @callstack
|
remove @callstack
|
||||||
return expr, statement
|
return expr, statement
|
||||||
|
|
||||||
@ -260,7 +267,7 @@ class NomsuCompiler
|
|||||||
-- TODO: maybe optimize this by making the callstack a Counter and using a
|
-- TODO: maybe optimize this by making the callstack a Counter and using a
|
||||||
-- move-to-front optimization on the whitelist to check most likely candidates sooner
|
-- move-to-front optimization on the whitelist to check most likely candidates sooner
|
||||||
for caller in *@callstack
|
for caller in *@callstack
|
||||||
if whiteset[caller] then return true
|
if whiteset[caller[1]] then return true
|
||||||
@error "You do not have the authority to call: #{stub}"
|
@error "You do not have the authority to call: #{stub}"
|
||||||
|
|
||||||
check_permission: (fn_def)=>
|
check_permission: (fn_def)=>
|
||||||
@ -274,17 +281,20 @@ class NomsuCompiler
|
|||||||
-- TODO: maybe optimize this by making the callstack a Counter and using a
|
-- TODO: maybe optimize this by making the callstack a Counter and using a
|
||||||
-- move-to-front optimization on the whitelist to check most likely candidates sooner
|
-- move-to-front optimization on the whitelist to check most likely candidates sooner
|
||||||
for caller in *@callstack
|
for caller in *@callstack
|
||||||
if whiteset[caller] then return true
|
if whiteset[caller[1]] then return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
parse: (str, filename)=>
|
parse: (str, filename)=>
|
||||||
if @debug
|
if @debug
|
||||||
@writeln("#{colored.bright "PARSING:"}\n#{str}")
|
@writeln("#{colored.bright "PARSING:"}\n#{str}")
|
||||||
str = str\gsub("\r","")
|
str = str\gsub("\r","")
|
||||||
export indent_stack
|
export indent_stack, CURRENT_FILE
|
||||||
|
old_file = CURRENT_FILE
|
||||||
old_indent_stack, indent_stack = indent_stack, {0}
|
old_indent_stack, indent_stack = indent_stack, {0}
|
||||||
|
CURRENT_FILE = filename
|
||||||
tree = nomsu\match(str)
|
tree = nomsu\match(str)
|
||||||
indent_stack = old_indent_stack -- Put it back, just in case.
|
indent_stack = old_indent_stack -- Put it back, just in case.
|
||||||
|
CURRENT_FILE = old_file
|
||||||
assert tree, "Failed to parse: #{str}"
|
assert tree, "Failed to parse: #{str}"
|
||||||
if @debug
|
if @debug
|
||||||
@writeln "PARSE TREE:"
|
@writeln "PARSE TREE:"
|
||||||
@ -392,7 +402,7 @@ class NomsuCompiler
|
|||||||
if statement
|
if statement
|
||||||
statement = "nomsu:assert_permission(#{repr stub});\n"..statement
|
statement = "nomsu:assert_permission(#{repr stub});\n"..statement
|
||||||
return expr, statement
|
return expr, statement
|
||||||
args = {repr(stub)}
|
args = {repr(stub), repr(tree.line_no)}
|
||||||
for arg in *tree.value
|
for arg in *tree.value
|
||||||
if arg.type == 'Word' then continue
|
if arg.type == 'Word' then continue
|
||||||
expr,statement = @tree_to_lua arg
|
expr,statement = @tree_to_lua arg
|
||||||
@ -566,8 +576,10 @@ class NomsuCompiler
|
|||||||
if msg
|
if msg
|
||||||
@errorln(colored.bright colored.yellow colored.onred msg)
|
@errorln(colored.bright colored.yellow colored.onred msg)
|
||||||
@errorln("Callstack:")
|
@errorln("Callstack:")
|
||||||
|
maxlen = utils.max([#c[2] for c in *@callstack])
|
||||||
for i=#@callstack,1,-1
|
for i=#@callstack,1,-1
|
||||||
@errorln " #{@callstack[i]}"
|
if @callstack[i] != "#macro"
|
||||||
|
@errorln " #{"%-#{maxlen}s"\format @callstack[i][2]}| #{@callstack[i][1]}"
|
||||||
@errorln " <top level>"
|
@errorln " <top level>"
|
||||||
@callstack = {}
|
@callstack = {}
|
||||||
error!
|
error!
|
||||||
|
Loading…
Reference in New Issue
Block a user