blob: ff7e011aee7565dd36693458928d9ab144a6a6f8 [file] [log] [blame]
// 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 "garnet/bin/zxdb/expr/expr_parser.h"
#include <algorithm>
#include "garnet/bin/zxdb/expr/name_lookup.h"
#include "garnet/bin/zxdb/expr/template_type_extractor.h"
#include "garnet/bin/zxdb/symbols/modified_type.h"
#include "lib/fxl/arraysize.h"
#include "lib/fxl/logging.h"
#include "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 kPrecedenceUnary = 150; // ++ -- +a -a ! ~ *a &a
constexpr int kPrecedenceCallAccess = 160; // () . -> []
//constexpr int kPrecedenceScope = 170; // :: (Highest precedence)
// clang-format on
} // 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[] = {
{nullptr, nullptr, -1}, // kInvalid
{&ExprParser::NamePrefix, nullptr, -1}, // kName
{&ExprParser::LiteralPrefix, nullptr, -1}, // kInteger
{nullptr, &ExprParser::BinaryOpInfix, kPrecedenceAssignment}, // kEquals
{nullptr, &ExprParser::BinaryOpInfix, kPrecedenceEquality}, // kEqualsEquals
{nullptr, &ExprParser::DotOrArrowInfix, kPrecedenceCallAccess}, // kDot
{nullptr, nullptr, -1}, // kComma
{&ExprParser::StarPrefix, nullptr, kPrecedenceUnary}, // 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
{nullptr, &ExprParser::LessInfix, kPrecedenceUnary}, // kLess
{nullptr, nullptr, -1}, // kGreater
{&ExprParser::MinusPrefix, nullptr, -1}, // kMinus
{nullptr, nullptr, -1}, // kPlus (currently unhandled)
{&ExprParser::NamePrefix, nullptr, -1}, // kColonColon
{&ExprParser::LiteralPrefix, nullptr, -1}, // kTrue
{&ExprParser::LiteralPrefix, nullptr, -1}, // kFalse
{&ExprParser::NamePrefix, nullptr, -1}, // kConst
{&ExprParser::NamePrefix, nullptr, -1}, // kVolatile
{&ExprParser::NamePrefix, nullptr, -1}, // kRestrict
};
// clang-format on
// static
const ExprToken ExprParser::kInvalidToken;
ExprParser::ExprParser(std::vector<ExprToken> tokens,
NameLookupCallback name_lookup)
: name_lookup_callback_(std::move(name_lookup)),
tokens_(std::move(tokens)) {
static_assert(arraysize(ExprParser::kDispatchInfo) ==
static_cast<int>(ExprTokenType::kNumTypes),
"kDispatchInfo needs updating to match ExprTokenType");
}
fxl::RefPtr<ExprNode> ExprParser::Parse() {
auto result = ParseExpression(0);
// 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;
}
if (!result && !has_error()) {
SetError(ExprToken(), "No input to parse.");
return nullptr;
}
return result;
}
fxl::RefPtr<ExprNode> ExprParser::ParseExpression(int precedence) {
if (at_end())
return nullptr;
const ExprToken& token = Consume();
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;
while (!at_end() && precedence < DispatchForToken(cur_token()).precedence) {
const ExprToken& next_token = Consume();
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;
}
return left;
}
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 | other-identifier
//
// type-name := [ scope-name "::" ] identifier [ "<" template-list ">" ]
//
// 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, // Inital state with no previous context.
kColonColon, // Just saw a "::", expecting a name next.
kType, // Idenfitier is a type.
kTemplate, // Idenfitier 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;
result.ident.AppendComponent(token, ExprToken()); // Append "::".
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 ">".
const ExprToken& template_end =
Consume(ExprTokenType::kGreater, token, "Expected '>' to match.");
if (has_error())
return ParseNameResult();
// Construct a replacement for the last component of the identifier
// with the template arguments added.
Identifier::Component& back = result.ident.components().back();
back = Identifier::Component(back.separator(), back.name(), token,
std::move(list), template_end);
// The thing we just made is either a type or a name, look it up.
if (name_lookup_callback_) {
NameLookupResult lookup = name_lookup_callback_(result.ident);
switch (lookup.kind) {
case NameLookupResult::kType:
mode = kType;
result.type = std::move(lookup.type);
break;
case NameLookupResult::kNamespace:
case NameLookupResult::kTemplate:
// The lookup shouldn't tell us a template name or namespace for
// something that has template parameters.
FXL_NOTREACHED();
// Fall through to "other" case for fallback.
case NameLookupResult::kOther:
mode = kOtherName;
break;
}
} else {
mode = kAnything;
}
continue; // Don't Consume() since we already ate the token.
}
case ExprTokenType::kName: {
// 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 = Identifier(token);
} else if (mode == kColonColon) {
FXL_DCHECK(!result.ident.components().empty());
result.ident.components().back().set_name(cur_token());
} 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_) {
NameLookupResult lookup = name_lookup_callback_(result.ident);
switch (lookup.kind) {
case NameLookupResult::kNamespace:
mode = kNamespace;
break;
case NameLookupResult::kTemplate:
mode = kTemplate;
break;
case NameLookupResult::kType:
mode = kType;
result.type = std::move(lookup.type);
break;
case NameLookupResult::kOther:
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) {
// When we found a type, add on any trailing modifiers like "*".
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:
FXL_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();
}
FXL_NOTREACHED();
SetError(ExprToken(), "Internal error.");
return ParseNameResult();
}
fxl::RefPtr<Type> ExprParser::ParseType(fxl::RefPtr<Type> optional_base) {
// 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.
fxl::RefPtr<Type> type;
std::vector<DwarfTag> type_qual;
if (optional_base) {
// Type name already known, start parsing after it.
type = std::move(optional_base);
} 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.
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);
}
// 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,
LazySymbol(std::move(type)));
} else if (token.type() == ExprTokenType::kAmpersand) {
type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kReferenceType,
LazySymbol(std::move(type)));
} else if (token.type() == ExprTokenType::kDoubleAnd) {
type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kRvalueReferenceType,
LazySymbol(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;
}
// 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) {
SetError(cur_token(), "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() && !right)
SetError(token, "Expected expression for '&'.");
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);
fxl::RefPtr<ExprNode> right = ParseExpression(dispatch.precedence);
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.
fxl::RefPtr<ExprNode> right = ParseExpression(kPrecedenceCallAccess);
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, token, "Expected ')' to match.");
if (has_error())
return nullptr;
if (const TypeExprNode* type_expr = expr->AsType()) {
// Convert "(TypeName)..." into a cast.
auto cast_expr = ParseExpression(kPrecedenceCallAccess);
if (has_error())
return nullptr;
fxl::RefPtr<TypeExprNode> type_ref(const_cast<TypeExprNode*>(type_expr));
return fxl::MakeRefCounted<CastExprNode>(CastType::kC, std::move(type_ref),
std::move(cast_expr));
}
return expr;
}
fxl::RefPtr<ExprNode> ExprParser::LeftParenInfix(fxl::RefPtr<ExprNode> left,
const ExprToken& token) {
// "(" as an infix is a function call. In this case, expect the thing on the
// left to be an identifier which is the name of the function.
const IdentifierExprNode* left_ident_node = left->AsIdentifier();
if (!left_ident_node) {
SetError(token, "Unexpected '('.");
return nullptr;
}
// Const cast is required because the type conversions only have const
// versions, although our object is not const.
Identifier name =
const_cast<IdentifierExprNode*>(left_ident_node)->TakeIdentifier();
// Read the function parameters.
std::vector<fxl::RefPtr<ExprNode>> args =
ParseExpressionList(ExprTokenType::kRightParen);
if (has_error())
return nullptr;
Consume(ExprTokenType::kRightParen, token, "Expected ')' to match.");
return fxl::MakeRefCounted<FunctionCallExprNode>(std::move(name),
std::move(args));
}
fxl::RefPtr<ExprNode> ExprParser::LeftSquareInfix(fxl::RefPtr<ExprNode> left,
const ExprToken& token) {
auto inner = ParseExpression(0);
if (!has_error() && !inner)
SetError(token, "Expected expression inside '['.");
if (!has_error())
Consume(ExprTokenType::kRightSquare, token, "Expected ']' to match.");
if (has_error())
return nullptr;
return fxl::MakeRefCounted<ArrayAccessExprNode>(std::move(left),
std::move(inner));
}
fxl::RefPtr<ExprNode> ExprParser::LessInfix(fxl::RefPtr<ExprNode> left,
const ExprToken& token) {
SetError(token, "Comparisons not supported yet.");
return nullptr;
}
fxl::RefPtr<ExprNode> ExprParser::LiteralPrefix(const ExprToken& token) {
return fxl::MakeRefCounted<LiteralExprNode>(token);
}
fxl::RefPtr<ExprNode> ExprParser::GreaterInfix(fxl::RefPtr<ExprNode> left,
const ExprToken& token) {
SetError(token, "Comparisons not supported yet.");
return nullptr;
}
fxl::RefPtr<ExprNode> ExprParser::MinusPrefix(const ExprToken& token) {
// Currently we only implement "-" as a prefix which is for unary "-" when
// you type "-5" or "-foo[6]". An infix version would be needed to parse the
// binary operator for "a - 6".
auto inner = ParseExpression(kPrecedenceUnary);
if (!has_error() && !inner)
SetError(token, "Expected expression for '-'.");
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>") or a variable name ("i",
// "std::basic_string<char>::npos").
// Back up so the current token is the first component of the name so we
// can hand-off to the specialized name parser.
FXL_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() && !right)
SetError(token, "Expected expression for '*'.");
if (has_error())
return nullptr;
return fxl::MakeRefCounted<DereferenceExprNode>(std::move(right));
}
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 ExprToken& error_token,
const char* error_msg) {
FXL_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, error_msg);
return kInvalidToken;
}
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, LazySymbol(std::move(type)));
}
return type;
}
void ExprParser::SetError(const ExprToken& token, std::string msg) {
err_ = Err(std::move(msg));
error_token_ = token;
}
// static
const ExprParser::DispatchInfo& ExprParser::DispatchForToken(
const ExprToken& token) {
size_t index = static_cast<size_t>(token.type());
FXL_DCHECK(index < arraysize(kDispatchInfo));
return kDispatchInfo[index];
}
} // namespace zxdb