From 6d748a6714c9ba99139de4f33ff8b720626c72d7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 5 Oct 2015 16:05:19 -0700 Subject: [PATCH] Store parse actions' precedences as ranges, not sets --- project.gyp | 1 + .../parse_conflict_manager_spec.cc | 20 ++++----- .../build_tables/build_parse_table.cc | 42 ++++++++----------- .../build_tables/parse_conflict_manager.cc | 10 ++--- src/compiler/parse_table.cc | 15 ++++--- src/compiler/parse_table.h | 11 +++-- src/compiler/precedence_range.cc | 35 ++++++++++++++++ src/compiler/precedence_range.h | 21 ++++++++++ 8 files changed, 105 insertions(+), 50 deletions(-) create mode 100644 src/compiler/precedence_range.cc create mode 100644 src/compiler/precedence_range.h diff --git a/project.gyp b/project.gyp index d143d45a..c6ae92a1 100644 --- a/project.gyp +++ b/project.gyp @@ -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', diff --git a/spec/compiler/build_tables/parse_conflict_manager_spec.cc b/spec/compiler/build_tables/parse_conflict_manager_spec.cc index f8d58e3a..cbe2d5ca 100644 --- a/spec/compiler/build_tables/parse_conflict_manager_spec.cc +++ b/spec/compiler/build_tables/parse_conflict_manager_spec.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", [&]() { diff --git a/src/compiler/build_tables/build_parse_table.cc b/src/compiler/build_tables/build_parse_table.cc index bd15bda5..ea106fba 100644 --- a/src/compiler/build_tables/build_parse_table.cc +++ b/src/compiler/build_tables/build_parse_table.cc @@ -233,17 +233,17 @@ class ParseTableBuilder { return false; } - set precedence_values_for_item_set(const ParseItemSet &item_set) { - set 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 &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; } diff --git a/src/compiler/build_tables/parse_conflict_manager.cc b/src/compiler/build_tables/parse_conflict_manager.cc index 5e18dd78..1ee3323c 100644 --- a/src/compiler/build_tables/parse_conflict_manager.cc +++ b/src/compiler/build_tables/parse_conflict_manager.cc @@ -28,9 +28,9 @@ pair 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 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) { diff --git a/src/compiler/parse_table.cc b/src/compiler/parse_table.cc index 80698224..730472b4 100644 --- a/src/compiler/parse_table.cc +++ b/src/compiler/parse_table.cc @@ -1,4 +1,5 @@ #include "compiler/parse_table.h" +#include "compiler/precedence_range.h" #include 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 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 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 { diff --git a/src/compiler/parse_table.h b/src/compiler/parse_table.h index d00a9bc6..e546c929 100644 --- a/src/compiler/parse_table.h +++ b/src/compiler/parse_table.h @@ -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 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 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 precedence_values; + PrecedenceRange precedence_range; Associativity associativity; int production_id; }; @@ -62,6 +62,9 @@ struct hash { hash()(action.symbol) ^ hash()(action.state_index) ^ hash()(action.consumed_symbol_count) ^ + hash()(action.associativity) ^ + hash()(action.precedence_range.min) ^ + hash()(action.precedence_range.max) ^ hash()(action.production_id)); } }; diff --git a/src/compiler/precedence_range.cc b/src/compiler/precedence_range.cc new file mode 100644 index 00000000..c5a228bc --- /dev/null +++ b/src/compiler/precedence_range.cc @@ -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 diff --git a/src/compiler/precedence_range.h b/src/compiler/precedence_range.h new file mode 100644 index 00000000..690fc8a5 --- /dev/null +++ b/src/compiler/precedence_range.h @@ -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_