summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2012-05-13 22:57:59 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2012-05-13 22:57:59 +0100
commit0fa1e5fec46e9825e34a1776bbfc375e6ca36c0b (patch)
tree0d79ac3f758f628cabce1ed52aa6b141b223faf0
parent65cd5144b2ff7d3abf949a7f512995e00f77b68f (diff)
downloadlace-0fa1e5fec46e9825e34a1776bbfc375e6ca36c0b.tar.gz
Ruleset inclusion support
-rw-r--r--lib/lace/builtin.lua106
-rw-r--r--test/test-lace.builtin.lua168
2 files changed, 252 insertions, 22 deletions
diff --git a/lib/lace/builtin.lua b/lib/lace/builtin.lua
index 15b88af..5978bd0 100644
--- a/lib/lace/builtin.lua
+++ b/lib/lace/builtin.lua
@@ -15,6 +15,30 @@ local function compiler()
return require "lace.compiler"
end
+local function run_conditions(exec_context, cond)
+ for i = 1, #cond do
+ local name = cond[i]
+ local invert = false
+ if name:sub(1,1) == "!" then
+ invert = true
+ name = name:sub(2)
+ end
+ local res, msg = engine.test(exec_context, name)
+ if res == nil then
+ return nil, msg
+ end
+ if invert then
+ res = not res
+ end
+ if not res then
+ -- condition failed
+ return false
+ end
+ end
+ -- conditions passed
+ return true
+end
+
--[ Allow and Deny ]------------------------------------------------
local unconditional_result, last_result
@@ -32,24 +56,13 @@ local function get_set_last_result(newv)
end
local function _do_return(exec_context, result, reason, cond)
- for i = 1, #cond do
- local name = cond[i]
- local invert = false
- if name:sub(1,1) == "!" then
- invert = true
- name = name:sub(2)
- end
- local res, msg = engine.test(exec_context, name)
- if res == nil then
- return nil, msg
- end
- if invert then
- res = not res
- end
- if not res then
- -- condition failed, return true to continue execution
- return true
- end
+ local pass, msg = run_conditions(exec_context, cond)
+ if pass == nil then
+ -- Pass errors
+ return nil, msg
+ elseif pass == false then
+ -- Conditions failed, return true to continue execution
+ return true
end
return result, reason
end
@@ -153,6 +166,63 @@ end
builtin.def = builtin.define
+--[ Inclusion of rulesets ]-------------------------------------------
+
+local function _do_include(exec_context, ruleset, conds)
+ local pass, msg = run_conditions(exec_context, conds)
+ if pass == nil then
+ -- Pass errors
+ return nil, msg
+ elseif pass == false then
+ -- Conditions failed, return true to continue execution
+ return true
+ end
+ -- Essentially we run the ruleset and return its values
+ local result, msg = engine.internal_run(ruleset, exec_context)
+ if result == "" then
+ return true
+ end
+ return result, msg
+end
+
+function builtin.include(comp_context, cmd, file, ...)
+ local safe_if_not_present = cmd:sub(-1) == "?"
+
+ local conds = {...}
+
+ if type(file) ~= "string" then
+ return compiler().error("No file named for inclusion")
+ end
+
+ local loader = compiler().internal_loader(comp_context)
+ local real, content = loader(comp_context, file)
+
+ if not real then
+ -- Could not find the file
+ if safe_if_not_present then
+ -- Include file was not present, just return an empty command
+ return {
+ fn = function() return true end,
+ args = {}
+ }
+ end
+ -- Otherwise, propagate the error
+ return real, content
+ end
+
+ -- Okay, the file is present, let's parse it.
+ local ruleset, msg = compiler().internal_compile(comp_context, real, content, true)
+ if type(ruleset) ~= "table" then
+ return compiler().error(msg)
+ end
+
+ -- Okay, we parsed, so build the runtime
+ return {
+ fn = _do_include,
+ args = { ruleset, conds }
+ }
+end
+
return {
commands = builtin,
get_set_last_unconditional_result = get_set_last_unconditional_result,
diff --git a/test/test-lace.builtin.lua b/test/test-lace.builtin.lua
index a20f4cd..59d36cb 100644
--- a/test/test-lace.builtin.lua
+++ b/test/test-lace.builtin.lua
@@ -274,7 +274,6 @@ function suite.run_allow_deny_with_condition_present_failing()
}
local ectx = {[".lace"] = {defs = { cheese = _cheesetab }}}
local ok, msg = cmdtab.fn(ectx, unpack(cmdtab.args))
- print(ok, msg)
assert(ok == true, "Running a conditional allow where the conditions fail should return continuation")
end
@@ -290,7 +289,6 @@ function suite.run_allow_deny_with_condition_present_passing()
}
local ectx = {[".lace"] = {defs = { cheese = _cheesetab }}}
local ok, msg = cmdtab.fn(ectx, unpack(cmdtab.args))
- print(ok, msg)
assert(ok == "allow", "Running a conditional allow where the conditions pass should return allow")
assert(msg == "because", "Resulting in the reason given")
end
@@ -306,7 +304,6 @@ function suite.run_allow_deny_with_inverted_condition_present_failing()
}
local ectx = {[".lace"] = {defs = { cheese = _cheesetab }}}
local ok, msg = cmdtab.fn(ectx, unpack(cmdtab.args))
- print(ok, msg)
assert(ok == true, "Running a conditional allow where the conditions fail should return continuation")
end
@@ -322,11 +319,174 @@ function suite.run_allow_deny_with_inverted_condition_present_passing()
}
local ectx = {[".lace"] = {defs = { cheese = _cheesetab }}}
local ok, msg = cmdtab.fn(ectx, unpack(cmdtab.args))
- print(ok, msg)
assert(ok == "allow", "Running a conditional allow where the conditions pass should return allow")
assert(msg == "because", "Resulting in the reason given")
end
+function suite.compile_include_statement_noname()
+ local compctx = {[".lace"] = {}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include")
+ assert(cmdtab == false, "Internal errors should return false")
+ assert(type(msg) == "table", "Internal errors should return tables")
+ assert(type(msg.msg) == "string", "Internal errors should have string messages")
+ assert(msg.msg:match("file name"), "Expected error should mention a lack of name")
+end
+
+function suite.compile_include_statement_noloader()
+ local compctx = {[".lace"] = {}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include", "fish")
+ assert(cmdtab == false, "Internal errors should return false")
+ assert(type(msg) == "table", "Internal errors should return tables")
+ assert(type(msg.msg) == "string", "Internal errors should have string messages")
+ assert(msg.msg:match("fish"), "Expected error should mention a name of unloadable content")
+end
+
+function suite.compile_safe_include_statement_noloader()
+ local compctx = {[".lace"] = {}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include?", "fish")
+ assert(type(cmdtab) == "table", "Commands should be tables")
+ assert(type(cmdtab.fn) == "function", "With a function")
+ assert(type(cmdtab.args) == "table", "and arguments")
+end
+
+function suite.compile_include_statement_withloader()
+ local function _loader(cctx, name)
+ return name, "-- nada\n"
+ end
+ local compctx = {[".lace"] = {loader=_loader}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include", "fish")
+ assert(type(cmdtab) == "table", "Commands should be tables")
+ assert(type(cmdtab.fn) == "function", "With a function")
+ assert(type(cmdtab.args) == "table", "and arguments")
+end
+
+function suite.compile_include_statement_withloader_badcode()
+ local function _loader(cctx, name)
+ return name, "nada\n"
+ end
+ local compctx = {[".lace"] = {loader=_loader}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include", "fish")
+ assert(cmdtab == false, "Internal errors should return false")
+ assert(type(msg) == "table", "Internal errors should return tables")
+ assert(type(msg.msg) == "string", "Internal errors should have string messages")
+ assert(msg.msg:match("nada"), "Expected error should mention the bad command name")
+end
+
+function suite.run_include_statement_withloader_good_code()
+ local function _loader(cctx, name)
+ return name, "--nada\n"
+ end
+ local compctx = {[".lace"] = {loader=_loader}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include", "fish")
+ assert(type(cmdtab) == "table", "Commands should be tables")
+ assert(type(cmdtab.fn) == "function", "With a function")
+ assert(type(cmdtab.args) == "table", "and arguments")
+ local ectx = {}
+ local result, msg = cmdtab.fn(ectx, unpack(cmdtab.args))
+ assert(result == true, "Should be continuing as-is")
+end
+
+function suite.run_include_statement_withloader_good_code_allow()
+ local function _loader(cctx, name)
+ return name, "allow because\n"
+ end
+ local compctx = {[".lace"] = {loader=_loader}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include", "fish")
+ assert(type(cmdtab) == "table", "Commands should be tables")
+ assert(type(cmdtab.fn) == "function", "With a function")
+ assert(type(cmdtab.args) == "table", "and arguments")
+ local ectx = {}
+ local result, msg = cmdtab.fn(ectx, unpack(cmdtab.args))
+ assert(result == "allow", "Should pass result")
+ assert(msg == "because", "Should pass reason")
+end
+
+function suite.run_cond_include_present_erroring()
+ local function _loader(cctx, name)
+ return name, "allow because\n"
+ end
+ local compctx = {[".lace"] = {loader=_loader}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include", "fish", "cheese")
+ assert(type(cmdtab) == "table", "Commands should be tables")
+ assert(type(cmdtab.fn) == "function", "With a function")
+ assert(type(cmdtab.args) == "table", "and arguments")
+ local ectx = {}
+ local result, msg = cmdtab.fn(ectx, unpack(cmdtab.args))
+ assert(result == nil, "Should fail gracefully")
+ assert(msg.msg:match("cheese"), "Error should mention undefined variable")
+end
+
+function suite.run_cond_include_present_erroring()
+ local function _loader(cctx, name)
+ return name, "allow because\n"
+ end
+ local compctx = {[".lace"] = {loader=_loader}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include", "fish", "cheese")
+ assert(type(cmdtab) == "table", "Commands should be tables")
+ assert(type(cmdtab.fn) == "function", "With a function")
+ assert(type(cmdtab.args) == "table", "and arguments")
+ local ectx = {
+ [".lace"] = {
+ defs = {
+ cheese = {
+ fn = function() return nil, { msg = "FAIL" } end,
+ args = {}
+ }
+ }
+ }
+ }
+ local result, msg = cmdtab.fn(ectx, unpack(cmdtab.args))
+ assert(result == nil, "Should fail gracefully")
+ assert(msg.msg:match("FAIL"), "Error should mention undefined variable")
+end
+
+function suite.run_cond_include_present_failing()
+ local function _loader(cctx, name)
+ return name, "allow because\n"
+ end
+ local compctx = {[".lace"] = {loader=_loader}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include", "fish", "cheese")
+ assert(type(cmdtab) == "table", "Commands should be tables")
+ assert(type(cmdtab.fn) == "function", "With a function")
+ assert(type(cmdtab.args) == "table", "and arguments")
+ local ectx = {
+ [".lace"] = {
+ defs = {
+ cheese = {
+ fn = function() return false end,
+ args = {}
+ }
+ }
+ }
+ }
+ local result, msg = cmdtab.fn(ectx, unpack(cmdtab.args))
+ assert(result == true, "Should continue blithely")
+end
+
+function suite.run_cond_include_present_passing()
+ local function _loader(cctx, name)
+ return name, "allow because\n"
+ end
+ local compctx = {[".lace"] = {loader=_loader}}
+ local cmdtab, msg = builtin.commands.include(compctx, "include", "fish", "cheese")
+ assert(type(cmdtab) == "table", "Commands should be tables")
+ assert(type(cmdtab.fn) == "function", "With a function")
+ assert(type(cmdtab.args) == "table", "and arguments")
+ local ectx = {
+ [".lace"] = {
+ defs = {
+ cheese = {
+ fn = function() return true end,
+ args = {}
+ }
+ }
+ }
+ }
+ local result, msg = cmdtab.fn(ectx, unpack(cmdtab.args))
+ assert(result == "allow", "Should propagate success")
+ assert(msg == "because", "Should propagate reason")
+end
+
local count_ok = 0
for _, testname in ipairs(testnames) do
print("Run: " .. testname)