diff options
Diffstat (limited to 'relabelrec.lua')
-rw-r--r-- | relabelrec.lua | 396 |
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 | ||
4 | local tonumber, type, print, error, ipairs = tonumber, type, print, error, ipairs | ||
5 | local pcall = pcall | ||
6 | local setmetatable = setmetatable | ||
7 | local unpack, tinsert, concat = table.unpack or unpack, table.insert, table.concat | ||
8 | local rep = string.rep | ||
9 | local 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' | ||
14 | local mm = m | ||
15 | |||
16 | -- pattern's metatable | ||
17 | local mt = getmetatable(mm.P(0)) | ||
18 | |||
19 | |||
20 | |||
21 | -- No more global accesses after this point | ||
22 | local version = _VERSION | ||
23 | if version == "Lua 5.2" then _ENV = nil end | ||
24 | |||
25 | |||
26 | local any = m.P(1) | ||
27 | local dummy = mm.P(false) | ||
28 | |||
29 | |||
30 | local 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 | |||
77 | local errmsgs = {} | ||
78 | local labels = {} | ||
79 | |||
80 | for i, err in ipairs(errinfo) do | ||
81 | errmsgs[i] = err[2] | ||
82 | labels[err[1]] = i | ||
83 | end | ||
84 | |||
85 | local function expect (pattern, labelname) | ||
86 | local label = labels[labelname] | ||
87 | return pattern + m.T(label) | ||
88 | end | ||
89 | |||
90 | |||
91 | -- Pre-defined names | ||
92 | local Predef = { nl = m.P"\n" } | ||
93 | local tlabels = {} | ||
94 | |||
95 | |||
96 | local mem | ||
97 | local fmem | ||
98 | local gmem | ||
99 | |||
100 | |||
101 | local 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) | ||
130 | end | ||
131 | |||
132 | |||
133 | updatelocale() | ||
134 | |||
135 | |||
136 | |||
137 | local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end) | ||
138 | |||
139 | |||
140 | local 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 | ||
146 | end | ||
147 | |||
148 | |||
149 | local 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 | ||
157 | end | ||
158 | |||
159 | local 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 | ||
163 | end | ||
164 | |||
165 | |||
166 | local S = (Predef.space + "--" * (any - Predef.nl)^0)^0 | ||
167 | |||
168 | local name = m.C(m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0) | ||
169 | |||
170 | local arrow = S * "<-" | ||
171 | |||
172 | -- a defined name only have meaning in a given environment | ||
173 | local Def = name * m.Carg(1) | ||
174 | |||
175 | local num = m.C(m.R"09"^1) * S / tonumber | ||
176 | |||
177 | local String = "'" * m.C((any - "'" - m.P"\n")^0) * expect("'", "MisTerm1") | ||
178 | + '"' * m.C((any - '"' - m.P"\n")^0) * expect('"', "MisTerm2") | ||
179 | |||
180 | |||
181 | local 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 | ||
187 | end | ||
188 | |||
189 | local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R | ||
190 | |||
191 | local item = defined + Range + m.C(any - m.P"\n") | ||
192 | |||
193 | local 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 | |||
200 | local 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 | ||
207 | end | ||
208 | |||
209 | local function firstdef (n, r) return adddef({n}, n, r) end | ||
210 | |||
211 | |||
212 | local 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 | ||
217 | end | ||
218 | |||
219 | local 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 | ||
231 | end | ||
232 | |||
233 | local 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 | |||
292 | local pattern = S * m.Cg(m.Cc(false), "G") * expect(exp, "NoPatt") / mm.P | ||
293 | * S * expect(-any, "ExtraChars") | ||
294 | |||
295 | local 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 | ||
306 | end | ||
307 | |||
308 | local 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 | ||
313 | end | ||
314 | |||
315 | local 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 | ||
335 | end | ||
336 | |||
337 | local 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) | ||
344 | end | ||
345 | |||
346 | local 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 | ||
357 | end | ||
358 | |||
359 | local 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) | ||
369 | end | ||
370 | |||
371 | local function setlabels (t) | ||
372 | tlabels = t | ||
373 | end | ||
374 | |||
375 | local 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 | ||
380 | end | ||
381 | |||
382 | |||
383 | -- exported names | ||
384 | local re = { | ||
385 | compile = compile, | ||
386 | match = match, | ||
387 | find = find, | ||
388 | gsub = gsub, | ||
389 | updatelocale = updatelocale, | ||
390 | setlabels = setlabels, | ||
391 | calcline = calcline | ||
392 | } | ||
393 | |||
394 | if version == "Lua 5.1" then _G.re = re end | ||
395 | |||
396 | return re | ||