summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2012-05-13 18:45:17 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2012-05-13 18:45:17 +0100
commit7d87eb9e8b7b59a6b89c5ac5eb7d2e9ee081fdf6 (patch)
tree8b8a3e31c9b58cae56c75068b1b7c7af8dbd4071
parent56ccdf057a44b57d19e770ee7cbd9f71aa1a5f6c (diff)
downloadlace-7d87eb9e8b7b59a6b89c5ac5eb7d2e9ee081fdf6.tar.gz
Initial compiler module and tests. Doesn't compile anything yet
-rw-r--r--Makefile4
-rw-r--r--lib/lace.lua16
-rw-r--r--lib/lace/compiler.lua72
-rw-r--r--test/test-lace.compile-nothing.rules6
-rw-r--r--test/test-lace.compiler.lua118
5 files changed, 214 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 4a1b181..980a5f3 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
all: test
-TEST_MODULES := lace.lex
+TEST_MODULES := lace.lex lace.compiler
LUA := LUA_PATH="$(shell pwd)/lib/?.lua;$(shell pwd)/extras/luacov/src/?.lua;$(HOME)/dev-bzr/luxio/?.lua;;" LUA_CPATH="$(HOME)/dev-bzr/luxio/?.so;;" lua5.1
@@ -14,7 +14,7 @@ distclean: clean
test:
@$(RM) luacov.stats.out
@ERR=0; \
- for MOD in $(TEST_MODULES); do \
+ for MOD in $(sort $(TEST_MODULES)); do \
echo "$${MOD}:"; \
$(LUA) test/test-$${MOD}.lua; \
test "x$$?" = "x0" || ERR=1; \
diff --git a/lib/lace.lua b/lib/lace.lua
new file mode 100644
index 0000000..f6818f1
--- /dev/null
+++ b/lib/lace.lua
@@ -0,0 +1,16 @@
+-- lib/lace.lua
+--
+-- Lua Access Control Engine
+--
+-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
+--
+-- For licence terms, see COPYING
+--
+
+local lex = require "lace.lex"
+local compiler = require "lace.compiler"
+
+return {
+ lex = lex,
+ compiler = compiler,
+}
diff --git a/lib/lace/compiler.lua b/lib/lace/compiler.lua
new file mode 100644
index 0000000..e176b39
--- /dev/null
+++ b/lib/lace/compiler.lua
@@ -0,0 +1,72 @@
+-- lib/lace/compiler.lua
+--
+-- Lua Access Control Engine - Ruleset compiler
+--
+-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
+--
+-- For licence terms, see COPYING
+--
+
+local lex = require "lace.lex"
+
+local function _error(str, words)
+ return false, { msg = str, words = words }
+end
+
+local function _fake_loader(ctx, name)
+ return _error("Ruleset not found: " .. name, {1})
+end
+
+local function _loader(ctx)
+ -- We know the context is a table with a .lace table, so retrieve
+ -- the loader function. If it's absent, return a loader which
+ -- fails due to no loader being present.
+ return ctx[".lace"].loader or _fake_loader
+end
+
+local function normalise_error(ctx, err)
+ -- For now, just return the string
+ return err.msg
+end
+
+local function internal_compile_ruleset(compcontext, sourcename, content)
+ assert(type(compcontext) == "table", "Compilation context must be a table")
+ assert(type(compcontext[".lace"]) == "table", "Compilation context must contain a .lace table")
+ assert(type(sourcename) == "string", "Source name must be a string")
+ assert(content == nil or type(content) == "string", "Content must be nil or a string")
+ if not content then
+ -- No content supplied, try and load it.
+ sourcename, content = _loader(compcontext)(compcontext, sourcename)
+ if type(sourcename) ~= "string" then
+ return false, normalise_error(compcontext, content)
+ end
+ end
+
+ -- We have some content, let's lex it.
+ -- We cannot fail to lex a string, it's not possible in our API
+ local lexed_content = lex.string(content, sourcename)
+
+ -- Now define the basis ruleset
+ local ruleset = {
+ name = sourcename,
+ content = lexed_content,
+ rules = {},
+ }
+
+ return ruleset
+end
+
+local function compile_ruleset(ctx, src, cnt)
+ local ok, ret, msg = xpcall(function()
+ return internal_compile_ruleset(ctx, src, cnt)
+ end, debug.traceback)
+ if not ok then
+ return nil, ret
+ end
+ return ret, msg
+end
+
+return {
+ compile = compile_ruleset,
+ error = _error,
+} \ No newline at end of file
diff --git a/test/test-lace.compile-nothing.rules b/test/test-lace.compile-nothing.rules
new file mode 100644
index 0000000..4a5f8c9
--- /dev/null
+++ b/test/test-lace.compile-nothing.rules
@@ -0,0 +1,6 @@
+-- This is a ruleset which should result in no rules being
+-- compiled.
+
+# It is comprised entirely of comments and whitespace
+
+// Is it not nifty?
diff --git a/test/test-lace.compiler.lua b/test/test-lace.compiler.lua
new file mode 100644
index 0000000..8651969
--- /dev/null
+++ b/test/test-lace.compiler.lua
@@ -0,0 +1,118 @@
+-- test/test-compiler.lua
+--
+-- Lua Access Control Engine -- Tests for the compilerer
+--
+-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
+--
+-- For Licence terms, see COPYING
+--
+
+-- Step one, start coverage
+
+local luacov = require 'luacov'
+
+local compiler = require 'lace.compiler'
+local sio = require 'luxio.simple'
+
+local testnames = {}
+
+local function add_test(suite, name, value)
+ rawset(suite, name, value)
+ testnames[#testnames+1] = name
+end
+
+local suite = setmetatable({}, {__newindex = add_test})
+
+function suite.context_missing()
+ local result, msg = compiler.compile(nil, "")
+ assert(result == nil, "Lua errors should return nil")
+ assert(msg:match("context must be a table"), "Supposed to whinge about context not being a table")
+end
+
+function suite.context_missing_dot_lace()
+ local result, msg = compiler.compile({}, "")
+ assert(result == nil, "Lua errors should return nil")
+ assert(msg:match("context must contain"), "Supposed to whinge about context missing .lace")
+end
+
+function suite.context_dot_lace_not_table()
+ local result, msg = compiler.compile({[".lace"] = true}, "")
+ assert(result == nil, "Lua errors should return nil")
+ assert(msg:match("context must contain"), "Supposed to whinge about context missing .lace")
+end
+
+function suite.source_not_string()
+ local result, msg = compiler.compile({[".lace"] = {}}, false)
+ assert(result == nil, "Lua errors should return nil")
+ assert(msg:match("name must be a string"), "Supposed to whinge about name not being a string")
+end
+
+function suite.content_not_string()
+ local result, msg = compiler.compile({[".lace"] = {}}, "", false)
+ assert(result == nil, "Lua errors should return nil")
+ assert(msg:match("must be nil or a string"), "Supposed to whinge about content not being a string but being non-nil")
+end
+
+function suite.empty_content_no_loader()
+ local result, msg = compiler.compile({[".lace"] = {}}, "", "")
+ assert(type(result) == "table", "No ruleset returned?")
+end
+
+function suite.no_content_no_loader()
+ local result, msg = compiler.compile({[".lace"] = {}}, "")
+ assert(result == false, "Internal errors should return false")
+ assert(msg:match("Ruleset not found:"), "Supposed to whinge about ruleset not being found")
+end
+
+-- Now we set up a more useful context and use that going forward:
+
+local comp_context = {
+ [".lace"] = {
+ loader = function(ctx, name)
+ if name == "THROW_ERROR" then
+ error("THROWN")
+ end
+ local fh, msg = sio.open("test/test-lace.compile-" .. name .. ".rules", "r")
+ if not fh then
+ return compiler.error("LOADER: Unknown: " .. name, {1})
+ end
+ local content = fh:read("*a")
+ fh:close()
+ return "real-" .. name, content
+ end,
+ },
+}
+
+function suite.loader_errors()
+ local result, msg = compiler.compile(comp_context, "THROW_ERROR")
+ assert(result == nil, "Lua errors should return nil")
+ assert(msg:match("THROWN"), "Error returned didn't match what we threw")
+end
+
+function suite.load_no_file()
+ local result, msg = compiler.compile(comp_context, "NOT_FOUND")
+ assert(result == false, "Internal errors should return false")
+ assert(msg:match("LOADER: Unknown: NOT_FOUND"), "Error returned didn't match what we returned from loader")
+end
+
+function suite.load_file_with_no_rules()
+ local result, msg = compiler.compile(comp_context, "nothing")
+ assert(type(result) == "table", "Loading a ruleset should result in a table")
+ assert(#result.rules == 0, "There should be no rules present")
+end
+
+local count_ok = 0
+for _, testname in ipairs(testnames) do
+ print("Run: " .. testname)
+ local ok, err = xpcall(suite[testname], debug.traceback)
+ if not ok then
+ print(err)
+ print()
+ else
+ count_ok = count_ok + 1
+ end
+end
+
+print("Compiler: " .. tostring(count_ok) .. "/" .. tostring(#testnames) .. " OK")
+
+os.exit(count_ok == #testnames and 0 or 1)