| // Copyright 2018 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/developer/debug/zxdb/expr/expr_parser.h" | |
| #include <lib/syslog/cpp/macros.h> | |
| #include <algorithm> | |
| #include <iterator> | |
| #include "src/developer/debug/zxdb/expr/expr_tokenizer.h" | |
| #include "src/developer/debug/zxdb/expr/name_lookup.h" | |
| #include "src/developer/debug/zxdb/expr/number_parser.h" | |
| #include "src/developer/debug/zxdb/expr/parse_special_identifier.h" | |
| #include "src/developer/debug/zxdb/expr/template_type_extractor.h" | |
| #include "src/developer/debug/zxdb/symbols/array_type.h" | |
| #include "src/developer/debug/zxdb/symbols/collection.h" | |
| #include "src/developer/debug/zxdb/symbols/modified_type.h" | |
| #include "src/developer/debug/zxdb/symbols/symbol_utils.h" | |
| #include "src/lib/fxl/strings/string_printf.h" | |
| // The parser is a Pratt parser. The basic idea there is to have the precedences (and | |
| // associativities) encoded relative to each other and only parse up until you hit something of that | |
| // precedence. There's a dispatch table in kDispatchInfo that describes how each token dispatches if | |
| // it's seen as either a prefix or infix operator, and if it's infix, what its precedence is. | |
| // | |
| // References: | |
| // http://javascript.crockford.com/tdop/tdop.html | |
| // http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/ | |
| namespace zxdb { | |
| namespace { | |
| // An infix operator is one that combines two sides of things and it modifies both, like "a + b" | |
| // ("a" is the "left" and "+" is the token in the params). | |
| // | |
| // Other things are infix like "[" which combines the expression on the left with some expression to | |
| // the right of it. | |
| // | |
| // A prefix operator are binary operators like "!" in C that only apply to the thing on the right | |
| // and don't require anything on the left. Standalone numbers and names are also considered prefix | |
| // since they represent themselves (not requiring anything on the left). | |
| // | |
| // Some things can be both prefix and infix. An example in C is "(" which is prefix when used in | |
| // casts and math expressions: "(a + b)" "a + (b + c)" but infix when used for function calls: | |
| // "foo(bar)". | |
| using PrefixFunc = fxl::RefPtr<ExprNode> (ExprParser::*)(const ExprToken&); | |
| using InfixFunc = fxl::RefPtr<ExprNode> (ExprParser::*)(fxl::RefPtr<ExprNode> left, | |
| const ExprToken& token); | |
| // Precedence constants used in DispatchInfo. Note that these aren't contiguous. At least need to do | |
| // every-other-one to handle the possible "precedence - 1" that occurs when evaluating | |
| // right-associative operators. We don't want that operation to push the precedence into a | |
| // completely other category, rather, it should only affect comparisons that would otherwise be | |
| // equal. | |
| // | |
| // This should match the C operator precedence for the subset of operations that we support: | |
| // https://en.cppreference.com/w/cpp/language/operator_precedence | |
| // The commented-out values are ones we don't currently implement. | |
| // clang-format off | |
| constexpr int kPrecedenceComma = 10; // , (lowest precedence) | |
| constexpr int kPrecedenceAssignment = 20; // = += -= *= -= /= %= <<= >>= &= ^= |= | |
| constexpr int kPrecedenceLogicalOr = 30; // || | |
| constexpr int kPrecedenceLogicalAnd = 40; // && | |
| constexpr int kPrecedenceBitwiseOr = 50; // | | |
| constexpr int kPrecedenceBitwiseXor = 60; // ^ | |
| constexpr int kPrecedenceBitwiseAnd = 70; // & | |
| constexpr int kPrecedenceEquality = 80; // == != | |
| constexpr int kPrecedenceComparison = 90; // < <= > >= | |
| constexpr int kPrecedenceThreeWayComparison = 100; // <=> | |
| constexpr int kPrecedenceShift = 110; // << >> | |
| constexpr int kPrecedenceAddition = 120; // + - | |
| constexpr int kPrecedenceMultiplication = 130; // * / % | |
| //constexpr int kPrecedencePointerToMember = 140; // .* ->* | |
| constexpr int kPrecedenceRustCast = 140; // foo as Bar | |
| constexpr int kPrecedenceUnary = 150; // ++ -- +a -a ! ~ *a &a | |
| constexpr int kPrecedenceCallAccess = 160; // () . -> [] | |
| //constexpr int kPrecedenceScope = 170; // :: (Highest precedence) | |
| // clang-format on | |
| // Returns true if two tokens are adjacent (have no space between them). | |
| bool IsAdjacent(const ExprToken& a, const ExprToken& b) { | |
| return a.byte_offset() + a.value().size() == b.byte_offset(); | |
| } | |
| } // namespace | |
| struct ExprParser::DispatchInfo { | |
| PrefixFunc prefix = nullptr; | |
| InfixFunc infix = nullptr; | |
| int precedence = 0; // Only needed when |infix| is set. | |
| }; | |
| // The table is more clear without line wrapping. | |
| // clang-format off | |
| ExprParser::DispatchInfo ExprParser::kDispatchInfo[] = { | |
| // Prefix handler Infix handler Precedence for infix | |
| {nullptr, nullptr, -1}, // kInvalid | |
| {&ExprParser::NamePrefix, nullptr, -1}, // kName | |
| {&ExprParser::NamePrefix, nullptr, -1}, // kSpecialName | |
| {&ExprParser::LiteralPrefix, nullptr, -1}, // kInteger | |
| {&ExprParser::LiteralPrefix, nullptr, -1}, // kFloat | |
| {&ExprParser::LiteralPrefix, nullptr, -1}, // kStringLiteral | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceAssignment}, // kEquals | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceEquality}, // kEquality | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceEquality}, // kInequality | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceComparison}, // kLessEqual | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceComparison}, // kGreaterEqual | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceThreeWayComparison}, // kSpaceship | |
| {nullptr, &ExprParser::DotOrArrowInfix, kPrecedenceCallAccess}, // kDot | |
| {nullptr, nullptr, -1}, // kComma | |
| {nullptr, nullptr, -1}, // kSemicolon | |
| {&ExprParser::StarPrefix, &ExprParser::BinaryOpInfix, kPrecedenceMultiplication}, // kStar | |
| {&ExprParser::AmpersandPrefix, &ExprParser::BinaryOpInfix, kPrecedenceBitwiseAnd}, // kAmpersand | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceLogicalAnd}, // kDoubleAnd | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceBitwiseOr}, // kBitwiseOr | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceLogicalOr}, // kLogicalOr | |
| {nullptr, &ExprParser::DotOrArrowInfix, kPrecedenceCallAccess}, // kArrow | |
| {nullptr, &ExprParser::LeftSquareInfix, kPrecedenceCallAccess}, // kLeftSquare | |
| {nullptr, nullptr, -1}, // kRightSquare | |
| {&ExprParser::LeftParenPrefix, &ExprParser::LeftParenInfix, kPrecedenceCallAccess}, // kLeftParen | |
| {nullptr, nullptr, -1}, // kRightParen | |
| {&ExprParser::LeftBracketPrefix, nullptr, -1}, // kLeftBracket | |
| {nullptr, nullptr, -1}, // kRightBracket | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceComparison}, // kLess | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceComparison}, // kGreater | |
| {&ExprParser::UnaryPrefix, &ExprParser::BinaryOpInfix, kPrecedenceAddition}, // kMinus | |
| {&ExprParser::UnaryPrefix, nullptr, kPrecedenceUnary}, // kBang | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceAddition}, // kPlus | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceMultiplication}, // kSlash | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceBitwiseXor}, // kCaret | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceMultiplication}, // kPercent | |
| {nullptr, &ExprParser::QuestionInfix, kPrecedenceAssignment}, // kQuestion | |
| {nullptr, nullptr, -1}, // kColon | |
| {&ExprParser::NamePrefix, nullptr, -1}, // kColonColon | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceShift}, // kShiftLeft | |
| {nullptr, &ExprParser::BinaryOpInfix, kPrecedenceShift}, // kShiftRight | |
| {&ExprParser::LiteralPrefix, nullptr, -1}, // kTrue | |
| {&ExprParser::LiteralPrefix, nullptr, -1}, // kFalse | |
| {&ExprParser::NamePrefix, nullptr, -1}, // kConst | |
| {nullptr, nullptr, -1}, // kMut | |
| {&ExprParser::NamePrefix, nullptr, -1}, // kVolatile | |
| {&ExprParser::NamePrefix, nullptr, -1}, // kRestrict | |
| {&ExprParser::CastPrefix, nullptr, -1}, // kReinterpretCast | |
| {&ExprParser::CastPrefix, nullptr, -1}, // kStaticCast | |
| {&ExprParser::SizeofPrefix, nullptr, -1}, // kSizeof | |
| {nullptr, &ExprParser::RustCastInfix, kPrecedenceRustCast}, // kAs | |
| {&ExprParser::IfPrefix, nullptr, kPrecedenceAssignment}, // kIf | |
| {nullptr, nullptr, kPrecedenceAssignment}, // kElse | |
| }; | |
| // clang-format on | |
| // static | |
| const ExprToken ExprParser::kInvalidToken; | |
| ExprParser::ExprParser(std::vector<ExprToken> tokens, ExprLanguage lang, | |
| NameLookupCallback name_lookup) | |
| : language_(lang), name_lookup_callback_(std::move(name_lookup)), tokens_(std::move(tokens)) { | |
| static_assert(std::size(ExprParser::kDispatchInfo) == static_cast<int>(ExprTokenType::kNumTypes), | |
| "kDispatchInfo needs updating to match ExprTokenType"); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::ParseExpression() { | |
| auto result = ParseExpression(0, EmptyExpression::kReject, StatementEnd::kAny); | |
| // That should have consumed everything, as we don't support multiple expressions being next to | |
| // each other (probably the user forgot an operator and wrote something like "foo 5" | |
| if (!has_error() && !at_end()) { | |
| SetError(cur_token(), "Unexpected input, did you forget an operator?"); | |
| return nullptr; | |
| } | |
| return result; | |
| } | |
| fxl::RefPtr<BlockExprNode> ExprParser::ParseBlock(BlockDelimiter delimiter) { | |
| if (delimiter == BlockDelimiter::kExplicit) { | |
| // Expect block to start with a {. | |
| Consume(ExprTokenType::kLeftBracket, "Expected '{'."); | |
| if (has_error()) | |
| return nullptr; | |
| } | |
| auto block = ParseBlockContents(delimiter == BlockDelimiter::kExplicit ? BlockEnd::kExplicit | |
| : BlockEnd::kEndOfInput); | |
| if (!has_error() && delimiter == BlockDelimiter::kExplicit) { | |
| // Consume the ending }. | |
| Consume(ExprTokenType::kRightBracket, "Expected '}'."); | |
| } | |
| if (has_error()) | |
| return nullptr; | |
| return block; | |
| } | |
| // static | |
| Err ExprParser::ParseIdentifier(const std::string& input, Identifier* output) { | |
| ParsedIdentifier parsed; | |
| Err err = ParseIdentifier(input, &parsed); | |
| if (err.has_error()) | |
| return err; | |
| *output = ToIdentifier(parsed); | |
| return Err(); | |
| } | |
| // static | |
| Err ExprParser::ParseIdentifier(const std::string& input, ParsedIdentifier* output) { | |
| ExprTokenizer tokenizer(input); | |
| if (!tokenizer.Tokenize()) | |
| return tokenizer.err(); | |
| ExprParser parser(tokenizer.TakeTokens(), tokenizer.language()); | |
| auto root = parser.ParseExpression(); | |
| if (!root) | |
| return parser.err(); | |
| auto identifier_node = root->AsIdentifier(); | |
| if (!identifier_node) | |
| return Err("Input did not parse as an identifier."); | |
| *output = const_cast<IdentifierExprNode*>(identifier_node)->TakeIdentifier(); | |
| return Err(); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::ParseExpression(int precedence, EmptyExpression empty_expr, | |
| StatementEnd statement_end, | |
| bool* had_statement_end) { | |
| if (had_statement_end) | |
| *had_statement_end = false; | |
| if (at_end()) { | |
| if (empty_expr == EmptyExpression::kReject) | |
| SetError(ExprToken(), "Expected expression instead of end of input."); | |
| return nullptr; | |
| } | |
| // Here we need ">>" to be treated as a shift operation. When that appears as part of a name and | |
| // should be separated, the identifier parser will handle it separately. | |
| ExprToken token = ConsumeWithShiftTokenConversion(); | |
| if (token.type() == ExprTokenType::kSemicolon) { | |
| if (had_statement_end) | |
| *had_statement_end = true; // record that this empty expression had an explicit end. | |
| if (empty_expr == EmptyExpression::kReject) | |
| SetError(token, "Empty expression not permitted here."); | |
| return nullptr; | |
| } | |
| PrefixFunc prefix = DispatchForToken(token).prefix; | |
| if (!prefix) { | |
| SetError(token, fxl::StringPrintf("Unexpected token '%s'.", token.value().c_str())); | |
| return nullptr; | |
| } | |
| fxl::RefPtr<ExprNode> left = (this->*prefix)(token); | |
| if (has_error()) | |
| return left; // In both C++ and Rust, the end of a block is the end of a statement. | |
| // Checks for the next thing being a semicolon or the last thing being a {} which indicates the | |
| // end of the expression. | |
| while (!at_end() && !LookAhead(ExprTokenType::kSemicolon) && !left->AsBlock() && | |
| precedence < CurPrecedenceWithShiftTokenConversion()) { | |
| const ExprToken& next_token = ConsumeWithShiftTokenConversion(); | |
| InfixFunc infix = DispatchForToken(next_token).infix; | |
| if (!infix) { | |
| SetError(next_token, fxl::StringPrintf("Unexpected token '%s'.", next_token.value().c_str())); | |
| return nullptr; | |
| } | |
| left = (this->*infix)(std::move(left), next_token); | |
| if (has_error()) | |
| return nullptr; | |
| } | |
| // Handle end-of-statement requirements. | |
| switch (statement_end) { | |
| case StatementEnd::kAny: | |
| case StatementEnd::kExplicit: | |
| if (left->AsBlock()) { | |
| // Blocks are their own statements, nothing to do. | |
| if (had_statement_end) | |
| *had_statement_end = true; | |
| break; | |
| } else if (LookAhead(ExprTokenType::kSemicolon)) { | |
| // Semicolon indicating end of statement, consume it. | |
| Consume(); | |
| if (had_statement_end) | |
| *had_statement_end = true; | |
| break; | |
| } else if (statement_end == StatementEnd::kExplicit) { | |
| // Statement end was required but not present. | |
| SetErrorAtCur("Expected ';'."); | |
| return nullptr; | |
| } | |
| break; | |
| case StatementEnd::kNone: | |
| // Nothing to check. | |
| break; | |
| } | |
| return left; | |
| } | |
| // We parse all blocks as expressions. In C++ this is not correct but simplifies the parsing logic. | |
| // During evaluation, C++ blocks will return empty which will throw errors when using blocks in | |
| // places where they can't be used in expressions. | |
| fxl::RefPtr<BlockExprNode> ExprParser::ParseBlockContents(BlockEnd block_end) { | |
| // Expect a sequence of semicolon-terminated expressions. In Rust the last expression does not | |
| // need a semicolon. As we start parsing each new statement, we check whether the previous | |
| // statement had a separator. | |
| bool previous_had_statement_end = true; | |
| constexpr ExprTokenType kBlockEndTokenType = ExprTokenType::kRightBracket; | |
| std::vector<fxl::RefPtr<ExprNode>> statements; | |
| while (!at_end() && !LookAhead(kBlockEndTokenType)) { | |
| if (!previous_had_statement_end) { | |
| SetError(cur_token(), "Expected ';'."); | |
| return nullptr; | |
| } | |
| // Accept any type of statement end and validate later to accommodate Rust. | |
| auto stmt = ParseExpression(0, EmptyExpression::kAllow, StatementEnd::kAny, | |
| &previous_had_statement_end); | |
| if (has_error()) | |
| return nullptr; | |
| if (stmt) // Statement could be empty. | |
| statements.push_back(std::move(stmt)); | |
| } | |
| // C++ requires a statement terminator for everything. In Rust we can skip this check since the | |
| // last semicolon is optional. | |
| if (language_ == ExprLanguage::kC) { | |
| if (!previous_had_statement_end) { | |
| SetErrorAtCur("Expected ';'."); | |
| return nullptr; | |
| } | |
| } | |
| // Validate the correct end of the block. | |
| switch (block_end) { | |
| case BlockEnd::kExplicit: | |
| if (!LookAhead(kBlockEndTokenType)) | |
| SetErrorAtCur("Expected '}'."); | |
| break; | |
| case BlockEnd::kEndOfInput: | |
| if (!at_end()) | |
| SetErrorAtCur("Unexpected."); | |
| break; | |
| } | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<BlockExprNode>(std::move(statements)); | |
| } | |
| ExprParser::ParseNameResult ExprParser::ParseName(bool expand_types) { | |
| // Grammar we support. Note "identifier" in this context is a single token of type "name" (more | |
| // like how the C++ spec uses it), while our Identifier class represents a whole name with scopes | |
| // and templates. | |
| // | |
| // name := type-name | special-identifier | other-identifier | |
| // | |
| // type-name := [ scope-name "::" ] identifier [ "<" template-list ">" ] | |
| // | |
| // special-identifier := "$" [ special-name ] [ "(" special-contents ")" ] | |
| // | |
| // other-identifier := [ <scope-name> "::" ] <identifier> | |
| // | |
| // scope-name := ( namespace-name | type-name ) | |
| // | |
| // The thing that differentiates type names, namespace names, and other identifiers is the symbol | |
| // lookup function rather than something in the grammar. | |
| // | |
| // The thing this doesn't handle is templatized functions, for example: | |
| // auto foo = &MyClass::MyFunc<int>; | |
| // To handle this we will need the type lookup function to be able to tell us "MyClass::MyFunc" is | |
| // a thing that has a template so we know to parse the following "<" as part of the name and not | |
| // as a comparison. Note that when we need to parse function names, there is special handling | |
| // required for operators. | |
| // The mode of the state machine. | |
| enum Mode { | |
| kBegin, // Initial state with no previous context. | |
| kColonColon, // Just saw a "::", expecting a name next. | |
| kType, // Identifier is a type. | |
| kTemplate, // Identifier is a template, expecting "<" next. | |
| kNamespace, // Identifier is a namespace. | |
| kOtherName, // Identifier is something other than the above (normally this | |
| // means a variable). | |
| kAnything // Caller can't do symbol lookups, accept anything that makes | |
| // sense. | |
| }; | |
| Mode mode = kBegin; | |
| ParseNameResult result; | |
| const ExprToken* prev_token = nullptr; | |
| while (!at_end()) { | |
| const ExprToken& token = cur_token(); | |
| switch (token.type()) { | |
| case ExprTokenType::kColonColon: { | |
| // "::" can only follow nothing, a namespace or type name. | |
| if (mode != kBegin && mode != kNamespace && mode != kType && mode != kAnything) { | |
| SetError(token, | |
| "Could not identify thing to the left of '::' as a type or " | |
| "namespace."); | |
| return ParseNameResult(); | |
| } | |
| mode = kColonColon; | |
| if (result.ident.empty()) // Globally qualified (starts with "::"). | |
| result.ident = ParsedIdentifier(IdentifierQualification::kGlobal); | |
| result.type = nullptr; // No longer a type. | |
| break; | |
| } | |
| case ExprTokenType::kLess: { | |
| // "<" can only come after a template name. | |
| if (mode == kNamespace || mode == kType) { | |
| // Generate a nicer error for these cases. | |
| SetError(token, "Template parameters not valid on this object type."); | |
| return ParseNameResult(); | |
| } | |
| if (mode != kTemplate && mode != kAnything) { | |
| // "<" after anything but a template means the end of the name. In "anything" mode we | |
| // assume "<" means a template since this is used to parse random identifiers and function | |
| // names. | |
| return result; | |
| } | |
| if (result.ident.components().back().has_template()) { | |
| // Got a "<" after a template parameter list was already defined (this will happen in | |
| // "anything" mode since we don't know what it is for sure). That means this is a | |
| // comparison operator which will be handled by the outer parser. | |
| return result; | |
| } | |
| prev_token = &Consume(); // Eat the "<". | |
| // Extract the contents of the template. | |
| std::vector<std::string> list = ParseTemplateList(ExprTokenType::kGreater); | |
| if (has_error()) | |
| return ParseNameResult(); | |
| // Ending ">". | |
| Consume(ExprTokenType::kGreater, "Expected '>' to match."); | |
| if (has_error()) | |
| return ParseNameResult(); | |
| // Construct a replacement for the last component of the identifier with the template | |
| // arguments added. | |
| ParsedIdentifierComponent& back = result.ident.components().back(); | |
| back = ParsedIdentifierComponent(back.name(), std::move(list)); | |
| // The thing we just made is either a type or a name, look it up. | |
| if (name_lookup_callback_) { | |
| FoundName lookup = | |
| name_lookup_callback_(result.ident, FindNameOptions(FindNameOptions::kAllKinds)); | |
| switch (lookup.kind()) { | |
| case FoundName::kType: | |
| mode = kType; | |
| result.type = std::move(lookup.type()); | |
| break; | |
| case FoundName::kNamespace: | |
| case FoundName::kTemplate: | |
| // The lookup shouldn't tell us a template name or namespace for something that has | |
| // template parameters. | |
| FX_NOTREACHED(); | |
| // Fall through to "other" case for fallback. | |
| case FoundName::kVariable: | |
| case FoundName::kMemberVariable: | |
| case FoundName::kFunction: | |
| case FoundName::kOtherSymbol: | |
| case FoundName::kNone: | |
| mode = kOtherName; | |
| break; | |
| } | |
| } else { | |
| mode = kAnything; | |
| } | |
| continue; // Don't Consume() since we already ate the token. | |
| } | |
| case ExprTokenType::kName: | |
| case ExprTokenType::kSpecialName: { | |
| // Names can only follow nothing or "::". | |
| if (mode == kType) { | |
| // Normally in C++ a name can follow a type, so make a special error for this case. | |
| SetError(token, "This looks like a declaration which is not supported."); | |
| return ParseNameResult(); | |
| } else if (mode == kBegin) { | |
| // Found an identifier name with nothing before it. | |
| result.ident = ParsedIdentifier(GetIdentifierComponent()); | |
| } else if (mode == kColonColon) { | |
| result.ident.AppendComponent(GetIdentifierComponent()); | |
| } else { | |
| // Anything else like "std::vector foo" or "foo bar". | |
| SetError(token, "Unexpected identifier, did you forget an operator?"); | |
| return ParseNameResult(); | |
| } | |
| // Decode what adding the name just generated. | |
| if (name_lookup_callback_) { | |
| FoundName lookup = | |
| name_lookup_callback_(result.ident, FindNameOptions(FindNameOptions::kAllKinds)); | |
| switch (lookup.kind()) { | |
| case FoundName::kNamespace: | |
| mode = kNamespace; | |
| break; | |
| case FoundName::kTemplate: | |
| mode = kTemplate; | |
| break; | |
| case FoundName::kType: | |
| mode = kType; | |
| result.type = std::move(lookup.type()); | |
| break; | |
| case FoundName::kVariable: | |
| case FoundName::kMemberVariable: | |
| case FoundName::kFunction: | |
| case FoundName::kOtherSymbol: | |
| case FoundName::kNone: | |
| mode = kOtherName; | |
| break; | |
| } | |
| } else { | |
| mode = kAnything; | |
| } | |
| break; | |
| } | |
| default: { | |
| // Any other token type means we're done. The outer parser will figure out what it means. | |
| if (expand_types && result.type && language_ != ExprLanguage::kRust) { | |
| // When we found a type, add on any trailing modifiers like "*". Rust doesn't have | |
| // trailing modifiers, so skip this for Rust. | |
| result.type = ParseType(std::move(result.type)); | |
| } | |
| return result; | |
| } | |
| } | |
| prev_token = &Consume(); | |
| } | |
| // Hit end-of-input. | |
| switch (mode) { | |
| case kOtherName: | |
| case kAnything: | |
| case kType: | |
| return result; // Success cases. | |
| case kBegin: | |
| FX_NOTREACHED(); | |
| SetError(ExprToken(), "Unexpected end of input."); | |
| return ParseNameResult(); | |
| case kColonColon: | |
| SetError(*prev_token, "Expected name after '::'."); | |
| return ParseNameResult(); | |
| case kTemplate: | |
| SetError(*prev_token, "Expected template args after template name."); | |
| return ParseNameResult(); | |
| case kNamespace: | |
| SetError(*prev_token, "Expected expression after namespace name."); | |
| return ParseNameResult(); | |
| } | |
| FX_NOTREACHED(); | |
| SetError(ExprToken(), "Internal error."); | |
| return ParseNameResult(); | |
| } | |
| ParsedIdentifierComponent ExprParser::GetIdentifierComponent() { | |
| const ExprToken& token = cur_token(); | |
| FX_DCHECK(!token.value().empty()); // Should not have an empty name token. | |
| if (token.value()[0] == '$') { | |
| // Special identifier, need to parse it. | |
| size_t cur = 0; // Special name is at the beginning of the token. | |
| size_t error_location = 0; // Don't need this. | |
| SpecialIdentifier si = SpecialIdentifier::kNone; | |
| std::string special_cont; | |
| if (Err err = ParseSpecialIdentifier(token.value(), &cur, &si, &special_cont, &error_location); | |
| err.has_error()) { | |
| SetError(token, err); | |
| return ParsedIdentifierComponent(); | |
| } | |
| return ParsedIdentifierComponent(si, special_cont); | |
| } | |
| // Regular identifier, can just return a component for the literal value. | |
| return ParsedIdentifierComponent(token.value()); | |
| } | |
| fxl::RefPtr<Type> ExprParser::ParseType(fxl::RefPtr<Type> optional_base) { | |
| // For C++, the thing we want to parse is: | |
| // | |
| // cv-qualifier := [ "const" ] [ "volatile" ] [ "restrict" ] | |
| // | |
| // ptr-operator := ( "*" | "&" | "&&" ) cv-qualifier | |
| // | |
| // type-id := cv-qualifier type-name cv-qualifier [ ptr-operator ] * | |
| // | |
| // Our logic is much more permissive than C++. This is both because it makes the code simpler, and | |
| // because certain constructs may be used by other languages. For example, this allows references | |
| // to references and "int & const" while C++ says you can't apply const to the reference itself | |
| // (it permits only "const int&" or "int const &" which are the same). It also allows "restrict" | |
| // to be used in invalid places. | |
| // | |
| // For Rust, the thing we want to parse is: | |
| // | |
| // qualifier := "&" | "*" | "mut" | |
| // | |
| // type-id = qualifier* (type-name | array-type | tuple-type) | |
| // | |
| // array-type = "[" type ";" integer "]" | |
| // | |
| // tuple-type = "(" type ("," type)* ")" | |
| // | |
| // This is simpler than the C++ case. Rust has some restrictions on how qualifiers appear, and | |
| // again we are more permissive, mostly because Rust's mutability constraints aren't important | |
| // to the debugger case, so we can afford to be less particular. References and pointers are all | |
| // the same in the compiled code, so we interchange them freely. | |
| fxl::RefPtr<Type> type; | |
| std::vector<DwarfTag> type_qual; | |
| size_t pointer_levels = 0; | |
| if (optional_base) { | |
| FX_DCHECK(language_ != ExprLanguage::kRust); | |
| // Type name already known, start parsing after it. | |
| type = std::move(optional_base); | |
| } else { | |
| if (language_ == ExprLanguage::kRust) { | |
| while (!at_end()) { | |
| const ExprToken& token = cur_token(); | |
| if (token.type() == ExprTokenType::kStar) { | |
| pointer_levels++; | |
| } else if (token.type() == ExprTokenType::kAmpersand) { | |
| pointer_levels++; | |
| } else if (token.type() == ExprTokenType::kDoubleAnd) { | |
| pointer_levels += 2; | |
| } else if (token.type() == ExprTokenType::kMut) { | |
| // Ignore mut. | |
| } else { | |
| // Done with the ptr-operators. | |
| break; | |
| } | |
| Consume(); // Eat the operator token. | |
| } | |
| } else { | |
| // Read "const", etc. that comes before the type name. | |
| ConsumeCVQualifier(&type_qual); | |
| if (has_error()) | |
| return nullptr; | |
| } | |
| // Read the type name itself. | |
| if (at_end()) { | |
| SetError(ExprToken(), "Expected type name before end of input."); | |
| return nullptr; | |
| } | |
| const ExprToken& first_name_token = cur_token(); // For error blame below. | |
| if (language_ == ExprLanguage::kRust && | |
| (first_name_token.type() == ExprTokenType::kLeftSquare || | |
| first_name_token.type() == ExprTokenType::kLeftParen)) { | |
| if (first_name_token.type() == ExprTokenType::kLeftParen) { | |
| Consume(); | |
| std::vector<fxl::RefPtr<Type>> members; | |
| bool expect_type = true; | |
| while (!at_end() && cur_token().type() != ExprTokenType::kRightParen && expect_type) { | |
| auto next = ParseType(nullptr); | |
| if (!next) { | |
| return nullptr; | |
| } | |
| members.push_back(std::move(next)); | |
| expect_type = !at_end() && cur_token().type() == ExprTokenType::kComma; | |
| if (expect_type) { | |
| Consume(); | |
| } | |
| } | |
| if (at_end()) { | |
| SetError(ExprToken(), "Expected ')' or ',' before end of input."); | |
| return nullptr; | |
| } else if (cur_token().type() != ExprTokenType::kRightParen) { | |
| SetError(cur_token(), "Expected ')' or ','."); | |
| return nullptr; | |
| } else { | |
| Consume(); | |
| } | |
| if (members.size() == 1 && !expect_type) { | |
| // This was just a type in parentheses. Rust appears to handle this in this way so we will | |
| // too. | |
| return std::move(members[0]); | |
| } | |
| std::string name = "("; | |
| for (const auto& type : members) { | |
| if (name.size() > 1) { | |
| name += ", "; | |
| } | |
| name += type->GetAssignedName(); | |
| } | |
| if (members.size() == 1) { | |
| name += ","; | |
| } | |
| name += ")"; | |
| return MakeRustTuple(name, members); | |
| } else { | |
| type = ParseRustArrayType(); | |
| if (!type) { | |
| return nullptr; | |
| } | |
| } | |
| } else { | |
| ParseNameResult parse_result = ParseName(false); | |
| if (has_error()) | |
| return nullptr; | |
| if (!parse_result.type) { | |
| SetError(first_name_token, | |
| fxl::StringPrintf("Expected a type name but could not find a type named '%s'.", | |
| parse_result.ident.GetFullName().c_str())); | |
| return nullptr; | |
| } | |
| type = std::move(parse_result.type); | |
| } | |
| for (size_t i = 0; i < pointer_levels; i++) { | |
| type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, std::move(type)); | |
| } | |
| } | |
| if (language_ == ExprLanguage::kRust) { | |
| return type; | |
| } | |
| // Read "const" etc. that comes after the type name. These apply the same as the ones that come | |
| // before it so get appended and can't duplicate them. | |
| ConsumeCVQualifier(&type_qual); | |
| if (has_error()) | |
| return nullptr; | |
| type = ApplyQualifiers(std::move(type), type_qual); | |
| // Parse the ptr-operators that can be present after the type. | |
| while (!at_end()) { | |
| // Read the operator. | |
| const ExprToken& token = cur_token(); | |
| if (token.type() == ExprTokenType::kStar) { | |
| type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, std::move(type)); | |
| } else if (token.type() == ExprTokenType::kAmpersand) { | |
| type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kReferenceType, std::move(type)); | |
| } else if (token.type() == ExprTokenType::kDoubleAnd) { | |
| type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kRvalueReferenceType, std::move(type)); | |
| } else { | |
| // Done with the ptr-operators. | |
| break; | |
| } | |
| Consume(); // Eat the operator token. | |
| // Apply any const-volatile-restrict to the operator. | |
| std::vector<DwarfTag> qual; | |
| ConsumeCVQualifier(&qual); | |
| if (has_error()) | |
| return nullptr; | |
| type = ApplyQualifiers(std::move(type), qual); | |
| } | |
| return type; | |
| } | |
| fxl::RefPtr<Type> ExprParser::ParseRustArrayType() { | |
| FX_DCHECK(!at_end() && cur_token().type() == ExprTokenType::kLeftSquare); | |
| Consume(); | |
| auto element = ParseType(nullptr); | |
| if (!element) { | |
| return nullptr; | |
| } | |
| std::optional<size_t> count = std::nullopt; | |
| if (!at_end() && cur_token().type() == ExprTokenType::kSemicolon) { | |
| Consume(); | |
| if (at_end()) { | |
| SetError(ExprToken(), "Expected element count before end of input."); | |
| return nullptr; | |
| } | |
| if (cur_token().type() != ExprTokenType::kInteger) { | |
| SetError(cur_token(), "Expected element count."); | |
| return nullptr; | |
| } | |
| auto got = StringToNumber(cur_token().value()); | |
| if (got.has_error()) { | |
| // Should this even be possible? | |
| SetError(cur_token(), "Could not parse integer: " + got.err().msg()); | |
| return nullptr; | |
| } | |
| int64_t value; | |
| auto promote_err = got.value().PromoteTo64(&value); | |
| if (promote_err.has_error()) { | |
| SetError(cur_token(), "Invalid array size: " + promote_err.msg()); | |
| return nullptr; | |
| } | |
| if (value < 0) { | |
| SetError(cur_token(), "Negative array size."); | |
| return nullptr; | |
| } | |
| Consume(); | |
| count = static_cast<size_t>(value); | |
| } | |
| if (at_end()) { | |
| SetError(ExprToken(), "Expected ']' before end of input."); | |
| return nullptr; | |
| } | |
| if (cur_token().type() != ExprTokenType::kRightSquare) { | |
| SetError(cur_token(), "Expected ']'."); | |
| return nullptr; | |
| } | |
| Consume(); | |
| return fxl::MakeRefCounted<ArrayType>(std::move(element), std::move(count)); | |
| } | |
| // A list is any sequence of comma-separated types. We don't parse the types (this is hard) but | |
| // instead skip over them. | |
| std::vector<std::string> ExprParser::ParseTemplateList(ExprTokenType stop_before) { | |
| std::vector<std::string> result; | |
| bool first_time = true; | |
| while (!at_end() && !LookAhead(stop_before)) { | |
| if (first_time) { | |
| first_time = false; | |
| } else { | |
| // Expect comma to separate items. | |
| if (LookAhead(ExprTokenType::kComma)) { | |
| Consume(); | |
| } else { | |
| SetError(cur_token(), "Expected ',' separating expressions."); | |
| return {}; | |
| } | |
| } | |
| TemplateTypeResult type_result = ExtractTemplateType(tokens_, cur_); | |
| if (!type_result.success) { | |
| SetError(tokens_[type_result.unmatched_error_token], | |
| fxl::StringPrintf("Unmatched '%s'.", | |
| tokens_[type_result.unmatched_error_token].value().c_str())); | |
| return {}; | |
| } else if (cur_ == type_result.end_token) { | |
| if (!at_end()) { | |
| SetError(cur_token(), "Expected template parameter."); | |
| } else { | |
| SetError(ExprToken(), "Expected template parameter."); | |
| } | |
| return {}; | |
| } | |
| cur_ = type_result.end_token; | |
| result.push_back(std::move(type_result.canonical_name)); | |
| } | |
| return result; | |
| } | |
| // This function is called in contexts where we expect a comma-separated list. Currently these are | |
| // all known in advance so this simple manual parsing will do. A more general approach would | |
| // implement a comma infix which constructs a new type of ExprNode. | |
| std::vector<fxl::RefPtr<ExprNode>> ExprParser::ParseExpressionList(ExprTokenType stop_before) { | |
| std::vector<fxl::RefPtr<ExprNode>> result; | |
| bool first_time = true; | |
| while (!at_end() && !LookAhead(stop_before)) { | |
| if (first_time) { | |
| first_time = false; | |
| } else { | |
| // Expect comma to separate items. | |
| if (LookAhead(ExprTokenType::kComma)) { | |
| Consume(); | |
| } else { | |
| SetError(cur_token(), "Expected ',' separating expressions."); | |
| return {}; | |
| } | |
| } | |
| fxl::RefPtr<ExprNode> cur = ParseExpression(kPrecedenceComma); | |
| if (has_error()) | |
| return {}; | |
| result.push_back(std::move(cur)); | |
| } | |
| return result; | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::AmpersandPrefix(const ExprToken& token) { | |
| fxl::RefPtr<ExprNode> right = ParseExpression(kPrecedenceUnary); | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<AddressOfExprNode>(std::move(right)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::BinaryOpInfix(fxl::RefPtr<ExprNode> left, | |
| const ExprToken& token) { | |
| const DispatchInfo& dispatch = DispatchForToken(token); | |
| // Allow empty expressions during parse so we can give a slightly better error message below. | |
| fxl::RefPtr<ExprNode> right = ParseExpression(dispatch.precedence, EmptyExpression::kAllow); | |
| if (!has_error() && !right) { | |
| SetError(token, fxl::StringPrintf("Expected expression after '%s'.", token.value().c_str())); | |
| } | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<BinaryOpExprNode>(std::move(left), token, std::move(right)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::DotOrArrowInfix(fxl::RefPtr<ExprNode> left, | |
| const ExprToken& token) { | |
| // These are left-associative so use the same precedence as the token. Allow empty expressions | |
| // during parse so we can give a slightly better error message below. | |
| fxl::RefPtr<ExprNode> right = ParseExpression(kPrecedenceCallAccess, EmptyExpression::kAllow); | |
| auto literal = right ? right->AsLiteral() : nullptr; | |
| // Rust supports tuple structs, which can be addressed like "foo.0" | |
| if (language_ == ExprLanguage::kRust && literal && token.type() == ExprTokenType::kDot) { | |
| auto literal_token = literal->token(); | |
| if (literal_token.type() == ExprTokenType::kInteger) { | |
| auto value = literal_token.value(); | |
| if (value.find_first_not_of("0123456789") == std::string::npos && | |
| (value.length() <= 1 || value[0] != '0')) { | |
| right = fxl::MakeRefCounted<IdentifierExprNode>(value); | |
| } | |
| } | |
| } | |
| if (!right || !right->AsIdentifier()) { | |
| SetError(token, fxl::StringPrintf("Expected identifier for right-hand-side of \"%s\".", | |
| token.value().c_str())); | |
| return nullptr; | |
| } | |
| // Use the name from the right-hand-side identifier, we don't need a full expression for that. If | |
| // we add function calls it will be necessary. | |
| return fxl::MakeRefCounted<MemberAccessExprNode>(std::move(left), token, | |
| right->AsIdentifier()->ident()); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::LeftParenPrefix(const ExprToken& token) { | |
| // "(" as a prefix is a grouping or cast: "a + (b + c)" or "(Foo)bar" where it doesn't modify the | |
| // thing on the left. Evaluate the thing inside the () and return it. | |
| // | |
| // Currently there's no infix version of "(" which would be something like a function call. | |
| auto expr = ParseExpression(0); | |
| if (!has_error() && !expr) | |
| SetError(token, "Expected expression inside '('."); | |
| if (!has_error()) | |
| Consume(ExprTokenType::kRightParen, "Expected ')' to match.", token); | |
| if (has_error()) | |
| return nullptr; | |
| if (const TypeExprNode* type_expr = expr->AsType()) { | |
| // Convert "(TypeName)..." into a cast. Note the "-1" here which converts to right-associative. | |
| // With variable names, () is left-associative in that "(foo)(bar)[baz]" means to execute | |
| // left-to-right. But when "(foo)" is a C-style cast, this means "(bar)[baz]" is a unit. | |
| auto cast_expr = ParseExpression(kPrecedenceCallAccess - 1); | |
| if (has_error()) | |
| return nullptr; | |
| fxl::RefPtr<TypeExprNode> type_ref = RefPtrTo(type_expr); | |
| return fxl::MakeRefCounted<CastExprNode>(CastType::kC, std::move(type_ref), | |
| std::move(cast_expr)); | |
| } | |
| return expr; | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::LeftBracketPrefix(const ExprToken& token) { | |
| // We could say to terminate on a bracket. But by saying "none" we have the opportunity to give a | |
| // better error message below, referencing the opening bracket. | |
| auto block = ParseBlockContents(BlockEnd::kExplicit); | |
| if (!block) | |
| return nullptr; | |
| // Cosume the terminating bracket. | |
| Consume(ExprTokenType::kRightBracket, "Expected '}' to match.", token); | |
| if (has_error()) | |
| return nullptr; | |
| return block; | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::LeftParenInfix(fxl::RefPtr<ExprNode> left, | |
| const ExprToken& token) { | |
| // "(" as an infix is a function call. In this case, the thing on the left identifies what to | |
| // call (either a bare identifier or an object accessor). | |
| if (!FunctionCallExprNode::IsValidCall(left)) { | |
| SetError(token, "Unexpected '('."); | |
| return nullptr; | |
| } | |
| // Read the function parameters. | |
| std::vector<fxl::RefPtr<ExprNode>> args = ParseExpressionList(ExprTokenType::kRightParen); | |
| if (has_error()) | |
| return nullptr; | |
| Consume(ExprTokenType::kRightParen, "Expected ')' to match.", token); | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<FunctionCallExprNode>(std::move(left), std::move(args)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::LeftSquareInfix(fxl::RefPtr<ExprNode> left, | |
| const ExprToken& token) { | |
| auto inner = ParseExpression(0); | |
| if (!has_error()) | |
| Consume(ExprTokenType::kRightSquare, "Expected ']' to match.", token); | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<ArrayAccessExprNode>(std::move(left), std::move(inner)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::RustCastInfix(fxl::RefPtr<ExprNode> left, | |
| const ExprToken& token) { | |
| fxl::RefPtr<Type> type = ParseType(fxl::RefPtr<Type>()); | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<CastExprNode>( | |
| CastType::kRust, fxl::MakeRefCounted<TypeExprNode>(std::move(type)), std::move(left)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::QuestionInfix(fxl::RefPtr<ExprNode> left, | |
| const ExprToken& token) { | |
| if (language_ == ExprLanguage::kRust) { | |
| // Rust uses '?' for error handling. | |
| SetError(token, "Rust '?' operators are not supported."); | |
| return nullptr; | |
| } | |
| // "If true" block. | |
| auto then = ParseExpression(0); | |
| if (has_error()) | |
| return nullptr; | |
| // Colon. | |
| Consume(ExprTokenType::kColon, "Expected ':' for previous '?'."); | |
| if (has_error()) | |
| return nullptr; | |
| // Else block. | |
| auto else_then = ParseExpression(0); | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<ConditionExprNode>( | |
| std::vector<ConditionExprNode::Pair>{{std::move(left), std::move(then)}}, | |
| std::move(else_then)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::LiteralPrefix(const ExprToken& token) { | |
| return fxl::MakeRefCounted<LiteralExprNode>(token); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::UnaryPrefix(const ExprToken& token) { | |
| // Allow empty expressions during parse to give a better error message below. | |
| auto inner = ParseExpression(kPrecedenceUnary, EmptyExpression::kAllow); | |
| if (!has_error() && !inner) | |
| SetError(token, fxl::StringPrintf("Expected expression for '%s'.", token.value().c_str())); | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<UnaryOpExprNode>(token, std::move(inner)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::NamePrefix(const ExprToken& token) { | |
| // Handles names and "::" which precedes names. This could be a typename ("int", or | |
| // "::std::vector<int>"), a variable name ("i", "std::basic_string<char>::npos"), and can also | |
| // include special names like "$plt(zx_foo_bar)" | |
| // Back up so the current token is the first component of the name so we can hand-off to the | |
| // specialized name parser. | |
| FX_DCHECK(cur_ > 0); | |
| cur_--; | |
| if (token.type() == ExprTokenType::kConst || token.type() == ExprTokenType::kVolatile || | |
| token.type() == ExprTokenType::kRestrict) { | |
| // These start a type name, force type parsing mode. | |
| fxl::RefPtr<Type> type = ParseType(fxl::RefPtr<Type>()); | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<TypeExprNode>(std::move(type)); | |
| } | |
| ParseNameResult result = ParseName(true); | |
| if (has_error()) | |
| return nullptr; | |
| if (result.type) | |
| return fxl::MakeRefCounted<TypeExprNode>(std::move(result.type)); | |
| return fxl::MakeRefCounted<IdentifierExprNode>(std::move(result.ident)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::StarPrefix(const ExprToken& token) { | |
| fxl::RefPtr<ExprNode> right = ParseExpression(kPrecedenceUnary); | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<DereferenceExprNode>(std::move(right)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::CastPrefix(const ExprToken& token) { | |
| CastType cast_type = | |
| token.type() == ExprTokenType::kReinterpretCast ? CastType::kReinterpret : CastType::kStatic; | |
| // "<" after reinterpret_cast. | |
| ExprToken left_angle = Consume(ExprTokenType::kLess, "Expected '< >' after cast."); | |
| if (has_error()) | |
| return nullptr; | |
| // Type name. | |
| fxl::RefPtr<Type> dest_type = ParseType(fxl::RefPtr<Type>()); | |
| if (has_error()) | |
| return nullptr; | |
| // ">" after type name. | |
| Consume(ExprTokenType::kGreater, "Expected '>' to match.", left_angle); | |
| if (has_error()) | |
| return nullptr; | |
| // "(" containing expression. | |
| ExprToken left_paren = Consume(ExprTokenType::kLeftParen, "Expected '(' for cast."); | |
| if (has_error()) | |
| return nullptr; | |
| // Expression to be casted. | |
| fxl::RefPtr<ExprNode> expr = ParseExpression(0); | |
| if (has_error()) | |
| return nullptr; | |
| // ")" at end. | |
| Consume(ExprTokenType::kRightParen, "Expected ')' to match.", left_paren); | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<CastExprNode>( | |
| cast_type, fxl::MakeRefCounted<TypeExprNode>(std::move(dest_type)), std::move(expr)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::SizeofPrefix(const ExprToken& token) { | |
| // "(" containing expression. | |
| ExprToken left_paren = Consume(ExprTokenType::kLeftParen, "Expected '(' for cast."); | |
| if (has_error()) | |
| return nullptr; | |
| // Expression to be sized. | |
| fxl::RefPtr<ExprNode> expr = ParseExpression(0); | |
| if (has_error()) | |
| return nullptr; | |
| // ")" at end. | |
| Consume(ExprTokenType::kRightParen, "Expected ')' to match.", left_paren); | |
| if (has_error()) | |
| return nullptr; | |
| return fxl::MakeRefCounted<SizeofExprNode>(std::move(expr)); | |
| } | |
| fxl::RefPtr<ExprNode> ExprParser::IfPrefix(const ExprToken& token) { | |
| bool require_parens = language_ == ExprLanguage::kC; // Rust doesn't need parens. | |
| std::vector<ConditionExprNode::Pair> conditions; | |
| fxl::RefPtr<ExprNode> else_then; | |
| // This is the language-specific way to parse a conditional block. | |
| std::function<fxl::RefPtr<ExprNode>()> parse_block; | |
| switch (language_) { | |
| case ExprLanguage::kRust: | |
| // Rust requires {}. | |
| parse_block = [this]() { return ParseBlock(BlockDelimiter::kExplicit); }; | |
| break; | |
| case ExprLanguage::kC: | |
| // C allows a standalone statement or a block. | |
| parse_block = [this]() { | |
| return ParseExpression(0, EmptyExpression::kAllow, StatementEnd::kExplicit); | |
| }; | |
| break; | |
| } | |
| while (true) { | |
| // "(" at beginning of condition. | |
| ExprToken left_paren; | |
| if (require_parens) { | |
| left_paren = Consume(ExprTokenType::kLeftParen, "Expected '(' for if."); | |
| if (has_error()) | |
| return nullptr; | |
| } | |
| // Conditional expression. | |
| auto condition = ParseExpression(0); | |
| if (has_error()) | |
| return nullptr; | |
| // ")" at end of condition. | |
| if (require_parens) { | |
| Consume(ExprTokenType::kRightParen, "Expected ')' to match.", left_paren); | |
| if (has_error()) | |
| return nullptr; | |
| } | |
| // Block to execute on match. | |
| conditions.emplace_back(std::move(condition), parse_block()); | |
| if (has_error()) | |
| return nullptr; | |
| // Can be optionally followed by an "else". | |
| if (!LookAhead(ExprTokenType::kElse)) | |
| break; // No "else", done with condition. | |
| // Handle "else". | |
| Consume(); // Consume the "else" we already identified. | |
| if (LookAhead(ExprTokenType::kIf)) { | |
| // "else if", wrap around to doing a new condition for this arm. | |
| Consume(); // Consume the "if" we already identified. | |
| continue; | |
| } else { | |
| // Standalone "else", get the expression for it. | |
| else_then = parse_block(); | |
| if (has_error()) | |
| return nullptr; | |
| break; | |
| } | |
| } | |
| return fxl::MakeRefCounted<ConditionExprNode>(std::move(conditions), std::move(else_then)); | |
| } | |
| bool ExprParser::LookAhead(ExprTokenType type) const { | |
| if (at_end()) | |
| return false; | |
| return cur_token().type() == type; | |
| } | |
| const ExprToken& ExprParser::Consume() { | |
| if (at_end()) | |
| return kInvalidToken; | |
| return tokens_[cur_++]; | |
| } | |
| const ExprToken& ExprParser::Consume(ExprTokenType type, const char* error_msg, | |
| const ExprToken& error_token) { | |
| FX_DCHECK(!has_error()); // Should have error-checked before calling. | |
| if (at_end()) { | |
| SetError(error_token, std::string(error_msg) + " Hit the end of input instead."); | |
| return kInvalidToken; | |
| } | |
| if (cur_token().type() == type) | |
| return Consume(); | |
| SetError(error_token.type() == ExprTokenType::kInvalid ? cur_token() : error_token, error_msg); | |
| return kInvalidToken; | |
| } | |
| ExprToken ExprParser::ConsumeWithShiftTokenConversion() { | |
| if (IsCurTokenShiftRight()) { | |
| // Eat both ">" operators and synthesize a shift operator. | |
| const ExprToken& first = Consume(); | |
| Consume(); | |
| return ExprToken(ExprTokenType::kShiftRight, ">>", first.byte_offset()); | |
| } | |
| return Consume(); | |
| } | |
| void ExprParser::ConsumeCVQualifier(std::vector<DwarfTag>* qual) { | |
| while (!at_end()) { | |
| const ExprToken& token = cur_token(); | |
| DwarfTag tag = DwarfTag::kNone; | |
| if (token.type() == ExprTokenType::kConst) { | |
| tag = DwarfTag::kConstType; | |
| } else if (token.type() == ExprTokenType::kVolatile) { | |
| tag = DwarfTag::kVolatileType; | |
| } else if (token.type() == ExprTokenType::kRestrict) { | |
| tag = DwarfTag::kRestrictType; | |
| } else { | |
| // Not a qualification token, done. | |
| return; | |
| } | |
| // Can't have duplicates. | |
| if (std::find(qual->begin(), qual->end(), tag) != qual->end()) { | |
| SetError(token, | |
| fxl::StringPrintf("Duplicate '%s' type qualification.", token.value().c_str())); | |
| return; | |
| } | |
| qual->push_back(tag); | |
| Consume(); | |
| } | |
| } | |
| fxl::RefPtr<Type> ExprParser::ApplyQualifiers(fxl::RefPtr<Type> input, | |
| const std::vector<DwarfTag>& qual) { | |
| fxl::RefPtr<Type> type = std::move(input); | |
| // Apply the qualifiers in reverse order so the rightmost one is applied first. | |
| for (auto iter = qual.rbegin(); iter != qual.rend(); ++iter) { | |
| type = fxl::MakeRefCounted<ModifiedType>(*iter, std::move(type)); | |
| } | |
| return type; | |
| } | |
| void ExprParser::SetError(const ExprToken& token, Err err) { | |
| err_ = std::move(err); | |
| error_token_ = token; | |
| } | |
| void ExprParser::SetError(const ExprToken& token, std::string msg) { | |
| err_ = Err(std::move(msg)); | |
| error_token_ = token; | |
| } | |
| void ExprParser::SetErrorAtCur(std::string msg) { | |
| if (at_end()) | |
| SetError(ExprToken(), msg + " Hit the end of input instead."); | |
| else | |
| SetError(cur_token(), msg); | |
| } | |
| bool ExprParser::IsCurTokenShiftRight() const { | |
| if (tokens_.size() < 2 || cur_ > tokens_.size() - 2) | |
| return false; // Not enough room for two tokens. | |
| if (tokens_[cur_].type() != ExprTokenType::kGreater || | |
| tokens_[cur_ + 1].type() != ExprTokenType::kGreater) | |
| return false; // Not two ">" in a row. | |
| // They must also be next to each other with no space. | |
| return IsAdjacent(tokens_[cur_], tokens_[cur_ + 1]); | |
| } | |
| int ExprParser::CurPrecedenceWithShiftTokenConversion() const { | |
| if (IsCurTokenShiftRight()) | |
| return kPrecedenceShift; | |
| return DispatchForToken(cur_token()).precedence; | |
| } | |
| // static | |
| const ExprParser::DispatchInfo& ExprParser::DispatchForToken(const ExprToken& token) { | |
| size_t index = static_cast<size_t>(token.type()); | |
| FX_DCHECK(index < std::size(kDispatchInfo)); | |
| return kDispatchInfo[index]; | |
| } | |
| } // namespace zxdb |