| // 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. |
| |
| #ifndef SRC_DEVELOPER_SHELL_PARSER_AST_H_ |
| #define SRC_DEVELOPER_SHELL_PARSER_AST_H_ |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <variant> |
| #include <vector> |
| |
| namespace shell::parser::ast { |
| |
| class Terminal; |
| class Nonterminal; |
| class Error; |
| class Program; |
| class VariableDecl; |
| class Identifier; |
| class Integer; |
| class Expression; |
| |
| // A node in our AST. |
| class Node { |
| public: |
| explicit Node(size_t start) : start_(start) {} |
| virtual ~Node() = default; |
| |
| size_t start() const { return start_; } |
| |
| // Child nodes of this node. Always empty for terminals, may be empty for non-terminals. |
| virtual const std::vector<std::shared_ptr<Node>>& Children() const = 0; |
| |
| // Create an s-expression-like string representation of this node. We don't store the parsed text |
| // in the node itself so we must be passed the original parsed string. |
| virtual std::string ToString(std::string_view unit) const = 0; |
| |
| // Number of characters this node corresponds to in the original text. |
| virtual size_t Size() const = 0; |
| |
| // Whether this node marks a parse error. |
| virtual bool IsError() const { return false; } |
| |
| // Whether this node is a whitespace node. |
| virtual bool IsWhitespace() const { return false; } |
| |
| // Whether this node or any of its children contains parse errors. |
| virtual bool HasErrors() const { return IsError(); } |
| |
| // Downcasting methods |
| virtual Error* AsError() { return nullptr; } |
| const Error* AsError() const { return const_cast<Node*>(this)->AsError(); } |
| virtual Program* AsProgram() { return nullptr; } |
| const Program* AsProgram() const { return const_cast<Node*>(this)->AsProgram(); } |
| virtual VariableDecl* AsVariableDecl() { return nullptr; } |
| const VariableDecl* AsVariableDecl() const { return const_cast<Node*>(this)->AsVariableDecl(); } |
| virtual Identifier* AsIdentifier() { return nullptr; } |
| const Identifier* AsIdentifier() const { return const_cast<Node*>(this)->AsIdentifier(); } |
| virtual Integer* AsInteger() { return nullptr; } |
| const Integer* AsInteger() const { return const_cast<Node*>(this)->AsInteger(); } |
| virtual Expression* AsExpression() { return nullptr; } |
| const Expression* AsExpression() const { return const_cast<Node*>(this)->AsExpression(); } |
| |
| private: |
| // Offset into the original text where the text this node corresponds to starts. |
| const size_t start_; |
| }; |
| |
| // Superclass of all terminal nodes in our AST. |
| class Terminal : public Node { |
| public: |
| Terminal(size_t start, size_t size) : Node(start), size_(size) {} |
| |
| const std::vector<std::shared_ptr<Node>>& Children() const override { return kEmptyChildren; } |
| size_t Size() const override { return size_; } |
| |
| std::string ToString(std::string_view unit) const override; |
| |
| private: |
| static const std::vector<std::shared_ptr<Node>> kEmptyChildren; |
| |
| const size_t size_; |
| }; |
| |
| class Error : public Terminal { |
| public: |
| Error(size_t start, size_t size, const std::string& message) |
| : Terminal(start, size), message_(message) {} |
| |
| const std::string& message() const { return message_; } |
| |
| bool IsError() const override { return true; } |
| |
| std::string ToString(std::string_view unit) const override; |
| Error* AsError() override { return this; } |
| |
| private: |
| const std::string message_; |
| }; |
| |
| // Superclass of all non-terminal nodes in our AST. |
| class Nonterminal : public Node { |
| public: |
| Nonterminal(size_t start, std::vector<std::shared_ptr<Node>> children) |
| : Node(start), children_(std::move(children)) { |
| for (const auto& child : children_) { |
| if (child->HasErrors()) { |
| has_errors_ = true; |
| break; |
| } |
| } |
| } |
| |
| // Name of this node as a string. |
| virtual std::string_view Name() const = 0; |
| |
| const std::vector<std::shared_ptr<Node>>& Children() const override { return children_; } |
| size_t Size() const override { |
| if (children_.empty()) { |
| return 0; |
| } else { |
| return children_.back()->start() - start() + children_.back()->Size(); |
| } |
| } |
| |
| std::string ToString(std::string_view unit) const override; |
| |
| bool HasErrors() const override { return has_errors_; } |
| |
| private: |
| bool has_errors_ = false; |
| std::vector<std::shared_ptr<Node>> children_; |
| }; |
| |
| // Result of an attempt to parse a single token. Usually that will result in a terminal, but if |
| // there are errors, we may get one of these instead. Its children will be error nodes and the |
| // fragments of the token that parsed correctly. |
| class TokenResult : public Nonterminal { |
| public: |
| TokenResult(size_t start, std::vector<std::shared_ptr<Node>> children) |
| : Nonterminal(start, std::move(children)) {} |
| |
| std::string_view Name() const override { return ""; } |
| |
| // If one of these ends up in output outside of the Token() combinator, then it's definitely an |
| // error. |
| bool IsError() const override { return true; } |
| }; |
| |
| class Whitespace : public Nonterminal { |
| public: |
| Whitespace(size_t start, std::vector<std::shared_ptr<Node>> children) |
| : Nonterminal(start, std::move(children)) {} |
| |
| std::string_view Name() const override { return "Whitespace"; } |
| bool IsWhitespace() const override { return true; } |
| }; |
| |
| class Program : public Nonterminal { |
| public: |
| Program(size_t start, std::vector<std::shared_ptr<Node>> children) |
| : Nonterminal(start, std::move(children)) {} |
| |
| std::string_view Name() const override { return "Program"; } |
| Program* AsProgram() override { return this; } |
| }; |
| |
| class VariableDecl : public Nonterminal { |
| public: |
| VariableDecl(size_t start, std::vector<std::shared_ptr<Node>> children) |
| : Nonterminal(start, std::move(children)) {} |
| |
| std::string_view Name() const override { return "VariableDecl"; } |
| VariableDecl* AsVariableDecl() override { return this; } |
| }; |
| |
| class Integer : public Nonterminal { |
| public: |
| Integer(size_t start, std::vector<std::shared_ptr<Node>> children) |
| : Nonterminal(start, std::move(children)) {} |
| |
| std::string_view Name() const override { return "Integer"; } |
| |
| std::optional<std::string> GetInteger(std::string_view unit) const; |
| Integer* AsInteger() override { return this; } |
| }; |
| |
| class Identifier : public Nonterminal { |
| public: |
| Identifier(size_t start, std::vector<std::shared_ptr<Node>> children) |
| : Nonterminal(start, std::move(children)) {} |
| |
| std::string_view Name() const override { return "Identifier"; } |
| |
| std::optional<std::string> GetIdentifier(std::string_view unit) const; |
| Identifier* AsIdentifier() override { return this; } |
| }; |
| |
| class Expression : public Nonterminal { |
| public: |
| Expression(size_t start, std::vector<std::shared_ptr<Node>> children) |
| : Nonterminal(start, std::move(children)) {} |
| |
| std::string_view Name() const override { return "Expression"; } |
| Expression* AsExpression() override { return this; } |
| }; |
| |
| } // namespace shell::parser::ast |
| |
| #endif // SRC_DEVELOPER_SHELL_PARSER_AST_H_ |