summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2012-08-23 11:11:52 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2012-08-23 11:11:52 +0100
commitd2f52487f795ab3c0c5bd8074b5a6c9177f8fbb3 (patch)
tree003aad9e73f7ab902193e9020d14cf2f5d6289a5
parent74a5ed928f3c2f852be92df0fe03cf2089123a3f (diff)
downloadclod-d2f52487f795ab3c0c5bd8074b5a6c9177f8fbb3.tar.gz
Initial clod library
-rw-r--r--lib/clod.lua127
1 files changed, 127 insertions, 0 deletions
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 <dsilvers@digital-scurf.org>
+--
+
+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,
+}