blob: e4c1afe7fbdd5b441c22de29ab253c502583c3b2 [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 <stdlib.h>
#include <regex>
#include <string_view>
#include <vector>
namespace shell::console {
Command::Command() = default;
Command::~Command() = default;
namespace {
// Temporary parser. Delete when we have something good.
bool IsIdentifierChar(char ch) { return isalnum(ch) || ch == '_'; }
class Marker {
public:
Marker(const std::string& str) : source_(str) {}
Marker(const Marker& m, size_t offset) : source_(m.source_.data() + offset) {}
Marker MatchNextIdentifier(const std::string& keyword);
Marker MatchAnyIdentifier(std::string* identifier);
Marker MatchSpecial(const std::string& special);
Marker MatchIntegerLiteral(int64_t* val);
const char* data() { return source_.data(); }
bool operator==(const Marker& other) { return other.source_.data() == source_.data(); }
private:
std::string_view source_;
};
Marker Marker::MatchNextIdentifier(const std::string& keyword) {
size_t pos = 0;
while (std::isspace(source_[pos])) {
pos++;
}
if (source_.find(keyword, pos) == pos &&
(pos == source_.length() || !IsIdentifierChar(source_[pos + keyword.length()]))) {
return Marker(*this, pos + keyword.length());
}
return *this;
}
Marker Marker::MatchAnyIdentifier(std::string* identifier) {
size_t pos = 0;
while (std::isspace(source_[pos])) {
pos++;
}
if (isdigit(source_[pos] || !IsIdentifierChar(source_[pos]))) {
return *this;
}
while (IsIdentifierChar(source_[pos])) {
identifier->push_back(source_[pos]);
pos++;
}
return Marker(*this, pos);
}
Marker Marker::MatchSpecial(const std::string& special) {
size_t pos = 0;
while (std::isspace(source_[pos])) {
pos++;
}
if (source_.find(special, pos) != pos) {
return *this;
}
return Marker(*this, pos + special.length());
}
Marker Marker::MatchIntegerLiteral(int64_t* val) {
size_t pos = 0;
while (std::isspace(source_[pos])) {
pos++;
}
std::string number;
const char* begin = source_.data() + pos;
const char* curr = begin;
while (isdigit(*curr)) {
number += *curr;
curr++;
}
if (*curr == '_') {
if (curr - begin > 3) {
return *this;
}
do {
++curr;
for (int i = 0; i < 3; i++) {
if (!isdigit(*curr)) {
return *this;
}
number += *curr;
curr++;
}
} while (*curr == '_');
}
if (curr == begin) {
return *this;
}
size_t idx;
*val = std::stoll(number, &idx, 10);
if (errno == ERANGE || idx == source_.length()) {
return *this;
}
return Marker(*this, idx);
}
// The result of a parsing step. Contains an updated position (marker()), the node_id of the
// topmost node (node_id()), the type of that node (type()), and whether there was an error
// (result()).
class ParseResult {
public:
ParseResult(const Marker& marker, const Err& err, llcpp::fuchsia::shell::ShellType&& type,
uint64_t node_id)
: marker_(marker), err_(err), type_(std::move(type)), node_id_(node_id) {}
Marker marker() { return marker_; }
Err result() { return err_; }
llcpp::fuchsia::shell::ShellType type() { return std::move(type_); }
uint64_t node_id() { return node_id_; }
private:
Marker marker_;
Err err_;
llcpp::fuchsia::shell::ShellType type_;
uint64_t node_id_;
};
class Parser {
public:
Parser(AstBuilder* builder) : builder_(builder) {}
ParseResult ParseValue(Marker m) {
// int64 Integer literals for now.
int64_t i;
Marker after_int = m.MatchIntegerLiteral(&i);
if (after_int == m) {
return ParseResult(m, Err(kBadParse, "Integer not found"), builder_->undef(), 0);
}
uint64_t node_id = builder_->AddIntegerLiteral(i);
llcpp::fuchsia::shell::BuiltinType type = llcpp::fuchsia::shell::BuiltinType::INTEGER;
llcpp::fuchsia::shell::BuiltinType* type_ptr = builder_->ManageCopyOf(&type);
return ParseResult(after_int, Err(),
llcpp::fuchsia::shell::ShellType::WithBuiltinType(fidl::unowned(type_ptr)),
node_id);
}
ParseResult ParseSimpleExpression(Marker m) {
// Skip some levels for now
return ParseValue(m);
}
ParseResult ParseExpression(Marker m) { return ParseSimpleExpression(m); }
ParseResult ParseVariableDecl(Marker m) {
bool is_const = false;
Marker end_var = m.MatchNextIdentifier("var");
if (end_var == m) {
end_var = m.MatchNextIdentifier("const");
if (end_var == m) {
return ParseResult(m, Err(kBadParse, "Keyword var or const not found"), builder_->undef(),
0);
}
is_const = true;
}
std::string identifier;
Marker end_ident = end_var.MatchAnyIdentifier(&identifier);
if (end_ident == end_var) {
return ParseResult(m, Err(kBadParse, "Identifier not found"), builder_->undef(), 0);
}
Marker end_equals = end_ident.MatchSpecial("=");
if (end_equals == end_ident) {
return ParseResult(m, Err(kBadParse, "'=' not found"), builder_->undef(), 0);
}
ParseResult result = ParseExpression(end_equals);
if (!result.result().ok()) {
return result;
}
uint64_t node_id =
builder_->AddVariableDeclaration(identifier, result.type(), result.node_id(), is_const);
return ParseResult(result.marker(), Err(), builder_->undef(), node_id);
}
private:
AstBuilder* builder_;
};
} // namespace
bool Command::Parse(const std::string& line) {
if (line.empty()) {
return true;
}
Marker marker(line);
AstBuilder builder;
Parser p(&builder);
ParseResult result = p.ParseVariableDecl(marker);
builder.SetRoot(result.node_id());
parse_error_ = result.result();
if (parse_error_.ok()) {
accumulated_nodes_ = std::move(builder);
return true;
}
return false;
}
} // namespace shell::console