aboutsummaryrefslogtreecommitdiff
path: root/relabelrec.lua
diff options
context:
space:
mode:
Diffstat (limited to 'relabelrec.lua')
-rw-r--r--relabelrec.lua396
1 files changed, 396 insertions, 0 deletions
diff --git a/relabelrec.lua b/relabelrec.lua
new file mode 100644
index 0000000..16ca7f0
--- /dev/null
+++ b/relabelrec.lua
@@ -0,0 +1,396 @@
1-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $
2
3-- imported functions and modules
4local tonumber, type, print, error, ipairs = tonumber, type, print, error, ipairs
5local pcall = pcall
6local setmetatable = setmetatable
7local unpack, tinsert, concat = table.unpack or unpack, table.insert, table.concat
8local rep = string.rep
9local m = require"lpeglabelrec"
10
11-- 'm' will be used to parse expressions, and 'mm' will be used to
12-- create expressions; that is, 're' runs on 'm', creating patterns
13-- on 'mm'
14local mm = m
15
16-- pattern's metatable
17local mt = getmetatable(mm.P(0))
18
19
20
21-- No more global accesses after this point
22local version = _VERSION
23if version == "Lua 5.2" then _ENV = nil end
24
25
26local any = m.P(1)
27local dummy = mm.P(false)
28
29
30local errinfo = {
31 {"NoPatt", "no pattern found"},
32 {"ExtraChars", "unexpected characters after the pattern"},
33
34 {"ExpPatt1", "expected a pattern after '/' or the label(s)"},
35
36 {"ExpPatt2", "expected a pattern after '&'"},
37 {"ExpPatt3", "expected a pattern after '!'"},
38
39 {"ExpPatt4", "expected a pattern after '('"},
40 {"ExpPatt5", "expected a pattern after ':'"},
41 {"ExpPatt6", "expected a pattern after '{~'"},
42 {"ExpPatt7", "expected a pattern after '{|'"},
43
44 {"ExpPatt8", "expected a pattern after '<-'"},
45
46 {"ExpPattOrClose", "expected a pattern or closing '}' after '{'"},
47
48 {"ExpNum", "expected a number after '^', '+' or '-' (no space)"},
49 {"ExpCap", "expected a string, number, '{}' or name after '->'"},
50
51 {"ExpName1", "expected the name of a rule after '=>'"},
52 {"ExpName2", "expected the name of a rule after '=' (no space)"},
53 {"ExpName3", "expected the name of a rule after '<' (no space)"},
54
55 {"ExpLab1", "expected at least one label after '{'"},
56 {"ExpLab2", "expected a label after the comma"},
57
58 {"ExpNameOrLab", "expected a name or label after '%' (no space)"},
59
60 {"ExpItem", "expected at least one item after '[' or '^'"},
61
62 {"MisClose1", "missing closing ')'"},
63 {"MisClose2", "missing closing ':}'"},
64 {"MisClose3", "missing closing '~}'"},
65 {"MisClose4", "missing closing '|}'"},
66 {"MisClose5", "missing closing '}'"}, -- for the captures
67
68 {"MisClose6", "missing closing '>'"},
69 {"MisClose7", "missing closing '}'"}, -- for the labels
70
71 {"MisClose8", "missing closing ']'"},
72
73 {"MisTerm1", "missing terminating single quote"},
74 {"MisTerm2", "missing terminating double quote"},
75}
76
77local errmsgs = {}
78local labels = {}
79
80for i, err in ipairs(errinfo) do
81 errmsgs[i] = err[2]
82 labels[err[1]] = i
83end
84
85local function expect (pattern, labelname)
86 local label = labels[labelname]
87 return pattern + m.T(label)
88end
89
90
91-- Pre-defined names
92local Predef = { nl = m.P"\n" }
93local tlabels = {}
94
95
96local mem
97local fmem
98local gmem
99
100
101local function updatelocale ()
102 mm.locale(Predef)
103 Predef.a = Predef.alpha
104 Predef.c = Predef.cntrl
105 Predef.d = Predef.digit
106 Predef.g = Predef.graph
107 Predef.l = Predef.lower
108 Predef.p = Predef.punct
109 Predef.s = Predef.space
110 Predef.u = Predef.upper
111 Predef.w = Predef.alnum
112 Predef.x = Predef.xdigit
113 Predef.A = any - Predef.a
114 Predef.C = any - Predef.c
115 Predef.D = any - Predef.d
116 Predef.G = any - Predef.g
117 Predef.L = any - Predef.l
118 Predef.P = any - Predef.p
119 Predef.S = any - Predef.s
120 Predef.U = any - Predef.u
121 Predef.W = any - Predef.w
122 Predef.X = any - Predef.x
123 mem = {} -- restart memoization
124 fmem = {}
125 gmem = {}
126 local mt = {__mode = "v"}
127 setmetatable(mem, mt)
128 setmetatable(fmem, mt)
129 setmetatable(gmem, mt)
130end
131
132
133updatelocale()
134
135
136
137local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
138
139
140local function getdef (id, defs)
141 local c = defs and defs[id]
142 if not c then
143 error("undefined name: " .. id)
144 end
145 return c
146end
147
148
149local function mult (p, n)
150 local np = mm.P(true)
151 while n >= 1 do
152 if n%2 >= 1 then np = np * p end
153 p = p * p
154 n = n/2
155 end
156 return np
157end
158
159local function equalcap (s, i, c)
160 if type(c) ~= "string" then return nil end
161 local e = #c + i
162 if s:sub(i, e - 1) == c then return e else return nil end
163end
164
165
166local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
167
168local name = m.C(m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0)
169
170local arrow = S * "<-"
171
172-- a defined name only have meaning in a given environment
173local Def = name * m.Carg(1)
174
175local num = m.C(m.R"09"^1) * S / tonumber
176
177local String = "'" * m.C((any - "'" - m.P"\n")^0) * expect("'", "MisTerm1")
178 + '"' * m.C((any - '"' - m.P"\n")^0) * expect('"', "MisTerm2")
179
180
181local defined = "%" * Def / function (c,Defs)
182 local cat = Defs and Defs[c] or Predef[c]
183 if not cat then
184 error("name '" .. c .. "' undefined")
185 end
186 return cat
187end
188
189local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
190
191local item = defined + Range + m.C(any - m.P"\n")
192
193local Class =
194 "["
195 * (m.C(m.P"^"^-1)) -- optional complement symbol
196 * m.Cf(expect(item, "ExpItem") * (item - "]")^0, mt.__add)
197 / function (c, p) return c == "^" and any - p or p end
198 * expect("]", "MisClose8")
199
200local function adddef (t, k, exp)
201 if t[k] then
202 error("'"..k.."' already defined as a rule")
203 else
204 t[k] = exp
205 end
206 return t
207end
208
209local function firstdef (n, r) return adddef({n}, n, r) end
210
211
212local function NT (n, b)
213 if not b then
214 error("rule '"..n.."' used outside a grammar")
215 else return mm.V(n)
216 end
217end
218
219local function choicerec (...)
220 local t = { ... }
221 local n = #t
222 local p = t[1]
223 local i = 2
224 while i + 1 <= n do
225 -- t[i] == nil when there are no labels
226 p = t[i] and mm.Rec(p, t[i+1], unpack(t[i])) or mt.__add(p, t[i+1])
227 i = i + 2
228 end
229
230 return p
231end
232
233local exp = m.P{ "Exp",
234 Exp = S * ( m.V"Grammar"
235 + (m.V"Seq" * (S * (("//" * m.Ct(m.V"Labels")) + ("/" * m.Cc(nil)))
236 * expect(S * m.V"Seq", "ExpPatt1")
237 )^0
238 ) / choicerec);
239 Labels = m.P"{" * expect(S * m.V"Label", "ExpLab1")
240 * (S * "," * expect(S * m.V"Label", "ExpLab2"))^0
241 * expect(S * "}", "MisClose7");
242 Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix" * (S * m.V"Prefix")^0, mt.__mul);
243 Prefix = "&" * expect(S * m.V"Prefix", "ExpPatt2") / mt.__len
244 + "!" * expect(S * m.V"Prefix", "ExpPatt3") / mt.__unm
245 + m.V"Suffix";
246 Suffix = m.Cf(m.V"Primary" *
247 ( S * ( m.P"+" * m.Cc(1, mt.__pow)
248 + m.P"*" * m.Cc(0, mt.__pow)
249 + m.P"?" * m.Cc(-1, mt.__pow)
250 + "^" * expect( m.Cg(num * m.Cc(mult))
251 + m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow)
252 ),
253 "ExpNum")
254 + "->" * expect(S * ( m.Cg((String + num) * m.Cc(mt.__div))
255 + m.P"{}" * m.Cc(nil, m.Ct)
256 + m.Cg(Def / getdef * m.Cc(mt.__div))
257 ),
258 "ExpCap")
259 + "=>" * expect(S * m.Cg(Def / getdef * m.Cc(m.Cmt)),
260 "ExpName1")
261 )
262 )^0, function (a,b,f) return f(a,b) end );
263 Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1")
264 + String / mm.P
265 + Class
266 + defined
267 + "%" * expect(m.P"{", "ExpNameOrLab")
268 * expect(S * m.V"Label", "ExpLab1")
269 * expect(S * "}", "MisClose7") / mm.T
270 + "{:" * (name * ":" + m.Cc(nil)) * expect(m.V"Exp", "ExpPatt5")
271 * expect(S * ":}", "MisClose2")
272 / function (n, p) return mm.Cg(p, n) end
273 + "=" * expect(name, "ExpName2")
274 / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
275 + m.P"{}" / mm.Cp
276 + "{~" * expect(m.V"Exp", "ExpPatt6")
277 * expect(S * "~}", "MisClose3") / mm.Cs
278 + "{|" * expect(m.V"Exp", "ExpPatt7")
279 * expect(S * "|}", "MisClose4") / mm.Ct
280 + "{" * expect(m.V"Exp", "ExpPattOrClose")
281 * expect(S * "}", "MisClose5") / mm.C
282 + m.P"." * m.Cc(any)
283 + (name * -arrow + "<" * expect(name, "ExpName3")
284 * expect(">", "MisClose6")) * m.Cb("G") / NT;
285 Label = num + name / function (f) return tlabels[f] end;
286 Definition = name * arrow * expect(m.V"Exp", "ExpPatt8");
287 Grammar = m.Cg(m.Cc(true), "G")
288 * m.Cf(m.V"Definition" / firstdef * (S * m.Cg(m.V"Definition"))^0,
289 adddef) / mm.P;
290}
291
292local pattern = S * m.Cg(m.Cc(false), "G") * expect(exp, "NoPatt") / mm.P
293 * S * expect(-any, "ExtraChars")
294
295local function lineno (s, i)
296 if i == 1 then return 1, 1 end
297 local adjustment = 0
298 -- report the current line if at end of line, not the next
299 if s:sub(i,i) == '\n' then
300 i = i-1
301 adjustment = 1
302 end
303 local rest, num = s:sub(1,i):gsub("[^\n]*\n", "")
304 local r = #rest
305 return 1 + num, (r ~= 0 and r or 1) + adjustment
306end
307
308local function splitlines(str)
309 local t = {}
310 local function helper(line) tinsert(t, line) return "" end
311 helper((str:gsub("(.-)\r?\n", helper)))
312 return t
313end
314
315local function compile (p, defs)
316 if mm.type(p) == "pattern" then return p end -- already compiled
317 p = p .. " " -- for better reporting of column numbers in errors when at EOF
318 local ok, cp, label, suffix = pcall(function() return pattern:match(p, 1, defs) end)
319 if not ok and cp then
320 if type(cp) == "string" then
321 cp = cp:gsub("^[^:]+:[^:]+: ", "")
322 end
323 error(cp, 3)
324 end
325 if not cp then
326 local lines = splitlines(p)
327 local line, col = lineno(p, #p - #suffix + 1)
328 local err = {}
329 tinsert(err, "L" .. line .. ":C" .. col .. ": " .. errmsgs[label])
330 tinsert(err, lines[line])
331 tinsert(err, rep(" ", col-1) .. "^")
332 error("syntax error(s) in pattern\n" .. concat(err, "\n"), 3)
333 end
334 return cp
335end
336
337local function match (s, p, i)
338 local cp = mem[p]
339 if not cp then
340 cp = compile(p)
341 mem[p] = cp
342 end
343 return cp:match(s, i or 1)
344end
345
346local function find (s, p, i)
347 local cp = fmem[p]
348 if not cp then
349 cp = compile(p) / 0
350 cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
351 fmem[p] = cp
352 end
353 local i, e = cp:match(s, i or 1)
354 if i then return i, e - 1
355 else return i
356 end
357end
358
359local function gsub (s, p, rep)
360 local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
361 gmem[p] = g
362 local cp = g[rep]
363 if not cp then
364 cp = compile(p)
365 cp = mm.Cs((cp / rep + 1)^0)
366 g[rep] = cp
367 end
368 return cp:match(s)
369end
370
371local function setlabels (t)
372 tlabels = t
373end
374
375local function calcline (s, i)
376 if i == 1 then return 1, 1 end
377 local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
378 local col = #rest
379 return 1 + line, col ~= 0 and col or 1
380end
381
382
383-- exported names
384local re = {
385 compile = compile,
386 match = match,
387 find = find,
388 gsub = gsub,
389 updatelocale = updatelocale,
390 setlabels = setlabels,
391 calcline = calcline
392}
393
394if version == "Lua 5.1" then _G.re = re end
395
396return re