• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rieske / trans / 29766085819

20 Jul 2026 06:02PM UTC coverage: 90.582% (+0.05%) from 90.53%
29766085819

push

github

web-flow
Merge pull request #47 from rieske/perf/parser/1-lookahead-model

perf(parser): dense lookahead bitsets and terminal maps

146 of 147 new or added lines in 7 files covered. (99.32%)

1 existing line in 1 file now uncovered.

5155 of 5691 relevant lines covered (90.58%)

340886.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.97
/src/parser/Grammar.cpp
1
#include "Grammar.h"
2

3
#include <algorithm>
4
#include <sstream>
5

6
#include "scanner/Token.h"
7

8
namespace parser {
9

10
Grammar::Grammar(std::map<std::string, int> symbolIDs,
430✔
11
        std::vector<int> terminals,
12
        std::vector<int> nonterminals,
13
        std::vector<Production> rules):
430✔
14
    symbolIDs{std::move(symbolIDs)},
430✔
15
    nonterminalIDs{std::move(nonterminals)},
430✔
16
    terminalIDs{std::move(terminals)},
430✔
17
    startSymbol { -1 },
430✔
18
    endSymbol { 0 },
430✔
19
    topRule{startSymbol, {nonterminalIDs[0]}, static_cast<int>(rules.size())}
1,290✔
20
{
21
    int maxId = 0;
430✔
22
    for (const auto& entry : this->symbolIDs) {
60,908✔
23
        maxId = std::max(maxId, entry.second);
60,478✔
24
    }
25
    endSymbol = maxId + 1;
430✔
26

27
    this->symbolIDs.insert({"<__start__>", startSymbol});
430✔
28
    this->symbolIDs.insert({scanner::Token::END, endSymbol});
430✔
29

30
    firstTerminalId = this->terminalIDs.empty() ? endSymbol : this->terminalIDs[0];
430✔
31
    this->terminalIDs.push_back(endSymbol);
430✔
32

33
    rules.push_back(topRule);
430✔
34
    for (const auto& production: rules) {
95,228✔
35
        rulesByDefiningSymbol.insert({production.getDefiningSymbol(), {}}).first->second.push_back(production);
94,798✔
36
        rulesById.insert({production.getId(), production});
94,798✔
37
    }
38

39
    idToTerminalBit_.fill(-1);
430✔
40
    terminalBitToId_.reserve(this->terminalIDs.size());
430✔
41
    for (const int terminalId : this->terminalIDs) {
34,796✔
42
        if (terminalId < 0 || static_cast<std::size_t>(terminalId) >= idToTerminalBit_.size()) {
68,740✔
43
            throw std::runtime_error { "terminal symbol id out of mapping range" };
2✔
44
        }
45
        if (idToTerminalBit_[static_cast<std::size_t>(terminalId)] >= 0) {
34,368✔
46
            continue;
2✔
47
        }
48
        if (terminalBitToId_.size() >= LOOKAHEAD_BITSET_SIZE) {
34,366✔
49
            throw std::runtime_error { "too many terminals for lookahead bitset" };
2✔
50
        }
51
        idToTerminalBit_[static_cast<std::size_t>(terminalId)] = static_cast<int>(terminalBitToId_.size());
34,364✔
52
        terminalBitToId_.push_back(terminalId);
34,364✔
53
    }
54
}
454✔
55

56
std::size_t Grammar::ruleCount() const {
402✔
57
    return rulesById.size();
402✔
58
}
59

60
const Production& Grammar::getTopRule() const {
1,630✔
61
    return topRule;
1,630✔
62
}
63

64
const Production& Grammar::getRuleById(int index) const {
1,967,616✔
65
    return rulesById.at(index);
1,967,616✔
66
}
67

68
const std::vector<Production>& Grammar::getProductionsOfSymbol(int symbolId) const {
501,252✔
69
    return rulesByDefiningSymbol.at(symbolId);
501,252✔
70
}
71

72
const std::vector<int>& Grammar::getTerminalIDs() const {
294,482✔
73
    return terminalIDs;
294,482✔
74
}
75

76
const std::vector<int>& Grammar::getNonterminalIDs() const {
3,256✔
77
    return nonterminalIDs;
3,256✔
78
}
79

80
int Grammar::getStartSymbol() const {
6✔
81
    return startSymbol;
6✔
82
}
83

84
int Grammar::getEndSymbol() const {
24✔
85
    return endSymbol;
24✔
86
}
87

88
std::string Grammar::getSymbolById(int symbolId) const {
990,176✔
89
    auto symbolIt = std::find_if(symbolIDs.begin(), symbolIDs.end(), [&](const std::pair<std::string, int>& pair) {
990,176✔
90
        return pair.second == symbolId;
56,588,748✔
91
    });
92
    return symbolIt->first;
1,980,352✔
93
}
94

95
int Grammar::symbolId(std::string definition) const {
173,472✔
96
    return symbolIDs.at(definition);
173,472✔
97
}
98

99
bool Grammar::isTerminal(int symbolId) const {
960,030✔
100
    return symbolId >= firstTerminalId;
960,030✔
101
}
102

103
int Grammar::terminalBit(int symbolId) const {
1,756✔
104
    if (symbolId < 0 || static_cast<std::size_t>(symbolId) >= idToTerminalBit_.size()) {
3,508✔
105
        throw std::out_of_range { "symbol id out of range for terminal bit map" };
6✔
106
    }
107
    const int bit = idToTerminalBit_[static_cast<std::size_t>(symbolId)];
1,750✔
108
    if (bit < 0) {
1,750✔
109
        throw std::out_of_range { "symbol is not a terminal" };
2✔
110
    }
111
    return bit;
1,748✔
112
}
113

114
int Grammar::terminalIdFromBit(std::size_t bit) const {
4✔
115
    return terminalBitToId_.at(bit);
4✔
116
}
117

118
LookaheadSet Grammar::toLookaheadBits(const std::vector<int>& symbolIds) const {
24✔
119
    LookaheadSet bits;
24✔
120
    for (const int id : symbolIds) {
48✔
121
        bits.set(static_cast<std::size_t>(terminalBit(id)));
24✔
122
    }
123
    return bits;
24✔
124
}
125

126
std::vector<int> Grammar::toTerminalIds(const LookaheadSet& bits) const {
37,980✔
127
    std::vector<int> ids;
37,980✔
128
    ids.reserve(bits.count());
37,980✔
129
    for (std::size_t bit = 0; bit < terminalBitToId_.size(); ++bit) {
2,666,852✔
130
        if (bits.test(bit)) {
2,628,872✔
131
            ids.push_back(terminalBitToId_[bit]);
739,508✔
132
        }
133
    }
134
    return ids;
37,980✔
NEW
135
}
×
136

137
std::string Grammar::str(int symbolId) const {
888,538✔
138
    return getSymbolById(symbolId);
888,538✔
139
}
140

141
std::string Grammar::str(const Production& production) const {
32✔
142
    std::stringstream s;
32✔
143
    s << str(production.getDefiningSymbol()) << " ::= ";
32✔
144
    for (const auto& symbol: production) {
88✔
145
        s << str(symbol) << " ";
56✔
146
    }
147
    auto productionStr = s.str();
32✔
148
    return productionStr.substr(0, productionStr.length()-1);
64✔
149
}
32✔
150

151
std::ostream& operator<<(std::ostream& out, const Grammar& grammar) {
396✔
152
    out << "\nTerminals:\n";
396✔
153
    for (auto& terminal : grammar.getTerminalIDs()) {
34,008✔
154
        out << terminal << ":" << grammar.str(terminal) << "\n";
33,612✔
155
    }
156
    out << "\nNonterminals:\n";
396✔
157
    for (auto& nonterminal : grammar.getNonterminalIDs()) {
26,572✔
158
        out << nonterminal << ":" << grammar.str(nonterminal) << "\n";
26,176✔
159
    }
160
    out << "\nRules total: " << grammar.ruleCount() << "\n";
396✔
161
    return out;
396✔
162
}
163

164
} // namespace parser
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc