Store parse actions' precedences as ranges, not sets

This commit is contained in:
Max Brunsfeld 2015-10-05 16:05:19 -07:00
parent ef2acf9496
commit 6d748a6714
8 changed files with 105 additions and 50 deletions

View file

@ -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',

View file

@ -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", [&]() {

View file

@ -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;
}

View file

@ -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) {

View file

@ -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 {

View file

@ -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));
}
};

View 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

View 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_