1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include "lalr.h" |
5 | |
6 | #include <limits.h> |
7 | |
8 | #include <algorithm> |
9 | |
10 | #define QLALR_NO_DEBUG_NULLABLES |
11 | #define QLALR_NO_DEBUG_LOOKBACKS |
12 | #define QLALR_NO_DEBUG_DIRECT_READS |
13 | #define QLALR_NO_DEBUG_READS |
14 | #define QLALR_NO_DEBUG_INCLUDES |
15 | #define QLALR_NO_DEBUG_LOOKAHEADS |
16 | |
17 | using namespace Qt::StringLiterals; |
18 | |
19 | QT_BEGIN_NAMESPACE |
20 | QTextStream &qerr() |
21 | { |
22 | static QTextStream result(stderr, QTextStream::WriteOnly); |
23 | return result; |
24 | } |
25 | |
26 | QTextStream &qout() |
27 | { |
28 | static QTextStream result(stdout, QTextStream::WriteOnly); |
29 | return result; |
30 | } |
31 | QT_END_NAMESPACE |
32 | |
33 | namespace std { |
34 | bool operator < (Name a, Name b) |
35 | { |
36 | return *a < *b; |
37 | } |
38 | |
39 | bool operator < (ItemPointer a, ItemPointer b) |
40 | { |
41 | return &*a < &*b; |
42 | } |
43 | |
44 | bool operator < (StatePointer a, StatePointer b) |
45 | { |
46 | return &*a < &*b; |
47 | } |
48 | } |
49 | |
50 | bool Read::operator < (const Read &other) const |
51 | { |
52 | if (state == other.state) |
53 | return nt < other.nt; |
54 | |
55 | return state < other.state; |
56 | } |
57 | |
58 | bool Include::operator < (const Include &other) const |
59 | { |
60 | if (state == other.state) |
61 | return nt < other.nt; |
62 | |
63 | return state < other.state; |
64 | } |
65 | |
66 | bool Lookback::operator < (const Lookback &other) const |
67 | { |
68 | if (state == other.state) |
69 | return nt < other.nt; |
70 | |
71 | return state < other.state; |
72 | } |
73 | |
74 | QTextStream &operator << (QTextStream &out, const Name &n) |
75 | { |
76 | return out << *n; |
77 | } |
78 | |
79 | QTextStream &operator << (QTextStream &out, const Rule &r) |
80 | { |
81 | out << *r.lhs << " ::=" ; |
82 | |
83 | for (NameList::const_iterator name = r.rhs.begin (); name != r.rhs.end (); ++name) |
84 | out << " " << **name; |
85 | |
86 | return out; |
87 | } |
88 | |
89 | QTextStream &operator << (QTextStream &out, const NameSet &ns) |
90 | { |
91 | out << "{" ; |
92 | |
93 | for (NameSet::const_iterator n = ns.begin (); n != ns.end (); ++n) |
94 | { |
95 | if (n != ns.begin ()) |
96 | out << ", " ; |
97 | |
98 | out << *n; |
99 | } |
100 | |
101 | return out << "}" ; |
102 | } |
103 | |
104 | Item Item::next () const |
105 | { |
106 | Q_ASSERT (! isReduceItem ()); |
107 | |
108 | Item n; |
109 | n.rule = rule; |
110 | n.dot = dot; |
111 | ++n.dot; |
112 | |
113 | return n; |
114 | } |
115 | |
116 | QTextStream &operator << (QTextStream &out, const Item &item) |
117 | { |
118 | RulePointer r = item.rule; |
119 | |
120 | out << *r->lhs << ":" ; |
121 | for (NameList::iterator name = r->rhs.begin (); name != r->rhs.end (); ++name) |
122 | { |
123 | out << " " ; |
124 | |
125 | if (item.dot == name) |
126 | out << ". " ; |
127 | |
128 | out << **name; |
129 | } |
130 | |
131 | if (item.isReduceItem ()) |
132 | out << " ." ; |
133 | |
134 | return out; |
135 | } |
136 | |
137 | State::State (Grammar *g): |
138 | defaultReduce (g->rules.end ()) |
139 | { |
140 | } |
141 | |
142 | QPair<ItemPointer, bool> State::insert (const Item &item) |
143 | { |
144 | ItemPointer it = std::find (first: kernel.begin (), last: kernel.end (), val: item); |
145 | |
146 | if (it != kernel.end ()) |
147 | return qMakePair (value1&: it, value2: false); |
148 | |
149 | return qMakePair (value1: kernel.insert (position: it, x: item), value2: true); |
150 | } |
151 | |
152 | QPair<ItemPointer, bool> State::insertClosure (const Item &item) |
153 | { |
154 | ItemPointer it = std::find (first: closure.begin (), last: closure.end (), val: item); |
155 | |
156 | if (it != closure.end ()) |
157 | return qMakePair (value1&: it, value2: false); |
158 | |
159 | return qMakePair (value1: closure.insert (position: it, x: item), value2: true); |
160 | } |
161 | |
162 | |
163 | ///////////////////////////////////////////////////////////// |
164 | // Grammar |
165 | ///////////////////////////////////////////////////////////// |
166 | Grammar::Grammar (): |
167 | start (names.end ()) |
168 | { |
169 | expected_shift_reduce = 0; |
170 | expected_reduce_reduce = 0; |
171 | current_prec = 0; |
172 | current_assoc = NonAssoc; |
173 | |
174 | table_name = "parser_table"_L1 ; |
175 | |
176 | tk_end = intern (id: "$end" ); |
177 | terminals.insert (x: tk_end); |
178 | spells.insert (key: tk_end, value: "end of file"_L1 ); |
179 | |
180 | /*tk_error= terminals.insert (intern ("error"))*/; |
181 | } |
182 | |
183 | Name Grammar::intern (const QString &id) |
184 | { |
185 | Name name = std::find (first: names.begin (), last: names.end (), val: id); |
186 | |
187 | if (name == names.end ()) |
188 | name = names.insert (position: names.end (), x: id); |
189 | |
190 | return name; |
191 | } |
192 | |
193 | void Grammar::buildRuleMap () |
194 | { |
195 | NameSet undefined; |
196 | for (RulePointer rule = rules.begin (); rule != rules.end (); ++rule) |
197 | { |
198 | for (NameList::iterator it = rule->rhs.begin (); it != rule->rhs.end (); ++it) |
199 | { |
200 | Name name = *it; |
201 | if (isTerminal (name) || declared_lhs.find (x: name) != declared_lhs.end () |
202 | || undefined.find (x: name) != undefined.end ()) |
203 | continue; |
204 | |
205 | undefined.insert(x: name); |
206 | fprintf (stderr, format: "*** Warning. Symbol `%s' is not defined\n" , qPrintable (*name)); |
207 | } |
208 | |
209 | rule_map.insert (key: rule->lhs, value: rule); |
210 | } |
211 | } |
212 | |
213 | void Grammar::buildExtendedGrammar () |
214 | { |
215 | accept_symbol = intern (id: "$accept" ); |
216 | goal = rules.insert (position: rules.end (), x: Rule ()); |
217 | goal->lhs = accept_symbol; |
218 | goal->rhs.push_back (x: start); |
219 | goal->rhs.push_back (x: tk_end); |
220 | |
221 | non_terminals.insert (x: accept_symbol); |
222 | } |
223 | |
224 | struct NotNullable |
225 | { |
226 | typedef Name argument_type; |
227 | Automaton *_M_automaton; |
228 | |
229 | NotNullable (Automaton *aut): |
230 | _M_automaton (aut) {} |
231 | |
232 | bool operator () (Name name) const |
233 | { return _M_automaton->nullables.find (x: name) == _M_automaton->nullables.end (); } |
234 | }; |
235 | |
236 | Automaton::Automaton (Grammar *g): |
237 | _M_grammar (g), |
238 | start (states.end ()) |
239 | { |
240 | } |
241 | |
242 | int Automaton::id (RulePointer rule) |
243 | { |
244 | return 1 + std::distance (first: _M_grammar->rules.begin (), last: rule); |
245 | } |
246 | |
247 | int Automaton::id (Name name) |
248 | { |
249 | return std::distance (first: _M_grammar->names.begin (), last: name); |
250 | } |
251 | |
252 | int Automaton::id (StatePointer state) |
253 | { |
254 | return std::distance (first: states.begin (), last: state); |
255 | } |
256 | |
257 | void Automaton::build () |
258 | { |
259 | Item item; |
260 | item.rule = _M_grammar->goal; |
261 | item.dot = _M_grammar->goal->rhs.begin (); |
262 | |
263 | State tmp (_M_grammar); |
264 | tmp.insert (item); |
265 | start = internState (state: tmp).first; |
266 | |
267 | closure (state: start); |
268 | |
269 | buildNullables (); |
270 | buildLookbackSets (); |
271 | buildReads (); |
272 | buildIncludesAndFollows (); |
273 | buildLookaheads (); |
274 | buildDefaultReduceActions (); |
275 | } |
276 | |
277 | void Automaton::buildNullables () |
278 | { |
279 | bool changed = true; |
280 | |
281 | while (changed) |
282 | { |
283 | changed = false; |
284 | |
285 | for (RulePointer rule = _M_grammar->rules.begin (); rule != _M_grammar->rules.end (); ++rule) |
286 | { |
287 | NameList::iterator nn = std::find_if(first: rule->rhs.begin(), last: rule->rhs.end(), pred: NotNullable(this)); |
288 | |
289 | if (nn == rule->rhs.end ()) |
290 | changed |= nullables.insert (x: rule->lhs).second; |
291 | } |
292 | } |
293 | |
294 | #ifndef QLALR_NO_DEBUG_NULLABLES |
295 | qerr() << "nullables = {" << nullables << Qt::endl; |
296 | #endif |
297 | } |
298 | |
299 | QPair<StatePointer, bool> Automaton::internState (const State &state) |
300 | { |
301 | StatePointer it = std::find (first: states.begin (), last: states.end (), val: state); |
302 | |
303 | if (it != states.end ()) |
304 | return qMakePair (value1&: it, value2: false); |
305 | |
306 | return qMakePair (value1: states.insert (position: it, x: state), value2: true); |
307 | } |
308 | |
309 | struct _Bucket |
310 | { |
311 | std::list<ItemPointer> items; |
312 | |
313 | void insert (ItemPointer item) |
314 | { items.push_back (x: item); } |
315 | |
316 | State toState (Automaton *aut) |
317 | { |
318 | State st (aut->_M_grammar); |
319 | |
320 | for (auto &item : items) |
321 | st.insert(item: item->next()); |
322 | |
323 | return st; |
324 | } |
325 | }; |
326 | |
327 | void Automaton::closure (StatePointer state) |
328 | { |
329 | if (! state->closure.empty ()) // ### not true. |
330 | return; |
331 | |
332 | typedef QMap<Name, _Bucket> bucket_map_type; |
333 | |
334 | bucket_map_type buckets; |
335 | QStack<ItemPointer> working_list; |
336 | |
337 | for (ItemPointer item = state->kernel.begin (); item != state->kernel.end (); ++item) |
338 | working_list.push (t: item); |
339 | |
340 | state->closure = state->kernel; |
341 | |
342 | while (! working_list.empty ()) |
343 | { |
344 | ItemPointer item = working_list.top (); |
345 | working_list.pop (); |
346 | |
347 | if (item->isReduceItem ()) |
348 | continue; |
349 | |
350 | buckets [*item->dot].insert (item); |
351 | |
352 | if (_M_grammar->isNonTerminal (name: *item->dot)) |
353 | { |
354 | const auto range = std::as_const(t&: _M_grammar->rule_map).equal_range(akey: *item->dot); |
355 | for (auto it = range.first; it != range.second; ++it) |
356 | { |
357 | const RulePointer &rule = *it; |
358 | Item ii; |
359 | ii.rule = rule; |
360 | ii.dot = rule->rhs.begin (); |
361 | |
362 | QPair<ItemPointer, bool> r = state->insertClosure (item: ii); |
363 | |
364 | if (r.second) |
365 | working_list.push (t: r.first); |
366 | } |
367 | } |
368 | } |
369 | |
370 | QList<StatePointer> todo; |
371 | |
372 | for (bucket_map_type::iterator bucket = buckets.begin (); bucket != buckets.end (); ++bucket) |
373 | { |
374 | QPair<StatePointer, bool> r = internState (state: bucket->toState (aut: this)); |
375 | |
376 | StatePointer target = r.first; |
377 | |
378 | if (r.second) |
379 | todo.push_back (t: target); |
380 | |
381 | state->bundle.insert (key: bucket.key(), value: target); |
382 | } |
383 | |
384 | while (! todo.empty ()) |
385 | { |
386 | closure (state: todo.front ()); |
387 | todo.pop_front (); |
388 | } |
389 | } |
390 | |
391 | void Automaton::buildLookbackSets () |
392 | { |
393 | for (StatePointer p = states.begin (); p != states.end (); ++p) |
394 | { |
395 | for (Bundle::iterator a = p->bundle.begin (); a != p->bundle.end (); ++a) |
396 | { |
397 | Name A = a.key (); |
398 | |
399 | if (! _M_grammar->isNonTerminal (name: A)) |
400 | continue; |
401 | |
402 | const auto range = std::as_const(t&: _M_grammar->rule_map).equal_range(akey: A); |
403 | for (auto it = range.first; it != range.second; ++it) |
404 | { |
405 | const RulePointer &rule = *it; |
406 | StatePointer q = p; |
407 | |
408 | for (NameList::iterator dot = rule->rhs.begin (); dot != rule->rhs.end (); ++dot) |
409 | q = q->bundle.value (key: *dot, defaultValue: states.end ()); |
410 | |
411 | Q_ASSERT (q != states.end ()); |
412 | |
413 | ItemPointer item = q->closure.begin (); |
414 | |
415 | for (; item != q->closure.end (); ++item) |
416 | { |
417 | if (item->rule == rule && item->dot == item->end_rhs ()) |
418 | break; |
419 | } |
420 | |
421 | if (item == q->closure.end ()) |
422 | { |
423 | Q_ASSERT (q == p); |
424 | Q_ASSERT (rule->rhs.begin () == rule->rhs.end ()); |
425 | |
426 | for (item = q->closure.begin (); item != q->closure.end (); ++item) |
427 | { |
428 | if (item->rule == rule && item->dot == item->end_rhs ()) |
429 | break; |
430 | } |
431 | } |
432 | |
433 | Q_ASSERT (item != q->closure.end ()); |
434 | |
435 | lookbacks.insert (key: item, value: Lookback (p, A)); |
436 | |
437 | #ifndef QLALR_NO_DEBUG_LOOKBACKS |
438 | qerr() << "*** (" << id (q) << ", " << *rule << ") lookback (" << id (p) << ", " << *A << ")" << Qt::endl; |
439 | #endif |
440 | } |
441 | } |
442 | } |
443 | } |
444 | |
445 | void Automaton::buildDirectReads () |
446 | { |
447 | for (StatePointer q = states.begin (); q != states.end (); ++q) |
448 | { |
449 | for (Bundle::iterator a = q->bundle.begin (); a != q->bundle.end (); ++a) |
450 | { |
451 | if (! _M_grammar->isNonTerminal (name: a.key ())) |
452 | continue; |
453 | |
454 | StatePointer r = a.value (); |
455 | |
456 | for (Bundle::iterator z = r->bundle.begin (); z != r->bundle.end (); ++z) |
457 | { |
458 | Name sym = z.key (); |
459 | |
460 | if (! _M_grammar->isTerminal (name: sym)) |
461 | continue; |
462 | |
463 | q->reads [a.key ()].insert (x: sym); |
464 | } |
465 | } |
466 | |
467 | #ifndef QLALR_NO_DEBUG_DIRECT_READS |
468 | for (QMap<Name, NameSet>::iterator dr = q->reads.begin (); dr != q->reads.end (); ++dr) |
469 | qerr() << "*** DR(" << id (q) << ", " << dr.key () << ") = " << dr.value () << Qt::endl; |
470 | #endif |
471 | } |
472 | } |
473 | |
474 | void Automaton::buildReadsDigraph () |
475 | { |
476 | for (StatePointer q = states.begin (); q != states.end (); ++q) |
477 | { |
478 | for (Bundle::iterator a = q->bundle.begin (); a != q->bundle.end (); ++a) |
479 | { |
480 | if (! _M_grammar->isNonTerminal (name: a.key ())) |
481 | continue; |
482 | |
483 | StatePointer r = a.value (); |
484 | |
485 | for (Bundle::iterator z = r->bundle.begin (); z != r->bundle.end (); ++z) |
486 | { |
487 | Name sym = z.key (); |
488 | |
489 | if (! _M_grammar->isNonTerminal(name: sym) || nullables.find (x: sym) == nullables.end ()) |
490 | continue; |
491 | |
492 | ReadsGraph::iterator source = ReadsGraph::get (data: Read (q, a.key ())); |
493 | ReadsGraph::iterator target = ReadsGraph::get (data: Read (r, sym)); |
494 | |
495 | source->insertEdge (other: target); |
496 | |
497 | #ifndef QLALR_NO_DEBUG_READS |
498 | qerr() << "*** " ; |
499 | dump (qerr(), source); |
500 | qerr() << " reads " ; |
501 | dump (qerr(), target); |
502 | qerr() << Qt::endl; |
503 | #endif |
504 | } |
505 | } |
506 | } |
507 | } |
508 | |
509 | void Automaton::buildReads () |
510 | { |
511 | buildDirectReads (); |
512 | buildReadsDigraph (); |
513 | |
514 | _M_reads_dfn = 0; |
515 | |
516 | for (ReadsGraph::iterator node = ReadsGraph::begin_nodes (); node != ReadsGraph::end_nodes (); ++node) |
517 | { |
518 | if (! node->root) |
519 | continue; |
520 | |
521 | visitReadNode (node); |
522 | } |
523 | |
524 | for (ReadsGraph::iterator node = ReadsGraph::begin_nodes (); node != ReadsGraph::end_nodes (); ++node) |
525 | visitReadNode (node); |
526 | } |
527 | |
528 | void Automaton::visitReadNode (ReadNode node) |
529 | { |
530 | if (node->dfn != 0) |
531 | return; // nothing to do |
532 | |
533 | int N = node->dfn = ++_M_reads_dfn; |
534 | _M_reads_stack.push (t: node); |
535 | |
536 | #ifndef QLALR_NO_DEBUG_INCLUDES |
537 | // qerr() << "*** Debug. visit node (" << id (node->data.state) << ", " << node->data.nt << ") N = " << N << Qt::endl; |
538 | #endif |
539 | |
540 | for (ReadsGraph::edge_iterator edge = node->begin (); edge != node->end (); ++edge) |
541 | { |
542 | ReadsGraph::iterator r = *edge; |
543 | |
544 | visitReadNode (node: r); |
545 | |
546 | node->dfn = qMin (a: N, b: r->dfn); |
547 | |
548 | NameSet &dst = node->data.state->reads [node->data.nt]; |
549 | NameSet &src = r->data.state->reads [r->data.nt]; |
550 | dst.insert (first: src.begin (), last: src.end ()); |
551 | } |
552 | |
553 | if (node->dfn == N) |
554 | { |
555 | ReadsGraph::iterator tos = _M_reads_stack.top (); |
556 | |
557 | do { |
558 | tos = _M_reads_stack.top (); |
559 | _M_reads_stack.pop (); |
560 | tos->dfn = INT_MAX; |
561 | } while (tos != node); |
562 | } |
563 | } |
564 | |
565 | void Automaton::buildIncludesAndFollows () |
566 | { |
567 | for (StatePointer p = states.begin (); p != states.end (); ++p) |
568 | p->follows = p->reads; |
569 | |
570 | buildIncludesDigraph (); |
571 | |
572 | _M_includes_dfn = 0; |
573 | |
574 | for (IncludesGraph::iterator node = IncludesGraph::begin_nodes (); node != IncludesGraph::end_nodes (); ++node) |
575 | { |
576 | if (! node->root) |
577 | continue; |
578 | |
579 | visitIncludeNode (node); |
580 | } |
581 | |
582 | for (IncludesGraph::iterator node = IncludesGraph::begin_nodes (); node != IncludesGraph::end_nodes (); ++node) |
583 | visitIncludeNode (node); |
584 | } |
585 | |
586 | void Automaton::buildIncludesDigraph () |
587 | { |
588 | for (StatePointer pp = states.begin (); pp != states.end (); ++pp) |
589 | { |
590 | for (Bundle::iterator a = pp->bundle.begin (); a != pp->bundle.end (); ++a) |
591 | { |
592 | Name name = a.key (); |
593 | |
594 | if (! _M_grammar->isNonTerminal (name)) |
595 | continue; |
596 | |
597 | const auto range = std::as_const(t&: _M_grammar->rule_map).equal_range(akey: name); |
598 | for (auto it = range.first; it != range.second; ++it) |
599 | { |
600 | const RulePointer &rule = *it; |
601 | StatePointer p = pp; |
602 | |
603 | for (NameList::iterator A = rule->rhs.begin (); A != rule->rhs.end (); ++A) |
604 | { |
605 | NameList::iterator dot = A; |
606 | ++dot; |
607 | |
608 | if (_M_grammar->isNonTerminal (name: *A) && dot == rule->rhs.end ()) |
609 | { |
610 | // found an include edge. |
611 | IncludesGraph::iterator target = IncludesGraph::get (data: Include (pp, name)); |
612 | IncludesGraph::iterator source = IncludesGraph::get (data: Include (p, *A)); |
613 | |
614 | source->insertEdge (other: target); |
615 | |
616 | #ifndef QLALR_NO_DEBUG_INCLUDES |
617 | qerr() << "*** (" << id (p) << ", " << *A << ") includes (" << id (pp) << ", " << *name << ")" << Qt::endl; |
618 | #endif // QLALR_NO_DEBUG_INCLUDES |
619 | |
620 | continue; |
621 | } |
622 | |
623 | p = p->bundle.value (key: *A); |
624 | |
625 | if (! _M_grammar->isNonTerminal (name: *A)) |
626 | continue; |
627 | |
628 | NameList::iterator first_not_nullable = std::find_if(first: dot, last: rule->rhs.end(), pred: NotNullable(this)); |
629 | if (first_not_nullable != rule->rhs.end ()) |
630 | continue; |
631 | |
632 | // found an include edge. |
633 | IncludesGraph::iterator target = IncludesGraph::get (data: Include (pp, name)); |
634 | IncludesGraph::iterator source = IncludesGraph::get (data: Include (p, *A)); |
635 | |
636 | source->insertEdge (other: target); |
637 | |
638 | #ifndef QLALR_NO_DEBUG_INCLUDES |
639 | qerr() << "*** (" << id (p) << ", " << *A << ") includes (" << id (pp) << ", " << *name << ")" << Qt::endl; |
640 | #endif // QLALR_NO_DEBUG_INCLUDES |
641 | } |
642 | } |
643 | } |
644 | } |
645 | } |
646 | |
647 | void Automaton::visitIncludeNode (IncludeNode node) |
648 | { |
649 | if (node->dfn != 0) |
650 | return; // nothing to do |
651 | |
652 | int N = node->dfn = ++_M_includes_dfn; |
653 | _M_includes_stack.push (t: node); |
654 | |
655 | #ifndef QLALR_NO_DEBUG_INCLUDES |
656 | // qerr() << "*** Debug. visit node (" << id (node->data.state) << ", " << node->data.nt << ") N = " << N << Qt::endl; |
657 | #endif |
658 | |
659 | for (IncludesGraph::edge_iterator edge = node->begin (); edge != node->end (); ++edge) |
660 | { |
661 | IncludesGraph::iterator r = *edge; |
662 | |
663 | visitIncludeNode (node: r); |
664 | |
665 | node->dfn = qMin (a: N, b: r->dfn); |
666 | |
667 | #ifndef QLALR_NO_DEBUG_INCLUDES |
668 | qerr() << "*** Merge. follows" ; |
669 | dump (qerr(), node); |
670 | qerr() << " += follows" ; |
671 | dump (qerr(), r); |
672 | qerr() << Qt::endl; |
673 | #endif |
674 | |
675 | NameSet &dst = node->data.state->follows [node->data.nt]; |
676 | NameSet &src = r->data.state->follows [r->data.nt]; |
677 | |
678 | dst.insert (first: src.begin (), last: src.end ()); |
679 | } |
680 | |
681 | if (node->dfn == N) |
682 | { |
683 | IncludesGraph::iterator tos = _M_includes_stack.top (); |
684 | |
685 | do { |
686 | tos = _M_includes_stack.top (); |
687 | _M_includes_stack.pop (); |
688 | tos->dfn = INT_MAX; |
689 | } while (tos != node); |
690 | } |
691 | } |
692 | |
693 | void Automaton::buildLookaheads () |
694 | { |
695 | for (StatePointer p = states.begin (); p != states.end (); ++p) |
696 | { |
697 | for (ItemPointer item = p->closure.begin (); item != p->closure.end (); ++item) |
698 | { |
699 | const auto range = std::as_const(t&: lookbacks).equal_range(akey: item); |
700 | for (auto it = range.first; it != range.second; ++it) |
701 | { |
702 | const Lookback &lookback = *it; |
703 | StatePointer q = lookback.state; |
704 | |
705 | #ifndef QLALR_NO_DEBUG_LOOKAHEADS |
706 | qerr() << "(" << id (p) << ", " << *item->rule << ") lookbacks " ; |
707 | dump (qerr(), lookback); |
708 | qerr() << " with follows (" << id (q) << ", " << lookback.nt << ") = " << q->follows [lookback.nt] << Qt::endl; |
709 | #endif |
710 | |
711 | lookaheads [item].insert (first: q->follows [lookback.nt].begin (), last: q->follows [lookback.nt].end ()); |
712 | } |
713 | } |
714 | |
715 | // propagate the lookahead in the kernel |
716 | ItemPointer k = p->kernel.begin (); |
717 | ItemPointer c = p->closure.begin (); |
718 | |
719 | for (; k != p->kernel.end (); ++k, ++c) |
720 | lookaheads [k] = lookaheads [c]; |
721 | } |
722 | } |
723 | |
724 | void Automaton::buildDefaultReduceActions () |
725 | { |
726 | for (StatePointer state = states.begin (); state != states.end (); ++state) |
727 | { |
728 | ItemPointer def = state->closure.end (); |
729 | int size = -1; |
730 | |
731 | for (ItemPointer item = state->closure.begin (); item != state->closure.end (); ++item) |
732 | { |
733 | if (item->dot != item->end_rhs ()) |
734 | continue; |
735 | |
736 | int la = static_cast<int>(lookaheads.value(key: item).size()); |
737 | if (def == state->closure.end () || la > size) |
738 | { |
739 | def = item; |
740 | size = la; |
741 | } |
742 | } |
743 | |
744 | if (def != state->closure.end ()) |
745 | { |
746 | Q_ASSERT (size >= 0); |
747 | state->defaultReduce = def->rule; |
748 | } |
749 | } |
750 | } |
751 | |
752 | void Automaton::dump (QTextStream &out, IncludeNode incl) |
753 | { |
754 | out << "(" << id (state: incl->data.state) << ", " << incl->data.nt << ")" ; |
755 | } |
756 | |
757 | void Automaton::dump (QTextStream &out, ReadNode rd) |
758 | { |
759 | out << "(" << id (state: rd->data.state) << ", " << rd->data.nt << ")" ; |
760 | } |
761 | |
762 | void Automaton::dump (QTextStream &out, const Lookback &lp) |
763 | { |
764 | out << "(" << id (state: lp.state) << ", " << lp.nt << ")" ; |
765 | } |
766 | |