From d2f52487f795ab3c0c5bd8074b5a6c9177f8fbb3 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Thu, 23 Aug 2012 11:11:52 +0100 Subject: Initial clod library --- lib/clod.lua | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 lib/clod.lua diff --git a/lib/clod.lua b/lib/clod.lua new file mode 100644 index 0000000..d25048b --- /dev/null +++ b/lib/clod.lua @@ -0,0 +1,127 @@ +-- lib/clod.lua +-- +-- Configuration Language Organised (by) Dots +-- +-- Copyright 2012 Daniel Silverstone +-- + +local type = type +local getinfo = debug.getinfo +local pcall = pcall +local loadstring = loadstring +local tconcat = table.concat +local setmetatable = setmetatable +local setfenv = setfenv + +-- metatable for clod config operations +local clod_mt = {} +-- metadata for clod config instances +local metadata = setmetatable({}, {__mode = "k"}) + +-- Methods for clod instances +local methods = {} + +function methods:serialise() + local entries = metadata[self].entries + local retstr = {} + local function serialise_entry(entry) + local key, value, line = entry.key, entry.value, "" + if entry.key then + local vtype = type(value) + if vtype == "string" then + line = ("%s %q"):format(key, value) + elseif vtype == "number" then + line = ("%s = %d"):format(key, value) + elseif vtype == "boolean" then + line = ("%s = %s"):format(key, value and "true" or "false") + else + line = ("-- %s was a %s. Cannot serialise"):format(key, vtype) + end + end + retstr[#retstr+1] = line + end + while entries do + serialise_entry(entries) + entries = entries.next + end + serialise_entry({}) + return tconcat(retstr, "\n") +end + +-- Metamethods for clod instances +function clod_mt:__index(key) + if key == "settings" then + return gen_settings(self) + elseif methods[key] then + return methods[key] + end +end + +local function parse_config(conf, confname) + local ret = {} + local settings = {} + local last_entry = {lineno = 0} + local front_entry = last_entry + local keys = {} + local parse_mt = {} + local function gen_hook(key) + local ret = setmetatable({}, parse_mt) + keys[ret] = key + return ret + end + function parse_mt:__index(key) + local prefix = keys[self] + if not prefix then + -- This is a global indexing, so return a fresh entry + return gen_hook(key) + end + -- A 'local' indexing, so combine with the key + return gen_hook(("%s.%s"):format(prefix, key)) + end + function parse_mt:__setindex(key, value) + -- This is the equivalent of 'foo = "bar"' instead of 'foo "bar"' + return self[key](value) + end + function parse_mt:__call(value) + local key = assert(keys[self]) + local curline = getinfo(2, "Snlf").currentline + local entry = { key = key, value = value, lineno = curline } + while last_entry.lineno < (curline - 1) do + local empty = { + lineno = last_entry.lineno + 1, + prev = last_entry + } + last_entry.next = empty + last_entry = empty + end + last_entry.next = entry + entry.prev = last_entry + last_entry = entry + settings[key] = entry + end + local func, msg = loadstring(conf, ("@%s"):format(confname or "clod-config")) + if not func then + return nil, msg + end + local globs = setmetatable({}, parse_mt) + setfenv(func, globs) + local ok, err = pcall(func) + if not ok then + return nil, err + end + -- Successfully loaded the settings, they're in settings and front_entry + -- points to line zero which we can now detach + front_entry.next.prev = nil + front_entry = front_entry.next + -- Construct a return object ready for magic + local ret = setmetatable({}, clod_mt) + metadata[ret] = { + settings = settings, + entries = front_entry, + } + return ret +end + +return { + parse = parse_config, +} -- cgit v1.2.1