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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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,
}
|