Store parse actions' precedences as ranges, not sets
This commit is contained in:
parent
ef2acf9496
commit
6d748a6714
8 changed files with 105 additions and 50 deletions
|
|
@ -28,6 +28,7 @@
|
|||
'src/compiler/grammar.cc',
|
||||
'src/compiler/lex_table.cc',
|
||||
'src/compiler/parse_table.cc',
|
||||
'src/compiler/precedence_range.cc',
|
||||
'src/compiler/prepare_grammar/expand_repeats.cc',
|
||||
'src/compiler/prepare_grammar/expand_tokens.cc',
|
||||
'src/compiler/prepare_grammar/extract_choices.cc',
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ describe("ParseConflictManager", []() {
|
|||
describe(".resolve", [&]() {
|
||||
describe("errors", [&]() {
|
||||
ParseAction error = ParseAction::Error();
|
||||
ParseAction non_error = ParseAction::Shift(2, { 0 });
|
||||
ParseAction non_error = ParseAction::Shift(2, { 0, 0 });
|
||||
|
||||
it("favors non-errors and reports no conflict", [&]() {
|
||||
result = conflict_manager->resolve(non_error, error, sym1);
|
||||
|
|
@ -49,7 +49,7 @@ describe("ParseConflictManager", []() {
|
|||
|
||||
describe("shift-extra actions", [&]() {
|
||||
ParseAction shift_extra = ParseAction::Error();
|
||||
ParseAction other = ParseAction::Shift(2, { 0 });
|
||||
ParseAction other = ParseAction::Shift(2, { 0, 0 });
|
||||
|
||||
it("favors other actions over shift-extra actions", [&]() {
|
||||
result = conflict_manager->resolve(other, shift_extra, sym1);
|
||||
|
|
@ -64,8 +64,8 @@ describe("ParseConflictManager", []() {
|
|||
|
||||
describe("shift/reduce conflicts", [&]() {
|
||||
describe("when the shift has higher precedence", [&]() {
|
||||
ParseAction shift = ParseAction::Shift(2, { 3 });
|
||||
ParseAction reduce = ParseAction::Reduce(sym2, 1, 1, AssociativityLeft, 0);
|
||||
ParseAction shift = ParseAction::Shift(2, {3, 4});
|
||||
ParseAction reduce = ParseAction::Reduce(sym2, 1, 2, AssociativityLeft, 0);
|
||||
|
||||
it("favors the shift and reports the conflict as resolved", [&]() {
|
||||
result = conflict_manager->resolve(shift, reduce, sym1);
|
||||
|
|
@ -79,9 +79,7 @@ describe("ParseConflictManager", []() {
|
|||
});
|
||||
|
||||
describe("when the reduce has higher precedence", [&]() {
|
||||
ParseAction shift = ParseAction::Shift(2, {
|
||||
{1, AssociativityLeft}
|
||||
});
|
||||
ParseAction shift = ParseAction::Shift(2, {1, 2});
|
||||
ParseAction reduce = ParseAction::Reduce(sym2, 1, 3, AssociativityLeft, 0);
|
||||
|
||||
it("favors the reduce and reports the conflict as resolved", [&]() {
|
||||
|
|
@ -96,7 +94,7 @@ describe("ParseConflictManager", []() {
|
|||
});
|
||||
|
||||
describe("when the precedences are equal and the reduce's rule is left associative", [&]() {
|
||||
ParseAction shift = ParseAction::Shift(2, { 0 });
|
||||
ParseAction shift = ParseAction::Shift(2, { 0, 0 });
|
||||
ParseAction reduce = ParseAction::Reduce(sym2, 1, 0, AssociativityLeft, 0);
|
||||
|
||||
it("favors the reduce and reports the conflict as resolved", [&]() {
|
||||
|
|
@ -111,7 +109,7 @@ describe("ParseConflictManager", []() {
|
|||
});
|
||||
|
||||
describe("when the precedences are equal and the reduce's rule is right-associative", [&]() {
|
||||
ParseAction shift = ParseAction::Shift(2, { 0 });
|
||||
ParseAction shift = ParseAction::Shift(2, { 0, 0 });
|
||||
ParseAction reduce = ParseAction::Reduce(sym2, 1, 0, AssociativityRight, 0);
|
||||
|
||||
it("favors the shift, and reports the conflict as resolved", [&]() {
|
||||
|
|
@ -127,7 +125,7 @@ describe("ParseConflictManager", []() {
|
|||
|
||||
describe("when the precedences are equal and the reduce's rule has no associativity", [&]() {
|
||||
it("reports an unresolved conflict", [&]() {
|
||||
ParseAction shift = ParseAction::Shift(2, { 0 });
|
||||
ParseAction shift = ParseAction::Shift(2, { 0, 0 });
|
||||
ParseAction reduce = ParseAction::Reduce(Symbol(2), 1, 0, AssociativityNone, 0);
|
||||
|
||||
result = conflict_manager->resolve(reduce, shift, lookahead_sym);
|
||||
|
|
@ -140,7 +138,7 @@ describe("ParseConflictManager", []() {
|
|||
});
|
||||
|
||||
describe("when the shift has conflicting precedences compared to the reduce", [&]() {
|
||||
ParseAction shift = ParseAction::Shift(2, { 0, 1, 3 });
|
||||
ParseAction shift = ParseAction::Shift(2, { 1, 3 });
|
||||
ParseAction reduce = ParseAction::Reduce(Symbol(2), 1, 2, AssociativityLeft, 0);
|
||||
|
||||
it("returns false and reports an unresolved conflict", [&]() {
|
||||
|
|
|
|||
|
|
@ -233,17 +233,17 @@ class ParseTableBuilder {
|
|||
return false;
|
||||
}
|
||||
|
||||
set<int> precedence_values_for_item_set(const ParseItemSet &item_set) {
|
||||
set<int> result;
|
||||
PrecedenceRange precedence_values_for_item_set(const ParseItemSet &item_set) {
|
||||
PrecedenceRange result;
|
||||
for (const auto &pair : item_set.entries) {
|
||||
const ParseItem &item = pair.first;
|
||||
const Production &production =
|
||||
grammar.productions(item.lhs())[item.production_index];
|
||||
if (item.step_index > 0) {
|
||||
if (item.step_index < production.size())
|
||||
result.insert(production[item.step_index].precedence);
|
||||
result.add(production[item.step_index].precedence);
|
||||
else
|
||||
result.insert(production[item.step_index - 1].precedence);
|
||||
result.add(production[item.step_index - 1].precedence);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
@ -305,20 +305,10 @@ class ParseTableBuilder {
|
|||
|
||||
string action_description(const ParseAction &action,
|
||||
const set<Symbol> &goal_symbols) const {
|
||||
string symbols_string;
|
||||
bool started = false;
|
||||
for (const auto &symbol : goal_symbols) {
|
||||
if (started)
|
||||
symbols_string += ", ";
|
||||
symbols_string += symbol_name(symbol);
|
||||
started = true;
|
||||
}
|
||||
|
||||
string result;
|
||||
|
||||
switch (action.type) {
|
||||
case ParseActionTypeReduce: {
|
||||
result = "Reduce";
|
||||
result += "Reduce";
|
||||
for (const ProductionStep &step :
|
||||
grammar.productions(action.symbol)[action.production_id])
|
||||
result += " " + symbol_name(step.symbol);
|
||||
|
|
@ -327,21 +317,25 @@ class ParseTableBuilder {
|
|||
}
|
||||
|
||||
case ParseActionTypeShift: {
|
||||
result = "Shift";
|
||||
result += "Shift ";
|
||||
bool started = false;
|
||||
for (const auto &symbol : goal_symbols) {
|
||||
if (started)
|
||||
result += ", ";
|
||||
started = true;
|
||||
result += symbol_name(symbol);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return "";
|
||||
break;
|
||||
}
|
||||
|
||||
if (action.precedence_values.size() > 1) {
|
||||
result += " (Precedences " + to_string(*action.precedence_values.begin()) +
|
||||
", " + to_string(*action.precedence_values.rbegin()) + ")";
|
||||
} else {
|
||||
result +=
|
||||
" (Precedence " + to_string(*action.precedence_values.begin()) + ")";
|
||||
}
|
||||
result += (action.precedence_range.min == action.precedence_range.max)
|
||||
? " (Precedence " + to_string(action.precedence_range.min) + ")"
|
||||
: " (Precedences " + to_string(action.precedence_range.min) +
|
||||
", " + to_string(action.precedence_range.max) + ")";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ pair<bool, ConflictType> ParseConflictManager::resolve(
|
|||
|
||||
case ParseActionTypeShift:
|
||||
if (new_action.type == ParseActionTypeReduce) {
|
||||
int min_precedence = *old_action.precedence_values.begin();
|
||||
int max_precedence = *old_action.precedence_values.rbegin();
|
||||
int new_precedence = *new_action.precedence_values.rbegin();
|
||||
int min_precedence = old_action.precedence_range.min;
|
||||
int max_precedence = old_action.precedence_range.max;
|
||||
int new_precedence = new_action.precedence_range.max;
|
||||
if (new_precedence < min_precedence ||
|
||||
(new_precedence == min_precedence &&
|
||||
min_precedence < max_precedence)) {
|
||||
|
|
@ -55,8 +55,8 @@ pair<bool, ConflictType> ParseConflictManager::resolve(
|
|||
|
||||
case ParseActionTypeReduce:
|
||||
if (new_action.type == ParseActionTypeReduce) {
|
||||
int old_precedence = *old_action.precedence_values.begin();
|
||||
int new_precedence = *new_action.precedence_values.begin();
|
||||
int old_precedence = old_action.precedence_range.min;
|
||||
int new_precedence = new_action.precedence_range.min;
|
||||
if (new_precedence > old_precedence) {
|
||||
return { true, ConflictTypeResolved };
|
||||
} else if (new_precedence < old_precedence) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "compiler/parse_table.h"
|
||||
#include "compiler/precedence_range.h"
|
||||
#include <string>
|
||||
|
||||
namespace tree_sitter {
|
||||
|
|
@ -12,13 +13,13 @@ using rules::Symbol;
|
|||
|
||||
ParseAction::ParseAction(ParseActionType type, ParseStateId state_index,
|
||||
Symbol symbol, size_t consumed_symbol_count,
|
||||
set<int> precedence_values,
|
||||
PrecedenceRange precedence_range,
|
||||
Associativity associativity, int production_id)
|
||||
: type(type),
|
||||
symbol(symbol),
|
||||
state_index(state_index),
|
||||
consumed_symbol_count(consumed_symbol_count),
|
||||
precedence_values(precedence_values),
|
||||
precedence_range(precedence_range),
|
||||
associativity(associativity),
|
||||
production_id(production_id) {}
|
||||
|
||||
|
|
@ -40,9 +41,9 @@ ParseAction ParseAction::Accept() {
|
|||
}
|
||||
|
||||
ParseAction ParseAction::Shift(ParseStateId state_index,
|
||||
set<int> precedence_values) {
|
||||
PrecedenceRange precedence_range) {
|
||||
return ParseAction(ParseActionTypeShift, state_index, Symbol(-1), 0,
|
||||
precedence_values, AssociativityNone, -1);
|
||||
precedence_range, AssociativityNone, -1);
|
||||
}
|
||||
|
||||
ParseAction ParseAction::ShiftExtra() {
|
||||
|
|
@ -62,7 +63,7 @@ ParseAction ParseAction::Reduce(Symbol symbol, size_t consumed_symbol_count,
|
|||
int precedence, Associativity associativity,
|
||||
unsigned int production_id) {
|
||||
return ParseAction(ParseActionTypeReduce, 0, symbol, consumed_symbol_count,
|
||||
{ precedence }, associativity, production_id);
|
||||
{ precedence, precedence }, associativity, production_id);
|
||||
}
|
||||
|
||||
bool ParseAction::operator==(const ParseAction &other) const {
|
||||
|
|
@ -71,7 +72,9 @@ bool ParseAction::operator==(const ParseAction &other) const {
|
|||
bool state_indices_eq = state_index == other.state_index;
|
||||
bool consumed_symbol_counts_eq =
|
||||
consumed_symbol_count == other.consumed_symbol_count;
|
||||
return types_eq && symbols_eq && state_indices_eq && consumed_symbol_counts_eq;
|
||||
bool precedences_eq = precedence_range == other.precedence_range;
|
||||
return types_eq && symbols_eq && state_indices_eq &&
|
||||
consumed_symbol_counts_eq && precedences_eq;
|
||||
}
|
||||
|
||||
bool ParseAction::operator<(const ParseAction &other) const {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "compiler/lex_table.h"
|
||||
#include "compiler/rules/symbol.h"
|
||||
#include "compiler/rules/metadata.h"
|
||||
#include "compiler/precedence_range.h"
|
||||
|
||||
namespace tree_sitter {
|
||||
|
||||
|
|
@ -26,14 +27,13 @@ typedef enum {
|
|||
class ParseAction {
|
||||
ParseAction(ParseActionType type, ParseStateId state_index,
|
||||
rules::Symbol symbol, size_t consumed_symbol_count,
|
||||
std::set<int> precedence_values, Associativity, int production_id);
|
||||
PrecedenceRange range, Associativity, int production_id);
|
||||
|
||||
public:
|
||||
ParseAction();
|
||||
static ParseAction Accept();
|
||||
static ParseAction Error();
|
||||
static ParseAction Shift(ParseStateId state_index,
|
||||
std::set<int> precedence_values);
|
||||
static ParseAction Shift(ParseStateId state_index, PrecedenceRange precedence);
|
||||
static ParseAction Reduce(rules::Symbol symbol, size_t consumed_symbol_count,
|
||||
int precedence, Associativity,
|
||||
unsigned int production_id);
|
||||
|
|
@ -46,7 +46,7 @@ class ParseAction {
|
|||
rules::Symbol symbol;
|
||||
ParseStateId state_index;
|
||||
size_t consumed_symbol_count;
|
||||
std::set<int> precedence_values;
|
||||
PrecedenceRange precedence_range;
|
||||
Associativity associativity;
|
||||
int production_id;
|
||||
};
|
||||
|
|
@ -62,6 +62,9 @@ struct hash<tree_sitter::ParseAction> {
|
|||
hash<tree_sitter::rules::Symbol>()(action.symbol) ^
|
||||
hash<size_t>()(action.state_index) ^
|
||||
hash<size_t>()(action.consumed_symbol_count) ^
|
||||
hash<int>()(action.associativity) ^
|
||||
hash<int>()(action.precedence_range.min) ^
|
||||
hash<int>()(action.precedence_range.max) ^
|
||||
hash<size_t>()(action.production_id));
|
||||
}
|
||||
};
|
||||
|
|
|
|||
35
src/compiler/precedence_range.cc
Normal file
35
src/compiler/precedence_range.cc
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#include "compiler/precedence_range.h"
|
||||
|
||||
namespace tree_sitter {
|
||||
|
||||
PrecedenceRange::PrecedenceRange() : min(0), max(0), empty(true) {}
|
||||
|
||||
PrecedenceRange::PrecedenceRange(int min, int max)
|
||||
: min(min), max(max), empty(false) {}
|
||||
|
||||
void PrecedenceRange::add(int new_value) {
|
||||
if (empty) {
|
||||
min = new_value;
|
||||
max = new_value;
|
||||
empty = false;
|
||||
} else {
|
||||
if (new_value < min)
|
||||
min = new_value;
|
||||
else if (new_value > max)
|
||||
max = new_value;
|
||||
}
|
||||
}
|
||||
|
||||
bool PrecedenceRange::operator<(const PrecedenceRange &other) const {
|
||||
if (empty)
|
||||
return !other.empty;
|
||||
else
|
||||
return (min < other.min && max <= other.min) ||
|
||||
(min == other.min && max < other.max);
|
||||
}
|
||||
|
||||
bool PrecedenceRange::operator==(const PrecedenceRange &other) const {
|
||||
return (empty == other.empty) && (min == other.min) && (max == other.max);
|
||||
}
|
||||
|
||||
} // namespace tree_sitter
|
||||
21
src/compiler/precedence_range.h
Normal file
21
src/compiler/precedence_range.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef COMPILER_PRECEDENCE_RANGE_H_
|
||||
#define COMPILER_PRECEDENCE_RANGE_H_
|
||||
|
||||
namespace tree_sitter {
|
||||
|
||||
struct PrecedenceRange {
|
||||
PrecedenceRange();
|
||||
PrecedenceRange(int min, int max);
|
||||
|
||||
void add(int);
|
||||
bool operator==(const PrecedenceRange &other) const;
|
||||
bool operator<(const PrecedenceRange &other) const;
|
||||
|
||||
int min;
|
||||
int max;
|
||||
bool empty;
|
||||
};
|
||||
|
||||
} // namespace tree_sitter
|
||||
|
||||
#endif // COMPILER_PRECEDENCE_RANGE_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue