Coverage Report

Created: 2026-10-17 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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