blob: 740ea27914e6fc3e2e0db7bd5d50e6f91fedb833 [file] [log] [blame]
// 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