diff --git a/parser/parser_impl.cpp b/parser/parser_impl.cpp index 3849ab2f3c6e..1918deb636cb 100644 --- a/parser/parser_impl.cpp +++ b/parser/parser_impl.cpp @@ -593,12 +593,11 @@ auto ParseTree::Parser::ParsePostfixExpression() -> llvm::Optional { } auto ParseTree::Parser::ParseOperatorExpression( - llvm::Optional ambient_precedence) - -> llvm::Optional { + PrecedenceGroup ambient_precedence) -> llvm::Optional { auto start = StartSubtree(); llvm::Optional lhs; - llvm::Optional lhs_precedence; + PrecedenceGroup lhs_precedence = PrecedenceGroup::ForPostfixExpression(); // Check for a prefix operator. if (auto operator_precedence = @@ -606,9 +605,9 @@ auto ParseTree::Parser::ParseOperatorExpression( !operator_precedence) { lhs = ParsePostfixExpression(); } else { - if (ambient_precedence && PrecedenceGroup::GetPriority( - *ambient_precedence, *operator_precedence) != - OperatorPriority::RightFirst) { + if (PrecedenceGroup::GetPriority(ambient_precedence, + *operator_precedence) != + OperatorPriority::RightFirst) { // The precedence rules don't permit this prefix operator in this // context. Diagnose this, but carry on and parse it anyway. emitter.EmitError(*position); @@ -625,17 +624,15 @@ auto ParseTree::Parser::ParseOperatorExpression( while (auto trailing_operator = PrecedenceGroup::ForTrailing(tokens.GetKind(*position))) { auto [operator_precedence, is_binary] = *trailing_operator; - if (ambient_precedence && PrecedenceGroup::GetPriority( - *ambient_precedence, operator_precedence) != - OperatorPriority::RightFirst) { + if (PrecedenceGroup::GetPriority(ambient_precedence, operator_precedence) != + OperatorPriority::RightFirst) { // The precedence rules don't permit this operator in this context. Try // again in the enclosing expression context. return lhs; } - if (lhs_precedence && - PrecedenceGroup::GetPriority(*lhs_precedence, operator_precedence) != - OperatorPriority::LeftFirst) { + if (PrecedenceGroup::GetPriority(lhs_precedence, operator_precedence) != + OperatorPriority::LeftFirst) { // Either the LHS operator and this operator are ambiguous, or the // LHS operaor is a unary operator that can't be nested within // this operator. Either way, parentheses are required. @@ -660,7 +657,7 @@ auto ParseTree::Parser::ParseOperatorExpression( } auto ParseTree::Parser::ParseExpression() -> llvm::Optional { - return ParseOperatorExpression(llvm::None); + return ParseOperatorExpression(PrecedenceGroup::ForTopLevelExpression()); } auto ParseTree::Parser::ParseExpressionStatement() -> llvm::Optional { diff --git a/parser/parser_impl.h b/parser/parser_impl.h index 8cd12f9e58b7..9da905dc9810 100644 --- a/parser/parser_impl.h +++ b/parser/parser_impl.h @@ -157,7 +157,7 @@ class ParseTree::Parser { // Parses an expression involving operators, in a context with the given // precedence. - auto ParseOperatorExpression(llvm::Optional precedence) + auto ParseOperatorExpression(PrecedenceGroup precedence) -> llvm::Optional; // Parses an expression. diff --git a/parser/precedence.cpp b/parser/precedence.cpp index 8db1614f1e8d..b85c8cd6d485 100644 --- a/parser/precedence.cpp +++ b/parser/precedence.cpp @@ -10,6 +10,8 @@ namespace Carbon { namespace { enum PrecedenceLevel : int8_t { + // Sentinel representing the absence of any operator. + Highest, // Numeric. NumericPrefix, NumericPostfix, @@ -30,8 +32,10 @@ enum PrecedenceLevel : int8_t { // Assignment. SimpleAssignment, CompoundAssignment, + // Sentinel representing a context in which any operator can appear. + Lowest, }; -constexpr int8_t NumPrecedenceLevels = CompoundAssignment + 1; +constexpr int8_t NumPrecedenceLevels = Lowest + 1; // A precomputed lookup table determining the relative precedence of two // precedence groups. @@ -39,6 +43,8 @@ struct OperatorPriorityTable { constexpr OperatorPriorityTable() : table{} { // Start with a list of , // relationships. + MarkHigherThan({Highest}, {NumericPrefix, BitwisePrefix, LogicalPrefix, + NumericPostfix}); MarkHigherThan({NumericPrefix, NumericPostfix}, {Modulo, Multiplicative, BitShift}); MarkHigherThan({Multiplicative}, {Additive}); @@ -48,6 +54,9 @@ struct OperatorPriorityTable { {Modulo, Additive, BitwiseAnd, BitwiseOr, BitwiseXor, BitShift}, {SimpleAssignment, CompoundAssignment, Relational}); MarkHigherThan({Relational, LogicalPrefix}, {LogicalAnd, LogicalOr}); + MarkHigherThan( + {SimpleAssignment, CompoundAssignment, LogicalAnd, LogicalOr}, + {Lowest}); // Compute the transitive closure of the above relationships: if we parse // `a $ b @ c` as `(a $ b) @ c` and parse `b @ c % d` as `(b @ c) % d`, @@ -61,6 +70,8 @@ struct OperatorPriorityTable { // Fill in the diagonal, which represents operator associativity. AddAssociativityRules(); + + ConsistencyCheck(); } constexpr void MarkHigherThan( @@ -141,10 +152,35 @@ struct OperatorPriorityTable { // explicit parentheses. } + constexpr void ConsistencyCheck() { + for (int8_t level = 0; level != NumPrecedenceLevels; ++level) { + if (level != Highest) { + if (table[Highest][level] != OperatorPriority::LeftFirst || + table[level][Highest] != OperatorPriority::RightFirst) { + throw "Highest is not highest priority"; + } + } + if (level != Lowest) { + if (table[Lowest][level] != OperatorPriority::RightFirst || + table[level][Lowest] != OperatorPriority::LeftFirst) { + throw "Lowest is not lowest priority"; + } + } + } + } + OperatorPriority table[NumPrecedenceLevels][NumPrecedenceLevels]; }; } // namespace +auto PrecedenceGroup::ForPostfixExpression() -> PrecedenceGroup { + return PrecedenceGroup(Highest); +} + +auto PrecedenceGroup::ForTopLevelExpression() -> PrecedenceGroup { + return PrecedenceGroup(Lowest); +} + auto PrecedenceGroup::ForLeading(TokenKind kind) -> llvm::Optional { switch (kind) { diff --git a/parser/precedence.h b/parser/precedence.h index 8038e96189fc..f9cb142a30cd 100644 --- a/parser/precedence.h +++ b/parser/precedence.h @@ -37,6 +37,14 @@ class PrecedenceGroup { // functions below. PrecedenceGroup() = delete; + // Get the sentinel precedence level for a postfix expression. All operators + // should have lower precedence than this. + static auto ForPostfixExpression() -> PrecedenceGroup; + + // Get the sentinel precedence level for a top-level expression context. All + // operators should have higher precedence than this. + static auto ForTopLevelExpression() -> PrecedenceGroup; + // Look up the operator information of the given prefix operator token, or // return llvm::None if the given token is not a prefix operator. static auto ForLeading(TokenKind kind) -> llvm::Optional;