summaryrefslogtreecommitdiff
path: root/srcinfo.lua
blob: c03864139a4971a0e1f5300fc8796670d446a18a (plain)
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
local lpeg = require "lpeg"

local PKGBASE = "pkgbase"
local PKGNAME = "pkgname"
local modes = {
	[PKGBASE] = PKGBASE,
	[PKGNAME] = PKGNAME,
}
local package_mt = {}


function package_mt:__index (key)
	return self.base[key]
end


local
function shallow (list)
	local new = {}
	for i, value in ipairs(list) do
		new[i] = value
	end
	return new
end


local
function apply (state, method, ...)
	if state[method] then
		return state[method](state, ...)
	end
	return nil
end


local identifier = lpeg.R"az" + lpeg.R"09" + lpeg.S"@+_"
identifier =  identifier * (identifier + lpeg.S".-")^0
local whitespace = lpeg.S" \t"^0
local newline = lpeg.P"\r\n" + "\n"
local rest = (1 - newline)^0
local definition = lpeg.Cc"definition" * whitespace * lpeg.C(identifier) * whitespace * "=" * whitespace * lpeg.C(rest)
local comment = lpeg.Cc"comment" * whitespace * lpeg.P"#" * lpeg.C(rest)
local empty = lpeg.Cc"empty" * whitespace
local invalid = lpeg.Cc"invalid" * lpeg.C(rest)
local line = lpeg.Carg(1) * (definition + comment + empty + invalid) / apply * newline
local capture = lpeg.Ct(line^0) * whitespace * -lpeg.P(1)


local
function srcinfo (text, options)
	options = options or {}
	return capture:match(
		text,
		nil,
		{
			mode = modes[options.mode] or PKGNAME,
			base = nil,
			current = nil,
			definition = function (self, name, value)
				if name == PKGBASE then
					self.base = {name=value}
					self.current = self.base
					if self.mode == PKGBASE then
						self.base.packages = {}
						return self.current
					end
				elseif name == PKGNAME then
					self.current = {name=value}
					if self.mode == PKGNAME then
						self.current.base = self.base
						return setmetatable(self.current, package_mt)
					else
						table.insert(self.base.packages, self.current)
					end
				else
					local current_value = self.current[name]
					if type(current_value) == "table" then
						current_value = shallow(current_value)  -- Wasteful, but won't spill
						table.insert(current_value, value)
						self.current[name] = current_value
					elseif current_value ~= nil then
						self.current[name] = {current_value, value}
					else
						self.current[name] = value
					end
				end
			end,
			comment = function (self, text) end,
			empty = function (self) end,
			invalid = function (self, text) return text end,
		}
	)
end


return srcinfo