296 lines
9.2 KiB
C++
296 lines
9.2 KiB
C++
#pragma once
|
|
|
|
#include <spider/compiler/common.hpp>
|
|
|
|
#include <spider/compiler/text/unicode.hpp>
|
|
#include <spider/compiler/text/Token.hpp>
|
|
|
|
namespace spider {
|
|
|
|
class ParseTree;
|
|
|
|
/**
|
|
* @brief DOM style handle over a single node of a parsed token tree.
|
|
* @details Every node knows its parent, its children and its siblings, so a
|
|
* parsed program can be walked and inspected much like a document.
|
|
* Nodes are owned by the ParseTree that produced them and stay
|
|
* valid while that tree is alive. The tree itself is built by
|
|
* ParseTree, never by hand.
|
|
*/
|
|
class ParseNode {
|
|
friend class ParseTree;
|
|
|
|
public:
|
|
|
|
/** @brief Index value used for "no node" links, since isize is unsigned. */
|
|
static constexpr isize npos = static_cast<isize>(-1);
|
|
|
|
private:
|
|
|
|
ParseTree* owner = nullptr;
|
|
|
|
isize parent = npos;
|
|
isize first_child = npos;
|
|
isize last_child = npos;
|
|
isize next_sibling = npos;
|
|
isize prev_sibling = npos;
|
|
isize children_count = 0;
|
|
|
|
optional<std::string_view> tag = {};
|
|
std::u32string own = U"";
|
|
std::u32string text = U"";
|
|
|
|
public:
|
|
|
|
ParseNode() = default;
|
|
|
|
public:
|
|
|
|
/**
|
|
* @brief The grammar tag of this node, or nothing if untagged.
|
|
*/
|
|
const optional<std::string_view>& tagName() const { return tag; }
|
|
|
|
/**
|
|
* @brief Checks if this node carries the given tag.
|
|
*/
|
|
bool hasTag(std::string_view name) const { return tag.has_value() && *tag == name; }
|
|
|
|
/**
|
|
* @brief The full source text covered by this node and all of its children.
|
|
*/
|
|
const std::u32string& fullText() const { return text; }
|
|
|
|
/**
|
|
* @brief The source text held by this node alone, without its children.
|
|
* @note For tagged nodes that fold their children, this is the full text.
|
|
*/
|
|
const std::u32string& ownText() const { return own; }
|
|
|
|
/**
|
|
* @brief UTF-8 rendering of text().
|
|
*/
|
|
std::string textUtf8() const { return unicode::toUTF8(text); }
|
|
|
|
/**
|
|
* @brief UTF-8 rendering of ownText().
|
|
*/
|
|
std::string ownTextUtf8() const { return unicode::toUTF8(own); }
|
|
|
|
/**
|
|
* @brief Amount of direct children of this node.
|
|
*/
|
|
isize childCount() const { return children_count; }
|
|
|
|
/**
|
|
* @brief True when this node holds no text and no children.
|
|
*/
|
|
bool isLeaf() const { return children_count == 0; }
|
|
|
|
/**
|
|
* @brief Distance from the root of the tree, zero for the root itself.
|
|
*/
|
|
isize depth() const;
|
|
|
|
/**
|
|
* @brief Storage index of this node inside its parent, npos for the root.
|
|
*/
|
|
isize indexInParent() const { return parent; }
|
|
|
|
public:
|
|
|
|
// ---------------------------------------------------------------- //
|
|
// Navigation //
|
|
// ---------------------------------------------------------------- //
|
|
|
|
ParseNode* parentNode();
|
|
const ParseNode* parentNode() const;
|
|
|
|
ParseNode* firstChild();
|
|
const ParseNode* firstChild() const;
|
|
|
|
ParseNode* lastChild();
|
|
const ParseNode* lastChild() const;
|
|
|
|
ParseNode* nextSibling();
|
|
const ParseNode* nextSibling() const;
|
|
|
|
ParseNode* previousSibling();
|
|
const ParseNode* previousSibling() const;
|
|
|
|
/**
|
|
* @brief The index-th direct child of this node, nullptr when out of range.
|
|
*/
|
|
ParseNode* childAt(isize index);
|
|
const ParseNode* childAt(isize index) const;
|
|
|
|
public:
|
|
|
|
// ---------------------------------------------------------------- //
|
|
// Navigation filtered by tag //
|
|
// ---------------------------------------------------------------- //
|
|
|
|
/**
|
|
* @brief First direct child carrying the given tag.
|
|
*/
|
|
ParseNode* firstChild(std::string_view name);
|
|
const ParseNode* firstChild(std::string_view name) const;
|
|
|
|
/**
|
|
* @brief Last direct child carrying the given tag.
|
|
*/
|
|
ParseNode* lastChild(std::string_view name);
|
|
const ParseNode* lastChild(std::string_view name) const;
|
|
|
|
/**
|
|
* @brief Next sibling of this node carrying the given tag.
|
|
*/
|
|
ParseNode* nextSibling(std::string_view name);
|
|
const ParseNode* nextSibling(std::string_view name) const;
|
|
|
|
/**
|
|
* @brief Nearest ancestor carrying the given tag, nullptr when there is none.
|
|
*/
|
|
ParseNode* ancestor(std::string_view name);
|
|
const ParseNode* ancestor(std::string_view name) const;
|
|
|
|
public:
|
|
|
|
// ---------------------------------------------------------------- //
|
|
// Queries //
|
|
// ---------------------------------------------------------------- //
|
|
|
|
/**
|
|
* @brief First node in document order carrying the given tag, this node included.
|
|
*/
|
|
ParseNode* find(std::string_view name);
|
|
const ParseNode* find(std::string_view name) const;
|
|
|
|
/**
|
|
* @brief Every node in document order carrying the given tag, this node included.
|
|
*/
|
|
vector<ParseNode*> findAll(std::string_view name);
|
|
vector<const ParseNode*> findAll(std::string_view name) const;
|
|
|
|
/**
|
|
* @brief Amount of nodes in document order carrying the given tag.
|
|
*/
|
|
isize count(std::string_view name) const;
|
|
|
|
/**
|
|
* @brief True when at least one node carries the given tag.
|
|
*/
|
|
bool contains(std::string_view name) const { return find(name) != nullptr; }
|
|
|
|
public:
|
|
|
|
/**
|
|
* @brief Human readable XML-like rendering of this node and its children.
|
|
*/
|
|
std::string toString() const;
|
|
|
|
/**
|
|
* @brief XML-like rendering of this node alone, indented by the given depth.
|
|
*/
|
|
std::string toString(isize depth) const;
|
|
|
|
private:
|
|
|
|
std::string describe(isize depth) const;
|
|
|
|
};
|
|
|
|
/**
|
|
* @brief Owner of a parsed token tree, exposing a document like interface.
|
|
* @details Use parse() to turn source text into an inspectable tree. The tree
|
|
* is materialized once and then only read, so any number of consumers
|
|
* can walk the same nodes safely.
|
|
*/
|
|
class ParseTree {
|
|
friend class ParseNode;
|
|
|
|
private:
|
|
|
|
deque<ParseNode> nodes;
|
|
isize root_index = ParseNode::npos;
|
|
|
|
private:
|
|
|
|
ParseNode* resolve(isize index);
|
|
const ParseNode* resolve(isize index) const;
|
|
|
|
isize addNode(const TokenResult& result);
|
|
|
|
void linkChildren(isize parent, const vector<isize>& children);
|
|
|
|
/**
|
|
* @brief Materializes a parse result, dropping the grammar scaffolding.
|
|
* @details Only tagged rules become nodes, so a consumer walks syntax and not
|
|
* combinators: untagged rules are pure plumbing and simply hoist their
|
|
* children upwards, and empty shells are discarded. Returns the top
|
|
* level nodes produced by this subtree.
|
|
*/
|
|
vector<isize> collapse(const TokenResult& result);
|
|
|
|
public:
|
|
|
|
ParseTree() = default;
|
|
~ParseTree() = default;
|
|
|
|
// Nodes point back at their owning tree, so trees are never copied or moved.
|
|
ParseTree(const ParseTree&) = delete;
|
|
ParseTree& operator=(const ParseTree&) = delete;
|
|
ParseTree(ParseTree&&) = delete;
|
|
ParseTree& operator=(ParseTree&&) = delete;
|
|
|
|
public:
|
|
|
|
/**
|
|
* @brief Parses source text, requiring the grammar to consume all of it.
|
|
* @returns True on success, in which case the tree holds the parsed nodes.
|
|
*/
|
|
bool parse(const Token* token, const std::string& source);
|
|
|
|
/**
|
|
* @brief Parses from a reader, requiring the grammar to consume all of it.
|
|
* @returns True on success, in which case the tree holds the parsed nodes.
|
|
*/
|
|
bool parse(const Token* token, TextReader& reader);
|
|
|
|
/**
|
|
* @brief Parses the longest matching prefix of a reader.
|
|
* @returns True when at least something was matched.
|
|
*/
|
|
bool parsePrefix(const Token* token, TextReader& reader);
|
|
|
|
/**
|
|
* @brief Adopts an already produced parse result.
|
|
*/
|
|
void build(const TokenResult& result);
|
|
|
|
public:
|
|
|
|
ParseNode* root() { return resolve(root_index); }
|
|
const ParseNode* root() const { return resolve(root_index); }
|
|
|
|
bool empty() const { return root_index == ParseNode::npos; }
|
|
|
|
public:
|
|
|
|
ParseNode* find(std::string_view name);
|
|
const ParseNode* find(std::string_view name) const;
|
|
|
|
vector<ParseNode*> findAll(std::string_view name);
|
|
vector<const ParseNode*> findAll(std::string_view name) const;
|
|
|
|
isize count(std::string_view name) const;
|
|
|
|
/**
|
|
* @brief Human readable XML-like rendering of the whole tree.
|
|
*/
|
|
std::string toString() const;
|
|
|
|
};
|
|
|
|
}
|