1 //===--- LRGraph.h - Build an LR automaton ------------------*- C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // LR parsers are bottom-up parsers -- they scan the input from left to right, 10 // and collect the right-hand side of a production rule (called handle) on top 11 // of the stack, then replace (reduce) the handle with the nonterminal defined 12 // by the production rule. 13 // 14 // This file defines LRGraph, a deterministic handle-finding finite-state 15 // automaton, which is a key component in LR parsers to recognize any of 16 // handles in the grammar efficiently. We build the LR table (ACTION and GOTO 17 // Table) based on the LRGraph. 18 // 19 // LRGraph can be constructed for any context-free grammars. 20 // Even for a LR-ambiguous grammar, we can construct a deterministic FSA, but 21 // interpretation of the FSA is nondeterministic -- we might in a state where 22 // we can continue searching an handle and identify a handle (called 23 // shift/reduce conflicts), or identify more than one handle (callled 24 // reduce/reduce conflicts). 25 // 26 // LRGraph is a common model for all variants of LR automatons, from the most 27 // basic one LR(0), the powerful SLR(1), LR(1) which uses a one-token lookahead 28 // in making decisions. 29 //===----------------------------------------------------------------------===// 30 31 #ifndef CLANG_PSEUDO_GRAMMAR_LRGRAPH_H 32 #define CLANG_PSEUDO_GRAMMAR_LRGRAPH_H 33 34 #include "clang-pseudo/grammar/Grammar.h" 35 #include "llvm/ADT/Hashing.h" 36 #include <vector> 37 38 namespace clang { 39 namespace pseudo { 40 41 // An LR item -- a grammar rule with a dot at some position of the body. 42 // e.g. a production rule A := X Y yields 3 items: 43 // A := . X Y 44 // A := X . Y 45 // A := X Y . 46 // An item indicates how much of a production rule has been recognized at a 47 // position (described by dot), for example, A := X . Y indicates that we have 48 // recognized the X part from the input, and we hope next to see the input 49 // derivable from Y. 50 class Item { 51 public: start(RuleID ID,const Grammar & G)52 static Item start(RuleID ID, const Grammar &G) { 53 Item I; 54 I.RID = ID; 55 I.RuleLength = G.lookupRule(ID).Size; 56 return I; 57 } sentinel(RuleID ID)58 static Item sentinel(RuleID ID) { 59 Item I; 60 I.RID = ID; 61 return I; 62 } 63 rule()64 RuleID rule() const { return RID; } dot()65 uint8_t dot() const { return DotPos; } 66 hasNext()67 bool hasNext() const { return DotPos < RuleLength; } next(const Grammar & G)68 SymbolID next(const Grammar &G) const { 69 assert(hasNext()); 70 return G.lookupRule(RID).Sequence[DotPos]; 71 } 72 advance()73 Item advance() const { 74 assert(hasNext()); 75 Item I = *this; 76 ++I.DotPos; 77 return I; 78 } 79 80 std::string dump(const Grammar &G) const; 81 82 bool operator==(const Item &I) const { 83 return DotPos == I.DotPos && RID == I.RID; 84 } 85 bool operator<(const Item &I) const { 86 return std::tie(RID, DotPos) < std::tie(I.RID, I.DotPos); 87 } hash_value(const Item & I)88 friend llvm::hash_code hash_value(const Item &I) { 89 return llvm::hash_combine(I.RID, I.DotPos); 90 } 91 92 private: 93 RuleID RID = 0; 94 uint8_t DotPos = 0; 95 uint8_t RuleLength = 0; // the length of rule body. 96 }; 97 98 // A state represents a node in the LR automaton graph. It is an item set, which 99 // contains all possible rules that the LR parser may be parsing in that state. 100 // 101 // Conceptually, If we knew in advance what we're parsing, at any point we're 102 // partway through parsing a production, sitting on a stack of partially parsed 103 // productions. But because we don't know, there could be *several* productions 104 // we're partway through. The set of possibilities is the parser state, and we 105 // precompute all the transitions between these states. 106 struct State { 107 // A full set of items (including non-kernel items) representing the state, 108 // in a canonical order (see SortByNextSymbol in the cpp file). 109 std::vector<Item> Items; 110 111 std::string dump(const Grammar &G, unsigned Indent = 0) const; 112 }; 113 114 // LRGraph is a deterministic finite state automaton for LR parsing. 115 // 116 // Intuitively, an LR automaton is a transition graph. The graph has a 117 // collection of nodes, called States. Each state corresponds to a particular 118 // item set, which represents a condition that could occur during the process of 119 // parsing a production. Edges are directed from one state to another. Each edge 120 // is labeled by a grammar symbol (terminal or nonterminal). 121 // 122 // LRGraph is used to construct the LR parsing table which is a core 123 // data-structure driving the LR parser. 124 class LRGraph { 125 public: 126 // StateID is the index in States table. 127 using StateID = uint16_t; 128 129 // Constructs an LR(0) automaton. 130 static LRGraph buildLR0(const Grammar &); 131 132 // An edge in the LR graph, it represents a transition in the LR automaton. 133 // If the parser is at state Src, with a lookahead Label, then it 134 // transits to state Dst. 135 struct Edge { 136 StateID Src, Dst; 137 SymbolID Label; 138 }; 139 140 // A possible error recovery: choose to match some tokens against a symbol. 141 // 142 // e.g. a state that contains 143 // stmt := { . stmt-seq [recover=braces] } 144 // has a Recovery { Src = S, Strategy=braces, Result=stmt-seq }. 145 struct Recovery { 146 StateID Src; // The state we are in when encountering the error. 147 ExtensionID Strategy; // Heuristic choosing the tokens to match. 148 SymbolID Result; // The symbol that is produced. 149 }; 150 states()151 llvm::ArrayRef<State> states() const { return States; } edges()152 llvm::ArrayRef<Edge> edges() const { return Edges; } recoveries()153 llvm::ArrayRef<Recovery> recoveries() const { return Recoveries; } startStates()154 llvm::ArrayRef<std::pair<SymbolID, StateID>> startStates() const { 155 return StartStates; 156 } 157 158 std::string dumpForTests(const Grammar &) const; 159 160 private: LRGraph(std::vector<State> States,std::vector<Edge> Edges,std::vector<Recovery> Recoveries,std::vector<std::pair<SymbolID,StateID>> StartStates)161 LRGraph(std::vector<State> States, std::vector<Edge> Edges, 162 std::vector<Recovery> Recoveries, 163 std::vector<std::pair<SymbolID, StateID>> StartStates) 164 : States(std::move(States)), Edges(std::move(Edges)), 165 Recoveries(std::move(Recoveries)), StartStates(std::move(StartStates)) { 166 } 167 168 std::vector<State> States; 169 std::vector<Edge> Edges; 170 std::vector<Recovery> Recoveries; 171 std::vector<std::pair<SymbolID, StateID>> StartStates; 172 }; 173 174 } // namespace pseudo 175 } // namespace clang 176 177 namespace llvm { 178 // Support clang::pseudo::Item as DenseMap keys. 179 template <> struct DenseMapInfo<clang::pseudo::Item> { 180 static inline clang::pseudo::Item getEmptyKey() { 181 return clang::pseudo::Item::sentinel(-1); 182 } 183 static inline clang::pseudo::Item getTombstoneKey() { 184 return clang::pseudo::Item::sentinel(-2); 185 } 186 static unsigned getHashValue(const clang::pseudo::Item &I) { 187 return hash_value(I); 188 } 189 static bool isEqual(const clang::pseudo::Item &LHS, 190 const clang::pseudo::Item &RHS) { 191 return LHS == RHS; 192 } 193 }; 194 } // namespace llvm 195 196 #endif // CLANG_PSEUDO_GRAMMAR_LRGRAPH_H 197