| // 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 |