diff options
author | Daniel Silverstone <dsilvers@digital-scurf.org> | 2012-05-13 18:45:17 +0100 |
---|---|---|
committer | Daniel Silverstone <dsilvers@digital-scurf.org> | 2012-05-13 18:45:17 +0100 |
commit | 7d87eb9e8b7b59a6b89c5ac5eb7d2e9ee081fdf6 (patch) | |
tree | 8b8a3e31c9b58cae56c75068b1b7c7af8dbd4071 | |
parent | 56ccdf057a44b57d19e770ee7cbd9f71aa1a5f6c (diff) | |
download | lace-7d87eb9e8b7b59a6b89c5ac5eb7d2e9ee081fdf6.tar.gz |
Initial compiler module and tests. Doesn't compile anything yet
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | lib/lace.lua | 16 | ||||
-rw-r--r-- | lib/lace/compiler.lua | 72 | ||||
-rw-r--r-- | test/test-lace.compile-nothing.rules | 6 | ||||
-rw-r--r-- | test/test-lace.compiler.lua | 118 |
5 files changed, 214 insertions, 2 deletions
@@ -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) |