blob: 50638925a430d666ca6bb9e89e98cc263df04aa1 [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/shell/parser/ast.h"
#include <lib/syslog/cpp/macros.h>
#include <limits>
#include <sstream>
#include "third_party/icu/source/common/unicode/utf8.h"
namespace shell::parser::ast {
namespace {
uint8_t CharFromHex(char ch) {
if (ch - '0' <= 9) {
return ch - '0';
} else if (ch - 'A' <= 5) {
return ch - 'A' + 10;
} else {
return ch - 'a' + 10;
}
}
} // namespace
const std::vector<std::shared_ptr<Node>> Terminal::kEmptyChildren = {};
std::string Terminal::ToString(std::string_view unit) const {
std::string prefix;
return prefix + "'" + std::string(unit.substr(start(), size_)) + "'";
}
std::string Nonterminal::ToString(std::string_view unit) const {
std::stringstream out;
out << Name() << "(";
std::string spacer = "";
for (const auto& child : Children()) {
out << spacer << child->ToString(unit);
spacer = " ";
}
out << ")";
return out.str();
}
std::string Error::ToString(std::string_view unit) const { return "E[" + message_ + "]"; }
DecimalGroup::DecimalGroup(size_t start, size_t size, std::string_view content)
: Terminal(start, size, content), digits_(content.size()) {
digits_ = content.size();
for (const char& ch : content) {
uint64_t old_value = value_;
value_ *= 10;
FX_DCHECK(value_ >= old_value) << "Insufficient precision to store DecimalGroup value.";
value_ += ch - '0';
}
}
HexGroup::HexGroup(size_t start, size_t size, std::string_view content)
: Terminal(start, size, content), digits_(content.size()) {
digits_ = content.size();
FX_DCHECK(digits_ <= 16) << "Insufficient precision to store HexGroup value.";
for (const char& ch : content) {
value_ *= 16;
value_ += CharFromHex(ch);
}
}
std::string EscapeSequence::Decode(std::string_view sequence) {
if (sequence == "\\n") {
return "\n";
} else if (sequence == "\\t") {
return "\t";
} else if (sequence == "\\r") {
return "\r";
} else if (sequence == "\\\"") {
return "\"";
} else if (sequence == "\\\\") {
return "\\";
} else if (sequence == "\\\n") {
// TODO: Do something fancy for escaped newlines?
return "\n";
} else if (sequence.size() == 8 && sequence.substr(0, 2) == "\\u") {
uint32_t codepoint = CharFromHex(sequence[2]) << 20;
codepoint += CharFromHex(sequence[3]) << 16;
codepoint += CharFromHex(sequence[4]) << 12;
codepoint += CharFromHex(sequence[5]) << 8;
codepoint += CharFromHex(sequence[6]) << 4;
codepoint += CharFromHex(sequence[7]);
// TODO: Figure out how to handle a bad unicode character. U8_APPEND_UNSAFE should just give us
// some garbage we can ignore for now.
uint8_t bytes[4] = {0, 0, 0, 0};
size_t length = 0;
U8_APPEND_UNSAFE(bytes, length, codepoint);
return std::string(reinterpret_cast<char*>(bytes), length);
} else {
// We might get odd things if we're in an error path, so fail gently.
return "";
}
}
Integer::Integer(size_t start, std::vector<std::shared_ptr<Node>> children)
: Nonterminal(start, std::move(children)) {
for (const auto& child : Children()) {
if (auto hex = child->AsHexGroup()) {
auto next = value_ << hex->digits() * 4;
FX_DCHECK(next >= value_) << "Insufficient precision to store Integer value.";
value_ = next + hex->value();
} else if (auto dec = child->AsDecimalGroup()) {
auto old_value = value_;
for (size_t i = 0; i < dec->digits(); i++, value_ *= 10)
;
FX_DCHECK(value_ >= old_value) << "Insufficient precision to store Integer value.";
value_ += dec->value();
}
}
}
String::String(size_t start, std::vector<std::shared_ptr<Node>> children)
: Nonterminal(start, std::move(children)) {
for (const auto& child : Children()) {
if (auto entity = child->AsStringEntity()) {
value_ += entity->content();
}
}
}
VariableDecl::VariableDecl(size_t start, std::vector<std::shared_ptr<Node>> children)
: Nonterminal(start, std::move(children)) {
for (const auto& child : Children()) {
if (child->IsConst()) {
is_const_ = true;
} else if (auto expr = child->AsExpression()) {
expression_ = expr;
} else if (auto id = child->AsIdentifier()) {
identifier_ = id->identifier();
}
}
}
Identifier::Identifier(size_t start, std::vector<std::shared_ptr<Node>> children)
: Nonterminal(start, std::move(children)) {
for (const auto& child : Children()) {
if (auto ue = child->AsUnescapedIdentifier()) {
identifier_ = ue->identifier();
break;
}
}
}
Object::Object(size_t start, std::vector<std::shared_ptr<Node>> children)
: Nonterminal(start, std::move(children)) {
for (const auto& child : Children()) {
if (auto field = child->AsField()) {
fields_.push_back(field);
}
}
}
Field::Field(size_t start, std::vector<std::shared_ptr<Node>> children)
: Nonterminal(start, std::move(children)) {
bool seen_name = false;
for (const auto& child : Children()) {
if (seen_name == false) {
if (auto ident = child->AsIdentifier()) {
name_ = ident->identifier();
seen_name = true;
} else if (auto str = child->AsString()) {
name_ = str->value();
seen_name = true;
}
} else if (!child->IsError() && !child->IsFieldSeparator()) {
value_ = child.get();
}
}
}
Path::Path(size_t start, std::vector<std::shared_ptr<Node>> children)
: Nonterminal(start, std::move(children)) {
std::stringstream element;
is_local_ = true;
bool seen_element = false;
for (const auto& child : Children()) {
if (auto el = child->AsPathElement()) {
element << el->content();
} else if (child->IsPathSeparator()) {
if (element.tellp() > 0) {
seen_element = true;
if (element.str() != ".") {
elements_.push_back(element.str());
}
element.str("");
element.clear();
}
if (!seen_element) {
is_local_ = false;
}
}
}
if (element.tellp() > 0 && element.str() != ".") {
elements_.push_back(element.str());
}
}
AddSub::AddSub(size_t start, std::vector<std::shared_ptr<Node>> children)
: Nonterminal(start, std::move(children)) {
bool seen_op = false;
for (const auto& child : Children()) {
if (auto op = child->AsOperator()) {
seen_op = true;
if (op->op() == "-") {
type_ = kSubtract;
} else {
FX_DCHECK(op->op() == "+");
type_ = kAdd;
}
} else if (!child->IsError()) {
if (!seen_op) {
a_ = child.get();
} else {
b_ = child.get();
}
}
}
}
// Visit implementations
void Terminal::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitTerminal(*this); }
void Error::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitError(*this); }
void Const::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitConst(*this); }
void Var::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitVar(*this); }
void FieldSeparator::VisitVoid(NodeVisitor<void>* visitor) const {
visitor->VisitFieldSeparator(*this);
}
void DecimalGroup::VisitVoid(NodeVisitor<void>* visitor) const {
visitor->VisitDecimalGroup(*this);
}
void HexGroup::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitHexGroup(*this); }
void UnescapedIdentifier::VisitVoid(NodeVisitor<void>* visitor) const {
visitor->VisitUnescapedIdentifier(*this);
}
void StringEntity::VisitVoid(NodeVisitor<void>* visitor) const {
visitor->VisitStringEntity(*this);
}
void EscapeSequence::VisitVoid(NodeVisitor<void>* visitor) const {
visitor->VisitEscapeSequence(*this);
}
void PathElement::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitPathElement(*this); }
void PathEscape::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitPathEscape(*this); }
void PathSeparator::VisitVoid(NodeVisitor<void>* visitor) const {
visitor->VisitPathSeparator(*this);
}
void Operator::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitOperator(*this); }
void Nonterminal::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitNonterminal(*this); }
void Program::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitProgram(*this); }
void VariableDecl::VisitVoid(NodeVisitor<void>* visitor) const {
visitor->VisitVariableDecl(*this);
}
void Integer::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitInteger(*this); }
void String::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitString(*this); }
void Identifier::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitIdentifier(*this); }
void Object::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitObject(*this); }
void Field::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitField(*this); }
void Path::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitPath(*this); }
void AddSub::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitAddSub(*this); }
void Expression::VisitVoid(NodeVisitor<void>* visitor) const { visitor->VisitExpression(*this); }
} // namespace shell::parser::ast