-
Notifications
You must be signed in to change notification settings - Fork 9
/
errorgen.lua
401 lines (289 loc) · 7.22 KB
/
errorgen.lua
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
-- Error generation code for LL(1) grammars
-- AST funcs:
local function isfinal(t)
return t["t"] or t["nt"] or t["func"] or t["s"] or t["num"]
end
local function isaction(t)
return t["action"]
end
local function isrule(t)
return t and t["rulename"]
end
local function isgrammar(t)
if type(t) == "table" and not(t["action"]) then
return isrule(t[1])
end
return false
end
local function istoken (t)
return t["token"] == "1"
end
local function finalNode (t)
if t["t"] then
return"t",t["t"] -- terminal
elseif t["nt"] then
return "nt", t["nt"], istoken(t) -- nonterminal
elseif t["func"] then
return "func", t["func"] -- function
elseif t["s"] then
return "s", t["s"]
elseif t["num"] then
return "num", t["num"]
end
return nil
end
--[[
function rightleaf:
returns the right-most concatenation in the AST.
used for followset keys
input: ((A B) C)
output: {"nt_C"}
input: (A / B / C) (D / 'e')
output: {"nt_D","t_e"}
input: A*
output: {'',"nt_A"}
input: !A
output: {"not_nt_A"}
]]
local function addnot(t)
local ret = {}
for k,v in pairs(t) do
ret[k] = "not_"..v
end
return ret
end
local function addepsilon(t)
local ret = t
table.insert(ret, '')
return ret
end
local function mergetables(first,second)
local ret = first
for k,v in pairs(second) do
table.insert(ret, v)
end
return ret
end
local function rightleaf(t)
local action = t.action
local op1 = t.op1
local op2 = t.op2
if isfinal(t) then
-- todo: replace nt_A with FIRST(A)
local typefn, fn, tok = finalNode(t)
local ret = typefn .. "_" .. fn -- terminals: t_if, nonterminals: nt_if
return {ret}
end
if action == "or" then
return mergetables(rightleaf(op1), rightleaf(op2))
elseif action == "and" then -- consider only RHS
return rightleaf(op2)
elseif action == "&" then
return rightleaf(op1)
elseif action == "!" then
return addnot(rightleaf(op1))
elseif action == "+" then
return rightleaf(op1)
elseif action == "*" or action == "?" then
return addepsilon(rightleaf(op1))
elseif action == "^" then
op2 = op2["num"] -- second operand is number
if op2 >= 1 then
return rightleaf(op1)
else
return addepsilon(rightleaf(op1))
end
elseif action == "^LABEL" or action == "->" or action == "=>" or action == "tcap" or action == "gcap" or action == "subcap" or action == "scap" then
return rightleaf(op1)
elseif action == "bref" or action == "poscap" then
return addepsilon({}) -- only empty string
elseif action == "anychar" then
return {"_anychar"}
elseif action == "label" then
return addepsilon({})
elseif action == "%" then
return addepsilon({})
elseif action == "invert" then
return addnot(rightleaf(op1))
elseif action == "range" then
return {"_anychar"}
else
error("Unsupported action '"..action.."'")
end
end
local FOLLOW = {}
local function follow_aux(t, dontsplit)
local action = t.action
local op1 = t.op1
local op2 = t.op2
if isfinal(t) then
return {t}
end
if action == "or" then
if dontsplit then -- do not split "(B / C)" in "A (B / C)"
return {t}
else -- return both
return mergetables(follow_aux(op1), follow_aux(op2))
end
elseif action == "and" then -- magic happens here
-- (A (B / D)) (!E C / D)
-- 1) FOLLOW(B) = FOLLOW(D) = {(!E C / D)}
local rightset = rightleaf(op1)
local rhs = follow_aux(op2)
for k,v in pairs(rightset) do
if not FOLLOW[v] then
FOLLOW[v] = {}
end
-- TODO: check if rhs already exists in FOLLOW(v)
table.insert(FOLLOW[v],rhs)
end
-- 2) FOLLOW(A) = {(B / D)}
return follow_aux(op1)
elseif action == "&" then
return follow_aux(op1)
elseif action == "!" then
return {action="!", op1=follow_aux(op1)}
elseif action == "+" then
return follow_aux(op1)
elseif action == "*" then
return addepsilon(follow_aux(op1))
elseif action == "?" then
return addepsilon(follow_aux(op1))
elseif action == "^" then
op2 = op2["num"]
if op2 >= 1 then
return follow_aux(op1)
else
return addepsilon(follow_aux(op1))
end
elseif action == "^LABEL" or action == "->" or action == "=>" or action == "tcap" or action == "gcap" or action == "subcap" or action == "scap" then
return follow_aux(op1)
elseif action == "bref" or action == "poscap" then
return addepsilon({}) -- only empty string
elseif action == "anychar" then
return {"_anychar"}
elseif action == "label" then
return addepsilon({})
elseif action == "%" then
return addepsilon({})
elseif action == "invert" then
return {t} -- whole table
elseif action == "range" then
return {"_anychar"}
else
error("Unsupported action '"..action.."'")
end
end
-- function: follow
-- finds follow set for the whole AST, with key (rule, term)
local function follow (t)
local followset = {}
if isgrammar(t) then
for pos,val in pairs(t) do
local rule = val.rulename
FOLLOW = {} -- reset for each rule
follow_aux(val.rule) -- apply recursive function
followset[rule] = FOLLOW
end
else
FOLLOW = {}
follow_aux(t)
followset[''] = FOLLOW
end
return followset
end
-- functions to add errors
-- find size of table
local function getn (t)
local size = 0
for _, _ in pairs(t) do
size = size+1
end
return size
end
-- generate error message by traversing table to the left
local function printexpect(op)
--peg.print_r(op)
if isfinal(op) then
if op["t"] then
return "'"..op["t"].."'"
end
return op["nt"] or op["func"] or op["s"] or op["num"]
else
local test = op.op1
if not test then
return op.action
else
return printexpect(test)
end
end
end
local GENERATED_ERRORS = 0
local TERRS = {}
local function generateerror(op, after)
local desc = "Expected "..printexpect(op)
local err = GENERATED_ERRORS+1
if err >= 255 then
error("Error label limit reached(255)")
end
local name = "errorgen"..err
TERRS[name] = desc
GENERATED_ERRORS = GENERATED_ERRORS+1
return name
end
local function tryadderror(op, after)
if FOLLOW then
local rhs = rightleaf(after)
-- (A / B) C
-- generate error iff #FOLLOW(A) OR #FOLLOW(B) = 1
local generate = false
for k,v in pairs(rhs) do
if FOLLOW[v] then
local n = getn(FOLLOW[v])
generate = generate or n==1
end
end
if generate then
local lab = generateerror(op, after)
return {action="^LABEL",op1=op,op2={s=lab}}
end
end
return op
end
-- function: adderrors
-- traverses the AST and adds error labels where possible
local function adderrors_aux(ast,tokenrule)
if not ast then
return nil
end
if isaction(ast) then
local act, op1, op2
act = ast["action"]
op1 = ast["op1"]
op2 = ast["op2"]
if act == "and" and not tokenrule then
op2 = tryadderror(op2, op1)
end
ast["op1"] = adderrors_aux(op1,tokenrule)
ast["op2"] = adderrors_aux(op2,tokenrule)
end
return ast
end
local function adderrors(t, followset)
GENERATED_ERRORS = 0
TERRS = {}
if isgrammar(t) then
for pos,val in pairs(t) do
local currentrule = val.rulename
FOLLOW = followset[currentrule]
local rule = val.rule
local istokenrule = val.token == "1"
adderrors_aux(rule,istokenrule)
end
else
FOLLOW = followset['']
adderrors_aux(t,false)
end
return TERRS
end
return {follow=follow,adderrors=adderrors}