When comparing parse items, ignore consumed part of their productions
This speeds up parser generation by increasing the likelihood that we'll recognize parse item sets as equivalent in advance, rather than having to merge their states after the fact.
This commit is contained in:
parent
a199b217f3
commit
26a25278cd
3 changed files with 66 additions and 16 deletions
|
|
@ -24,20 +24,43 @@ ParseItem::ParseItem(const Symbol &lhs, const Production &production,
|
|||
step_index(step_index) {}
|
||||
|
||||
bool ParseItem::operator==(const ParseItem &other) const {
|
||||
return ((variable_index == other.variable_index) &&
|
||||
(step_index == other.step_index) && (production == other.production));
|
||||
if (step_index != other.step_index) return false;
|
||||
if (variable_index != other.variable_index) return false;
|
||||
if (production->size() != other.production->size()) return false;
|
||||
if (is_done()) {
|
||||
if (!production->empty()) {
|
||||
if (production->back().precedence != other.production->back().precedence) return false;
|
||||
if (production->back().associativity != other.production->back().associativity) return false;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = step_index, n = production->size(); i < n; i++) {
|
||||
if (production->at(i) != other.production->at(i)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseItem::operator<(const ParseItem &other) const {
|
||||
if (step_index < other.step_index)
|
||||
return true;
|
||||
if (step_index > other.step_index)
|
||||
return false;
|
||||
if (variable_index < other.variable_index)
|
||||
return true;
|
||||
if (variable_index > other.variable_index)
|
||||
return false;
|
||||
return production < other.production;
|
||||
if (step_index < other.step_index) return true;
|
||||
if (other.step_index < step_index) return false;
|
||||
if (variable_index < other.variable_index) return true;
|
||||
if (other.variable_index < variable_index) return false;
|
||||
if (production->size() < other.production->size()) return true;
|
||||
if (other.production->size() < production->size()) return false;
|
||||
if (is_done()) {
|
||||
if (!production->empty()) {
|
||||
if (production->back().precedence < other.production->back().precedence) return true;
|
||||
if (other.production->back().precedence < production->back().precedence) return false;
|
||||
if (production->back().associativity < other.production->back().associativity) return true;
|
||||
if (other.production->back().associativity < production->back().associativity) return false;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = step_index, n = production->size(); i < n; i++) {
|
||||
if (production->at(i) < other.production->at(i)) return true;
|
||||
if (other.production->at(i) < production->at(i)) return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Symbol ParseItem::lhs() const {
|
||||
|
|
@ -128,7 +151,21 @@ struct hash<ParseItem> {
|
|||
size_t result = 0;
|
||||
hash_combine(&result, item.variable_index);
|
||||
hash_combine(&result, item.step_index);
|
||||
hash_combine(&result, item.production);
|
||||
hash_combine(&result, item.production->dynamic_precedence);
|
||||
hash_combine(&result, item.production->size());
|
||||
if (item.is_done()) {
|
||||
if (!item.production->empty()) {
|
||||
hash_combine(&result, item.production->back().precedence);
|
||||
hash_combine(&result, item.production->back().associativity);
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0, n = item.production->size(); i < n; i++) {
|
||||
auto &step = item.production->at(i);
|
||||
hash_combine(&result, step.symbol);
|
||||
hash_combine(&result, step.precedence);
|
||||
hash_combine(&result, step.associativity);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,6 +16,18 @@ struct ProductionStep {
|
|||
associativity == other.associativity;
|
||||
}
|
||||
|
||||
inline bool operator!=(const ProductionStep &other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
inline bool operator<(const ProductionStep &other) const {
|
||||
if (symbol < other.symbol) return true;
|
||||
if (other.symbol < symbol) return false;
|
||||
if (precedence < other.precedence) return true;
|
||||
if (other.precedence < precedence) return false;
|
||||
return associativity < other.associativity;
|
||||
}
|
||||
|
||||
rules::Symbol symbol;
|
||||
int precedence;
|
||||
rules::Associativity associativity;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "test_helper.h"
|
||||
#include "helpers/stream_methods.h"
|
||||
#include "compiler/syntax_grammar.h"
|
||||
#include "compiler/lexical_grammar.h"
|
||||
#include "compiler/build_tables/parse_item_set_builder.h"
|
||||
|
|
@ -53,7 +54,7 @@ describe("ParseItemSetBuilder", []() {
|
|||
|
||||
ParseItemSet item_set({
|
||||
{
|
||||
ParseItem(Symbol::non_terminal(0), production(0, 0), 0),
|
||||
ParseItem(rules::START(), production(0, 0), 0),
|
||||
LookaheadSet({ Symbol::terminal(10) }),
|
||||
}
|
||||
});
|
||||
|
|
@ -63,7 +64,7 @@ describe("ParseItemSetBuilder", []() {
|
|||
|
||||
AssertThat(item_set, Equals(ParseItemSet({
|
||||
{
|
||||
ParseItem(Symbol::non_terminal(0), production(0, 0), 0),
|
||||
ParseItem(rules::START(), production(0, 0), 0),
|
||||
LookaheadSet({ Symbol::terminal(10) })
|
||||
},
|
||||
{
|
||||
|
|
@ -104,7 +105,7 @@ describe("ParseItemSetBuilder", []() {
|
|||
|
||||
ParseItemSet item_set({
|
||||
{
|
||||
ParseItem(Symbol::non_terminal(0), production(0, 0), 0),
|
||||
ParseItem(rules::START(), production(0, 0), 0),
|
||||
LookaheadSet({ Symbol::terminal(10) }),
|
||||
}
|
||||
});
|
||||
|
|
@ -114,7 +115,7 @@ describe("ParseItemSetBuilder", []() {
|
|||
|
||||
AssertThat(item_set, Equals(ParseItemSet({
|
||||
{
|
||||
ParseItem(Symbol::non_terminal(0), production(0, 0), 0),
|
||||
ParseItem(rules::START(), production(0, 0), 0),
|
||||
LookaheadSet({ Symbol::terminal(10) })
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue