summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2012-07-17 19:14:11 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2012-07-17 19:14:11 +0100
commit2b925da62d6242d3982ec71d09731c5206ddc7b3 (patch)
tree01796deb91dc510a0cda9f3212fe6a3d234783d1
parent0d831cb329f0e8f0ab1b9eef6dbf043396bd49fc (diff)
downloadlace-2b925da62d6242d3982ec71d09731c5206ddc7b3.tar.gz
LACE: Ensure engine errors are propagated properly at runtime
-rw-r--r--lib/lace/builtin.lua2
-rw-r--r--lib/lace/compiler.lua1
-rw-r--r--lib/lace/engine.lua11
-rw-r--r--test/test-lace.compiler.lua1
-rw-r--r--test/test-lace.engine-doubledefine.rules7
-rw-r--r--test/test-lace.engine-unknownanyof.rules5
-rw-r--r--test/test-lace.engine-unknowndefine.rules3
-rw-r--r--test/test-lace.engine.lua41
8 files changed, 63 insertions, 8 deletions
diff --git a/lib/lace/builtin.lua b/lib/lace/builtin.lua
index 507e864..2f03549 100644
--- a/lib/lace/builtin.lua
+++ b/lib/lace/builtin.lua
@@ -27,6 +27,7 @@ local function run_conditions(exec_context, cond, anyof)
end
local res, msg = engine.test(exec_context, name)
if res == nil then
+ msg.words = {i}
return nil, msg
end
if invert then
@@ -68,6 +69,7 @@ local function _do_return(exec_context, result, reason, cond)
local pass, msg = run_conditions(exec_context, cond)
if pass == nil then
-- Pass errors
+ err.offset(msg, 2)
return nil, msg
elseif pass == false then
-- Conditions failed, return true to continue execution
diff --git a/lib/lace/compiler.lua b/lib/lace/compiler.lua
index 8100ff3..2b50227 100644
--- a/lib/lace/compiler.lua
+++ b/lib/lace/compiler.lua
@@ -114,6 +114,7 @@ local function internal_compile_ruleset(compcontext, sourcename, content, suppre
return rule, err.augment(msg, ruleset.content, i)
end
rule.linenr = i
+ rule.source = ruleset.content
ruleset.rules[#ruleset.rules+1] = rule
end
end
diff --git a/lib/lace/engine.lua b/lib/lace/engine.lua
index b4db68d..5484c9b 100644
--- a/lib/lace/engine.lua
+++ b/lib/lace/engine.lua
@@ -30,7 +30,7 @@ local function test_define(exec_context, name)
dlace.defs = dlace.defs or {}
local defn = dlace.defs[name]
if not defn then
- return err.error("Unknown definition: " .. name, {2}, true)
+ return err.error("Unknown definition: " .. name, {1}, true)
end
-- Otherwise we evaluate the definition and return it
return defn.fn(exec_context, unpack(defn.args))
@@ -46,7 +46,7 @@ local function internal_run_ruleset(ruleset, exec_context)
dlace.linenr = rule.linenr
local result, msg = rule.fn(exec_context, unpack(rule.args))
if not result then
- return false, msg
+ return false, err.augment(msg, rule.source, rule.linenr)
elseif result ~= true then
-- Explicit result, return it
return result, msg
@@ -63,15 +63,14 @@ local function run_ruleset(ruleset, exec_context)
return internal_run_ruleset(ruleset, exec_context)
end, debug.traceback)
if not ok then
- return nil, ret
+ local _, msg = err.error(ret, {1})
+ return nil, err.render(err.augment(msg, ruleset.rules[1].source, ruleset.rules[1].linenr))
end
assert(ret ~= "", "It should not be possible for a ruleset to fail to return a result")
if type(msg) == "table" then
- -- TODO: Extract position information etc from error and
- -- formulate a gorgeous multiline error message.
- msg = msg.msg or "Empty error"
+ msg = err.render(msg)
end
return ret, msg
diff --git a/test/test-lace.compiler.lua b/test/test-lace.compiler.lua
index 42936eb..acd7e5d 100644
--- a/test/test-lace.compiler.lua
+++ b/test/test-lace.compiler.lua
@@ -368,7 +368,6 @@ function suite.error_in_include2()
assert(result == false, "Errors compiling should return false")
assert(type(msg) == "string", "Compilation errors should be strings")
assert(msg:find("\n"), "Compilation errors are multiline")
- print(msg)
local line1, line2, line3, line4 = msg:match("^([^\n]*)\n([^\n]*)\n([^\n]*)\n([^\n]*)$")
assert(line1:find("NOTFOUND"), "The first line must mention the error")
assert(line2 == "real-errorininclude2 :: 3", "The second line is where the error happened")
diff --git a/test/test-lace.engine-doubledefine.rules b/test/test-lace.engine-doubledefine.rules
new file mode 100644
index 0000000..81dc401
--- /dev/null
+++ b/test/test-lace.engine-doubledefine.rules
@@ -0,0 +1,7 @@
+-- Expect an error on line 5 word 2
+
+define fish equal state one
+
+define fish equal state two
+
+allow "anyway"
diff --git a/test/test-lace.engine-unknownanyof.rules b/test/test-lace.engine-unknownanyof.rules
new file mode 100644
index 0000000..2ea2141
--- /dev/null
+++ b/test/test-lace.engine-unknownanyof.rules
@@ -0,0 +1,5 @@
+-- Expect error at line 5 word 3
+
+define fish anyof pants cake
+
+allow anyway fish
diff --git a/test/test-lace.engine-unknowndefine.rules b/test/test-lace.engine-unknowndefine.rules
new file mode 100644
index 0000000..42a2fc5
--- /dev/null
+++ b/test/test-lace.engine-unknowndefine.rules
@@ -0,0 +1,3 @@
+-- Expect error on line 3, word 2
+
+allow anyway fish
diff --git a/test/test-lace.engine.lua b/test/test-lace.engine.lua
index f8faaf8..d0a0b6f 100644
--- a/test/test-lace.engine.lua
+++ b/test/test-lace.engine.lua
@@ -85,7 +85,7 @@ end
function suite.check_error_propagates()
local function _explode()
- return { fn = function() return false, "EXPLODE" end, args = {} }
+ return { fn = function() return lace.error.error("EXPLODE", {1}) end, args = {} }
end
local compctx = {_lace={commands={explode=_explode}}}
local ruleset, msg = lace.compiler.compile(compctx, "src", "explode\nallow because")
@@ -206,6 +206,45 @@ function suite.test_runtime_error()
assert(msg:find("woah"), "Did not generate the right error: " .. msg)
end
+function suite.doubledefine()
+ local ruleset, msg = lace.compiler.compile(comp_context, "doubledefine")
+ assert(type(ruleset) == "table", "Ruleset did not compile")
+ local ectx = {error = true}
+ local result, msg = lace.engine.run(ruleset, ectx)
+ assert(result == false, "Did not error out")
+ local line1, line2, line3, line4 = msg:match("^([^\n]*)\n([^\n]*)\n([^\n]*)\n([^\n]*)$")
+ assert(line1:find("redefine fish"), "The first line must mention the error")
+ assert(line2 == "real-doubledefine :: 5", "The second line is where the error happened")
+ assert(line3 == "define fish equal state two", "The third line is the original line")
+ assert(line4 == " ^^^^ ", "The fourth line highlights relevant words")
+end
+
+function suite.unknowndefine()
+ local ruleset, msg = lace.compiler.compile(comp_context, "unknowndefine")
+ assert(type(ruleset) == "table", "Ruleset did not compile")
+ local ectx = {error = true}
+ local result, msg = lace.engine.run(ruleset, ectx)
+ assert(result == false, "Did not error out")
+ local line1, line2, line3, line4 = msg:match("^([^\n]*)\n([^\n]*)\n([^\n]*)\n([^\n]*)$")
+ assert(line1:find("definition: fish"), "The first line must mention the error")
+ assert(line2 == "real-unknowndefine :: 3", "The second line is where the error happened")
+ assert(line3 == "allow anyway fish", "The third line is the original line")
+ assert(line4 == " ^^^^", "The fourth line highlights relevant words")
+end
+
+function suite.unknownanyof()
+ local ruleset, msg = lace.compiler.compile(comp_context, "unknownanyof")
+ assert(type(ruleset) == "table", "Ruleset did not compile")
+ local ectx = {error = true}
+ local result, msg = lace.engine.run(ruleset, ectx)
+ assert(result == false, "Did not error out")
+ local line1, line2, line3, line4 = msg:match("^([^\n]*)\n([^\n]*)\n([^\n]*)\n([^\n]*)$")
+ assert(line1:find("definition: pants"), "The first line must mention the error")
+ assert(line2 == "real-unknownanyof :: 5", "The second line is where the error happened")
+ assert(line3 == "allow anyway fish", "The third line is the original line")
+ assert(line4 == " ^^^^", "The fourth line highlights relevant words")
+end
+
local count_ok = 0
for _, testname in ipairs(testnames) do
-- print("Run: " .. testname)