/src/hobbes/lib/hobbes/parse/lalr.C
Line | Count | Source |
1 | | |
2 | | #include <hobbes/parse/lalr.H> |
3 | | #include <stdexcept> |
4 | | |
5 | | namespace hobbes { |
6 | | |
7 | | /* |
8 | | * basic grammar/rule analysis |
9 | | */ |
10 | 0 | void removeRuleDefs(terminal* s, grammar* g) { |
11 | 0 | auto r = g->find(s); |
12 | 0 | if (r != g->end()) { |
13 | 0 | g->erase(r); |
14 | 0 | } |
15 | 0 | } |
16 | | |
17 | 0 | void removeRuleRefs(terminal* s, grammar* g) { |
18 | 0 | for (auto &sd : *g) { |
19 | 0 | for (nat r = 0; r < sd.second.size();) { |
20 | 0 | if (in(s, sd.second[r])) { |
21 | 0 | sd.second.erase(sd.second.begin() + r); |
22 | 0 | } else { |
23 | 0 | ++r; |
24 | 0 | } |
25 | 0 | } |
26 | 0 | } |
27 | 0 | } |
28 | | |
29 | 0 | void undefineSymbol(terminal* s, grammar* g) { |
30 | 0 | removeRuleDefs(s, g); |
31 | 0 | removeRuleRefs(s, g); |
32 | 0 | } |
33 | | |
34 | 0 | grammar removeRuleDefs(terminal* s, const grammar& g) { |
35 | 0 | grammar r = g; |
36 | 0 | removeRuleDefs(s, &r); |
37 | 0 | return r; |
38 | 0 | } |
39 | | |
40 | 0 | grammar removeRuleRefs(terminal* s, const grammar& g) { |
41 | 0 | grammar r = g; |
42 | 0 | removeRuleRefs(s, &r); |
43 | 0 | return r; |
44 | 0 | } |
45 | | |
46 | 0 | grammar undefineSymbol(terminal* s, const grammar& g) { |
47 | 0 | grammar r = g; |
48 | 0 | undefineSymbol(s, &r); |
49 | 0 | return r; |
50 | 0 | } |
51 | | |
52 | 0 | terminalset definedSymbols(const grammar& g) { |
53 | 0 | return keys(g); |
54 | 0 | } |
55 | | |
56 | | // a top-level symbol is used by nothing, or just by itself |
57 | 0 | terminalset topLevelSymbols(const grammar& g) { |
58 | 0 | terminalset result; |
59 | 0 | terminalset dsyms = definedSymbols(g); |
60 | |
|
61 | 0 | for (auto *dsym : dsyms) { |
62 | 0 | terminalset users = symbolsUsing(g, dsym); |
63 | 0 | if (users.empty() || (users.size() == 1 && in(dsym, users))) { |
64 | 0 | result.insert(dsym); |
65 | 0 | } |
66 | 0 | } |
67 | |
|
68 | 0 | return result; |
69 | 0 | } |
70 | | |
71 | 0 | void symbolsUsed(const grammar& g, const rule& r, terminalset* ss) { |
72 | 0 | for (auto *t : r) { |
73 | 0 | if (hasRule(g, t)) { |
74 | 0 | if (!in(t, *ss)) { |
75 | 0 | ss->insert(t); |
76 | 0 | symbolsUsed(g, t, ss); |
77 | 0 | } |
78 | 0 | } |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | 0 | void symbolsUsed(const grammar& g, const rules& rs, terminalset* ss) { |
83 | 0 | for (const auto &r : rs) { |
84 | 0 | symbolsUsed(g, r, ss); |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | 0 | void symbolsUsed(const grammar& g, terminal* s, terminalset* ss) { |
89 | 0 | auto sd = g.find(s); |
90 | 0 | if (sd != g.end()) { |
91 | 0 | symbolsUsed(g, sd->second, ss); |
92 | 0 | } |
93 | 0 | } |
94 | | |
95 | 0 | terminalset symbolsUsed(const grammar& g, terminal* s) { |
96 | 0 | terminalset r; |
97 | 0 | symbolsUsed(g, s, &r); |
98 | 0 | return r; |
99 | 0 | } |
100 | | |
101 | 0 | terminalset symbolsUsing(const grammar& g, terminal* s) { |
102 | 0 | terminalset result; |
103 | 0 | terminalset dsyms = definedSymbols(g); |
104 | |
|
105 | 0 | for (auto *dsym : dsyms) { |
106 | 0 | if (in(s, symbolsUsed(g, dsym))) { |
107 | 0 | result.insert(dsym); |
108 | 0 | } |
109 | 0 | } |
110 | |
|
111 | 0 | return result; |
112 | 0 | } |
113 | | |
114 | 0 | bool hasRule(const grammar& g, terminal* t) { |
115 | 0 | return g.find(t) != g.end(); |
116 | 0 | } |
117 | | |
118 | 0 | const rules* aggRules(const grammar& g, terminal* t) { |
119 | 0 | auto ad = g.find(t); |
120 | 0 | if (ad == g.end()) { |
121 | 0 | throw std::runtime_error("Internal error, no such aggregate defined."); |
122 | 0 | } else { |
123 | 0 | return &(ad->second); |
124 | 0 | } |
125 | 0 | } |
126 | | |
127 | 0 | const rule& nthRule(const rules* rs, nat n) { |
128 | 0 | if (n >= rs->size()) { |
129 | 0 | throw std::runtime_error("Internal error, no such rule."); |
130 | 0 | } else { |
131 | 0 | return (*rs)[n]; |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | 0 | terminal* ruleElement(const rule& r, nat i) { |
136 | 0 | if (i >= r.size()) { |
137 | 0 | return nullptr; |
138 | 0 | } else { |
139 | 0 | return r[i]; |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | 0 | bool directlyDerivesNull(const rule& r) { |
144 | 0 | return r.empty(); |
145 | 0 | } |
146 | | |
147 | 0 | bool directlyDerivesNull(const grammar& g, terminal* s) { |
148 | 0 | const rules* rs = aggRules(g, s); |
149 | 0 | for (const auto &r : *rs) { |
150 | 0 | if (directlyDerivesNull(r)) { |
151 | 0 | return true; |
152 | 0 | } |
153 | 0 | } |
154 | 0 | return false; |
155 | 0 | } |
156 | | |
157 | 0 | bool derivesNull(const terminalset& nullAggs, const rule& r) { |
158 | 0 | for (auto *v : r) { |
159 | 0 | if (nullAggs.find(v) == nullAggs.end()) { |
160 | 0 | return false; |
161 | 0 | } |
162 | 0 | } |
163 | 0 | return true; |
164 | 0 | } |
165 | | |
166 | 0 | bool derivesNull(const grammar& g, const terminalset& nullAggs, terminal* s) { |
167 | 0 | const rules* rs = aggRules(g, s); |
168 | 0 | for (const auto & r : *rs) { |
169 | 0 | if (derivesNull(nullAggs, r)) { |
170 | 0 | return true; |
171 | 0 | } |
172 | 0 | } |
173 | 0 | return false; |
174 | 0 | } |
175 | | |
176 | 0 | bool derivesNull(const grammar& g, const terminalset& nullAggs, const terminals& ss) { |
177 | 0 | for (auto *s : ss) { |
178 | 0 | if (!derivesNull(g, nullAggs, s)) { |
179 | 0 | return false; |
180 | 0 | } |
181 | 0 | } |
182 | 0 | return true; |
183 | 0 | } |
184 | | |
185 | 0 | bool derivesNull(const parserdef& p, terminal* s) { |
186 | 0 | return p.nullable.find(s) != p.nullable.end(); |
187 | 0 | } |
188 | | |
189 | 0 | terminalset symbolsDerivingNull(const grammar& g) { |
190 | 0 | terminalset nulls; |
191 | 0 | terminalset potentials = definedSymbols(g); |
192 | 0 | bool changed = true; |
193 | |
|
194 | 0 | while (changed) { |
195 | 0 | changed = false; |
196 | | // erasing the element an iterator refers to invalidates that iterator, so |
197 | | // advance from what erase returns rather than incrementing the dead one |
198 | 0 | for (auto s = potentials.begin(); s != potentials.end(); ) { |
199 | 0 | if (nulls.find(*s) == nulls.end() && derivesNull(g, nulls, *s)) { |
200 | 0 | nulls.insert(*s); |
201 | 0 | s = potentials.erase(s); |
202 | 0 | changed = true; |
203 | 0 | } else { |
204 | 0 | ++s; |
205 | 0 | } |
206 | 0 | } |
207 | 0 | } |
208 | 0 | return nulls; |
209 | 0 | } |
210 | | |
211 | | /* |
212 | | * LR(0) parser determination |
213 | | */ |
214 | 0 | item::item(terminal* agg, nat p, nat r, const terminalset& la) : agg(agg), p(p), r(r), la(la) { } |
215 | | |
216 | 0 | bool item::operator<(const item& rhs) const { |
217 | 0 | if (this->agg != rhs.agg) { |
218 | 0 | return this->agg < rhs.agg; |
219 | 0 | } else if (this->p != rhs.p) { |
220 | 0 | return this->p < rhs.p; |
221 | 0 | } else { |
222 | 0 | return this->r < rhs.r; |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | 0 | bool item::operator==(const item& rhs) const { |
227 | 0 | return this->agg == rhs.agg && |
228 | 0 | this->p == rhs.p && |
229 | 0 | this->r == rhs.r; |
230 | 0 | } |
231 | | |
232 | 0 | void show(std::ostream& out, const grammar& g, const item& i) { |
233 | 0 | i.agg->show(out); |
234 | 0 | out << " -> "; |
235 | 0 | const rule& r = nthRule(aggRules(g, i.agg), i.r); |
236 | 0 | for (nat k = 0; k < r.size(); ++k) { |
237 | 0 | if (k == i.p) { |
238 | 0 | out << "#"; |
239 | 0 | } |
240 | 0 | r[k]->show(out); |
241 | 0 | } |
242 | 0 | if (i.p == r.size()) { |
243 | 0 | out << "#"; |
244 | 0 | out << " ["; |
245 | 0 | if (!i.la.empty()) { |
246 | 0 | auto lat = i.la.begin(); |
247 | 0 | (*lat)->show(out); |
248 | 0 | ++lat; |
249 | 0 | while (lat != i.la.end()) { |
250 | 0 | out << ", "; |
251 | 0 | (*lat)->show(out); |
252 | 0 | ++lat; |
253 | 0 | } |
254 | 0 | } |
255 | 0 | out << "]"; |
256 | 0 | } |
257 | 0 | out << std::endl; |
258 | 0 | } |
259 | | |
260 | 0 | void show(std::ostream& out, const grammar& g, const itemset& is) { |
261 | 0 | for (const auto &i : is) { |
262 | 0 | show(out, g, i); |
263 | 0 | } |
264 | 0 | } |
265 | | |
266 | 0 | bool ruleItems(const grammar& g, terminal* t, itemset* is) { |
267 | 0 | bool inserted = false; |
268 | 0 | const rules* rs = aggRules(g, t); |
269 | |
|
270 | 0 | for (nat r = 0; r < rs->size(); ++r) { |
271 | 0 | inserted |= is->insert(item(t, 0, r)).second; |
272 | 0 | } |
273 | |
|
274 | 0 | return inserted; |
275 | 0 | } |
276 | | |
277 | 0 | itemset ruleItems(const grammar& g, terminal* t) { |
278 | 0 | itemset is; |
279 | 0 | ruleItems(g, t, &is); |
280 | 0 | return is; |
281 | 0 | } |
282 | | |
283 | 0 | item succ(const item& i) { |
284 | 0 | return item(i.agg, i.p+1, i.r, i.la); |
285 | 0 | } |
286 | | |
287 | 0 | terminal* next(const grammar& g, const item& i) { |
288 | 0 | return ruleElement(nthRule(aggRules(g, i.agg), i.r), i.p); |
289 | 0 | } |
290 | | |
291 | 0 | terminalset next(const grammar& g, const itemset& is) { |
292 | 0 | terminalset r; |
293 | 0 | for (const auto &i : is) { |
294 | 0 | if (terminal* t = next(g, i)) { |
295 | 0 | r.insert(t); |
296 | 0 | } |
297 | 0 | } |
298 | 0 | return r; |
299 | 0 | } |
300 | | |
301 | 0 | terminalset nextPrim(const grammar& g, const itemset& is) { |
302 | 0 | terminalset r; |
303 | 0 | for (const auto &i : is) { |
304 | 0 | if (terminal* v = next(g, i)) { |
305 | 0 | if (!hasRule(g, v)) { |
306 | 0 | r.insert(v); |
307 | 0 | } |
308 | 0 | } |
309 | 0 | } |
310 | 0 | return r; |
311 | 0 | } |
312 | | |
313 | 0 | itemset follow(const grammar& g, const itemset& is, terminal* v) { |
314 | 0 | itemset r; |
315 | 0 | for (const auto &i : is) { |
316 | 0 | if (next(g, i) == v) { |
317 | 0 | r.insert(succ(i)); |
318 | 0 | } |
319 | 0 | } |
320 | 0 | return r; |
321 | 0 | } |
322 | | |
323 | 0 | itemsets follow(const grammar& g, const itemset& is, const terminals& vs) { |
324 | 0 | itemsets r; |
325 | 0 | for (auto *v : vs) { |
326 | 0 | r.push_back(follow(g, is, v)); |
327 | 0 | } |
328 | 0 | return r; |
329 | 0 | } |
330 | | |
331 | 0 | void gclosure(const grammar& g, itemset* is) { |
332 | | // add implied items to a fixed point |
333 | 0 | bool changed = true; |
334 | 0 | while (changed) { |
335 | 0 | changed = false; |
336 | |
|
337 | 0 | terminalset ts = next(g, *is); |
338 | 0 | for (auto *t : ts) { |
339 | 0 | if ((t != nullptr) && hasRule(g, t)) { |
340 | 0 | changed |= ruleItems(g, t, is); |
341 | 0 | } |
342 | 0 | } |
343 | 0 | } |
344 | 0 | } |
345 | | |
346 | 0 | itemset gclosure(const grammar& g, const itemset& is) { |
347 | 0 | itemset r = is; |
348 | 0 | gclosure(g, &r); |
349 | 0 | return r; |
350 | 0 | } |
351 | | |
352 | 0 | nat parserState(const itemset& s, parserdef* p) { |
353 | 0 | parser_states::const_iterator si = p->states.find(s); |
354 | 0 | if (si != p->states.end()) { |
355 | 0 | return si->second; |
356 | 0 | } else { |
357 | 0 | nat n = p->states.size(); |
358 | 0 | p->states [s] = n; |
359 | 0 | p->state_defs[n] = s; |
360 | 0 | return n; |
361 | 0 | } |
362 | 0 | } |
363 | | |
364 | 0 | const itemset& parserStateDef(const parserdef& p, nat n) { |
365 | 0 | auto sd = p.state_defs.find(n); |
366 | 0 | if (sd == p.state_defs.end()) { |
367 | 0 | throw std::runtime_error("Internal error, invalid state referenced."); |
368 | 0 | } else { |
369 | 0 | return sd->second; |
370 | 0 | } |
371 | 0 | } |
372 | | |
373 | 0 | state_transitions follow(const grammar& g, const itemset& is, const terminalset& vs, parserdef* p) { |
374 | 0 | state_transitions r; |
375 | 0 | for (auto *v : vs) { |
376 | 0 | if (v != endOfFile::value()) { |
377 | 0 | r[v] = parserState(gclosure(g, follow(g, is, v)), p); |
378 | 0 | } |
379 | 0 | } |
380 | 0 | return r; |
381 | 0 | } |
382 | | |
383 | 0 | parserdef lr0parser(const grammar& g, terminal* s) { |
384 | 0 | parserdef p; |
385 | | |
386 | | // tweak this grammar to have a boot rule |
387 | | // s' -> s $ |
388 | 0 | p.g = g; |
389 | 0 | p.s = s; |
390 | |
|
391 | 0 | static symbol privstart(".S"); |
392 | 0 | terminal* boot = &privstart; |
393 | 0 | rule br; br.push_back(s); br.push_back(endOfFile::value()); |
394 | 0 | p.g[boot].push_back(br); |
395 | | |
396 | | // enumerate the state space implied by this grammar |
397 | 0 | nat n = parserState(gclosure(p.g, ruleItems(p.g, boot)), &p); |
398 | 0 | while (n < p.states.size()) { |
399 | 0 | const itemset& nis = parserStateDef(p, n); |
400 | 0 | p.transitions[n] = follow(p.g, nis, next(p.g, nis), &p); |
401 | 0 | ++n; |
402 | 0 | } |
403 | 0 | p.nullable = symbolsDerivingNull(p.g); |
404 | 0 | return p; |
405 | 0 | } |
406 | | |
407 | | /* |
408 | | * Relations on the LR(0) parser and LALR(1) determination |
409 | | */ |
410 | | |
411 | 0 | transitionset transitions(const parserdef& p) { |
412 | 0 | transitionset r; |
413 | 0 | for (auto pst = p.transitions.begin(); pst != p.transitions.end(); ++pst) { |
414 | 0 | for (auto st = pst->second.begin(); st != pst->second.end(); ++st) { |
415 | 0 | if (hasRule(p.g, st->first)) { |
416 | 0 | r.insert(transition(pst->first, st->first)); |
417 | 0 | } |
418 | 0 | } |
419 | 0 | } |
420 | 0 | return r; |
421 | 0 | } |
422 | | |
423 | 0 | const state_transitions& transitions(const parserdef& p, nat q) { |
424 | 0 | auto r = p.transitions.find(q); |
425 | 0 | if (r == p.transitions.end()) { |
426 | 0 | throw std::runtime_error("Internal error, invalid state given for finding transitions."); |
427 | 0 | } else { |
428 | 0 | return r->second; |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | 0 | bool follows(const state_transitions& st, terminal* t) { |
433 | 0 | return st.find(t) != st.end(); |
434 | 0 | } |
435 | | |
436 | 0 | bool follows(const parserdef& p, nat q, terminal* t) { |
437 | 0 | return follows(transitions(p, q), t); |
438 | 0 | } |
439 | | |
440 | 0 | nat follow(const state_transitions& st, terminal* t) { |
441 | 0 | auto x = st.find(t); |
442 | 0 | if (x == st.end()) { |
443 | 0 | throw std::runtime_error("Internal error, invalid symbol given for determining state transition."); |
444 | 0 | } else { |
445 | 0 | return x->second; |
446 | 0 | } |
447 | 0 | } |
448 | | |
449 | 0 | nat follow(const parserdef& p, nat q, terminal* t) { |
450 | 0 | return follow(transitions(p, q), t); |
451 | 0 | } |
452 | | |
453 | 0 | nat follow(const parserdef& p, const transition& x) { |
454 | 0 | return follow(p, x.first, x.second); |
455 | 0 | } |
456 | | |
457 | 0 | nat follow(const parserdef& p, nat s, const terminals& ts) { |
458 | 0 | nat r = s; |
459 | 0 | for (auto *t : ts) { |
460 | 0 | r = follow(p, r, t); |
461 | 0 | } |
462 | 0 | return r; |
463 | 0 | } |
464 | | |
465 | | // check if a transition "looks back" to a state where a given rule has been completed |
466 | 0 | bool lookback(const parserdef& p, nat q, terminal* r, const rule& w, const transition& t) { |
467 | 0 | if (t.second != r) { |
468 | 0 | return false; |
469 | 0 | } else { |
470 | 0 | nat s = t.first; |
471 | 0 | for (auto *t : w) { |
472 | 0 | const state_transitions& st = transitions(p, s); |
473 | 0 | if (follows(st, t)) { |
474 | 0 | s = follow(st, t); |
475 | 0 | } else { |
476 | 0 | return false; |
477 | 0 | } |
478 | 0 | } |
479 | 0 | return s == q; |
480 | 0 | } |
481 | 0 | } |
482 | | |
483 | | // for a rule w and sym A, find all prefixes b such that w = bAs, s =>* eps |
484 | | using prefixes = std::vector<terminals>; |
485 | | |
486 | 0 | prefixes endingAt(const parserdef& p, const rule& w, terminal* a) { |
487 | 0 | prefixes r; |
488 | 0 | nat i = w.size(); |
489 | 0 | while (i > 0) { |
490 | 0 | --i; |
491 | |
|
492 | 0 | if (w[i] == a) { |
493 | 0 | r.push_back(terminals(w.begin(), w.begin() + i)); |
494 | 0 | } |
495 | 0 | if (!derivesNull(p, w[i])) { |
496 | 0 | break; |
497 | 0 | } |
498 | 0 | } |
499 | 0 | return r; |
500 | 0 | } |
501 | | |
502 | | // x=(s,A) includes y=(s',B) if B -> wAg, g =>* eps, and s' --w--> s |
503 | 0 | bool includes(const parserdef& p, const transition& x, const transition& y) { |
504 | 0 | const rules* rs = aggRules(p.g, y.second); |
505 | 0 | for (const auto &r : *rs) { |
506 | 0 | prefixes pr = endingAt(p, r, x.second); |
507 | 0 | for (const auto & pfx : pr) { |
508 | 0 | if (follow(p, y.first, pfx) == x.first) { |
509 | 0 | return true; |
510 | 0 | } |
511 | 0 | } |
512 | 0 | } |
513 | 0 | return false; |
514 | 0 | } |
515 | | |
516 | | // (s,A) reads (s',B) if s --A--> s' and B =>* eps |
517 | 0 | bool reads(const parserdef& p, const transition& x, const transition& y) { |
518 | 0 | return follow(p, x) == y.first && derivesNull(p, y.second); |
519 | 0 | } |
520 | | |
521 | | // from x=(s,A), find all non-aggregate terminals immediately expected after transitioning from state s to s' along A |
522 | 0 | terminalset directReads(const parserdef& p, const transition& x) { |
523 | 0 | return nextPrim(p.g, parserStateDef(p, follow(p, x))); |
524 | 0 | } |
525 | | |
526 | | // a utility to make it easier to create closures |
527 | | template <typename E, typename X0, typename R> |
528 | | struct clos1 { |
529 | | using PFN = R (*)(const E &, const X0 &); |
530 | | const E& env; |
531 | | PFN fn; |
532 | | |
533 | 0 | clos1(const E& env, PFN fn) : env(env), fn(fn) { }Unexecuted instantiation: hobbes::clos1<hobbes::parserdef, std::pair<unsigned int, hobbes::terminal*>, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> > >::clos1(hobbes::parserdef const&, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> > (*)(hobbes::parserdef const&, std::pair<unsigned int, hobbes::terminal*> const&)) Unexecuted instantiation: hobbes::clos1<std::map<std::pair<unsigned int, hobbes::terminal*>, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> >, std::less<std::pair<unsigned int, hobbes::terminal*> >, std::allocator<std::pair<std::pair<unsigned int, hobbes::terminal*> const, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> > > > >, std::pair<unsigned int, hobbes::terminal*>, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> > >::clos1(std::map<std::pair<unsigned int, hobbes::terminal*>, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> >, std::less<std::pair<unsigned int, hobbes::terminal*> >, std::allocator<std::pair<std::pair<unsigned int, hobbes::terminal*> const, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> > > > > const&, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> > (*)(std::map<std::pair<unsigned int, hobbes::terminal*>, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> >, std::less<std::pair<unsigned int, hobbes::terminal*> >, std::allocator<std::pair<std::pair<unsigned int, hobbes::terminal*> const, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> > > > > const&, std::pair<unsigned int, hobbes::terminal*> const&)) |
534 | 0 | R operator()(const X0& x0) const { return fn(env, x0); }Unexecuted instantiation: hobbes::clos1<hobbes::parserdef, std::pair<unsigned int, hobbes::terminal*>, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> > >::operator()(std::pair<unsigned int, hobbes::terminal*> const&) const Unexecuted instantiation: hobbes::clos1<std::map<std::pair<unsigned int, hobbes::terminal*>, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> >, std::less<std::pair<unsigned int, hobbes::terminal*> >, std::allocator<std::pair<std::pair<unsigned int, hobbes::terminal*> const, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> > > > >, std::pair<unsigned int, hobbes::terminal*>, std::set<hobbes::terminal*, std::less<hobbes::terminal*>, std::allocator<hobbes::terminal*> > >::operator()(std::pair<unsigned int, hobbes::terminal*> const&) const |
535 | | }; |
536 | | |
537 | | template <typename E, typename X0, typename X1, typename R> |
538 | | struct clos2 { |
539 | | using PFN = R (*)(const E &, const X0 &, const X1 &); |
540 | | const E& env; |
541 | | PFN fn; |
542 | | |
543 | 0 | clos2(const E& env, PFN fn) : env(env), fn(fn) { } |
544 | 0 | R operator()(const X0& x0, const X1& x1) const { return fn(env, x0, x1); } |
545 | | }; |
546 | | |
547 | | // typedefs to work around type inference failure for the below digraph expressions |
548 | | using ptsappend = terminalset (*)(const terminalset &, const terminalset &); |
549 | | |
550 | | using DRT = clos1<parserdef, transition, terminalset>; |
551 | | using readsT = clos2<parserdef, transition, transition, bool>; |
552 | | |
553 | | using RT = clos1<transition_lookahead, transition, terminalset>; |
554 | | using includesT = clos2<parserdef, transition, transition, bool>; |
555 | | |
556 | | // compute Read(x=(s,A)) = directReads(s,A) + (Read(q,B) | (s,A) reads (q,B)) |
557 | 0 | transition_lookahead Reads(const parserdef& p, const transitionset& ts) { |
558 | 0 | return digraphF<transition, terminalset, DRT, ptsappend, readsT>::map(ts, DRT(p, &directReads), &setUnion, readsT(p, &reads)); |
559 | 0 | } |
560 | | |
561 | | // compute Follow(x=(s,A)) = Read((s,A)) + (Follow(q,B) | (s,A) includes (q,B)) |
562 | 0 | transition_lookahead Follow(const parserdef& p, const transitionset& ts) { |
563 | 0 | transition_lookahead r = Reads(p, ts); |
564 | 0 | return digraphF<transition, terminalset, RT, ptsappend, includesT>::map(ts, RT(r, &mapLookup), &setUnion, includesT(p, &includes)); |
565 | 0 | } |
566 | | |
567 | | // apply transition lookahead to every applicable completed item in an LR(0) parser |
568 | 0 | void apply(const transition_lookahead& tl, parserdef* p) { |
569 | 0 | for (auto s = p->states.begin(); s != p->states.end(); ++s) { |
570 | 0 | for (auto i = s->first.begin(); i != s->first.end(); ++i) { |
571 | | // if this is a completed item, then we need to give it lookahead terminals |
572 | 0 | if (next(p->g, *i) == nullptr) { |
573 | 0 | for (const auto &tli : tl) { |
574 | 0 | if (lookback(*p, s->second, i->agg, nthRule(aggRules(p->g, i->agg), i->r), tli.first)) { |
575 | 0 | const_cast<item*>(&(*(i)))->la.insert(tli.second.begin(), tli.second.end()); |
576 | 0 | } |
577 | 0 | } |
578 | 0 | } |
579 | 0 | } |
580 | 0 | } |
581 | 0 | } |
582 | | |
583 | | // determine the LALR(1) parser definition |
584 | 0 | parserdef lalr1parser(const grammar& g, terminal* s) { |
585 | 0 | parserdef p = lr0parser(g, s); |
586 | 0 | transitionset ts = transitions(p); |
587 | |
|
588 | 0 | hobbes::apply(Follow(p, ts), &p); |
589 | 0 | return p; |
590 | 0 | } |
591 | | |
592 | 0 | terminal* reduceOpTerminal(const parserdef& pd, const action& a) { |
593 | 0 | const rule& r = nthRule(aggRules(pd.g, a.reduceSym()), a.reduceRule()); |
594 | 0 | for (auto e = r.rbegin(); e != r.rend(); ++e) { |
595 | 0 | if (!hasRule(pd.g, *e)) { |
596 | 0 | return *e; |
597 | 0 | } |
598 | 0 | } |
599 | 0 | return nullptr; |
600 | 0 | } |
601 | | |
602 | 0 | bool shiftInstead(const parserdef& pd, const precedence& px, nat, terminal* t, const action& a) { |
603 | 0 | terminal* u = reduceOpTerminal(pd, a); |
604 | |
|
605 | 0 | if (u == nullptr) { |
606 | 0 | throw ambiguity_conflict("Unable to resolve shift/reduce conflict in grammar", t); |
607 | 0 | } |
608 | | |
609 | 0 | auto tp = px.find(t); |
610 | 0 | auto up = px.find(u); |
611 | |
|
612 | 0 | if (tp == px.end() || up == px.end()) { |
613 | 0 | throw ambiguity_conflict("Unable to resolve shift/reduce conflict in grammar", t); |
614 | 0 | } |
615 | | |
616 | 0 | if (tp->second.level < up->second.level) { |
617 | 0 | return false; |
618 | 0 | } else if (up->second.level < tp->second.level) { |
619 | 0 | return true; |
620 | 0 | } else if (tp->second.asc == up->second.asc && tp->second.asc != assoc::non) { |
621 | 0 | return tp->second.asc != assoc::left; |
622 | 0 | } else { |
623 | 0 | throw ambiguity_conflict("Unable to resolve shift/reduce conflict in grammar", t); |
624 | 0 | } |
625 | 0 | } |
626 | | |
627 | | // determine the LALR(1) table to drive an LR parser |
628 | 0 | lrtable lalrTable(const grammar& g, terminal* s, const precedence& px) { |
629 | 0 | return lalrTable(lalr1parser(g, s), px); |
630 | 0 | } |
631 | | |
632 | 0 | void addAction(const parserdef& pd, lrstate& s, terminal* t, const action& a, const precedence& px) { |
633 | 0 | auto sc = s.find(t); |
634 | 0 | if (sc == s.end()) { |
635 | 0 | s.insert(std::make_pair(t, a)); |
636 | 0 | } else if (sc->second != a) { |
637 | 0 | if (a.isReduce() && sc->second.isReduce()) { |
638 | 0 | std::ostringstream ss; |
639 | 0 | ss << "Conflict between " << a << " and " << sc->second << " on " << show(t); |
640 | 0 | throw ambiguity_conflict(ss.str(), t); |
641 | 0 | } else if (a.isShift()) { |
642 | 0 | if (shiftInstead(pd, px, a.shiftState(), t, sc->second)) { |
643 | 0 | sc->second = a; |
644 | 0 | } |
645 | 0 | } else { |
646 | 0 | if (!shiftInstead(pd, px, sc->second.shiftState(), t, a)) { |
647 | 0 | sc->second = a; |
648 | 0 | } |
649 | 0 | } |
650 | 0 | } |
651 | 0 | } |
652 | | |
653 | 0 | void addAccept(lrstate& s, terminal* t) { |
654 | 0 | s.insert(std::make_pair(t, action::accept())); |
655 | 0 | } |
656 | | |
657 | 0 | void addReduce(const parserdef& pd, lrstate& s, const terminalset& ts, terminal* agg, nat rule, nat len, const precedence& px) { |
658 | 0 | for (auto *t : ts) { |
659 | 0 | addAction(pd, s, t, action::reduce(agg, rule, len), px); |
660 | 0 | } |
661 | 0 | } |
662 | | |
663 | 0 | void addGoto(lrstate& s, terminal* nt, nat next) { |
664 | 0 | s.insert(std::make_pair(nt, action::goTo(next))); |
665 | 0 | } |
666 | | |
667 | 0 | void addShift(const parserdef& pd, lrstate& s, terminal* t, nat next, const precedence& px) { |
668 | 0 | addAction(pd, s, t, action::shift(next), px); |
669 | 0 | } |
670 | | |
671 | 0 | lrtable lalrTable(const parserdef& p, const precedence& px) { |
672 | 0 | lrtable r; |
673 | 0 | r.resize(p.states.size()); |
674 | |
|
675 | 0 | for (auto s = p.states.begin(); s != p.states.end(); ++s) { |
676 | | // each item in this state's set translates to an LR parser action (goto, shift, reduce, or accept) |
677 | 0 | const itemset& is = s->first; |
678 | 0 | nat n = s->second; |
679 | 0 | lrstate& ps = r[n]; |
680 | | |
681 | | // if we fail to compile a table row, report the itemset that failed |
682 | 0 | try { |
683 | 0 | for (const auto & i : is) { |
684 | 0 | terminal* t = next(p.g, i); |
685 | | |
686 | 0 | if (t == nullptr) { |
687 | | // reduce |
688 | 0 | addReduce(p, ps, i.la, i.agg, i.r, i.p, px); |
689 | 0 | } else if (hasRule(p.g, t)) { |
690 | | // goto |
691 | 0 | addGoto(ps, t, follow(p, n, t)); |
692 | 0 | } else if (t == endOfFile::value()) { |
693 | | // accept |
694 | 0 | addAccept(ps, t); |
695 | 0 | } else { |
696 | | // shift |
697 | 0 | addShift(p, ps, t, follow(p, n, t), px); |
698 | 0 | } |
699 | 0 | } |
700 | 0 | } catch (ambiguity_conflict& ex) { |
701 | 0 | throw compile_table_failure(ex.what(), p.g, is, ex.failedTerminal()); |
702 | 0 | } |
703 | 0 | } |
704 | | |
705 | 0 | return r; |
706 | 0 | } |
707 | | |
708 | 0 | ambiguity_conflict::ambiguity_conflict(const std::string& ex, terminal* t) noexcept : std::runtime_error(ex), t(t) { |
709 | 0 | } |
710 | | |
711 | 0 | terminal* ambiguity_conflict::failedTerminal() const { |
712 | 0 | return this->t; |
713 | 0 | } |
714 | | |
715 | 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) { |
716 | 0 | } |
717 | | |
718 | 0 | const grammar& compile_table_failure::failedGrammar() const { |
719 | 0 | return this->g; |
720 | 0 | } |
721 | | |
722 | 0 | const itemset& compile_table_failure::failedItems() const { |
723 | 0 | return this->faileditems; |
724 | 0 | } |
725 | | |
726 | 0 | terminal* compile_table_failure::failedTerminal() const { |
727 | 0 | return this->t; |
728 | 0 | } |
729 | | |
730 | 0 | inline bool referencesSym(const grammar& g, const item& i, terminal* t) { |
731 | 0 | return in(t, nthRule(aggRules(g, i.agg), i.r)) || in(t, i.la); |
732 | 0 | } |
733 | | |
734 | 0 | void compile_table_failure::print(std::ostream& out) const { |
735 | 0 | for (const auto &faileditem : this->faileditems) { |
736 | 0 | if (referencesSym(this->g, faileditem, this->t)) { |
737 | 0 | print(out, faileditem); |
738 | 0 | } |
739 | 0 | } |
740 | 0 | } |
741 | | |
742 | 0 | void beginHighlight(std::ostream& out) { |
743 | 0 | static const char bgccmd[] = { 27, '[', '4', '1', 'm' }; |
744 | 0 | static const char fgccmd[] = { 27, '[', '3', '7', 'm' }; |
745 | |
|
746 | 0 | out.write(bgccmd, sizeof(bgccmd)); |
747 | 0 | out.write(fgccmd, sizeof(fgccmd)); |
748 | 0 | } |
749 | | |
750 | 0 | void endHighlight(std::ostream& out) { |
751 | 0 | static const char rstcmd[] = { 27, '[', '0', 'm' }; |
752 | |
|
753 | 0 | out.write(rstcmd, sizeof(rstcmd)); |
754 | 0 | } |
755 | | |
756 | 0 | inline void showHLTerm(std::ostream& out, terminal* x, terminal* hx) { |
757 | 0 | if (x == hx) { |
758 | 0 | beginHighlight(out); |
759 | 0 | x->show(out); |
760 | 0 | endHighlight(out); |
761 | 0 | } else { |
762 | 0 | x->show(out); |
763 | 0 | } |
764 | 0 | } |
765 | | |
766 | 0 | void compile_table_failure::print(std::ostream& out, const item& i) const { |
767 | 0 | i.agg->show(out); |
768 | 0 | out << " ->"; |
769 | |
|
770 | 0 | const rule& r = nthRule(aggRules(this->g, i.agg), i.r); |
771 | 0 | for (nat k = 0; k < r.size(); ++k) { |
772 | 0 | out << ' '; |
773 | 0 | if (k == i.p) { |
774 | 0 | out << '#'; |
775 | 0 | showHLTerm(out, r[k], this->t); |
776 | 0 | } else { |
777 | 0 | r[k]->show(out); |
778 | 0 | } |
779 | 0 | } |
780 | 0 | if (i.p == r.size()) { |
781 | 0 | out << "#"; |
782 | 0 | out << " ["; |
783 | 0 | if (!i.la.empty()) { |
784 | 0 | auto lat = i.la.begin(); |
785 | 0 | showHLTerm(out, *lat, this->t); |
786 | 0 | ++lat; |
787 | 0 | while (lat != i.la.end()) { |
788 | 0 | out << ", "; |
789 | 0 | showHLTerm(out, *lat, this->t); |
790 | 0 | ++lat; |
791 | 0 | } |
792 | 0 | } |
793 | 0 | out << "]"; |
794 | 0 | } |
795 | 0 | out << std::endl; |
796 | 0 | } |
797 | | |
798 | | // lr tables / actions |
799 | 0 | action::action() = default; |
800 | | |
801 | 0 | action action::goTo(nat s) { |
802 | 0 | action a; |
803 | 0 | a.act = 0; |
804 | 0 | a.d.s = s; |
805 | 0 | return a; |
806 | 0 | } |
807 | 0 | bool action::isGoTo() const { return this->act == 0; } |
808 | 0 | nat action::goToState() const { return this->d.s; } |
809 | | |
810 | 0 | action action::shift(nat s) { |
811 | 0 | action a; |
812 | 0 | a.act = 1; |
813 | 0 | a.d.s = s; |
814 | 0 | return a; |
815 | 0 | } |
816 | 0 | bool action::isShift() const { return this->act == 1; } |
817 | 0 | nat action::shiftState() const { return this->d.s; } |
818 | | |
819 | 0 | action action::reduce(terminal* x, nat r, nat n) { |
820 | 0 | action a; |
821 | 0 | a.act = 2; |
822 | 0 | a.d.x = x; |
823 | 0 | a.d.r = r; |
824 | 0 | a.d.n = n; |
825 | 0 | return a; |
826 | 0 | } |
827 | 0 | bool action::isReduce() const { return this->act == 2; } |
828 | 0 | terminal* action::reduceSym() const { return this->d.x; } |
829 | 0 | nat action::reduceRule() const { return this->d.r; } |
830 | 0 | nat action::reduceSize() const { return this->d.n; } |
831 | | |
832 | 0 | action action::accept() { |
833 | 0 | action a; |
834 | 0 | a.act = 3; |
835 | 0 | return a; |
836 | 0 | } |
837 | 0 | bool action::isAccept() const { return this->act == 3; } |
838 | | |
839 | 0 | std::ostream& action::fmt(std::ostream& out) const { |
840 | 0 | switch (this->act) { |
841 | 0 | case 0: out << this->d.s; break; |
842 | 0 | case 1: out << "s" << this->d.s; break; |
843 | 0 | case 2: out << "r" << this->d.r; break; |
844 | 0 | case 3: out << "A"; break; |
845 | 0 | } |
846 | 0 | return out; |
847 | 0 | } |
848 | | |
849 | 0 | std::ostream& operator<<(std::ostream& out, const action& act) { |
850 | 0 | return act.fmt(out); |
851 | 0 | } |
852 | | |
853 | 0 | bool action::operator==(const action& rhs) const { |
854 | 0 | if (this->act != rhs.act) { |
855 | 0 | return false; |
856 | 0 | } else { |
857 | 0 | switch (this->act) { |
858 | 0 | default: |
859 | 0 | case 0: return rhs.d.s == this->d.s; |
860 | 0 | case 1: return rhs.d.s == this->d.s; |
861 | 0 | case 2: return rhs.d.x == this->d.x && rhs.d.r == this->d.r && rhs.d.n == this->d.n; |
862 | 0 | case 3: return true; |
863 | 0 | } |
864 | 0 | } |
865 | 0 | } |
866 | | |
867 | 0 | bool action::operator!=(const action& rhs) const { |
868 | 0 | return !(*this == rhs); |
869 | 0 | } |
870 | | |
871 | 0 | terminalset definedSymbols(const lrtable& t) { |
872 | 0 | terminalset r; |
873 | 0 | for (const auto& s : t) { |
874 | 0 | for (const auto& k : s) { |
875 | 0 | r.insert(k.first); |
876 | 0 | } |
877 | 0 | } |
878 | 0 | return r; |
879 | 0 | } |
880 | | |
881 | 0 | terminals dispOrd(const terminalset& ts) { |
882 | 0 | terminals r(ts.begin(), ts.end()); |
883 | 0 | std::sort(r.begin(), r.end(), |
884 | 0 | [](terminal* a, terminal* b) { |
885 | 0 | if (a == endOfFile::value()) { |
886 | 0 | return b != endOfFile::value(); |
887 | 0 | } else if (b == endOfFile::value()) { |
888 | 0 | return false; |
889 | 0 | } else if (const character* c = dynamic_cast<character*>(a)) { |
890 | 0 | if (const character* z = dynamic_cast<character*>(b)) { |
891 | 0 | return c->value() < z->value(); |
892 | 0 | } else { |
893 | 0 | return true; |
894 | 0 | } |
895 | 0 | } else if (dynamic_cast<character*>(b) != nullptr) { |
896 | 0 | return false; |
897 | 0 | } else { |
898 | 0 | std::ostringstream as, bs; |
899 | 0 | a->show(as); |
900 | 0 | b->show(bs); |
901 | 0 | return as.str() < bs.str(); |
902 | 0 | } |
903 | 0 | } |
904 | 0 | ); |
905 | 0 | return r; |
906 | 0 | } |
907 | | |
908 | 0 | void show(std::ostream& out, const lrtable& tbl) { |
909 | 0 | auto ts = dispOrd(definedSymbols(tbl)); |
910 | 0 | str::seqs stbl; |
911 | 0 | for (auto *t : ts) { |
912 | 0 | stbl.push_back(str::seq()); |
913 | 0 | str::seq& c = stbl.back(); |
914 | |
|
915 | 0 | std::ostringstream ss; |
916 | 0 | t->show(ss); |
917 | 0 | c.push_back(ss.str()); |
918 | |
|
919 | 0 | for (const auto& s : tbl) { |
920 | 0 | auto ki = s.find(t); |
921 | 0 | if (ki == s.end()) { |
922 | 0 | c.push_back(""); |
923 | 0 | } else { |
924 | 0 | std::ostringstream ss; |
925 | 0 | ki->second.fmt(ss); |
926 | 0 | c.push_back(ss.str()); |
927 | 0 | } |
928 | 0 | } |
929 | 0 | } |
930 | 0 | str::printRightAlignedTable(out, stbl); |
931 | 0 | } |
932 | | |
933 | | } |
934 | | |