| // Copyright 2019 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/console/command.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| #include <stdlib.h> |
| |
| #include <regex> |
| #include <sstream> |
| #include <string_view> |
| #include <vector> |
| |
| #include "src/developer/shell/parser/ast.h" |
| #include "src/developer/shell/parser/parser.h" |
| |
| namespace shell::console { |
| |
| Command::Command() = default; |
| |
| Command::~Command() = default; |
| |
| namespace { |
| |
| // Walk a parse tree for errors and collect their messages into the given output stringstream. |
| void CollectErrors(const parser::ast::Node& node, std::stringstream* out) { |
| if (auto err = node.AsError()) { |
| if (out->tellp() > 0) { |
| (*out) << "\n"; |
| } |
| |
| (*out) << err->message(); |
| } else { |
| for (const auto& child : node.Children()) { |
| CollectErrors(*child, out); |
| } |
| } |
| } |
| |
| // Walk a parse tree for errors and collect their messages. |
| std::string CollectErrors(const parser::ast::Node& node) { |
| std::stringstream out; |
| CollectErrors(node, &out); |
| return out.str(); |
| } |
| |
| struct IdAndType { |
| AstBuilder::NodeId id; |
| llcpp::fuchsia::shell::ShellType type; |
| }; |
| |
| // Visitor for loading a parser AST into a FIDL AST. |
| class NodeASTVisitor : public parser::ast::NodeVisitor<IdAndType> { |
| public: |
| explicit NodeASTVisitor(AstBuilder* builder) : builder_(builder) {} |
| |
| IdAndType VisitNode(const parser::ast::Node& node) override { |
| FX_NOTREACHED() << "Parser produced unknown node type."; |
| return {}; |
| } |
| |
| IdAndType VisitProgram(const parser::ast::Program& node) override { |
| // TODO: Multiple statements. |
| for (const auto& child : node.Children()) { |
| if (auto ch = child->AsVariableDecl()) { |
| auto ret = VisitVariableDecl(*ch); |
| // Return the value of the variable to the command line when done evaluating. |
| builder_->AddEmitResult(builder_->AddVariable(ch->identifier())); |
| return ret; |
| } |
| } |
| |
| FX_NOTREACHED(); |
| return {}; |
| } |
| |
| IdAndType VisitVariableDecl(const parser::ast::VariableDecl& node) override { |
| IdAndType expression = node.expression()->Visit(this); |
| AstBuilder::NodeId id = builder_->AddVariableDeclaration( |
| node.identifier(), std::move(expression.type), expression.id, false); |
| |
| return {.id = id, .type = llcpp::fuchsia::shell::ShellType()}; |
| } |
| |
| IdAndType VisitInteger(const parser::ast::Integer& node) override { |
| AstBuilder::NodeId id = builder_->AddIntegerLiteral(node.value()); |
| llcpp::fuchsia::shell::BuiltinType builtin_type = llcpp::fuchsia::shell::BuiltinType::INTEGER; |
| llcpp::fuchsia::shell::BuiltinType* type_ptr = builder_->ManageCopyOf(&builtin_type); |
| llcpp::fuchsia::shell::ShellType type = |
| llcpp::fuchsia::shell::ShellType::WithBuiltinType(fidl::unowned_ptr(type_ptr)); |
| |
| return {.id = id, .type = std::move(type)}; |
| } |
| |
| IdAndType VisitIdentifier(const parser::ast::Identifier& node) override { |
| FX_NOTREACHED() << "Variable fetches are unimplemented." << node.identifier(); |
| return {}; |
| } |
| |
| IdAndType VisitPath(const parser::ast::Path& node) override { |
| FX_NOTREACHED() << "Paths are unimplemented."; |
| return {}; |
| } |
| |
| IdAndType VisitAddSub(const parser::ast::AddSub& node) override { |
| FX_DCHECK(node.type() == parser::ast::AddSub::kAdd) << "Subtraction is unimplemented."; |
| AstBuilder::NodeId a_id = node.a()->Visit(this).id; |
| IdAndType b_value = node.b()->Visit(this); |
| AstBuilder::NodeId b_id = b_value.id; |
| |
| AstBuilder::NodeId id = builder_->AddAddition(/*with_exceptions=*/false, a_id, b_id); |
| return {.id = id, .type = std::move(b_value.type)}; |
| } |
| |
| IdAndType VisitExpression(const parser::ast::Expression& node) override { |
| FX_DCHECK(node.Children().size() > 0); |
| return node.Children()[0]->Visit(this); |
| } |
| |
| IdAndType VisitString(const parser::ast::String& node) override { |
| AstBuilder::NodeId id = builder_->AddStringLiteral(node.value()); |
| llcpp::fuchsia::shell::BuiltinType builtin_type = llcpp::fuchsia::shell::BuiltinType::STRING; |
| llcpp::fuchsia::shell::BuiltinType* type_ptr = builder_->ManageCopyOf(&builtin_type); |
| llcpp::fuchsia::shell::ShellType type = |
| llcpp::fuchsia::shell::ShellType::WithBuiltinType(fidl::unowned_ptr(type_ptr)); |
| return {.id = id, .type = std::move(type)}; |
| } |
| |
| IdAndType VisitObject(const parser::ast::Object& node) override { |
| builder_->OpenObject(); |
| |
| for (const auto& field : node.fields()) { |
| field->Visit(this); |
| } |
| |
| auto result = builder_->CloseObject(); |
| AstBuilder::NodeId id = result.value_node; |
| |
| llcpp::fuchsia::shell::NodeId shell_id; |
| shell_id = result.schema_node; |
| llcpp::fuchsia::shell::NodeId* id_ptr = builder_->ManageCopyOf(&shell_id); |
| llcpp::fuchsia::shell::ShellType type = |
| llcpp::fuchsia::shell::ShellType::WithObjectSchema(fidl::unowned_ptr(id_ptr)); |
| |
| return {.id = id, .type = std::move(type)}; |
| } |
| |
| IdAndType VisitField(const parser::ast::Field& node) override { |
| IdAndType value = node.value()->Visit(this); |
| builder_->AddField(node.name(), value.id, std::move(value.type)); |
| return {}; |
| } |
| |
| private: |
| AstBuilder* builder_; |
| }; |
| |
| } // namespace |
| |
| bool Command::Parse(const std::string& line) { |
| if (line.empty()) { |
| return true; |
| } |
| |
| auto node = parser::Parse(line); |
| |
| FX_DCHECK(node) << "Error handling failed."; |
| |
| if (node->HasErrors()) { |
| parse_error_ = Err(ErrorType::kBadParse, CollectErrors(*node)); |
| return false; |
| } |
| |
| auto program = node->AsProgram(); |
| FX_DCHECK(program) << "Parse did not yield a program node!"; |
| |
| // TODO: Change the file ID to something useful. |
| AstBuilder builder(1); |
| NodeASTVisitor visitor(&builder); |
| IdAndType value = program->Visit(&visitor); |
| builder.SetRoot(value.id); |
| accumulated_nodes_ = std::move(builder); |
| |
| return true; |
| } |
| |
| } // namespace shell::console |