Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/torque/earley-parser.h')
-rw-r--r--deps/v8/src/torque/earley-parser.h473
1 files changed, 473 insertions, 0 deletions
diff --git a/deps/v8/src/torque/earley-parser.h b/deps/v8/src/torque/earley-parser.h
new file mode 100644
index 00000000000..1e77734ab66
--- /dev/null
+++ b/deps/v8/src/torque/earley-parser.h
@@ -0,0 +1,473 @@
+// Copyright 2018 the V8 project 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 V8_TORQUE_EARLEY_PARSER_H_
+#define V8_TORQUE_EARLEY_PARSER_H_
+
+#include <map>
+#include <vector>
+
+#include "src/base/optional.h"
+#include "src/torque/contextual.h"
+#include "src/torque/source-positions.h"
+#include "src/torque/utils.h"
+
+namespace v8 {
+namespace internal {
+namespace torque {
+
+class Symbol;
+class Item;
+
+class ParseResultHolderBase {
+ public:
+ enum class TypeId;
+ virtual ~ParseResultHolderBase() = default;
+ template <class T>
+ T& Cast();
+ template <class T>
+ const T& Cast() const;
+
+ protected:
+ explicit ParseResultHolderBase(TypeId type_id) : type_id_(type_id) {
+ // MSVC wrongly complains about type_id_ being an unused private field.
+ USE(type_id_);
+ }
+
+ private:
+ const TypeId type_id_;
+};
+
+using ParseResultTypeId = ParseResultHolderBase::TypeId;
+
+template <class T>
+class ParseResultHolder : public ParseResultHolderBase {
+ public:
+ explicit ParseResultHolder(T value)
+ : ParseResultHolderBase(id), value_(std::move(value)) {}
+
+ private:
+ V8_EXPORT_PRIVATE static const TypeId id;
+ friend class ParseResultHolderBase;
+ T value_;
+};
+
+template <class T>
+T& ParseResultHolderBase::Cast() {
+ CHECK_EQ(ParseResultHolder<T>::id, type_id_);
+ return static_cast<ParseResultHolder<T>*>(this)->value_;
+}
+
+template <class T>
+const T& ParseResultHolderBase::Cast() const {
+ CHECK_EQ(ParseResultHolder<T>::id, type_id_);
+ return static_cast<const ParseResultHolder<T>*>(this)->value_;
+}
+
+class ParseResult {
+ public:
+ template <class T>
+ explicit ParseResult(T x) : value_(new ParseResultHolder<T>(std::move(x))) {}
+
+ template <class T>
+ const T& Cast() const {
+ return value_->Cast<T>();
+ }
+ template <class T>
+ T& Cast() {
+ return value_->Cast<T>();
+ }
+
+ private:
+ std::unique_ptr<ParseResultHolderBase> value_;
+};
+
+using InputPosition = const char*;
+
+struct MatchedInput {
+ MatchedInput(InputPosition begin, InputPosition end, SourcePosition pos)
+ : begin(begin), end(end), pos(pos) {}
+ InputPosition begin;
+ InputPosition end;
+ SourcePosition pos;
+ std::string ToString() const { return {begin, end}; }
+};
+
+class ParseResultIterator {
+ public:
+ explicit ParseResultIterator(std::vector<ParseResult> results,
+ MatchedInput matched_input)
+ : results_(std::move(results)), matched_input_(matched_input) {}
+ ~ParseResultIterator() {
+ // Check that all parse results have been used.
+ CHECK_EQ(results_.size(), i_);
+ }
+
+ ParseResult Next() {
+ CHECK_LT(i_, results_.size());
+ return std::move(results_[i_++]);
+ }
+ template <class T>
+ T NextAs() {
+ return std::move(Next().Cast<T>());
+ }
+ bool HasNext() const { return i_ < results_.size(); }
+
+ const MatchedInput& matched_input() const { return matched_input_; }
+
+ private:
+ std::vector<ParseResult> results_;
+ size_t i_ = 0;
+ MatchedInput matched_input_;
+
+ DISALLOW_COPY_AND_MOVE_AND_ASSIGN(ParseResultIterator);
+};
+
+struct LexerResult {
+ std::vector<Symbol*> token_symbols;
+ std::vector<MatchedInput> token_contents;
+};
+
+using Action =
+ base::Optional<ParseResult> (*)(ParseResultIterator* child_results);
+
+inline base::Optional<ParseResult> DefaultAction(
+ ParseResultIterator* child_results) {
+ if (!child_results->HasNext()) return base::nullopt;
+ return child_results->Next();
+}
+
+// A rule of the context-free grammar. Each rule can have an action attached to
+// it, which is executed after the parsing is finished.
+class Rule final {
+ public:
+ explicit Rule(std::vector<Symbol*> right_hand_side,
+ Action action = DefaultAction)
+ : right_hand_side_(std::move(right_hand_side)), action_(action) {}
+
+ Symbol* left() const {
+ DCHECK_NOT_NULL(left_hand_side_);
+ return left_hand_side_;
+ }
+ const std::vector<Symbol*>& right() const { return right_hand_side_; }
+
+ void SetLeftHandSide(Symbol* left_hand_side) {
+ DCHECK_NULL(left_hand_side_);
+ left_hand_side_ = left_hand_side;
+ }
+
+ V8_EXPORT_PRIVATE base::Optional<ParseResult> RunAction(
+ const Item* completed_item, const LexerResult& tokens) const;
+
+ private:
+ Symbol* left_hand_side_ = nullptr;
+ std::vector<Symbol*> right_hand_side_;
+ Action action_;
+};
+
+// A Symbol represents a terminal or a non-terminal of the grammar.
+// It stores the list of rules, which have this symbol as the
+// left-hand side.
+// Terminals have an empty list of rules, they are created by the Lexer
+// instead of from rules.
+// Symbols need to reside at stable memory addresses, because the addresses are
+// used in the parser.
+class Symbol {
+ public:
+ Symbol() : Symbol({}) {}
+ Symbol(std::initializer_list<Rule> rules) { *this = rules; }
+
+ V8_EXPORT_PRIVATE Symbol& operator=(std::initializer_list<Rule> rules);
+
+ bool IsTerminal() const { return rules_.empty(); }
+ Rule* rule(size_t index) const { return rules_[index].get(); }
+ size_t rule_number() const { return rules_.size(); }
+
+ void AddRule(const Rule& rule) {
+ rules_.push_back(base::make_unique<Rule>(rule));
+ rules_.back()->SetLeftHandSide(this);
+ }
+
+ V8_EXPORT_PRIVATE base::Optional<ParseResult> RunAction(
+ const Item* item, const LexerResult& tokens);
+
+ private:
+ std::vector<std::unique_ptr<Rule>> rules_;
+
+ // Disallow copying and moving to ensure Symbol has a stable address.
+ DISALLOW_COPY_AND_MOVE_AND_ASSIGN(Symbol);
+};
+
+// Items are the core datastructure of Earley's algorithm.
+// They consist of a (partially) matched rule, a marked position inside of the
+// right-hand side of the rule (traditionally written as a dot) and an input
+// range from {start} to {pos} that matches the symbols of the right-hand side
+// that are left of the mark. In addition, they store a child and a left-sibling
+// pointer to reconstruct the AST in the end.
+class Item {
+ public:
+ Item(const Rule* rule, size_t mark, size_t start, size_t pos)
+ : rule_(rule), mark_(mark), start_(start), pos_(pos) {
+ DCHECK_LE(mark_, right().size());
+ }
+
+ // A complete item has the mark at the right end, which means the input range
+ // matches the complete rule.
+ bool IsComplete() const {
+ DCHECK_LE(mark_, right().size());
+ return mark_ == right().size();
+ }
+
+ // The symbol right after the mark is expected at {pos} for this item to
+ // advance.
+ Symbol* NextSymbol() const {
+ DCHECK(!IsComplete());
+ DCHECK_LT(mark_, right().size());
+ return right()[mark_];
+ }
+
+ // We successfully parsed NextSymbol() between {pos} and {new_pos}.
+ // If NextSymbol() was a non-terminal, then {child} is a pointer to a
+ // completed item for this parse.
+ // We create a new item, which moves the mark one forward.
+ Item Advance(size_t new_pos, const Item* child = nullptr) const {
+ if (child) {
+ DCHECK(child->IsComplete());
+ DCHECK_EQ(pos(), child->start());
+ DCHECK_EQ(new_pos, child->pos());
+ DCHECK_EQ(NextSymbol(), child->left());
+ }
+ Item result(rule_, mark_ + 1, start_, new_pos);
+ result.prev_ = this;
+ result.child_ = child;
+ return result;
+ }
+
+ // Collect the items representing the AST children of this completed item.
+ std::vector<const Item*> Children() const;
+ // The matched input separated according to the next branching AST level.
+ std::string SplitByChildren(const LexerResult& tokens) const;
+ // Check if {other} results in the same AST as this Item.
+ void CheckAmbiguity(const Item& other, const LexerResult& tokens) const;
+
+ MatchedInput GetMatchedInput(const LexerResult& tokens) const {
+ return {tokens.token_contents[start_].begin,
+ start_ == pos_ ? tokens.token_contents[start_].begin
+ : tokens.token_contents[pos_ - 1].end,
+ tokens.token_contents[start_].pos};
+ }
+
+ // We exclude {prev_} and {child_} from equality and hash computations,
+ // because they are just globally unique data associated with an item.
+ bool operator==(const Item& other) const {
+ return rule_ == other.rule_ && mark_ == other.mark_ &&
+ start_ == other.start_ && pos_ == other.pos_;
+ }
+
+ friend size_t hash_value(const Item& i) {
+ return base::hash_combine(i.rule_, i.mark_, i.start_, i.pos_);
+ }
+
+ const Rule* rule() const { return rule_; }
+ Symbol* left() const { return rule_->left(); }
+ const std::vector<Symbol*>& right() const { return rule_->right(); }
+ size_t pos() const { return pos_; }
+ size_t start() const { return start_; }
+
+ private:
+ const Rule* rule_;
+ size_t mark_;
+ size_t start_;
+ size_t pos_;
+
+ const Item* prev_ = nullptr;
+ const Item* child_ = nullptr;
+};
+
+inline base::Optional<ParseResult> Symbol::RunAction(
+ const Item* item, const LexerResult& tokens) {
+ DCHECK(item->IsComplete());
+ DCHECK_EQ(item->left(), this);
+ return item->rule()->RunAction(item, tokens);
+}
+
+V8_EXPORT_PRIVATE const Item* RunEarleyAlgorithm(
+ Symbol* start, const LexerResult& tokens,
+ std::unordered_set<Item, base::hash<Item>>* processed);
+
+inline base::Optional<ParseResult> ParseTokens(Symbol* start,
+ const LexerResult& tokens) {
+ std::unordered_set<Item, base::hash<Item>> table;
+ const Item* final_item = RunEarleyAlgorithm(start, tokens, &table);
+ return start->RunAction(final_item, tokens);
+}
+
+// The lexical syntax is dynamically defined while building the grammar by
+// adding patterns and keywords to the Lexer.
+// The term keyword here can stand for any fixed character sequence, including
+// operators and parentheses.
+// Each pattern or keyword automatically gets a terminal symbol associated with
+// it. These symbols form the result of the lexing.
+// Patterns and keywords are matched using the longest match principle. If the
+// longest matching pattern coincides with a keyword, the keyword symbol is
+// chosen instead of the pattern.
+// In addition, there is a single whitespace pattern which is consumed but does
+// not become part of the token list.
+class Lexer {
+ public:
+ // Functions to define patterns. They try to match starting from {pos}. If
+ // successful, they return true and advance {pos}. Otherwise, {pos} stays
+ // unchanged.
+ using PatternFunction = bool (*)(InputPosition* pos);
+
+ void SetWhitespace(PatternFunction whitespace) {
+ match_whitespace_ = whitespace;
+ }
+
+ Symbol* Pattern(PatternFunction pattern) { return &patterns_[pattern]; }
+ Symbol* Token(const std::string& keyword) { return &keywords_[keyword]; }
+ V8_EXPORT_PRIVATE LexerResult RunLexer(const std::string& input);
+
+ private:
+ PatternFunction match_whitespace_ = [](InputPosition*) { return false; };
+ std::map<PatternFunction, Symbol> patterns_;
+ std::map<std::string, Symbol> keywords_;
+ Symbol* MatchToken(InputPosition* pos, InputPosition end);
+};
+
+// A grammar can have a result, which is the results of the start symbol.
+// Grammar is intended to be subclassed, with Symbol members forming the
+// mutually recursive rules of the grammar.
+class Grammar {
+ public:
+ using PatternFunction = Lexer::PatternFunction;
+
+ explicit Grammar(Symbol* start) : start_(start) {}
+
+ base::Optional<ParseResult> Parse(const std::string& input) {
+ LexerResult tokens = lexer().RunLexer(input);
+ return ParseTokens(start_, tokens);
+ }
+
+ protected:
+ Symbol* Token(const std::string& s) { return lexer_.Token(s); }
+ Symbol* Pattern(PatternFunction pattern) { return lexer_.Pattern(pattern); }
+ void SetWhitespace(PatternFunction ws) { lexer_.SetWhitespace(ws); }
+
+ // NewSymbol() allocates a fresh symbol and stores it in the current grammar.
+ // This is necessary to define helpers that create new symbols.
+ Symbol* NewSymbol(std::initializer_list<Rule> rules = {}) {
+ Symbol* result = new Symbol(rules);
+ generated_symbols_.push_back(std::unique_ptr<Symbol>(result));
+ return result;
+ }
+
+ // Helper functions to define lexer patterns. If they match, they return true
+ // and advance {pos}. Otherwise, {pos} is unchanged.
+ V8_EXPORT_PRIVATE static bool MatchChar(int (*char_class)(int),
+ InputPosition* pos);
+ V8_EXPORT_PRIVATE static bool MatchChar(bool (*char_class)(char),
+ InputPosition* pos);
+ V8_EXPORT_PRIVATE static bool MatchAnyChar(InputPosition* pos);
+ V8_EXPORT_PRIVATE static bool MatchString(const char* s, InputPosition* pos);
+
+ // The action MatchInput() produces the input matched by the rule as
+ // result.
+ static base::Optional<ParseResult> YieldMatchedInput(
+ ParseResultIterator* child_results) {
+ return ParseResult{child_results->matched_input().ToString()};
+ }
+
+ // Create a new symbol to parse the given sequence of symbols.
+ // At most one of the symbols can return a result.
+ Symbol* Sequence(std::vector<Symbol*> symbols) {
+ return NewSymbol({Rule(std::move(symbols))});
+ }
+
+ template <class T, T value>
+ static base::Optional<ParseResult> YieldIntegralConstant(
+ ParseResultIterator* child_results) {
+ return ParseResult{value};
+ }
+
+ template <class T>
+ static base::Optional<ParseResult> YieldDefaultValue(
+ ParseResultIterator* child_results) {
+ return ParseResult{T{}};
+ }
+
+ template <class From, class To>
+ static base::Optional<ParseResult> CastParseResult(
+ ParseResultIterator* child_results) {
+ To result = std::move(child_results->NextAs<From>());
+ return ParseResult{std::move(result)};
+ }
+
+ // Try to parse {s} and return the result of type {Result} casted to {T}.
+ // Otherwise, the result is a default-constructed {T}.
+ template <class T, class Result = T>
+ Symbol* TryOrDefault(Symbol* s) {
+ return NewSymbol({Rule({s}, CastParseResult<Result, T>),
+ Rule({}, YieldDefaultValue<T>)});
+ }
+
+ template <class T>
+ static base::Optional<ParseResult> MakeSingletonVector(
+ ParseResultIterator* child_results) {
+ T x = child_results->NextAs<T>();
+ std::vector<T> result;
+ result.push_back(std::move(x));
+ return ParseResult{std::move(result)};
+ }
+
+ template <class T>
+ static base::Optional<ParseResult> MakeExtendedVector(
+ ParseResultIterator* child_results) {
+ std::vector<T> l = child_results->NextAs<std::vector<T>>();
+ T x = child_results->NextAs<T>();
+ l.push_back(std::move(x));
+ return ParseResult{std::move(l)};
+ }
+
+ // For example, NonemptyList(Token("A"), Token(",")) parses any of
+ // A or A,A or A,A,A and so on.
+ template <class T>
+ Symbol* NonemptyList(Symbol* element,
+ base::Optional<Symbol*> separator = {}) {
+ Symbol* list = NewSymbol();
+ *list = {Rule({element}, MakeSingletonVector<T>),
+ separator
+ ? Rule({list, *separator, element}, MakeExtendedVector<T>)
+ : Rule({list, element}, MakeExtendedVector<T>)};
+ return list;
+ }
+
+ template <class T>
+ Symbol* List(Symbol* element, base::Optional<Symbol*> separator = {}) {
+ return TryOrDefault<std::vector<T>>(NonemptyList<T>(element, separator));
+ }
+
+ template <class T>
+ Symbol* Optional(Symbol* x) {
+ return TryOrDefault<base::Optional<T>, T>(x);
+ }
+
+ Symbol* CheckIf(Symbol* x) {
+ return NewSymbol({Rule({x}, YieldIntegralConstant<bool, true>),
+ Rule({}, YieldIntegralConstant<bool, false>)});
+ }
+
+ Lexer& lexer() { return lexer_; }
+
+ private:
+ Lexer lexer_;
+ std::vector<std::unique_ptr<Symbol>> generated_symbols_;
+ Symbol* start_;
+};
+
+} // namespace torque
+} // namespace internal
+} // namespace v8
+
+#endif // V8_TORQUE_EARLEY_PARSER_H_