1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
-- lib/lace/engine.lua
--
-- Lua Access Control Engine -- Ruleset runtime engine
--
-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
--
-- For licence terms, see COPYING
--
local err = require 'lace.error'
local function _dlace(ctx)
local ret = ctx._lace or {}
ctx._lace = ret
return ret
end
local function set_define(exec_context, name, defn)
local dlace = _dlace(exec_context)
dlace.defs = dlace.defs or {}
if dlace.defs[name] then
return err.error("Attempted to redefine " .. name, {2})
end
dlace.defs[name] = defn
return true
end
local function test_define(exec_context, name)
local dlace = _dlace(exec_context)
dlace.defs = dlace.defs or {}
local defn = dlace.defs[name]
if not defn then
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))
end
local function internal_run_ruleset(ruleset, exec_context)
-- We iterate the ruleset, returning the first time
-- a rule either errors, or returns a stopping result
local dlace = _dlace(exec_context)
dlace.ruleset = ruleset
for i = 1, #ruleset.rules do
local rule = ruleset.rules[i]
dlace.linenr = rule.linenr
local result, msg = rule.fn(exec_context, unpack(rule.args))
if not result then
return false, err.augment(msg, rule.source, rule.linenr)
elseif result ~= true then
-- Explicit result, return it
return result, msg
end
end
dlace.linenr = nil
-- Internally we use the empty string to indicate no result.
return ""
end
local function run_ruleset(ruleset, exec_context)
local ok, ret, msg = xpcall(function()
return internal_run_ruleset(ruleset, exec_context)
end, debug.traceback)
if not ok then
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
msg = err.render(msg)
end
return ret, msg
end
return {
internal_run = internal_run_ruleset,
run = run_ruleset,
test = test_define,
define = set_define,
}
|