blob: 6b75802ab14b1411f4a54e31bfa963aea83714d0 [file] [log] [blame]
// Copyright 2020 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/operator_keyword.h"
#include <ctype.h>
#include <lib/syslog/cpp/macros.h>
#include <algorithm>
#include <utility>
namespace zxdb {
namespace {
constexpr size_t kMaxOps = 3; // Can have at most 3 operators in a row ("new[]").
// This list is searched in-order so more specific tokens (longer ones) need to go first.
const ExprTokenType kOverloadableOperators[][kMaxOps] = {
// clang-format off
// Operators with triple tokens,
{ExprTokenType::kNew, ExprTokenType::kLeftSquare, ExprTokenType::kRightSquare}, // new[]
{ExprTokenType::kDelete, ExprTokenType::kLeftSquare, ExprTokenType::kRightSquare}, // delete[]
// Operators with double tokens. Note that the tokenizer generates two tokens for ">>" because of
// C++'s ambiguity so we need to treat that as a double one.
{ExprTokenType::kLeftParen, ExprTokenType::kRightParen, ExprTokenType::kInvalid}, // operator()
{ExprTokenType::kLeftSquare, ExprTokenType::kRightSquare, ExprTokenType::kInvalid}, // operator[]
{ExprTokenType::kGreater, ExprTokenType::kGreater, ExprTokenType::kInvalid}, // operator>>
{ExprTokenType::kGreater, ExprTokenType::kGreaterEqual, ExprTokenType::kInvalid}, // operator>>=
// Operators with single tokens.
{ExprTokenType::kPlus, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kMinus, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kStar, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kSlash, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kPercent, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kCaret, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kAmpersand, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kBitwiseOr, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kTilde, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kBang, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kLess, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kGreater, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kPlusEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kMinusEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kStarEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kSlashEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kPercentEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kCaretEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kAndEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kOrEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kShiftLeft, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kShiftRight, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kShiftLeftEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kShiftRightEquals, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kEquality, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kInequality, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kLessEqual, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kGreaterEqual, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kDoubleAnd, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kLogicalOr, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kPlusPlus, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kMinusMinus, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kComma, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kArrowStar, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kArrow, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kNew, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
{ExprTokenType::kDelete, ExprTokenType::kInvalid, ExprTokenType::kInvalid},
// clang-format on
};
// Makes a name like "operator<" or "operator[]" given a squence of operator token types.
std::string MakeCanonicalOperatorName(const ExprTokenType types[], size_t count) {
std::string result = "operator";
for (size_t i = 0; i < count; i++) {
const std::string_view& op_str = RecordForTokenType(types[i]).static_value;
if (isalnum(op_str[0]))
result.push_back(' '); // Alphanumeric operators like "new" and "delete" need a space.
result.append(op_str);
}
return result;
}
// Validates that the given sequences of tokens matches the given sequence of types. Both input
// arrays must be at least |count| long.
bool TokenSequenceMatches(const ExprToken tokens[], const ExprTokenType match[], size_t count) {
for (size_t i = 0; i < count; i++) {
// All token values must be equal.
if (tokens[i].type() != match[i])
return false;
// The tokens must be sequential with no whitespace between them.
if (i > 0 && !tokens[i - 1].ImmediatelyPrecedes(tokens[i]))
return false;
}
return true;
}
} // namespace
OperatorKeywordResult ParseOperatorKeyword(const std::vector<ExprToken>& tokens,
size_t keyword_token) {
// The keyword token should always be "operator".
FX_DCHECK(tokens[keyword_token].type() == ExprTokenType::kOperator);
// Which operator will follow the keyword.
size_t tokens_begin = keyword_token + 1;
if (tokens_begin >= tokens.size())
return OperatorKeywordResult(); // Nothing following "operator".
// This is just brute-force. It could be optimized by sorting if needed but parsing operators is
// not performance-critical.
for (const auto& candidate : kOverloadableOperators) {
// See how many tokens this sequence needs to match (the number of non-invalid token types).
// This could be done in one loop combined with the below, but the logic becomes more difficult
// to follow.
size_t match_count = 0;
for (size_t i = 0; i < kMaxOps && candidate[i] != ExprTokenType::kInvalid; i++)
match_count++;
if (tokens_begin + match_count > tokens.size())
continue; // Not enough room to match this one.
if (TokenSequenceMatches(&tokens[tokens_begin], candidate, match_count)) {
return OperatorKeywordResult{
.success = true,
.canonical_name = MakeCanonicalOperatorName(candidate, match_count),
.end_token = tokens_begin + match_count,
};
}
}
return OperatorKeywordResult();
}
} // namespace zxdb