From e4ca1cace7518fe91ba562b6e7324a2c138751c4 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 12 Sep 2017 21:10:22 -0700 Subject: [PATCH] Improvements to error messaging. --- core.nom | 39 +++++++++++++++++++++++++++++++-------- nomic.moon | 34 ++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/core.nom b/core.nom index 45b4116..8ef2a80 100644 --- a/core.nom +++ b/core.nom @@ -17,7 +17,7 @@ rule "macro block %spec %body": | local spec, body = vars.spec, vars.body | local wrapper = function(compiler, vars, kind) | if kind == "Expression" then - | error("Macro: "..spec.." was defined to be a block, but is being used as an expression.") + | compiler:error("Macro: "..spec.." was defined to be a block, but is being used as an expression.") | end | return body(compiler, vars, kind), true | end @@ -127,7 +127,7 @@ rule "do %action": macro block "if %condition %if_body": concat [..] "if ",%condition as lua expr," then" - "\n ",lua expr "vars.if_body.value.value" as lua block + "\n ",(lua expr "vars.if_body.value.value") as lua block "\nend" macro block "if %condition %if_body else %else_body": @@ -195,7 +195,7 @@ macro ["%index st in %list", "%index nd in %list", "%index rd in %list", "%index macro ["%item is in %list", "%list contains %item"]: concat ["(",%list as lua expr,"[",%index as lua expr,"] ~= nil)"] -macro ["length of %list", "size of %list"]: +macro ["length of %list", "size of %list", "number of %list"]: concat ["#(",%list as lua expr,")"] rule "restrict %fn to within %whitelist": @@ -203,11 +203,16 @@ rule "restrict %fn to within %whitelist": |local fns = (type(vars.fn) == 'string') and {vars.fn} or vars.fn |local whitelist = (type(vars.whitelist) == 'string') and {vars.whitelist} or vars.whitelist |local whiteset = {} - |for _,w in ipairs(whitelist) do whiteset[w] = true end + |for _,w in ipairs(whitelist) do + | if not compiler.defs[w] then + | compiler:error("Undefined function: "..tostring(w)) + | else whiteset[w] = true + | end + |end |for _,fn in ipairs(fns) do | local fn_info = compiler.defs[fn] | if fn_info == nil then - | print("Undefined function: "..tostring(fn)) + | compiler:error("Undefined function: "..tostring(fn)) | elseif not compiler:check_permission(fn) then | print("You do not have permission to restrict function: "..tostring(fn)) | else @@ -219,16 +224,23 @@ rule "allow %whitelist to use %fn": lua block ".." |local fns = (type(vars.fn) == 'string') and {vars.fn} or vars.fn |local whitelist = (type(vars.whitelist) == 'string') and {vars.whitelist} or vars.whitelist + |for _,w in ipairs(whitelist) do + | if not compiler.defs[w] then + | compiler:error("Undefined function: "..tostring(w)) + | end + |end |for _,fn in ipairs(fns) do | local fn_info = compiler.defs[fn] | if fn_info == nil then - | print("Undefined function: "..tostring(fn)) + | compiler:error("Undefined function: "..tostring(fn)) | elseif fn_info.whiteset == nil then | print("Function is already allowed by everyone: "..tostring(fn)) | elseif not compiler:check_permission(fn) then | print("You do not have permission to grant permissions for function: "..tostring(fn)) | else - | for _,w in ipairs(whitelist) do fn_info.whiteset[w] = true end + | for _,w in ipairs(whitelist) do + | fn_info.whiteset[w] = true + | end | end |end @@ -236,10 +248,15 @@ rule "forbid %blacklist to use %fn": lua block ".." |local fns = (type(vars.fn) == 'string') and {vars.fn} or vars.fn |local blacklist = (type(vars.blacklist) == 'string') and {vars.blacklist} or vars.blacklist + |for _,b in ipairs(blacklist) do + | if not compiler.defs[b] then + | compiler:error("Undefined function: "..tostring(b)) + | end + |end |for _,fn in ipairs(fns) do | local fn_info = compiler.defs[fn] | if fn_info == nil then - | print("Undefined function: "..tostring(fn)) + | compiler:error("Undefined function: "..tostring(fn)) | elseif fn_info.whiteset == nil then | print("Cannot remove items from a whitelist when there is no whitelist on function: "..tostring(fn)) | elseif not compiler:check_permission(fn) then @@ -248,3 +265,9 @@ rule "forbid %blacklist to use %fn": | for _,b in ipairs(blacklist) do fn_info.whiteset[b] = nil end | end |end + +rule "error!": + lua block "compiler:error()" + +rule "error %msg": + lua block "compiler:error(vars.msg)" diff --git a/nomic.moon b/nomic.moon index 73b48c2..3a72664 100755 --- a/nomic.moon +++ b/nomic.moon @@ -93,11 +93,11 @@ class Compiler call: (fn_name,...)=> fn_info = @defs[fn_name] if fn_info == nil - error "Attempt to call undefined function: #{fn_name}" + @error "Attempt to call undefined function: #{fn_name}" if fn_info.is_macro - error "Attempt to call macro at runtime: #{fn_name}" + @error "Attempt to call macro at runtime: #{fn_name}" unless @check_permission(fn_name) - error "You do not have the authority to call: #{fn_name}" + @error "You do not have the authority to call: #{fn_name}" table.insert @callstack, fn_name {:fn, :arg_names} = fn_info args = {name, select(i,...) for i,name in ipairs(arg_names)} @@ -110,7 +110,7 @@ class Compiler check_permission: (fn_name)=> fn_info = @defs[fn_name] if fn_info == nil - error "Undefined function: #{fn_name}" + @error "Undefined function: #{fn_name}" if fn_info.whiteset == nil then return true for caller in *@callstack if fn_info.whiteset[caller] @@ -135,7 +135,7 @@ class Compiler table.insert(invocations, invocation) if arg_names if not utils.equivalent(utils.set(arg_names), utils.set(_arg_names)) - error("Conflicting argument names #{utils.repr(arg_names)} and #{utils.repr(_arg_names)} for #{utils.repr(text)}") + @error("Conflicting argument names #{utils.repr(arg_names)} and #{utils.repr(_arg_names)} for #{utils.repr(text)}") else arg_names = _arg_names return invocations, arg_names @@ -249,10 +249,13 @@ class Compiler for statement in *tree.value.body.value code = to_lua(statement) -- Run the fuckers as we go - lua_thunk, err = loadstring("return (function(compiler, vars)\n#{code}\nend)") + lua_thunk, err = load("return (function(compiler, vars)\n#{code}\nend)") if not lua_thunk error("Failed to compile generated code:\n#{code}\n\n#{err}") - lua_thunk!(self, vars) + ok,err = pcall(lua_thunk) + if not ok then error(err) + ok,err = pcall(err, self, vars) + if not ok then @error(err) add code add [[ return ret @@ -354,9 +357,9 @@ class Compiler run_macro: (tree, kind="Expression")=> name = @fn_name_from_tree(tree) unless @defs[name] and @defs[name].is_macro - error("Macro not found: #{name}") + @error("Macro not found: #{name}") unless @check_permission(name) - error "You do not have the authority to call: #{name}" + @error "You do not have the authority to call: #{name}" {:fn, :arg_names} = @defs[name] args = [a for a in *tree.value when a.type != "Word"] args = {name,args[i] for i,name in ipairs(arg_names)} @@ -364,7 +367,7 @@ class Compiler ret, manual_mode = fn(self, args, kind) table.remove @callstack if not ret - error("No return value for macro: #{name}") + @error("No return value for macro: #{name}") if kind == "Statement" and not manual_mode ret = "ret = "..ret return ret @@ -450,6 +453,13 @@ class Compiler output\write(code) return code + error: (...)=> + print(...) + print("Callstack:") + for i=#@callstack,1,-1 + print " #{@callstack[i]}" + error! + test: (src, expected)=> i = 1 while i != nil @@ -459,13 +469,13 @@ class Compiler i = stop start,stop = test\find"===" if not start or not stop then - error("WHERE'S THE ===? in:\n#{test}") + @error("WHERE'S THE ===? in:\n#{test}") test_src, expected = test\sub(1,start-1), test\sub(stop+1,-1) expected = expected\match'[\n]*(.*[^\n])' tree = @parse(test_src) got = @stringify_tree(tree.value.body) if got != expected - error"TEST FAILED!\nSource:\n#{test_src}\nExpected:\n#{expected}\n\nGot:\n#{got}" + @error"TEST FAILED!\nSource:\n#{test_src}\nExpected:\n#{expected}\n\nGot:\n#{got}" initialize_core: =>