aboutsummaryrefslogtreecommitdiff
path: root/piecemeal.lua
blob: 0ddf8f9365031143351006777f5b00f41742d695 (plain) (blame)
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
--[[
	A parser has the signature: (list, sindex) -> eindex [, output]
	If the parser "fails", it must return exactly nil.
	If the parser "succeeds", it must return at least the eindex, and may also return some output.
]]--

return {
	eof = function ()
		return function (l, i)
			-- we don't have to, but we advance the pointer by one
			-- this prevents infinite looping
			if #l+1 == i then
				local pos = l[#l].stop + 1
				return i+1, {type = 'EOF', value = '', start = pos, stop = pos}
			end
			return nil
		end
	end,
	lexeme = function (type)
		return function (l, i)
			if l[i] and l[i].type == type then
				return i+1, l[i]
			end
			return nil
		end
	end,
	value = function (value)
		return function (l, i)
			if l[i] and l[i].value == value then
				return i+1, l[i]
			end
			return nil
		end
	end,
	all = function (...)
		local args = {...}
		return function (l, i)
			local res = {}
			local eind, out
			for _, v in ipairs(args) do
				eind, out = v(l, i)
				if not eind then return nil end
				i = eind
				table.insert(res, out)
			end
			return i, res
		end
	end,
	alt = function (...)
		local args = {...}
		return function (l, i)
			local eind, out
			for _, v in ipairs(args) do
				eind, out = v(l, i)
				if eind then
					return eind, out
				end
			end
			return nil
		end
	end,
	opt = function (p)
		return function (l, i)
			local eind, out = p(l, i)
			if eind
				then return eind, out
				else return i
			end
		end
	end,
	plus = function (p)
		return function (l, i)
			local res = {}
			local eind, out = p(l, i)
			if not eind then return nil end
			repeat
				i = eind
				table.insert(res, out)
				eind, out = p(l, i)
			until not eind
			return i, res
		end
	end,
	star = function (p)
		return function (l, i)
			local res = {}
			local eind, out
			repeat
				eind, out = p(l, i)
				if eind then
					table.insert(res, out)
					i = eind
				end
			until not eind
			return i, res
		end
	end,

	postp = function (p, f)
		return function (l, i)
			local eind, out = p(l, i)
			if out then out = f(out) end
			return eind, out
		end
	end,

	parse = function (text, lexer, parser)
		local tokens = {}
		lexer:reset(text)
		local next
		repeat
			next = lexer:next()
			if next then table.insert(tokens, next) end
		until next == nil
		local eind, out = parser(tokens, 1)
		-- a successful parse means eind is > the length of tokens
		if eind > #tokens then
			return out, tokens[#tokens].stop, nil
		else -- TODO: what if eind == 1? can that happen?
			return out, tokens[eind-1].stop, tokens[eind].start
		end
	end,
}