/src/hobbes/lib/hobbes/parse/lalr.C
Line | Count | Source |
1 | | |
2 | | #include <hobbes/parse/lalr.H> |
3 | | #include <algorithm> |
4 | | #include <stdexcept> |
5 | | #include <string> |
6 | | #include <vector> |
7 | | |
8 | | namespace hobbes { |
9 | | |
10 | | /* |
11 | | * basic grammar/rule analysis |
12 | | */ |
13 | 0 | void removeRuleDefs(terminal* s, grammar* g) { |
14 | 0 | auto r = g->find(s); |
15 | 0 | if (r != g->end()) { |
16 | 0 | g->erase(r); |
17 | 0 | } |
18 | 0 | } |
19 | | |
20 | 0 | void removeRuleRefs(terminal* s, grammar* g) { |
21 | 0 | for (auto &sd : *g) { |
22 | 0 | for (nat r = 0; r < sd.second.size();) { |
23 | 0 | if (in(s, sd.second[r])) { |
24 | 0 | sd.second.erase(sd.second.begin() + r); |
25 | 0 | } else { |
26 | 0 | ++r; |
27 | 0 | } |
28 | 0 | } |
29 | 0 | } |
30 | 0 | } |
31 | | |
32 | 0 | void undefineSymbol(terminal* s, grammar* g) { |
33 | 0 | removeRuleDefs(s, g); |
34 | 0 | removeRuleRefs(s, g); |
35 | 0 | } |
36 | | |
37 | 0 | grammar removeRuleDefs(terminal* s, const grammar& g) { |
38 | 0 | grammar r = g; |
39 | 0 | removeRuleDefs(s, &r); |
40 | 0 | return r; |
41 | 0 | } |
42 | | |
43 | 0 | grammar removeRuleRefs(terminal* s, const grammar& g) { |
44 | 0 | grammar r = g; |
45 | 0 | removeRuleRefs(s, &r); |
46 | 0 | return r; |
47 | 0 | } |
48 | | |
49 | 0 | grammar undefineSymbol(terminal* s, const grammar& g) { |
50 | 0 | grammar r = g; |
51 | 0 | undefineSymbol(s, &r); |
52 | 0 | return r; |
53 | 0 | } |
54 | | |
55 | 0 | terminalset definedSymbols(const grammar& g) { |
56 | 0 | return keys(g); |
57 | 0 | } |
58 | | |
59 | | // a top-level symbol is used by nothing, or just by itself |
60 | 0 | terminalset topLevelSymbols(const grammar& g) { |
61 | 0 | terminalset result; |
62 | 0 | terminalset dsyms = definedSymbols(g); |
63 | |
|
64 | 0 | for (auto *dsym : dsyms) { |
65 | 0 | terminalset users = symbolsUsing(g, dsym); |
66 | 0 | if (users.empty() || (users.size() == 1 && in(dsym, users))) { |
67 | 0 | result.insert(dsym); |
68 | 0 | } |
69 | 0 | } |
70 | |
|
71 | 0 | return result; |
72 | 0 | } |
73 | | |
74 | 0 | void symbolsUsed(const grammar& g, const rule& r, terminalset* ss) { |
75 | 0 | for (auto *t : r) { |
76 | 0 | if (hasRule(g, t)) { |
77 | 0 | if (!in(t, *ss)) { |
78 | 0 | ss->insert(t); |
79 | 0 | symbolsUsed(g, t, ss); |
80 | 0 | } |
81 | 0 | } |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | 0 | void symbolsUsed(const grammar& g, const rules& rs, terminalset* ss) { |
86 | 0 | for (const auto &r : rs) { |
87 | 0 | symbolsUsed(g, r, ss); |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | 0 | void symbolsUsed(const grammar& g, terminal* s, terminalset* ss) { |
92 | 0 | auto sd = g.find(s); |
93 | 0 | if (sd != g.end()) { |
94 | 0 | symbolsUsed(g, sd->second, ss); |
95 | 0 | } |
96 | 0 | } |
97 | | |
98 | 0 | terminalset symbolsUsed(const grammar& g, terminal* s) { |
99 | 0 | terminalset r; |
100 | 0 | symbolsUsed(g, s, &r); |
101 | 0 | return r; |
102 | 0 | } |
103 | | |
104 | 0 | terminalset symbolsUsing(const grammar& g, terminal* s) { |
105 | 0 | terminalset result; |
106 | 0 | terminalset dsyms = definedSymbols(g); |
107 | |
|
108 | 0 | for (auto *dsym : dsyms) { |
109 | 0 | if (in(s, symbolsUsed(g, dsym))) { |
110 | 0 | result.insert(dsym); |
111 | 0 | } |
112 | 0 | } |
113 | |
|
114 | 0 | return result; |
115 | 0 | } |
116 | | |
117 | 0 | bool hasRule(const grammar& g, terminal* t) { |
118 | 0 | return g.find(t) != g.end(); |
119 | 0 | } |
120 | | |
121 | 0 | const rules* aggRules(const grammar& g, terminal* t) { |
122 | 0 | auto ad = g.find(t); |
123 | 0 | if (ad == g.end()) { |
124 | 0 | throw std::runtime_error("Internal error, no such aggregate defined."); |
125 | 0 | } else { |
126 | 0 | return &(ad->second); |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | 0 | const rule& nthRule(const rules* rs, nat n) { |
131 | 0 | if (n >= rs->size()) { |
132 | 0 | throw std::runtime_error("Internal error, no such rule."); |
133 | 0 | } else { |
134 | 0 | return (*rs)[n]; |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | 0 | terminal* ruleElement(const rule& r, nat i) { |
139 | 0 | if (i >= r.size()) { |
140 | 0 | return nullptr; |
141 | 0 | } else { |
142 | 0 | return r[i]; |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | 0 | bool directlyDerivesNull(const rule& r) { |
147 | 0 | return r.empty(); |
148 | 0 | } |
149 | | |
150 | 0 | bool directlyDerivesNull(const grammar& g, terminal* s) { |
151 | 0 | const rules* rs = aggRules(g, s); |
152 | 0 | for (const auto &r : *rs) { |
153 | 0 | if (directlyDerivesNull(r)) { |
154 | 0 | return true; |
155 | 0 | } |
156 | 0 | } |
157 | 0 | return false; |
158 | 0 | } |
159 | | |
160 | 0 | bool derivesNull(const terminalset& nullAggs, const rule& r) { |
161 | 0 | for (auto *v : r) { |
162 | 0 | if (nullAggs.find(v) == nullAggs.end()) { |
163 | 0 | return false; |
164 | 0 | } |
165 | 0 | } |
166 | 0 | return true; |
167 | 0 | } |
168 | | |
169 | 0 | bool derivesNull(const grammar& g, const terminalset& nullAggs, terminal* s) { |
170 | 0 | const rules* rs = aggRules(g, s); |
171 | 0 | for (const auto & r : *rs) { |
172 | 0 | if (derivesNull(nullAggs, r)) { |
173 | 0 | return true; |
174 | 0 | } |
175 | 0 | } |
176 | 0 | return false; |
177 | 0 | } |
178 | | |
179 | 0 | bool derivesNull(const grammar& g, const terminalset& nullAggs, const terminals& ss) { |
180 | 0 | for (auto *s : ss) { |
181 | 0 | if (!derivesNull(g, nullAggs, s)) { |
182 | 0 | return false; |
183 | 0 | } |
184 | 0 | } |
185 | 0 | return true; |
186 | 0 | } |
187 | | |
188 | 0 | bool derivesNull(const parserdef& p, terminal* s) { |
189 | 0 | return p.nullable.find(s) != p.nullable.end(); |
190 | 0 | } |
191 | | |
192 | | // the least set of defined symbols with a rule all of whose symbols are in the set |
193 | | // (found from a worklist: each rule counts its symbols not yet known to derive null, and is |
194 | | // told as each becomes known, so each rule is visited once per symbol in it. Passing over all |
195 | | // of the symbols until none changed took time quadratic in a chain of them, A_i -> A_i+1, |
196 | | // which each pass could advance by just one) |
197 | 0 | terminalset symbolsDerivingNull(const grammar& g) { |
198 | 0 | terminalset nulls; |
199 | |
|
200 | 0 | std::vector<std::pair<terminal*, size_t>> unknowns; // for each rule, its symbol and how many of its symbols aren't known to derive null |
201 | 0 | std::map<terminal*, std::vector<size_t>> uses; // for each symbol, the rules it appears in (once for each time) |
202 | 0 | std::vector<terminal*> known; // symbols known to derive null, whose uses are still to be told |
203 | |
|
204 | 0 | for (const auto& sd : g) { |
205 | 0 | for (const auto& r : sd.second) { |
206 | 0 | size_t i = unknowns.size(); |
207 | 0 | unknowns.push_back(std::make_pair(sd.first, r.size())); |
208 | 0 | for (auto* v : r) { |
209 | 0 | uses[v].push_back(i); |
210 | 0 | } |
211 | 0 | if (r.empty() && nulls.insert(sd.first).second) { |
212 | 0 | known.push_back(sd.first); |
213 | 0 | } |
214 | 0 | } |
215 | 0 | } |
216 | |
|
217 | 0 | while (!known.empty()) { |
218 | 0 | terminal* v = known.back(); |
219 | 0 | known.pop_back(); |
220 | |
|
221 | 0 | auto u = uses.find(v); |
222 | 0 | if (u != uses.end()) { |
223 | 0 | for (size_t i : u->second) { |
224 | 0 | if (--unknowns[i].second == 0 && nulls.insert(unknowns[i].first).second) { |
225 | 0 | known.push_back(unknowns[i].first); |
226 | 0 | } |
227 | 0 | } |
228 | 0 | } |
229 | 0 | } |
230 | 0 | return nulls; |
231 | 0 | } |
232 | | |
233 | | /* |
234 | | * LR(0) parser determination |
235 | | */ |
236 | 0 | item::item(terminal* agg, nat p, nat r, const terminalset& la) : agg(agg), p(p), r(r), la(la) { } |
237 | | |
238 | 0 | bool item::operator<(const item& rhs) const { |
239 | 0 | if (this->agg != rhs.agg) { |
240 | 0 | return this->agg < rhs.agg; |
241 | 0 | } else if (this->p != rhs.p) { |
242 | 0 | return this->p < rhs.p; |
243 | 0 | } else { |
244 | 0 | return this->r < rhs.r; |
245 | 0 | } |
246 | 0 | } |
247 | | |
248 | 0 | bool item::operator==(const item& rhs) const { |
249 | 0 | return this->agg == rhs.agg && |
250 | 0 | this->p == rhs.p && |
251 | 0 | this->r == rhs.r; |
252 | 0 | } |
253 | | |
254 | 0 | void show(std::ostream& out, const grammar& g, const item& i) { |
255 | 0 | i.agg->show(out); |
256 | 0 | out << " -> "; |
257 | 0 | const rule& r = nthRule(aggRules(g, i.agg), i.r); |
258 | 0 | for (nat k = 0; k < r.size(); ++k) { |
259 | 0 | if (k == i.p) { |
260 | 0 | out << "#"; |
261 | 0 | } |
262 | 0 | r[k]->show(out); |
263 | 0 | } |
264 | 0 | if (i.p == r.size()) { |
265 | 0 | out << "#"; |
266 | 0 | out << " ["; |
267 | 0 | if (!i.la.empty()) { |
268 | 0 | auto lat = i.la.begin(); |
269 | 0 | (*lat)->show(out); |
270 | 0 | ++lat; |
271 | 0 | while (lat != i.la.end()) { |
272 | 0 | out << ", "; |
273 | 0 | (*lat)->show(out); |
274 | 0 | ++lat; |
275 | 0 | } |
276 | 0 | } |
277 | 0 | out << "]"; |
278 | 0 | } |
279 | 0 | out << std::endl; |
280 | 0 | } |
281 | | |
282 | 0 | void show(std::ostream& out, const grammar& g, const itemset& is) { |
283 | 0 | for (const auto &i : is) { |
284 | 0 | show(out, g, i); |
285 | 0 | } |
286 | 0 | } |
287 | | |
288 | 0 | bool ruleItems(const grammar& g, terminal* t, itemset* is) { |
289 | 0 | bool inserted = false; |
290 | 0 | const rules* rs = aggRules(g, t); |
291 | |
|
292 | 0 | for (nat r = 0; r < rs->size(); ++r) { |
293 | 0 | inserted |= is->insert(item(t, 0, r)).second; |
294 | 0 | } |
295 | |
|
296 | 0 | return inserted; |
297 | 0 | } |
298 | | |
299 | 0 | itemset ruleItems(const grammar& g, terminal* t) { |
300 | 0 | itemset is; |
301 | 0 | ruleItems(g, t, &is); |
302 | 0 | return is; |
303 | 0 | } |
304 | | |
305 | 0 | item succ(const item& i) { |
306 | 0 | return item(i.agg, i.p+1, i.r, i.la); |
307 | 0 | } |
308 | | |
309 | 0 | terminal* next(const grammar& g, const item& i) { |
310 | 0 | return ruleElement(nthRule(aggRules(g, i.agg), i.r), i.p); |
311 | 0 | } |
312 | | |
313 | 0 | terminalset next(const grammar& g, const itemset& is) { |
314 | 0 | terminalset r; |
315 | 0 | for (const auto &i : is) { |
316 | 0 | if (terminal* t = next(g, i)) { |
317 | 0 | r.insert(t); |
318 | 0 | } |
319 | 0 | } |
320 | 0 | return r; |
321 | 0 | } |
322 | | |
323 | 0 | terminalset nextPrim(const grammar& g, const itemset& is) { |
324 | 0 | terminalset r; |
325 | 0 | for (const auto &i : is) { |
326 | 0 | if (terminal* v = next(g, i)) { |
327 | 0 | if (!hasRule(g, v)) { |
328 | 0 | r.insert(v); |
329 | 0 | } |
330 | 0 | } |
331 | 0 | } |
332 | 0 | return r; |
333 | 0 | } |
334 | | |
335 | 0 | itemset follow(const grammar& g, const itemset& is, terminal* v) { |
336 | 0 | itemset r; |
337 | 0 | for (const auto &i : is) { |
338 | 0 | if (next(g, i) == v) { |
339 | 0 | r.insert(succ(i)); |
340 | 0 | } |
341 | 0 | } |
342 | 0 | return r; |
343 | 0 | } |
344 | | |
345 | 0 | itemsets follow(const grammar& g, const itemset& is, const terminals& vs) { |
346 | 0 | itemsets r; |
347 | 0 | for (auto *v : vs) { |
348 | 0 | r.push_back(follow(g, is, v)); |
349 | 0 | } |
350 | 0 | return r; |
351 | 0 | } |
352 | | |
353 | 0 | void gclosure(const grammar& g, itemset* is) { |
354 | | // add implied items to a fixed point |
355 | | // (each nonterminal is expanded once, and only the items it adds are looked at again, |
356 | | // rather than the whole set once per round) |
357 | 0 | terminalset expanded; |
358 | 0 | std::vector<const item*> pending; |
359 | 0 | for (const auto& i : *is) { |
360 | 0 | pending.push_back(&i); |
361 | 0 | } |
362 | |
|
363 | 0 | while (!pending.empty()) { |
364 | 0 | terminal* t = next(g, *pending.back()); |
365 | 0 | pending.pop_back(); |
366 | |
|
367 | 0 | if ((t != nullptr) && hasRule(g, t) && expanded.insert(t).second) { |
368 | 0 | const rules* rs = aggRules(g, t); |
369 | 0 | for (nat r = 0; r < rs->size(); ++r) { |
370 | 0 | auto ins = is->insert(item(t, 0, r)); |
371 | 0 | if (ins.second) { |
372 | 0 | pending.push_back(&(*ins.first)); |
373 | 0 | } |
374 | 0 | } |
375 | 0 | } |
376 | 0 | } |
377 | 0 | } |
378 | | |
379 | 0 | itemset gclosure(const grammar& g, const itemset& is) { |
380 | 0 | itemset r = is; |
381 | 0 | gclosure(g, &r); |
382 | 0 | return r; |
383 | 0 | } |
384 | | |
385 | 0 | nat parserState(const itemset& s, parserdef* p) { |
386 | 0 | parser_states::const_iterator si = p->states.find(s); |
387 | 0 | if (si != p->states.end()) { |
388 | 0 | return si->second; |
389 | 0 | } else { |
390 | 0 | nat n = p->states.size(); |
391 | 0 | p->states [s] = n; |
392 | 0 | p->state_defs[n] = s; |
393 | 0 | return n; |
394 | 0 | } |
395 | 0 | } |
396 | | |
397 | 0 | const itemset& parserStateDef(const parserdef& p, nat n) { |
398 | 0 | auto sd = p.state_defs.find(n); |
399 | 0 | if (sd == p.state_defs.end()) { |
400 | 0 | throw std::runtime_error("Internal error, invalid state referenced."); |
401 | 0 | } else { |
402 | 0 | return sd->second; |
403 | 0 | } |
404 | 0 | } |
405 | | |
406 | | parser_too_large::parser_too_large(limit l, size_t bound) : |
407 | 0 | std::runtime_error( |
408 | 0 | l == limit::items ? "parser states hold more than " + std::to_string(bound) + " items" : |
409 | 0 | l == limit::lookahead ? "parser lookahead takes more than " + std::to_string(bound) + " steps" : |
410 | 0 | "parser states' stack depths sum to more than " + std::to_string(bound)), |
411 | 0 | l(l), b(bound) { |
412 | 0 | } |
413 | 0 | parser_too_large::limit parser_too_large::which() const { return this->l; } |
414 | 0 | size_t parser_too_large::bound() const { return this->b; } |
415 | | |
416 | | namespace { |
417 | | |
418 | | // counts the size of an LR(0) parser as its states are made, giving up once it is past its limits |
419 | | // (checked as each state is made, not as it is visited: a state can have as many successors |
420 | | // as the grammar has rules, each of them as large as the grammar, so the parser can grow |
421 | | // quadratically in the grammar before the next state is visited) |
422 | | class lr0budget { |
423 | | public: |
424 | 0 | explicit lr0budget(const parserlimits& limits) : limits(limits) { } |
425 | | |
426 | | // the state reached from a kernel of items (the successors of some state's items on one symbol) |
427 | | // (a state is found by its kernel before its closure is made: many states can reach one |
428 | | // state, and making its closure again for each of them took time quadratic in the grammar; |
429 | | // a kernel's items have all read something, and its closure adds only items that haven't, |
430 | | // so kernels and the states made from them determine each other) |
431 | 0 | nat successor(const grammar& g, itemset* kernel, parserdef* p) { |
432 | 0 | auto k = this->kernels.find(*kernel); |
433 | 0 | if (k != this->kernels.end()) { |
434 | 0 | return k->second; |
435 | 0 | } |
436 | 0 | itemset ks = *kernel; |
437 | 0 | gclosure(g, kernel); |
438 | 0 | nat r = state(*kernel, p); |
439 | 0 | this->kernels[std::move(ks)] = r; |
440 | 0 | return r; |
441 | 0 | } |
442 | | |
443 | 0 | nat state(const itemset& s, parserdef* p) { |
444 | 0 | size_t n = p->states.size(); |
445 | 0 | nat r = parserState(s, p); |
446 | 0 | if (p->states.size() != n) { |
447 | 0 | nat d = 0; |
448 | 0 | for (const auto& i : s) { |
449 | 0 | d = std::max(d, i.p); |
450 | 0 | } |
451 | 0 | this->depthSum += d; |
452 | 0 | this->items += s.size(); |
453 | |
|
454 | 0 | if (this->depthSum > this->limits.maxStackDepthSum) { |
455 | 0 | throw parser_too_large(parser_too_large::limit::stackDepthSum, this->limits.maxStackDepthSum); |
456 | 0 | } |
457 | 0 | if (this->items > this->limits.maxItems) { |
458 | 0 | throw parser_too_large(parser_too_large::limit::items, this->limits.maxItems); |
459 | 0 | } |
460 | 0 | } |
461 | 0 | return r; |
462 | 0 | } |
463 | | private: |
464 | | const parserlimits& limits; |
465 | | size_t depthSum = 0; |
466 | | size_t items = 0; |
467 | | std::map<itemset, nat> kernels; |
468 | | }; |
469 | | |
470 | 0 | state_transitions follow(const grammar& g, const itemset& is, const terminalset& vs, parserdef* p, lr0budget* b) { |
471 | | // sort the items by the symbol they expect in one pass, |
472 | | // rather than passing over all of them for each symbol |
473 | 0 | std::map<terminal*, itemset> succs; |
474 | 0 | for (const auto& i : is) { |
475 | 0 | terminal* v = next(g, i); |
476 | 0 | if (v != nullptr && vs.count(v) != 0) { |
477 | 0 | succs[v].insert(succ(i)); |
478 | 0 | } |
479 | 0 | } |
480 | | |
481 | | // and number the states they make in the order of vs |
482 | 0 | state_transitions r; |
483 | 0 | for (auto *v : vs) { |
484 | 0 | if (v != endOfFile::value()) { |
485 | 0 | r[v] = b->successor(g, &succs[v], p); |
486 | 0 | } |
487 | 0 | } |
488 | 0 | return r; |
489 | 0 | } |
490 | | |
491 | | } |
492 | | |
493 | 0 | state_transitions follow(const grammar& g, const itemset& is, const terminalset& vs, parserdef* p) { |
494 | 0 | static const parserlimits unlimited; |
495 | 0 | lr0budget b(unlimited); |
496 | 0 | return follow(g, is, vs, p, &b); |
497 | 0 | } |
498 | | |
499 | 0 | parserdef lr0parser(const grammar& g, terminal* s) { |
500 | 0 | return lr0parser(g, s, parserlimits()); |
501 | 0 | } |
502 | | |
503 | 0 | parserdef lr0parser(const grammar& g, terminal* s, const parserlimits& limits) { |
504 | 0 | parserdef p; |
505 | 0 | lr0budget b(limits); |
506 | | |
507 | | // tweak this grammar to have a boot rule |
508 | | // s' -> s $ |
509 | 0 | p.g = g; |
510 | 0 | p.s = s; |
511 | |
|
512 | 0 | static symbol privstart(".S"); |
513 | 0 | terminal* boot = &privstart; |
514 | 0 | rule br; br.push_back(s); br.push_back(endOfFile::value()); |
515 | 0 | p.g[boot].push_back(br); |
516 | | |
517 | | // enumerate the state space implied by this grammar |
518 | 0 | nat n = b.state(gclosure(p.g, ruleItems(p.g, boot)), &p); |
519 | 0 | while (n < p.states.size()) { |
520 | 0 | const itemset& nis = parserStateDef(p, n); |
521 | 0 | p.transitions[n] = follow(p.g, nis, next(p.g, nis), &p, &b); |
522 | 0 | ++n; |
523 | 0 | } |
524 | 0 | p.nullable = symbolsDerivingNull(p.g); |
525 | 0 | return p; |
526 | 0 | } |
527 | | |
528 | | /* |
529 | | * Relations on the LR(0) parser and LALR(1) determination |
530 | | */ |
531 | | |
532 | 0 | transitionset transitions(const parserdef& p) { |
533 | 0 | transitionset r; |
534 | 0 | for (auto pst = p.transitions.begin(); pst != p.transitions.end(); ++pst) { |
535 | 0 | for (auto st = pst->second.begin(); st != pst->second.end(); ++st) { |
536 | 0 | if (hasRule(p.g, st->first)) { |
537 | 0 | r.insert(transition(pst->first, st->first)); |
538 | 0 | } |
539 | 0 | } |
540 | 0 | } |
541 | 0 | return r; |
542 | 0 | } |
543 | | |
544 | 0 | const state_transitions& transitions(const parserdef& p, nat q) { |
545 | 0 | auto r = p.transitions.find(q); |
546 | 0 | if (r == p.transitions.end()) { |
547 | 0 | throw std::runtime_error("Internal error, invalid state given for finding transitions."); |
548 | 0 | } else { |
549 | 0 | return r->second; |
550 | 0 | } |
551 | 0 | } |
552 | | |
553 | 0 | bool follows(const state_transitions& st, terminal* t) { |
554 | 0 | return st.find(t) != st.end(); |
555 | 0 | } |
556 | | |
557 | 0 | bool follows(const parserdef& p, nat q, terminal* t) { |
558 | 0 | return follows(transitions(p, q), t); |
559 | 0 | } |
560 | | |
561 | 0 | nat follow(const state_transitions& st, terminal* t) { |
562 | 0 | auto x = st.find(t); |
563 | 0 | if (x == st.end()) { |
564 | 0 | throw std::runtime_error("Internal error, invalid symbol given for determining state transition."); |
565 | 0 | } else { |
566 | 0 | return x->second; |
567 | 0 | } |
568 | 0 | } |
569 | | |
570 | 0 | nat follow(const parserdef& p, nat q, terminal* t) { |
571 | 0 | return follow(transitions(p, q), t); |
572 | 0 | } |
573 | | |
574 | 0 | nat follow(const parserdef& p, const transition& x) { |
575 | 0 | return follow(p, x.first, x.second); |
576 | 0 | } |
577 | | |
578 | 0 | nat follow(const parserdef& p, nat s, const terminals& ts) { |
579 | 0 | nat r = s; |
580 | 0 | for (auto *t : ts) { |
581 | 0 | r = follow(p, r, t); |
582 | 0 | } |
583 | 0 | return r; |
584 | 0 | } |
585 | | |
586 | | // check if a transition "looks back" to a state where a given rule has been completed |
587 | 0 | bool lookback(const parserdef& p, nat q, terminal* r, const rule& w, const transition& t) { |
588 | 0 | if (t.second != r) { |
589 | 0 | return false; |
590 | 0 | } else { |
591 | 0 | nat s = t.first; |
592 | 0 | for (auto *t : w) { |
593 | 0 | const state_transitions& st = transitions(p, s); |
594 | 0 | if (follows(st, t)) { |
595 | 0 | s = follow(st, t); |
596 | 0 | } else { |
597 | 0 | return false; |
598 | 0 | } |
599 | 0 | } |
600 | 0 | return s == q; |
601 | 0 | } |
602 | 0 | } |
603 | | |
604 | | // for a rule w and sym A, find all prefixes b such that w = bAs, s =>* eps |
605 | | using prefixes = std::vector<terminals>; |
606 | | |
607 | 0 | prefixes endingAt(const parserdef& p, const rule& w, terminal* a) { |
608 | 0 | prefixes r; |
609 | 0 | nat i = w.size(); |
610 | 0 | while (i > 0) { |
611 | 0 | --i; |
612 | |
|
613 | 0 | if (w[i] == a) { |
614 | 0 | r.push_back(terminals(w.begin(), w.begin() + i)); |
615 | 0 | } |
616 | 0 | if (!derivesNull(p, w[i])) { |
617 | 0 | break; |
618 | 0 | } |
619 | 0 | } |
620 | 0 | return r; |
621 | 0 | } |
622 | | |
623 | | // x=(s,A) includes y=(s',B) if B -> wAg, g =>* eps, and s' --w--> s |
624 | 0 | bool includes(const parserdef& p, const transition& x, const transition& y) { |
625 | 0 | const rules* rs = aggRules(p.g, y.second); |
626 | 0 | for (const auto &r : *rs) { |
627 | 0 | prefixes pr = endingAt(p, r, x.second); |
628 | 0 | for (const auto & pfx : pr) { |
629 | 0 | if (follow(p, y.first, pfx) == x.first) { |
630 | 0 | return true; |
631 | 0 | } |
632 | 0 | } |
633 | 0 | } |
634 | 0 | return false; |
635 | 0 | } |
636 | | |
637 | | // (s,A) reads (s',B) if s --A--> s' and B =>* eps |
638 | 0 | bool reads(const parserdef& p, const transition& x, const transition& y) { |
639 | 0 | return follow(p, x) == y.first && derivesNull(p, y.second); |
640 | 0 | } |
641 | | |
642 | | // from x=(s,A), find all non-aggregate terminals immediately expected after transitioning from state s to s' along A |
643 | 0 | terminalset directReads(const parserdef& p, const transition& x) { |
644 | 0 | return nextPrim(p.g, parserStateDef(p, follow(p, x))); |
645 | 0 | } |
646 | | |
647 | | // the relations above (reads, includes, lookback) define LALR(1) lookahead, |
648 | | // but deciding them one pair at a time costs a search over every pair of |
649 | | // transitions (and, for lookback, over every completed item besides): more |
650 | | // than a minute, under a sanitizer, for a `parse {}` grammar of a few |
651 | | // thousand states (OSS-Fuzz 571908642). |
652 | | // The edges of each relation can instead be built forward from each |
653 | | // transition, as DeRemer and Pennello do, in time linear in the size of the |
654 | | // LR(0) parser, which is what the functions below do. |
655 | | namespace { |
656 | | |
657 | | // the nonterminal transitions of a parser, numbered in the order of a |
658 | | // transitionset, with each relation as a list of edges between those numbers |
659 | | struct transitionIndex { |
660 | | std::vector<transition> ts; |
661 | | std::map<transition, nat> idx; |
662 | | |
663 | 0 | explicit transitionIndex(const transitionset& xs) : ts(xs.begin(), xs.end()) { |
664 | 0 | for (nat i = 0; i < this->ts.size(); ++i) { |
665 | 0 | this->idx[this->ts[i]] = i; |
666 | 0 | } |
667 | 0 | } |
668 | | |
669 | | // the number of a transition, or ts.size() if it isn't one |
670 | 0 | nat find(nat s, terminal* t) const { |
671 | 0 | auto i = this->idx.find(transition(s, t)); |
672 | 0 | return (i == this->idx.end()) ? static_cast<nat>(this->ts.size()) : i->second; |
673 | 0 | } |
674 | | }; |
675 | | |
676 | | // counts the steps taken to find a parser's lookahead (terminals copied and merged, rule walks |
677 | | // and edges), giving up once there are too many |
678 | | // (the sets are as many as its transitions and items, each as large as its terminals, so their |
679 | | // size can be quadratic in a grammar that passes the bounds on its LR(0) states; and they are |
680 | | // merged along each edge of the relations, so the time to make them can be cubic. Each copy |
681 | | // and merge is counted, whether or not it adds anything, so this bounds the time as well as |
682 | | // the size, which can't be more than what was copied and merged into it; and the walks along |
683 | | // rules that find the relations' edges are counted as well, as there can be as many of those |
684 | | // as transitions on a nonterminal times the length of its rules) |
685 | | class lookaheadBudget { |
686 | | public: |
687 | 0 | explicit lookaheadBudget(size_t limit) : limit(limit) { } |
688 | | |
689 | 0 | void count(size_t k) { |
690 | 0 | this->n += k; |
691 | 0 | if (this->n > this->limit) { |
692 | 0 | throw parser_too_large(parser_too_large::limit::lookahead, this->limit); |
693 | 0 | } |
694 | 0 | } |
695 | | |
696 | 0 | void assign(terminalset* s, const terminalset& ts) { |
697 | 0 | count(ts.size()); |
698 | 0 | *s = ts; |
699 | 0 | } |
700 | | |
701 | 0 | void insert(terminalset* s, const terminalset& ts) { |
702 | 0 | count(ts.size()); |
703 | 0 | s->insert(ts.begin(), ts.end()); |
704 | 0 | } |
705 | | private: |
706 | | size_t limit; |
707 | | size_t n = 0; |
708 | | }; |
709 | | |
710 | | using edges = std::vector<std::vector<nat>>; |
711 | | |
712 | | // list each node's edges in transitionset order, without repeats |
713 | 0 | void normalize(edges* es) { |
714 | 0 | for (auto& e : *es) { |
715 | 0 | std::sort(e.begin(), e.end()); |
716 | 0 | e.erase(std::unique(e.begin(), e.end()), e.end()); |
717 | 0 | } |
718 | 0 | } |
719 | | |
720 | | // (s,A) reads (s',B) if s --A--> s' and B =>* eps |
721 | | // (which depends only on s', so the edges are found once for each state that transitions lead |
722 | | // to, rather than once for each transition: many transitions can lead to one state, which can |
723 | | // have many transitions out of it) |
724 | 0 | edges readsEdges(const parserdef& p, const transitionIndex& ti) { |
725 | 0 | edges r(p.states.size()); |
726 | 0 | std::vector<bool> done(p.states.size(), false); |
727 | 0 | for (const auto& x : ti.ts) { |
728 | 0 | nat s = follow(p, x); |
729 | 0 | if (!done[s]) { |
730 | 0 | done[s] = true; |
731 | 0 | for (const auto& st : transitions(p, s)) { |
732 | 0 | if (derivesNull(p, st.first)) { |
733 | 0 | nat y = ti.find(s, st.first); |
734 | 0 | if (y < ti.ts.size()) { |
735 | 0 | r[s].push_back(y); |
736 | 0 | } |
737 | 0 | } |
738 | 0 | } |
739 | 0 | } |
740 | 0 | } |
741 | 0 | normalize(&r); |
742 | 0 | return r; |
743 | 0 | } |
744 | | |
745 | | // x=(s,A) includes y=(s',B) if B -> wAg, g =>* eps, and s' --w--> s |
746 | | // so walk each rule of B from s', and take the edge at each A in its nullable tail |
747 | 0 | edges includesEdges(const parserdef& p, const transitionIndex& ti, lookaheadBudget* b) { |
748 | 0 | edges r(ti.ts.size()); |
749 | 0 | std::vector<nat> path; |
750 | 0 | for (nat y = 0; y < ti.ts.size(); ++y) { |
751 | 0 | for (const auto& w : *aggRules(p.g, ti.ts[y].second)) { |
752 | | // the states before each symbol (but the last, after which nothing is looked for) |
753 | 0 | b->count(w.size() + 1); |
754 | 0 | path.resize(w.size()); |
755 | 0 | nat s = ti.ts[y].first; |
756 | 0 | for (nat i = 0; i < w.size(); ++i) { |
757 | 0 | path[i] = s; |
758 | 0 | if (i + 1 < w.size()) { |
759 | 0 | s = follow(p, s, w[i]); |
760 | 0 | } |
761 | 0 | } |
762 | |
|
763 | 0 | for (nat i = w.size(); i > 0;) { |
764 | 0 | --i; |
765 | 0 | nat x = ti.find(path[i], w[i]); |
766 | 0 | if (x < ti.ts.size()) { |
767 | 0 | b->count(1); |
768 | 0 | r[x].push_back(y); |
769 | 0 | } |
770 | 0 | if (!derivesNull(p, w[i])) { |
771 | 0 | break; |
772 | 0 | } |
773 | 0 | } |
774 | 0 | } |
775 | 0 | } |
776 | 0 | normalize(&r); |
777 | 0 | return r; |
778 | 0 | } |
779 | | |
780 | | |
781 | | // for each x, f(x) = fp(x) + sum(f(y) | x R y), as digraphF computes it |
782 | | // (visiting nodes and edges in the same order, so with the same result), |
783 | | // but with R given as edges, and without recursion: an edge chain can be as |
784 | | // long as the parser has transitions. |
785 | | // |
786 | | // Many nodes can share one fp and one list of edges (all of the transitions |
787 | | // to one state, for reads). Once one of them is done, having read only values |
788 | | // that were themselves done (none of its edges went back into the stack), its |
789 | | // value is what any other node with the same fp and edges would compute, so |
790 | | // that is used rather than visiting the same edges again for each of them. |
791 | 0 | transition_lookahead digraph(const transitionIndex& ti, const std::vector<const terminalset*>& fp, const std::vector<const std::vector<nat>*>& es, lookaheadBudget* b) { |
792 | 0 | static const nat inf = static_cast<nat>(-1); |
793 | |
|
794 | 0 | nat n = ti.ts.size(); |
795 | 0 | std::vector<nat> depth(n, 0); |
796 | 0 | std::vector<terminalset> f(n); |
797 | 0 | std::vector<nat> stack; |
798 | |
|
799 | 0 | using shape = std::pair<const terminalset*, const std::vector<nat>*>; |
800 | 0 | std::map<shape, nat> doneShapes; |
801 | |
|
802 | 0 | struct frame { nat x; nat d; nat e; bool partial; }; |
803 | 0 | std::vector<frame> frames; |
804 | |
|
805 | 0 | auto enter = [&](nat x) { |
806 | 0 | stack.push_back(x); |
807 | 0 | nat d = stack.size(); |
808 | 0 | depth[x] = d; |
809 | |
|
810 | 0 | auto ds = doneShapes.find(shape(fp[x], es[x])); |
811 | 0 | if (ds != doneShapes.end()) { |
812 | 0 | b->assign(&f[x], f[ds->second]); |
813 | 0 | frames.push_back(frame{x, d, static_cast<nat>(es[x]->size()), false}); |
814 | 0 | } else { |
815 | 0 | b->assign(&f[x], *fp[x]); |
816 | 0 | frames.push_back(frame{x, d, 0, false}); |
817 | 0 | } |
818 | 0 | }; |
819 | |
|
820 | 0 | for (nat root = 0; root < n; ++root) { |
821 | 0 | if (depth[root] != 0) { |
822 | 0 | continue; |
823 | 0 | } |
824 | 0 | enter(root); |
825 | |
|
826 | 0 | while (!frames.empty()) { |
827 | 0 | frame& fr = frames.back(); |
828 | 0 | const auto& e = *es[fr.x]; |
829 | |
|
830 | 0 | if (fr.e < e.size()) { |
831 | 0 | nat y = e[fr.e]; |
832 | 0 | if (depth[y] == 0) { |
833 | | // visit y first, then come back to this edge to add in its value |
834 | 0 | enter(y); |
835 | 0 | } else { |
836 | 0 | if (depth[y] != inf) { |
837 | 0 | fr.partial = true; // y is still in the stack, so its value isn't done |
838 | 0 | } |
839 | 0 | b->insert(&f[fr.x], f[y]); |
840 | 0 | ++fr.e; |
841 | 0 | } |
842 | 0 | continue; |
843 | 0 | } |
844 | | |
845 | | // join all the nodes in this SCC |
846 | 0 | nat x = fr.x; |
847 | 0 | bool partial = fr.partial; |
848 | 0 | if (depth[x] == fr.d) { |
849 | 0 | while (true) { |
850 | 0 | nat cx = stack.back(); |
851 | 0 | stack.pop_back(); |
852 | 0 | depth[cx] = inf; |
853 | 0 | if (cx == x) { |
854 | 0 | break; |
855 | 0 | } else { |
856 | 0 | b->assign(&f[cx], f[x]); |
857 | 0 | } |
858 | 0 | } |
859 | 0 | } |
860 | 0 | frames.pop_back(); |
861 | 0 | if (!partial) { |
862 | 0 | doneShapes.insert(std::make_pair(shape(fp[x], es[x]), x)); |
863 | 0 | } |
864 | | |
865 | | // and add this node's value to the edge that led here |
866 | 0 | if (!frames.empty()) { |
867 | 0 | frame& pr = frames.back(); |
868 | 0 | b->insert(&f[pr.x], f[x]); |
869 | 0 | ++pr.e; |
870 | 0 | } |
871 | 0 | } |
872 | 0 | } |
873 | |
|
874 | 0 | transition_lookahead r; |
875 | 0 | for (nat x = 0; x < n; ++x) { |
876 | 0 | r[ti.ts[x]] = std::move(f[x]); |
877 | 0 | } |
878 | 0 | return r; |
879 | 0 | } |
880 | | |
881 | 0 | transition_lookahead Reads(const parserdef& p, const transitionIndex& ti, lookaheadBudget* b) { |
882 | | // the terminals read directly after a transition, and the transitions it reads, |
883 | | // depend only on the state that it leads to, so are found once for each such state |
884 | 0 | std::vector<terminalset> drs(p.states.size()); |
885 | 0 | std::vector<bool> done(p.states.size(), false); |
886 | 0 | edges res = readsEdges(p, ti); |
887 | |
|
888 | 0 | std::vector<const terminalset*> dr(ti.ts.size()); |
889 | 0 | std::vector<const std::vector<nat>*> es(ti.ts.size()); |
890 | 0 | for (nat x = 0; x < ti.ts.size(); ++x) { |
891 | 0 | nat s = follow(p, ti.ts[x]); |
892 | 0 | if (!done[s]) { |
893 | 0 | done[s] = true; |
894 | 0 | drs[s] = directReads(p, ti.ts[x]); |
895 | 0 | } |
896 | 0 | dr[x] = &drs[s]; |
897 | 0 | es[x] = &res[s]; |
898 | 0 | } |
899 | 0 | return digraph(ti, dr, es, b); |
900 | 0 | } |
901 | | |
902 | 0 | transition_lookahead Follow(const parserdef& p, const transitionIndex& ti, lookaheadBudget* b) { |
903 | 0 | transition_lookahead rs = Reads(p, ti, b); |
904 | 0 | std::vector<terminalset> rds; |
905 | 0 | rds.reserve(ti.ts.size()); |
906 | 0 | for (const auto& x : ti.ts) { |
907 | 0 | rds.push_back(std::move(rs[x])); |
908 | 0 | } |
909 | 0 | rs.clear(); |
910 | |
|
911 | 0 | edges ies = includesEdges(p, ti, b); |
912 | 0 | std::vector<const terminalset*> fp(ti.ts.size()); |
913 | 0 | std::vector<const std::vector<nat>*> es(ti.ts.size()); |
914 | 0 | for (nat x = 0; x < ti.ts.size(); ++x) { |
915 | 0 | fp[x] = &rds[x]; |
916 | 0 | es[x] = &ies[x]; |
917 | 0 | } |
918 | 0 | return digraph(ti, fp, es, b); |
919 | 0 | } |
920 | | |
921 | | // (q, A -> w.) looks back to (s,A) when s --w--> q, so walk each rule of A from s |
922 | | // (counting each walk, as includesEdges does) |
923 | 0 | void apply(const transition_lookahead& tl, parserdef* p, lookaheadBudget* b) { |
924 | 0 | std::vector<itemset*> states(p->states.size(), nullptr); |
925 | 0 | for (auto& s : p->states) { |
926 | 0 | if (s.second < states.size()) { |
927 | 0 | states[s.second] = const_cast<itemset*>(&s.first); |
928 | 0 | } |
929 | 0 | } |
930 | |
|
931 | 0 | for (const auto& tli : tl) { |
932 | 0 | terminal* a = tli.first.second; |
933 | 0 | const rules* rs = aggRules(p->g, a); |
934 | 0 | for (nat r = 0; r < rs->size(); ++r) { |
935 | 0 | const rule& w = (*rs)[r]; |
936 | 0 | b->count(w.size() + 1); |
937 | 0 | nat q = tli.first.first; |
938 | 0 | bool ok = true; |
939 | 0 | for (auto* t : w) { |
940 | 0 | const state_transitions& st = transitions(*p, q); |
941 | 0 | auto n = st.find(t); |
942 | 0 | if (n == st.end()) { |
943 | 0 | ok = false; |
944 | 0 | break; |
945 | 0 | } |
946 | 0 | q = n->second; |
947 | 0 | } |
948 | 0 | if (!ok || q >= states.size() || states[q] == nullptr) { |
949 | 0 | continue; |
950 | 0 | } |
951 | | |
952 | 0 | auto i = states[q]->find(item(a, w.size(), r)); |
953 | 0 | if (i != states[q]->end()) { |
954 | 0 | b->insert(&const_cast<item*>(&(*i))->la, tli.second); |
955 | 0 | } |
956 | 0 | } |
957 | 0 | } |
958 | 0 | } |
959 | | |
960 | | } |
961 | | |
962 | | // compute Read(x=(s,A)) = directReads(s,A) + (Read(q,B) | (s,A) reads (q,B)) |
963 | 0 | transition_lookahead Reads(const parserdef& p, const transitionset& ts) { |
964 | 0 | lookaheadBudget b(static_cast<size_t>(-1)); |
965 | 0 | return Reads(p, transitionIndex(ts), &b); |
966 | 0 | } |
967 | | |
968 | | // compute Follow(x=(s,A)) = Read((s,A)) + (Follow(q,B) | (s,A) includes (q,B)) |
969 | 0 | transition_lookahead Follow(const parserdef& p, const transitionset& ts) { |
970 | 0 | lookaheadBudget b(static_cast<size_t>(-1)); |
971 | 0 | return Follow(p, transitionIndex(ts), &b); |
972 | 0 | } |
973 | | |
974 | | // apply transition lookahead to every applicable completed item in an LR(0) parser |
975 | 0 | void apply(const transition_lookahead& tl, parserdef* p) { |
976 | 0 | lookaheadBudget b(static_cast<size_t>(-1)); |
977 | 0 | apply(tl, p, &b); |
978 | 0 | } |
979 | | |
980 | | // determine the LALR(1) parser definition |
981 | 0 | void lalr1lookahead(parserdef* p) { |
982 | 0 | lalr1lookahead(p, parserlimits()); |
983 | 0 | } |
984 | | |
985 | 0 | void lalr1lookahead(parserdef* p, const parserlimits& limits) { |
986 | 0 | lookaheadBudget b(limits.maxLookahead); |
987 | 0 | apply(Follow(*p, transitionIndex(transitions(*p)), &b), p, &b); |
988 | 0 | } |
989 | | |
990 | 0 | parserdef lalr1parser(const grammar& g, terminal* s) { |
991 | 0 | parserdef p = lr0parser(g, s); |
992 | 0 | lalr1lookahead(&p); |
993 | 0 | return p; |
994 | 0 | } |
995 | | |
996 | 0 | terminal* reduceOpTerminal(const parserdef& pd, const action& a) { |
997 | 0 | const rule& r = nthRule(aggRules(pd.g, a.reduceSym()), a.reduceRule()); |
998 | 0 | for (auto e = r.rbegin(); e != r.rend(); ++e) { |
999 | 0 | if (!hasRule(pd.g, *e)) { |
1000 | 0 | return *e; |
1001 | 0 | } |
1002 | 0 | } |
1003 | 0 | return nullptr; |
1004 | 0 | } |
1005 | | |
1006 | 0 | bool shiftInstead(const parserdef& pd, const precedence& px, nat, terminal* t, const action& a) { |
1007 | 0 | terminal* u = reduceOpTerminal(pd, a); |
1008 | |
|
1009 | 0 | if (u == nullptr) { |
1010 | 0 | throw ambiguity_conflict("Unable to resolve shift/reduce conflict in grammar", t); |
1011 | 0 | } |
1012 | | |
1013 | 0 | auto tp = px.find(t); |
1014 | 0 | auto up = px.find(u); |
1015 | |
|
1016 | 0 | if (tp == px.end() || up == px.end()) { |
1017 | 0 | throw ambiguity_conflict("Unable to resolve shift/reduce conflict in grammar", t); |
1018 | 0 | } |
1019 | | |
1020 | 0 | if (tp->second.level < up->second.level) { |
1021 | 0 | return false; |
1022 | 0 | } else if (up->second.level < tp->second.level) { |
1023 | 0 | return true; |
1024 | 0 | } else if (tp->second.asc == up->second.asc && tp->second.asc != assoc::non) { |
1025 | 0 | return tp->second.asc != assoc::left; |
1026 | 0 | } else { |
1027 | 0 | throw ambiguity_conflict("Unable to resolve shift/reduce conflict in grammar", t); |
1028 | 0 | } |
1029 | 0 | } |
1030 | | |
1031 | | // determine the LALR(1) table to drive an LR parser |
1032 | 0 | lrtable lalrTable(const grammar& g, terminal* s, const precedence& px) { |
1033 | 0 | return lalrTable(lalr1parser(g, s), px); |
1034 | 0 | } |
1035 | | |
1036 | 0 | void addAction(const parserdef& pd, lrstate& s, terminal* t, const action& a, const precedence& px) { |
1037 | 0 | auto sc = s.find(t); |
1038 | 0 | if (sc == s.end()) { |
1039 | 0 | s.insert(std::make_pair(t, a)); |
1040 | 0 | } else if (sc->second != a) { |
1041 | 0 | if (a.isReduce() && sc->second.isReduce()) { |
1042 | 0 | std::ostringstream ss; |
1043 | 0 | ss << "Conflict between " << a << " and " << sc->second << " on " << show(t); |
1044 | 0 | throw ambiguity_conflict(ss.str(), t); |
1045 | 0 | } else if (a.isShift()) { |
1046 | 0 | if (shiftInstead(pd, px, a.shiftState(), t, sc->second)) { |
1047 | 0 | sc->second = a; |
1048 | 0 | } |
1049 | 0 | } else { |
1050 | 0 | if (!shiftInstead(pd, px, sc->second.shiftState(), t, a)) { |
1051 | 0 | sc->second = a; |
1052 | 0 | } |
1053 | 0 | } |
1054 | 0 | } |
1055 | 0 | } |
1056 | | |
1057 | 0 | void addAccept(lrstate& s, terminal* t) { |
1058 | 0 | s.insert(std::make_pair(t, action::accept())); |
1059 | 0 | } |
1060 | | |
1061 | 0 | void addReduce(const parserdef& pd, lrstate& s, const terminalset& ts, terminal* agg, nat rule, nat len, const precedence& px) { |
1062 | 0 | for (auto *t : ts) { |
1063 | 0 | addAction(pd, s, t, action::reduce(agg, rule, len), px); |
1064 | 0 | } |
1065 | 0 | } |
1066 | | |
1067 | 0 | void addGoto(lrstate& s, terminal* nt, nat next) { |
1068 | 0 | s.insert(std::make_pair(nt, action::goTo(next))); |
1069 | 0 | } |
1070 | | |
1071 | 0 | void addShift(const parserdef& pd, lrstate& s, terminal* t, nat next, const precedence& px) { |
1072 | 0 | addAction(pd, s, t, action::shift(next), px); |
1073 | 0 | } |
1074 | | |
1075 | 0 | lrtable lalrTable(const parserdef& p, const precedence& px) { |
1076 | 0 | lrtable r; |
1077 | 0 | r.resize(p.states.size()); |
1078 | |
|
1079 | 0 | for (auto s = p.states.begin(); s != p.states.end(); ++s) { |
1080 | | // each item in this state's set translates to an LR parser action (goto, shift, reduce, or accept) |
1081 | 0 | const itemset& is = s->first; |
1082 | 0 | nat n = s->second; |
1083 | 0 | lrstate& ps = r[n]; |
1084 | | |
1085 | | // if we fail to compile a table row, report the itemset that failed |
1086 | 0 | try { |
1087 | 0 | for (const auto & i : is) { |
1088 | 0 | terminal* t = next(p.g, i); |
1089 | | |
1090 | 0 | if (t == nullptr) { |
1091 | | // reduce |
1092 | 0 | addReduce(p, ps, i.la, i.agg, i.r, i.p, px); |
1093 | 0 | } else if (hasRule(p.g, t)) { |
1094 | | // goto |
1095 | 0 | addGoto(ps, t, follow(p, n, t)); |
1096 | 0 | } else if (t == endOfFile::value()) { |
1097 | | // accept |
1098 | 0 | addAccept(ps, t); |
1099 | 0 | } else { |
1100 | | // shift |
1101 | 0 | addShift(p, ps, t, follow(p, n, t), px); |
1102 | 0 | } |
1103 | 0 | } |
1104 | 0 | } catch (ambiguity_conflict& ex) { |
1105 | 0 | throw compile_table_failure(ex.what(), p.g, is, ex.failedTerminal()); |
1106 | 0 | } |
1107 | 0 | } |
1108 | | |
1109 | 0 | return r; |
1110 | 0 | } |
1111 | | |
1112 | 0 | ambiguity_conflict::ambiguity_conflict(const std::string& ex, terminal* t) noexcept : std::runtime_error(ex), t(t) { |
1113 | 0 | } |
1114 | | |
1115 | 0 | terminal* ambiguity_conflict::failedTerminal() const { |
1116 | 0 | return this->t; |
1117 | 0 | } |
1118 | | |
1119 | 0 | compile_table_failure::compile_table_failure(const std::string& msg, const grammar& g, const itemset& faileditems, terminal* t) noexcept : std::runtime_error(msg), g(g), faileditems(faileditems), t(t) { |
1120 | 0 | } |
1121 | | |
1122 | 0 | const grammar& compile_table_failure::failedGrammar() const { |
1123 | 0 | return this->g; |
1124 | 0 | } |
1125 | | |
1126 | 0 | const itemset& compile_table_failure::failedItems() const { |
1127 | 0 | return this->faileditems; |
1128 | 0 | } |
1129 | | |
1130 | 0 | terminal* compile_table_failure::failedTerminal() const { |
1131 | 0 | return this->t; |
1132 | 0 | } |
1133 | | |
1134 | 0 | inline bool referencesSym(const grammar& g, const item& i, terminal* t) { |
1135 | 0 | return in(t, nthRule(aggRules(g, i.agg), i.r)) || in(t, i.la); |
1136 | 0 | } |
1137 | | |
1138 | 0 | void compile_table_failure::print(std::ostream& out) const { |
1139 | 0 | for (const auto &faileditem : this->faileditems) { |
1140 | 0 | if (referencesSym(this->g, faileditem, this->t)) { |
1141 | 0 | print(out, faileditem); |
1142 | 0 | } |
1143 | 0 | } |
1144 | 0 | } |
1145 | | |
1146 | 0 | void beginHighlight(std::ostream& out) { |
1147 | 0 | static const char bgccmd[] = { 27, '[', '4', '1', 'm' }; |
1148 | 0 | static const char fgccmd[] = { 27, '[', '3', '7', 'm' }; |
1149 | |
|
1150 | 0 | out.write(bgccmd, sizeof(bgccmd)); |
1151 | 0 | out.write(fgccmd, sizeof(fgccmd)); |
1152 | 0 | } |
1153 | | |
1154 | 0 | void endHighlight(std::ostream& out) { |
1155 | 0 | static const char rstcmd[] = { 27, '[', '0', 'm' }; |
1156 | |
|
1157 | 0 | out.write(rstcmd, sizeof(rstcmd)); |
1158 | 0 | } |
1159 | | |
1160 | 0 | inline void showHLTerm(std::ostream& out, terminal* x, terminal* hx) { |
1161 | 0 | if (x == hx) { |
1162 | 0 | beginHighlight(out); |
1163 | 0 | x->show(out); |
1164 | 0 | endHighlight(out); |
1165 | 0 | } else { |
1166 | 0 | x->show(out); |
1167 | 0 | } |
1168 | 0 | } |
1169 | | |
1170 | 0 | void compile_table_failure::print(std::ostream& out, const item& i) const { |
1171 | 0 | i.agg->show(out); |
1172 | 0 | out << " ->"; |
1173 | |
|
1174 | 0 | const rule& r = nthRule(aggRules(this->g, i.agg), i.r); |
1175 | 0 | for (nat k = 0; k < r.size(); ++k) { |
1176 | 0 | out << ' '; |
1177 | 0 | if (k == i.p) { |
1178 | 0 | out << '#'; |
1179 | 0 | showHLTerm(out, r[k], this->t); |
1180 | 0 | } else { |
1181 | 0 | r[k]->show(out); |
1182 | 0 | } |
1183 | 0 | } |
1184 | 0 | if (i.p == r.size()) { |
1185 | 0 | out << "#"; |
1186 | 0 | out << " ["; |
1187 | 0 | if (!i.la.empty()) { |
1188 | 0 | auto lat = i.la.begin(); |
1189 | 0 | showHLTerm(out, *lat, this->t); |
1190 | 0 | ++lat; |
1191 | 0 | while (lat != i.la.end()) { |
1192 | 0 | out << ", "; |
1193 | 0 | showHLTerm(out, *lat, this->t); |
1194 | 0 | ++lat; |
1195 | 0 | } |
1196 | 0 | } |
1197 | 0 | out << "]"; |
1198 | 0 | } |
1199 | 0 | out << std::endl; |
1200 | 0 | } |
1201 | | |
1202 | | // lr tables / actions |
1203 | 0 | action::action() = default; |
1204 | | |
1205 | 0 | action action::goTo(nat s) { |
1206 | 0 | action a; |
1207 | 0 | a.act = 0; |
1208 | 0 | a.d.s = s; |
1209 | 0 | return a; |
1210 | 0 | } |
1211 | 0 | bool action::isGoTo() const { return this->act == 0; } |
1212 | 0 | nat action::goToState() const { return this->d.s; } |
1213 | | |
1214 | 0 | action action::shift(nat s) { |
1215 | 0 | action a; |
1216 | 0 | a.act = 1; |
1217 | 0 | a.d.s = s; |
1218 | 0 | return a; |
1219 | 0 | } |
1220 | 0 | bool action::isShift() const { return this->act == 1; } |
1221 | 0 | nat action::shiftState() const { return this->d.s; } |
1222 | | |
1223 | 0 | action action::reduce(terminal* x, nat r, nat n) { |
1224 | 0 | action a; |
1225 | 0 | a.act = 2; |
1226 | 0 | a.d.x = x; |
1227 | 0 | a.d.r = r; |
1228 | 0 | a.d.n = n; |
1229 | 0 | return a; |
1230 | 0 | } |
1231 | 0 | bool action::isReduce() const { return this->act == 2; } |
1232 | 0 | terminal* action::reduceSym() const { return this->d.x; } |
1233 | 0 | nat action::reduceRule() const { return this->d.r; } |
1234 | 0 | nat action::reduceSize() const { return this->d.n; } |
1235 | | |
1236 | 0 | action action::accept() { |
1237 | 0 | action a; |
1238 | 0 | a.act = 3; |
1239 | 0 | return a; |
1240 | 0 | } |
1241 | 0 | bool action::isAccept() const { return this->act == 3; } |
1242 | | |
1243 | 0 | std::ostream& action::fmt(std::ostream& out) const { |
1244 | 0 | switch (this->act) { |
1245 | 0 | case 0: out << this->d.s; break; |
1246 | 0 | case 1: out << "s" << this->d.s; break; |
1247 | 0 | case 2: out << "r" << this->d.r; break; |
1248 | 0 | case 3: out << "A"; break; |
1249 | 0 | } |
1250 | 0 | return out; |
1251 | 0 | } |
1252 | | |
1253 | 0 | std::ostream& operator<<(std::ostream& out, const action& act) { |
1254 | 0 | return act.fmt(out); |
1255 | 0 | } |
1256 | | |
1257 | 0 | bool action::operator==(const action& rhs) const { |
1258 | 0 | if (this->act != rhs.act) { |
1259 | 0 | return false; |
1260 | 0 | } else { |
1261 | 0 | switch (this->act) { |
1262 | 0 | default: |
1263 | 0 | case 0: return rhs.d.s == this->d.s; |
1264 | 0 | case 1: return rhs.d.s == this->d.s; |
1265 | 0 | case 2: return rhs.d.x == this->d.x && rhs.d.r == this->d.r && rhs.d.n == this->d.n; |
1266 | 0 | case 3: return true; |
1267 | 0 | } |
1268 | 0 | } |
1269 | 0 | } |
1270 | | |
1271 | 0 | bool action::operator!=(const action& rhs) const { |
1272 | 0 | return !(*this == rhs); |
1273 | 0 | } |
1274 | | |
1275 | 0 | terminalset definedSymbols(const lrtable& t) { |
1276 | 0 | terminalset r; |
1277 | 0 | for (const auto& s : t) { |
1278 | 0 | for (const auto& k : s) { |
1279 | 0 | r.insert(k.first); |
1280 | 0 | } |
1281 | 0 | } |
1282 | 0 | return r; |
1283 | 0 | } |
1284 | | |
1285 | 0 | terminals dispOrd(const terminalset& ts) { |
1286 | 0 | terminals r(ts.begin(), ts.end()); |
1287 | 0 | std::sort(r.begin(), r.end(), |
1288 | 0 | [](terminal* a, terminal* b) { |
1289 | 0 | if (a == endOfFile::value()) { |
1290 | 0 | return b != endOfFile::value(); |
1291 | 0 | } else if (b == endOfFile::value()) { |
1292 | 0 | return false; |
1293 | 0 | } else if (const character* c = dynamic_cast<character*>(a)) { |
1294 | 0 | if (const character* z = dynamic_cast<character*>(b)) { |
1295 | 0 | return c->value() < z->value(); |
1296 | 0 | } else { |
1297 | 0 | return true; |
1298 | 0 | } |
1299 | 0 | } else if (dynamic_cast<character*>(b) != nullptr) { |
1300 | 0 | return false; |
1301 | 0 | } else { |
1302 | 0 | std::ostringstream as, bs; |
1303 | 0 | a->show(as); |
1304 | 0 | b->show(bs); |
1305 | 0 | return as.str() < bs.str(); |
1306 | 0 | } |
1307 | 0 | } |
1308 | 0 | ); |
1309 | 0 | return r; |
1310 | 0 | } |
1311 | | |
1312 | 0 | void show(std::ostream& out, const lrtable& tbl) { |
1313 | 0 | auto ts = dispOrd(definedSymbols(tbl)); |
1314 | 0 | str::seqs stbl; |
1315 | 0 | for (auto *t : ts) { |
1316 | 0 | stbl.push_back(str::seq()); |
1317 | 0 | str::seq& c = stbl.back(); |
1318 | |
|
1319 | 0 | std::ostringstream ss; |
1320 | 0 | t->show(ss); |
1321 | 0 | c.push_back(ss.str()); |
1322 | |
|
1323 | 0 | for (const auto& s : tbl) { |
1324 | 0 | auto ki = s.find(t); |
1325 | 0 | if (ki == s.end()) { |
1326 | 0 | c.push_back(""); |
1327 | 0 | } else { |
1328 | 0 | std::ostringstream ss; |
1329 | 0 | ki->second.fmt(ss); |
1330 | 0 | c.push_back(ss.str()); |
1331 | 0 | } |
1332 | 0 | } |
1333 | 0 | } |
1334 | 0 | str::printRightAlignedTable(out, stbl); |
1335 | 0 | } |
1336 | | |
1337 | | } |
1338 | | |