blob: 72aa1295a94feeb83e56f33b5170e33e44a063a4 [file] [log] [blame] [edit]
// 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 <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"
#include "src/lib/syslog/cpp/logger.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();
}
// Visitor for loading a parser AST into a FIDL AST.
class NodeASTVisitor : public parser::ast::NodeVisitor {
public:
explicit NodeASTVisitor(AstBuilder* builder) : builder_(builder) {}
AstBuilder::NodeId id() const { return id_; }
void VisitNode(const parser::ast::Node& node) override {
FX_NOTREACHED() << "Parser produced unknown node type.";
}
void VisitProgram(const parser::ast::Program& node) override {
// TODO: Multiple statements.
for (const auto& child : node.Children()) {
if (auto ch = child->AsVariableDecl()) {
return VisitVariableDecl(*ch);
}
}
FX_NOTREACHED();
}
void VisitVariableDecl(const parser::ast::VariableDecl& node) override {
node.expression()->Visit(this);
id_ = builder_->AddVariableDeclaration(node.identifier(), std::move(type_), id_, false);
}
void VisitInteger(const parser::ast::Integer& node) override {
id_ = builder_->AddIntegerLiteral(node.value());
llcpp::fuchsia::shell::BuiltinType type = llcpp::fuchsia::shell::BuiltinType::INTEGER;
llcpp::fuchsia::shell::BuiltinType* type_ptr = builder_->ManageCopyOf(&type);
type_ = llcpp::fuchsia::shell::ShellType::WithBuiltinType(fidl::unowned_ptr(type_ptr));
}
void VisitIdentifier(const parser::ast::Identifier& node) override {
FX_NOTREACHED() << "Variable fetches are unimplemented." << node.identifier();
}
void VisitPath(const parser::ast::Path& node) override {
FX_NOTREACHED() << "Paths are unimplemented.";
}
void VisitExpression(const parser::ast::Expression& node) override {
FX_DCHECK(node.Children().size() > 0);
node.Children()[0]->Visit(this);
}
void VisitString(const parser::ast::String& node) override {
id_ = builder_->AddStringLiteral(node.value());
llcpp::fuchsia::shell::BuiltinType type = llcpp::fuchsia::shell::BuiltinType::STRING;
llcpp::fuchsia::shell::BuiltinType* type_ptr = builder_->ManageCopyOf(&type);
type_ = llcpp::fuchsia::shell::ShellType::WithBuiltinType(fidl::unowned_ptr(type_ptr));
}
void VisitObject(const parser::ast::Object& node) override {
builder_->OpenObject();
for (const auto& field : node.fields()) {
field->Visit(this);
}
auto result = builder_->CloseObject();
id_ = result.value_node;
llcpp::fuchsia::shell::NodeId id;
id = result.schema_node;
llcpp::fuchsia::shell::NodeId* id_ptr = builder_->ManageCopyOf(&id);
type_ = llcpp::fuchsia::shell::ShellType::WithObjectSchema(fidl::unowned_ptr(id_ptr));
}
void VisitField(const parser::ast::Field& node) override {
node.value()->Visit(this);
builder_->AddField(node.name(), id_, std::move(type_));
}
private:
AstBuilder* builder_;
AstBuilder::NodeId id_;
llcpp::fuchsia::shell::ShellType type_;
};
} // namespace
bool Command::Parse(const std::string& line) {
if (line.empty()) {
return true;
}
auto node = parser::Parse(line);
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);
program->Visit(&visitor);
builder.SetRoot(visitor.id());
accumulated_nodes_ = std::move(builder);
return true;
}
} // namespace shell::console