| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- // Exceptions. See /LICENSE for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #include "toolchain/parse/precedence.h"
- #include <initializer_list>
- #include <optional>
- #include "common/check.h"
- namespace Carbon::Parse {
- constexpr int8_t PrecedenceGroup::NumPrecedenceLevels = Lowest + 1;
- // A precomputed lookup table determining the relative precedence of two
- // precedence groups.
- struct PrecedenceGroup::OperatorPriorityTable {
- constexpr OperatorPriorityTable() : table() {
- // Start with a list of <higher precedence>, <lower precedence>
- // relationships.
- MarkHigherThan({Highest}, {TermPrefix, LogicalPrefix});
- MarkHigherThan({TermPrefix},
- {NumericPrefix, BitwisePrefix, IncrementDecrement});
- MarkHigherThan({NumericPrefix, BitwisePrefix, TypePostfix},
- {As, Multiplicative, Modulo, BitwiseAnd, BitwiseOr,
- BitwiseXor, BitShift});
- MarkHigherThan({Multiplicative}, {Additive});
- MarkHigherThan(
- {Additive, Modulo, BitwiseAnd, BitwiseOr, BitwiseXor, BitShift},
- {Relational, Where});
- MarkHigherThan({Relational, LogicalPrefix}, {LogicalAnd, LogicalOr});
- MarkHigherThan({As, LogicalAnd, LogicalOr, Where}, {If});
- MarkHigherThan({If}, {Assignment});
- MarkHigherThan({Assignment, IncrementDecrement}, {Lowest});
- // Types are mostly a separate precedence graph.
- MarkHigherThan({Highest}, {TypePrefix});
- MarkHigherThan({TypePrefix}, {TypePostfix});
- // 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`,
- // then we will parse `a $ b @ c % d` as `((a $ b) @ c) % d` and should
- // also parse `a $ bc % d` as `(a $ bc) % d`.
- MakeTransitivelyClosed();
- // Make the relation symmetric. If we parse `a $ b @ c` as `(a $ b) @ c`
- // then we want to parse `a @ b $ c` as `a @ (b $ c)`.
- MakeSymmetric();
- // Fill in the diagonal, which represents operator associativity.
- AddAssociativityRules();
- ConsistencyCheck();
- }
- constexpr auto MarkHigherThan(
- std::initializer_list<PrecedenceLevel> higher_group,
- std::initializer_list<PrecedenceLevel> lower_group) -> void {
- for (auto higher : higher_group) {
- for (auto lower : lower_group) {
- table[higher][lower] = OperatorPriority::LeftFirst;
- }
- }
- }
- constexpr auto MakeTransitivelyClosed() -> void {
- // A naive algorithm compiles acceptably fast for now (~0.5s). This should
- // be revisited if we see compile time problems after adding precedence
- // groups; it's easy to do this faster.
- bool changed = false;
- do {
- changed = false;
- // NOLINTNEXTLINE(modernize-loop-convert)
- for (int8_t a = 0; a != NumPrecedenceLevels; ++a) {
- for (int8_t b = 0; b != NumPrecedenceLevels; ++b) {
- if (table[a][b] == OperatorPriority::LeftFirst) {
- for (int8_t c = 0; c != NumPrecedenceLevels; ++c) {
- if (table[b][c] == OperatorPriority::LeftFirst &&
- table[a][c] != OperatorPriority::LeftFirst) {
- table[a][c] = OperatorPriority::LeftFirst;
- changed = true;
- }
- }
- }
- }
- }
- } while (changed);
- }
- constexpr auto MakeSymmetric() -> void {
- for (int8_t a = 0; a != NumPrecedenceLevels; ++a) {
- for (int8_t b = 0; b != NumPrecedenceLevels; ++b) {
- if (table[a][b] == OperatorPriority::LeftFirst) {
- CARBON_CHECK(table[b][a] != OperatorPriority::LeftFirst,
- "inconsistent lookup table entries");
- table[b][a] = OperatorPriority::RightFirst;
- }
- }
- }
- }
- constexpr auto AddAssociativityRules() -> void {
- // Associativity rules occupy the diagonal
- // For prefix operators, RightFirst would mean `@@x` is `@(@x)` and
- // Ambiguous would mean it's an error. LeftFirst is meaningless.
- for (PrecedenceLevel prefix : {TermPrefix, If}) {
- table[prefix][prefix] = OperatorPriority::RightFirst;
- }
- // Postfix operators are symmetric with prefix operators.
- for (PrecedenceLevel postfix : {TypePostfix}) {
- table[postfix][postfix] = OperatorPriority::LeftFirst;
- }
- // Traditionally-associative operators are given left-to-right
- // associativity.
- for (PrecedenceLevel assoc :
- {Multiplicative, Additive, BitwiseAnd, BitwiseOr, BitwiseXor,
- LogicalAnd, LogicalOr}) {
- table[assoc][assoc] = OperatorPriority::LeftFirst;
- }
- // For other operators, we require explicit parentheses.
- }
- constexpr auto ConsistencyCheck() -> void {
- for (int8_t level = 0; level != NumPrecedenceLevels; ++level) {
- if (level != Highest) {
- CARBON_CHECK(table[Highest][level] == OperatorPriority::LeftFirst &&
- table[level][Highest] == OperatorPriority::RightFirst,
- "Highest is not highest priority");
- }
- if (level != Lowest) {
- CARBON_CHECK(table[Lowest][level] == OperatorPriority::RightFirst &&
- table[level][Lowest] == OperatorPriority::LeftFirst,
- "Lowest is not lowest priority");
- }
- }
- }
- OperatorPriority table[NumPrecedenceLevels][NumPrecedenceLevels];
- };
- auto PrecedenceGroup::ForLeading(Lex::TokenKind kind)
- -> std::optional<PrecedenceGroup> {
- switch (kind) {
- case Lex::TokenKind::Star:
- case Lex::TokenKind::Amp:
- return PrecedenceGroup(TermPrefix);
- case Lex::TokenKind::Not:
- return PrecedenceGroup(LogicalPrefix);
- case Lex::TokenKind::Minus:
- return PrecedenceGroup(NumericPrefix);
- case Lex::TokenKind::MinusMinus:
- case Lex::TokenKind::PlusPlus:
- return PrecedenceGroup(IncrementDecrement);
- case Lex::TokenKind::Caret:
- return PrecedenceGroup(BitwisePrefix);
- case Lex::TokenKind::If:
- return PrecedenceGroup(If);
- case Lex::TokenKind::Const:
- case Lex::TokenKind::Partial:
- return PrecedenceGroup(TypePrefix);
- default:
- return std::nullopt;
- }
- }
- auto PrecedenceGroup::ForTrailing(Lex::TokenKind kind, bool infix)
- -> std::optional<Trailing> {
- switch (kind) {
- // Assignment operators.
- case Lex::TokenKind::Equal:
- case Lex::TokenKind::PlusEqual:
- case Lex::TokenKind::MinusEqual:
- case Lex::TokenKind::StarEqual:
- case Lex::TokenKind::SlashEqual:
- case Lex::TokenKind::PercentEqual:
- case Lex::TokenKind::AmpEqual:
- case Lex::TokenKind::PipeEqual:
- case Lex::TokenKind::CaretEqual:
- case Lex::TokenKind::GreaterGreaterEqual:
- case Lex::TokenKind::LessLessEqual:
- return Trailing{.level = Assignment, .is_binary = true};
- // Logical operators.
- case Lex::TokenKind::And:
- return Trailing{.level = LogicalAnd, .is_binary = true};
- case Lex::TokenKind::Or:
- return Trailing{.level = LogicalOr, .is_binary = true};
- // Bitwise operators.
- case Lex::TokenKind::Amp:
- return Trailing{.level = BitwiseAnd, .is_binary = true};
- case Lex::TokenKind::Pipe:
- return Trailing{.level = BitwiseOr, .is_binary = true};
- case Lex::TokenKind::Caret:
- return Trailing{.level = BitwiseXor, .is_binary = true};
- case Lex::TokenKind::GreaterGreater:
- case Lex::TokenKind::LessLess:
- return Trailing{.level = BitShift, .is_binary = true};
- // Relational operators.
- case Lex::TokenKind::EqualEqual:
- case Lex::TokenKind::ExclaimEqual:
- case Lex::TokenKind::Less:
- case Lex::TokenKind::LessEqual:
- case Lex::TokenKind::Greater:
- case Lex::TokenKind::GreaterEqual:
- case Lex::TokenKind::LessEqualGreater:
- return Trailing{.level = Relational, .is_binary = true};
- // Additive operators.
- case Lex::TokenKind::Plus:
- case Lex::TokenKind::Minus:
- return Trailing{.level = Additive, .is_binary = true};
- // Multiplicative operators.
- case Lex::TokenKind::Slash:
- return Trailing{.level = Multiplicative, .is_binary = true};
- case Lex::TokenKind::Percent:
- return Trailing{.level = Modulo, .is_binary = true};
- // `*` could be multiplication or pointer type formation.
- case Lex::TokenKind::Star:
- return infix ? Trailing{.level = Multiplicative, .is_binary = true}
- : Trailing{.level = TypePostfix, .is_binary = false};
- // Cast operator.
- case Lex::TokenKind::As:
- return Trailing{.level = As, .is_binary = true};
- // Requirement operator.
- case Lex::TokenKind::Where:
- return Trailing{.level = Where, .is_binary = true};
- // Prefix-only operators.
- case Lex::TokenKind::Const:
- case Lex::TokenKind::MinusMinus:
- case Lex::TokenKind::Not:
- case Lex::TokenKind::Partial:
- case Lex::TokenKind::PlusPlus:
- break;
- // Symbolic tokens that might be operators eventually.
- case Lex::TokenKind::Tilde:
- case Lex::TokenKind::Backslash:
- case Lex::TokenKind::Comma:
- case Lex::TokenKind::TildeEqual:
- case Lex::TokenKind::Exclaim:
- case Lex::TokenKind::LessGreater:
- case Lex::TokenKind::Question:
- case Lex::TokenKind::Colon:
- break;
- // Symbolic tokens that are intentionally not operators.
- case Lex::TokenKind::At:
- case Lex::TokenKind::LessMinus:
- case Lex::TokenKind::MinusGreater:
- case Lex::TokenKind::EqualGreater:
- case Lex::TokenKind::ColonEqual:
- case Lex::TokenKind::Period:
- case Lex::TokenKind::Semi:
- break;
- default:
- break;
- }
- return std::nullopt;
- }
- auto PrecedenceGroup::GetPriority(PrecedenceGroup left, PrecedenceGroup right)
- -> OperatorPriority {
- static constexpr OperatorPriorityTable Lookup;
- return Lookup.table[left.level_][right.level_];
- }
- } // namespace Carbon::Parse
|