From 9959fe35b0b746687fb46c7d32c063b9729d146e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 13 Oct 2015 11:23:02 -0700 Subject: [PATCH] Allow associativity to be specified in rules w/o precedence --- include/tree_sitter/compiler.h | 11 ++++----- .../prepare_grammar/flatten_grammar_spec.cc | 10 ++++---- spec/fixtures/grammars/c.cc | 4 ++-- spec/fixtures/grammars/helpers.cc | 2 +- spec/fixtures/grammars/javascript.cc | 24 +++++++++---------- src/compiler/associativity.h | 0 .../build_tables/build_parse_table.cc | 4 ++-- .../build_tables/get_completion_status.cc | 8 +++---- .../build_tables/get_completion_status.h | 3 ++- .../build_tables/parse_conflict_manager.cc | 4 ++-- src/compiler/parse_table.cc | 9 +++---- src/compiler/parse_table.h | 6 ++--- .../prepare_grammar/flatten_grammar.cc | 8 ++++--- src/compiler/rules/metadata.h | 6 +++++ src/compiler/rules/rules.cc | 19 ++++++++++++--- src/compiler/syntax_grammar.cc | 9 +++---- src/compiler/syntax_grammar.h | 7 +++--- 17 files changed, 78 insertions(+), 56 deletions(-) create mode 100644 src/compiler/associativity.h diff --git a/include/tree_sitter/compiler.h b/include/tree_sitter/compiler.h index dbbd3298..333b3249 100644 --- a/include/tree_sitter/compiler.h +++ b/include/tree_sitter/compiler.h @@ -11,12 +11,6 @@ namespace tree_sitter { class Rule; typedef std::shared_ptr rule_ptr; -enum Associativity { - AssociativityNone, - AssociativityLeft, - AssociativityRight, -}; - rule_ptr blank(); rule_ptr choice(const std::vector &); rule_ptr repeat(const rule_ptr &); @@ -27,7 +21,10 @@ rule_ptr pattern(const std::string &); rule_ptr str(const std::string &); rule_ptr err(const rule_ptr &); rule_ptr prec(int precedence, const rule_ptr &); -rule_ptr prec(int precedence, const rule_ptr &, Associativity); +rule_ptr prec_left(const rule_ptr &); +rule_ptr prec_left(int precedence, const rule_ptr &); +rule_ptr prec_right(const rule_ptr &); +rule_ptr prec_right(int precedence, const rule_ptr &); rule_ptr token(const rule_ptr &rule); class Grammar { diff --git a/spec/compiler/prepare_grammar/flatten_grammar_spec.cc b/spec/compiler/prepare_grammar/flatten_grammar_spec.cc index 44f37b3a..780d16f2 100644 --- a/spec/compiler/prepare_grammar/flatten_grammar_spec.cc +++ b/spec/compiler/prepare_grammar/flatten_grammar_spec.cc @@ -32,13 +32,13 @@ describe("flatten_grammar", []() { // When multiple precedence values are nested, the inner precedence wins. Variable("variable1", VariableTypeNamed, seq({ i_sym(1), - prec(101, seq({ + prec_left(101, seq({ i_sym(2), choice({ - prec(102, seq({ + prec_right(102, seq({ i_sym(3), i_sym(4) - }), AssociativityRight), + })), i_sym(5), }), i_sym(6), @@ -49,11 +49,11 @@ describe("flatten_grammar", []() { // When a precedence is applied to the end of a rule, its value is assigned // to the last step of the corresponding production. Variable("variable2", VariableTypeHidden, seq({ - prec(102, seq({ + prec_left(102, seq({ i_sym(1), i_sym(2), })), - prec(103, seq({ + prec_left(103, seq({ i_sym(3), i_sym(4), })), diff --git a/spec/fixtures/grammars/c.cc b/spec/fixtures/grammars/c.cc index 36b3e3d5..c75ade67 100644 --- a/spec/fixtures/grammars/c.cc +++ b/spec/fixtures/grammars/c.cc @@ -137,8 +137,8 @@ extern const Grammar c = Grammar({ sym("number") }) }, { "math_expression", choice({ - prec(1, seq({ sym("expression"), str("+"), sym("expression") })), - prec(2, seq({ sym("expression"), str("*"), sym("expression") })) }) }, + prec_left(1, seq({ sym("expression"), str("+"), sym("expression") })), + prec_left(2, seq({ sym("expression"), str("*"), sym("expression") })) }) }, { "call_expression", prec(3, seq({ sym("expression"), diff --git a/spec/fixtures/grammars/helpers.cc b/spec/fixtures/grammars/helpers.cc index 4dcf061e..5d827a0b 100644 --- a/spec/fixtures/grammars/helpers.cc +++ b/spec/fixtures/grammars/helpers.cc @@ -29,7 +29,7 @@ rule_ptr in_brackets(rule_ptr rule) { } rule_ptr infix_op(std::string op, std::string rule_name, int precedence) { - return prec(precedence, seq({ + return prec_left(precedence, seq({ sym(rule_name), str(op), sym(rule_name) })); diff --git a/spec/fixtures/grammars/javascript.cc b/spec/fixtures/grammars/javascript.cc index e4f9eeeb..5bff9169 100644 --- a/spec/fixtures/grammars/javascript.cc +++ b/spec/fixtures/grammars/javascript.cc @@ -68,13 +68,13 @@ extern const Grammar javascript = Grammar({ { "statement_block", prec(PREC_BLOCK, in_braces(err(repeat(sym("_statement"))))) }, - { "if_statement", prec(0, seq({ + { "if_statement", prec_right(0, seq({ str("if"), sym("_paren_expression"), sym("_statement"), optional(seq({ str("else"), - sym("_statement") })) }), AssociativityRight) }, + sym("_statement") })) })) }, { "switch_statement", seq({ str("switch"), @@ -219,13 +219,13 @@ extern const Grammar javascript = Grammar({ sym("arguments") })) }, { "constructor_call", choice({ - prec(PREC_SHORT_NEW, seq({ + prec_right(PREC_SHORT_NEW, seq({ str("new"), - sym("_expression") }), AssociativityRight), - prec(PREC_MEMBER, seq({ + sym("_expression") })), + prec_right(PREC_MEMBER, seq({ str("new"), sym("_expression"), - sym("arguments") }), AssociativityRight) }) }, + sym("arguments") })) }) }, { "member_access", prec(PREC_MEMBER, seq({ sym("_expression"), @@ -238,28 +238,28 @@ extern const Grammar javascript = Grammar({ err(sym("_expression")), str("]") })) }, - { "assignment", prec(PREC_ASSIGN, seq({ + { "assignment", prec_right(PREC_ASSIGN, seq({ choice({ sym("identifier"), sym("member_access"), sym("subscript_access") }), str("="), - sym("_expression") }), AssociativityRight) }, + sym("_expression") })) }, - { "math_assignment", prec(PREC_ASSIGN, seq({ + { "math_assignment", prec_right(PREC_ASSIGN, seq({ choice({ sym("identifier"), sym("member_access"), sym("subscript_access") }), choice({ str("+="), str("-="), str("*="), str("/=") }), - sym("_expression") }), AssociativityRight) }, + sym("_expression") })) }, - { "ternary", prec(PREC_TERNARY, seq({ + { "ternary", prec_right(PREC_TERNARY, seq({ sym("_expression"), str("?"), sym("_expression"), str(":"), - sym("_expression") }), AssociativityRight) }, + sym("_expression") })) }, { "bool_op", choice({ infix_op("||", "_expression", PREC_OR), diff --git a/src/compiler/associativity.h b/src/compiler/associativity.h new file mode 100644 index 00000000..e69de29b diff --git a/src/compiler/build_tables/build_parse_table.cc b/src/compiler/build_tables/build_parse_table.cc index 266a9e5e..ddaadaf7 100644 --- a/src/compiler/build_tables/build_parse_table.cc +++ b/src/compiler/build_tables/build_parse_table.cc @@ -108,7 +108,7 @@ class ParseTableBuilder { struct CompletionStatus { bool is_done; int precedence; - Associativity associativity; + rules::Associativity associativity; }; CompletionStatus get_completion_status(const ParseItem &item) { @@ -118,7 +118,7 @@ class ParseTableBuilder { const ProductionStep &last_step = production[item.step_index - 1]; return { true, last_step.precedence, last_step.associativity }; } - return { false, 0, AssociativityNone }; + return { false, 0, rules::AssociativityNone }; } void add_reduce_actions(const ParseItemSet &item_set, ParseStateId state_id) { diff --git a/src/compiler/build_tables/get_completion_status.cc b/src/compiler/build_tables/get_completion_status.cc index 0920b943..4cc70c60 100644 --- a/src/compiler/build_tables/get_completion_status.cc +++ b/src/compiler/build_tables/get_completion_status.cc @@ -16,7 +16,7 @@ class GetCompletionStatus : public rules::RuleFn { if (status.is_done) return status; } - return { false, 0, AssociativityNone }; + return { false, 0, rules::AssociativityNone }; } CompletionStatus apply_to(const rules::Metadata *rule) { @@ -24,7 +24,7 @@ class GetCompletionStatus : public rules::RuleFn { if (result.is_done && !result.associativity) { result.precedence = rule->value_for(rules::PRECEDENCE); result.associativity = - (Associativity)(rule->value_for(rules::ASSOCIATIVITY)); + (rules::Associativity)(rule->value_for(rules::ASSOCIATIVITY)); } return result; } @@ -34,7 +34,7 @@ class GetCompletionStatus : public rules::RuleFn { } CompletionStatus apply_to(const rules::Blank *rule) { - return { true, 0, AssociativityNone }; + return { true, 0, rules::AssociativityNone }; } CompletionStatus apply_to(const rules::Seq *rule) { @@ -42,7 +42,7 @@ class GetCompletionStatus : public rules::RuleFn { if (left_status.is_done) return apply(rule->right); else - return { false, 0, AssociativityNone }; + return { false, 0, rules::AssociativityNone }; } }; diff --git a/src/compiler/build_tables/get_completion_status.h b/src/compiler/build_tables/get_completion_status.h index fb341484..237f3eb2 100644 --- a/src/compiler/build_tables/get_completion_status.h +++ b/src/compiler/build_tables/get_completion_status.h @@ -2,6 +2,7 @@ #define COMPILER_BUILD_TABLES_GET_COMPLETION_STATUS_H_ #include "tree_sitter/compiler.h" +#include "compiler/rules/metadata.h" namespace tree_sitter { namespace build_tables { @@ -9,7 +10,7 @@ namespace build_tables { struct CompletionStatus { bool is_done; int precedence; - Associativity associativity; + rules::Associativity associativity; }; CompletionStatus get_completion_status(const rule_ptr &); diff --git a/src/compiler/build_tables/parse_conflict_manager.cc b/src/compiler/build_tables/parse_conflict_manager.cc index 1ee3323c..967f508e 100644 --- a/src/compiler/build_tables/parse_conflict_manager.cc +++ b/src/compiler/build_tables/parse_conflict_manager.cc @@ -41,9 +41,9 @@ pair ParseConflictManager::resolve( return { true, ConflictTypeResolved }; } else if (min_precedence == max_precedence) { switch (new_action.associativity) { - case AssociativityLeft: + case rules::AssociativityLeft: return { true, ConflictTypeResolved }; - case AssociativityRight: + case rules::AssociativityRight: return { false, ConflictTypeResolved }; default: return { false, ConflictTypeUnresolved }; diff --git a/src/compiler/parse_table.cc b/src/compiler/parse_table.cc index 50788d8e..79e53a3f 100644 --- a/src/compiler/parse_table.cc +++ b/src/compiler/parse_table.cc @@ -14,7 +14,7 @@ using rules::Symbol; ParseAction::ParseAction(ParseActionType type, ParseStateId state_index, Symbol symbol, size_t consumed_symbol_count, PrecedenceRange precedence_range, - Associativity associativity, int production_id) + rules::Associativity associativity, int production_id) : type(type), symbol(symbol), state_index(state_index), @@ -28,7 +28,7 @@ ParseAction::ParseAction() symbol(Symbol(-1)), state_index(-1), consumed_symbol_count(0), - associativity(AssociativityNone) {} + associativity(rules::AssociativityNone) {} ParseAction ParseAction::Error() { return ParseAction(); @@ -43,7 +43,7 @@ ParseAction ParseAction::Accept() { ParseAction ParseAction::Shift(ParseStateId state_index, PrecedenceRange precedence_range) { return ParseAction(ParseActionTypeShift, state_index, Symbol(-1), 0, - precedence_range, AssociativityNone, -1); + precedence_range, rules::AssociativityNone, -1); } ParseAction ParseAction::ShiftExtra() { @@ -60,7 +60,8 @@ ParseAction ParseAction::ReduceExtra(Symbol symbol) { } ParseAction ParseAction::Reduce(Symbol symbol, size_t consumed_symbol_count, - int precedence, Associativity associativity, + int precedence, + rules::Associativity associativity, unsigned int production_id) { return ParseAction(ParseActionTypeReduce, 0, symbol, consumed_symbol_count, { precedence, precedence }, associativity, production_id); diff --git a/src/compiler/parse_table.h b/src/compiler/parse_table.h index e546c929..36f11187 100644 --- a/src/compiler/parse_table.h +++ b/src/compiler/parse_table.h @@ -27,7 +27,7 @@ typedef enum { class ParseAction { ParseAction(ParseActionType type, ParseStateId state_index, rules::Symbol symbol, size_t consumed_symbol_count, - PrecedenceRange range, Associativity, int production_id); + PrecedenceRange range, rules::Associativity, int production_id); public: ParseAction(); @@ -35,7 +35,7 @@ class ParseAction { static ParseAction Error(); static ParseAction Shift(ParseStateId state_index, PrecedenceRange precedence); static ParseAction Reduce(rules::Symbol symbol, size_t consumed_symbol_count, - int precedence, Associativity, + int precedence, rules::Associativity, unsigned int production_id); static ParseAction ShiftExtra(); static ParseAction ReduceExtra(rules::Symbol symbol); @@ -47,7 +47,7 @@ class ParseAction { ParseStateId state_index; size_t consumed_symbol_count; PrecedenceRange precedence_range; - Associativity associativity; + rules::Associativity associativity; int production_id; }; diff --git a/src/compiler/prepare_grammar/flatten_grammar.cc b/src/compiler/prepare_grammar/flatten_grammar.cc index 4b6a7f50..d787e31f 100644 --- a/src/compiler/prepare_grammar/flatten_grammar.cc +++ b/src/compiler/prepare_grammar/flatten_grammar.cc @@ -20,7 +20,7 @@ using std::vector; class FlattenRule : public rules::RuleFn { private: vector precedence_stack; - vector associativity_stack; + vector associativity_stack; Production production; void apply_to(const rules::Symbol *sym) { @@ -35,7 +35,8 @@ class FlattenRule : public rules::RuleFn { if (precedence != 0) precedence_stack.push_back(precedence); if (associativity != 0) - associativity_stack.push_back(static_cast(associativity)); + associativity_stack.push_back( + static_cast(associativity)); apply(metadata->rule); @@ -57,7 +58,8 @@ class FlattenRule : public rules::RuleFn { public: FlattenRule() - : precedence_stack({ 0 }), associativity_stack({ AssociativityNone }) {} + : precedence_stack({ 0 }), + associativity_stack({ rules::AssociativityNone }) {} Production flatten(const rule_ptr &rule) { apply(rule); diff --git a/src/compiler/rules/metadata.h b/src/compiler/rules/metadata.h index d93bbc9f..ea4f9ba5 100644 --- a/src/compiler/rules/metadata.h +++ b/src/compiler/rules/metadata.h @@ -9,6 +9,12 @@ namespace tree_sitter { namespace rules { +enum Associativity { + AssociativityNone, + AssociativityLeft, + AssociativityRight, +}; + enum MetadataKey { START_TOKEN, PRECEDENCE, diff --git a/src/compiler/rules/rules.cc b/src/compiler/rules/rules.cc index 93941df6..49bb7ead 100644 --- a/src/compiler/rules/rules.cc +++ b/src/compiler/rules/rules.cc @@ -63,13 +63,26 @@ rule_ptr err(const rule_ptr &rule) { return choice({ rule, rules::ERROR().copy() }); } -rule_ptr prec(int precedence, const rule_ptr &rule, Associativity associativity) { +rule_ptr prec_left(const rule_ptr &rule) { + return metadata(rule, { { rules::ASSOCIATIVITY, rules::AssociativityLeft } }); +} + +rule_ptr prec_left(int precedence, const rule_ptr &rule) { return metadata(rule, { { rules::PRECEDENCE, precedence }, - { rules::ASSOCIATIVITY, associativity } }); + { rules::ASSOCIATIVITY, rules::AssociativityLeft } }); +} + +rule_ptr prec_right(const rule_ptr &rule) { + return metadata(rule, { { rules::ASSOCIATIVITY, rules::AssociativityRight } }); +} + +rule_ptr prec_right(int precedence, const rule_ptr &rule) { + return metadata(rule, { { rules::PRECEDENCE, precedence }, + { rules::ASSOCIATIVITY, rules::AssociativityRight } }); } rule_ptr prec(int precedence, const rule_ptr &rule) { - return prec(precedence, rule, AssociativityLeft); + return metadata(rule, { { rules::PRECEDENCE, precedence } }); } rule_ptr token(const rule_ptr &rule) { diff --git a/src/compiler/syntax_grammar.cc b/src/compiler/syntax_grammar.cc index ac313a29..11681f69 100644 --- a/src/compiler/syntax_grammar.cc +++ b/src/compiler/syntax_grammar.cc @@ -14,11 +14,12 @@ using std::vector; using std::set; static const vector START_PRODUCTIONS_TOKEN_ONLY({ - Production({ ProductionStep(rules::Symbol(0, true), 0, AssociativityNone) }), + Production({ ProductionStep(rules::Symbol(0, true), 0, + rules::AssociativityNone) }), }); static const vector START_PRODUCTIONS({ - Production({ ProductionStep(rules::Symbol(0), 0, AssociativityNone) }), + Production({ ProductionStep(rules::Symbol(0), 0, rules::AssociativityNone) }), }); static const vector NO_PRODUCTIONS({}); @@ -28,14 +29,14 @@ SyntaxVariable::SyntaxVariable(const string &name, VariableType type, : name(name), productions(productions), type(type) {} ProductionStep::ProductionStep(const rules::Symbol &symbol, int precedence, - Associativity associativity) + rules::Associativity associativity) : symbol(symbol), precedence(precedence), associativity(associativity), rule_id(0) {} ProductionStep::ProductionStep(const rules::Symbol &symbol, int precedence, - Associativity associativity, int rule_id) + rules::Associativity associativity, int rule_id) : symbol(symbol), precedence(precedence), associativity(associativity), diff --git a/src/compiler/syntax_grammar.h b/src/compiler/syntax_grammar.h index 7b18eec9..8ec876bf 100644 --- a/src/compiler/syntax_grammar.h +++ b/src/compiler/syntax_grammar.h @@ -6,18 +6,19 @@ #include #include "tree_sitter/compiler.h" #include "compiler/rules/symbol.h" +#include "compiler/rules/metadata.h" #include "compiler/variable.h" namespace tree_sitter { struct ProductionStep { - ProductionStep(const rules::Symbol &, int, Associativity); - ProductionStep(const rules::Symbol &, int, Associativity, int); + ProductionStep(const rules::Symbol &, int, rules::Associativity); + ProductionStep(const rules::Symbol &, int, rules::Associativity, int); bool operator==(const ProductionStep &) const; rules::Symbol symbol; int precedence; - Associativity associativity; + rules::Associativity associativity; int rule_id; };