1 //===--- GLR.cpp -----------------------------------------------*- 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 #include "clang-pseudo/GLR.h" 10 #include "clang-pseudo/Grammar.h" 11 #include "clang-pseudo/LRTable.h" 12 #include "clang/Basic/TokenKinds.h" 13 #include "llvm/ADT/ArrayRef.h" 14 #include "llvm/ADT/STLExtras.h" 15 #include "llvm/ADT/StringExtras.h" 16 #include "llvm/Support/Debug.h" 17 #include "llvm/Support/ErrorHandling.h" 18 #include "llvm/Support/FormatVariadic.h" 19 #include <algorithm> 20 #include <memory> 21 #include <queue> 22 23 #define DEBUG_TYPE "GLR.cpp" 24 25 namespace clang { 26 namespace pseudo { 27 28 using StateID = LRTable::StateID; 29 30 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const GSS::Node &N) { 31 std::vector<std::string> ParentStates; 32 for (const auto *Parent : N.parents()) 33 ParentStates.push_back(llvm::formatv("{0}", Parent->State)); 34 OS << llvm::formatv("state {0}, parsed symbol {1}, parents {2}", N.State, 35 N.Payload->symbol(), llvm::join(ParentStates, ", ")); 36 return OS; 37 } 38 39 const ForestNode &glrParse(const TokenStream &Tokens, const ParseParams &Params, 40 SymbolID StartSymbol) { 41 llvm::ArrayRef<ForestNode> Terminals = Params.Forest.createTerminals(Tokens); 42 auto &G = Params.G; 43 auto &GSS = Params.GSStack; 44 45 // Lists of active shift, reduce, accept actions. 46 std::vector<ParseStep> PendingShift, PendingReduce, PendingAccept; 47 auto AddSteps = [&](const GSS::Node *Head, SymbolID NextTok) { 48 for (const auto &Action : Params.Table.getActions(Head->State, NextTok)) { 49 switch (Action.kind()) { 50 case LRTable::Action::Shift: 51 PendingShift.push_back({Head, Action}); 52 break; 53 case LRTable::Action::Reduce: 54 PendingReduce.push_back({Head, Action}); 55 break; 56 case LRTable::Action::Accept: 57 PendingAccept.push_back({Head, Action}); 58 break; 59 default: 60 llvm_unreachable("unexpected action kind!"); 61 } 62 } 63 }; 64 std::vector<const GSS::Node *> NewHeads = { 65 GSS.addNode(/*State=*/Params.Table.getStartState(StartSymbol), 66 /*ForestNode=*/nullptr, {})}; 67 for (const ForestNode &Terminal : Terminals) { 68 LLVM_DEBUG(llvm::dbgs() << llvm::formatv("Next token {0} (id={1})\n", 69 G.symbolName(Terminal.symbol()), 70 Terminal.symbol())); 71 for (const auto *Head : NewHeads) 72 AddSteps(Head, Terminal.symbol()); 73 NewHeads.clear(); 74 glrReduce(PendingReduce, Params, 75 [&](const GSS::Node * NewHead) { 76 // A reduce will enable more steps. 77 AddSteps(NewHead, Terminal.symbol()); 78 }); 79 80 glrShift(PendingShift, Terminal, Params, 81 [&](const GSS::Node *NewHead) { NewHeads.push_back(NewHead); }); 82 } 83 LLVM_DEBUG(llvm::dbgs() << llvm::formatv("Next is eof\n")); 84 for (const auto *Heads : NewHeads) 85 AddSteps(Heads, tokenSymbol(tok::eof)); 86 glrReduce(PendingReduce, Params, 87 [&](const GSS::Node * NewHead) { 88 // A reduce will enable more steps. 89 AddSteps(NewHead, tokenSymbol(tok::eof)); 90 }); 91 92 if (!PendingAccept.empty()) { 93 LLVM_DEBUG({ 94 llvm::dbgs() << llvm::formatv("Accept: {0} accepted result:\n", 95 PendingAccept.size()); 96 for (const auto &Accept : PendingAccept) 97 llvm::dbgs() << " - " << G.symbolName(Accept.Head->Payload->symbol()) 98 << "\n"; 99 }); 100 assert(PendingAccept.size() == 1); 101 return *PendingAccept.front().Head->Payload; 102 } 103 // We failed to parse the input, returning an opaque forest node for recovery. 104 return Params.Forest.createOpaque(StartSymbol, /*Token::Index=*/0); 105 } 106 107 // Apply all pending shift actions. 108 // In theory, LR parsing doesn't have shift/shift conflicts on a single head. 109 // But we may have multiple active heads, and each head has a shift action. 110 // 111 // We merge the stack -- if multiple heads will reach the same state after 112 // shifting a token, we shift only once by combining these heads. 113 // 114 // E.g. we have two heads (2, 3) in the GSS, and will shift both to reach 4: 115 // 0---1---2 116 // └---3 117 // After the shift action, the GSS is: 118 // 0---1---2---4 119 // └---3---┘ 120 void glrShift(std::vector<ParseStep> &PendingShift, const ForestNode &NewTok, 121 const ParseParams &Params, NewHeadCallback NewHeadCB) { 122 assert(NewTok.kind() == ForestNode::Terminal); 123 assert(llvm::all_of(PendingShift, 124 [](const ParseStep &Step) { 125 return Step.Action.kind() == LRTable::Action::Shift; 126 }) && 127 "Pending shift actions must be shift actions"); 128 LLVM_DEBUG(llvm::dbgs() << llvm::formatv(" Shift {0} ({1} active heads):\n", 129 Params.G.symbolName(NewTok.symbol()), 130 PendingShift.size())); 131 132 // We group pending shifts by their target state so we can merge them. 133 llvm::stable_sort(PendingShift, [](const ParseStep &L, const ParseStep &R) { 134 return L.Action.getShiftState() < R.Action.getShiftState(); 135 }); 136 auto Rest = llvm::makeArrayRef(PendingShift); 137 llvm::SmallVector<const GSS::Node *> Parents; 138 while (!Rest.empty()) { 139 // Collect the batch of PendingShift that have compatible shift states. 140 // Their heads become TempParents, the parents of the new GSS node. 141 StateID NextState = Rest.front().Action.getShiftState(); 142 143 Parents.clear(); 144 for (const auto &Base : Rest) { 145 if (Base.Action.getShiftState() != NextState) 146 break; 147 Parents.push_back(Base.Head); 148 } 149 Rest = Rest.drop_front(Parents.size()); 150 151 LLVM_DEBUG(llvm::dbgs() << llvm::formatv(" --> S{0} ({1} heads)\n", 152 NextState, Parents.size())); 153 NewHeadCB(Params.GSStack.addNode(NextState, &NewTok, Parents)); 154 } 155 PendingShift.clear(); 156 } 157 158 namespace { 159 // A KeyedQueue yields pairs of keys and values in order of the keys. 160 template <typename Key, typename Value> 161 using KeyedQueue = 162 std::priority_queue<std::pair<Key, Value>, 163 std::vector<std::pair<Key, Value>>, llvm::less_first>; 164 165 template <typename T> void sortAndUnique(std::vector<T> &Vec) { 166 llvm::sort(Vec); 167 Vec.erase(std::unique(Vec.begin(), Vec.end()), Vec.end()); 168 } 169 } // namespace 170 171 // Perform reduces until no more are possible. 172 // 173 // Generally this means walking up from the heads gathering ForestNodes that 174 // will match the RHS of the rule we're reducing into a sequence ForestNode, 175 // and ending up at a base node. 176 // Then we push a new GSS node onto that base, taking care to: 177 // - pack alternative sequence ForestNodes into an ambiguous ForestNode. 178 // - use the same GSS node for multiple heads if the parse state matches. 179 // 180 // Examples of reduction: 181 // Before (simple): 182 // 0--1(expr)--2(semi) 183 // After reducing 2 by `stmt := expr semi`: 184 // 0--3(stmt) // 3 is goto(0, stmt) 185 // 186 // Before (splitting due to R/R conflict): 187 // 0--1(IDENTIFIER) 188 // After reducing 1 by `class-name := IDENTIFIER` & `enum-name := IDENTIFIER`: 189 // 0--2(class-name) // 2 is goto(0, class-name) 190 // └--3(enum-name) // 3 is goto(0, enum-name) 191 // 192 // Before (splitting due to multiple bases): 193 // 0--2(class-name)--4(STAR) 194 // └--3(enum-name)---┘ 195 // After reducing 4 by `ptr-operator := STAR`: 196 // 0--2(class-name)--5(ptr-operator) // 5 is goto(2, ptr-operator) 197 // └--3(enum-name)---6(ptr-operator) // 6 is goto(3, ptr-operator) 198 // 199 // Before (joining due to same goto state, multiple bases): 200 // 0--1(cv-qualifier)--3(class-name) 201 // └--2(cv-qualifier)--4(enum-name) 202 // After reducing 3 by `type-name := class-name` and 203 // 4 by `type-name := enum-name`: 204 // 0--1(cv-qualifier)--5(type-name) // 5 is goto(1, type-name) and 205 // └--2(cv-qualifier)--┘ // goto(2, type-name) 206 // 207 // Before (joining due to same goto state, the same base): 208 // 0--1(class-name)--3(STAR) 209 // └--2(enum-name)--4(STAR) 210 // After reducing 3 by `pointer := class-name STAR` and 211 // 2 by`enum-name := class-name STAR`: 212 // 0--5(pointer) // 5 is goto(0, pointer) 213 void glrReduce(std::vector<ParseStep> &PendingReduce, const ParseParams &Params, 214 NewHeadCallback NewHeadCB) { 215 // There are two interacting complications: 216 // 1. Performing one reduce can unlock new reduces on the newly-created head. 217 // 2a. The ambiguous ForestNodes must be complete (have all sequence nodes). 218 // This means we must have unlocked all the reduces that contribute to it. 219 // 2b. Similarly, the new GSS nodes must be complete (have all parents). 220 // 221 // We define a "family" of reduces as those that produce the same symbol and 222 // cover the same range of tokens. These are exactly the set of reductions 223 // whose sequence nodes would be covered by the same ambiguous node. 224 // We wish to process a whole family at a time (to satisfy complication 2), 225 // and can address complication 1 by carefully ordering the families: 226 // - Process families covering fewer tokens first. 227 // A reduce can't depend on a longer reduce! 228 // - For equal token ranges: if S := T, process T families before S families. 229 // Parsing T can't depend on an equal-length S, as the grammar is acyclic. 230 // 231 // This isn't quite enough: we don't know the token length of the reduction 232 // until we walk up the stack to perform the pop. 233 // So we perform the pop part upfront, and place the push specification on 234 // priority queues such that we can retrieve a family at a time. 235 236 // A reduction family is characterized by its token range and symbol produced. 237 // It is used as a key in the priority queues to group pushes by family. 238 struct Family { 239 // The start of the token range of the reduce. 240 Token::Index Start; 241 SymbolID Symbol; 242 // Rule must produce Symbol and can otherwise be arbitrary. 243 // RuleIDs have the topological order based on the acyclic grammar. 244 // FIXME: should SymbolIDs be so ordered instead? 245 RuleID Rule; 246 247 bool operator==(const Family &Other) const { 248 return Start == Other.Start && Symbol == Other.Symbol; 249 } 250 // The larger Family is the one that should be processed first. 251 bool operator<(const Family &Other) const { 252 if (Start != Other.Start) 253 return Start < Other.Start; 254 if (Symbol != Other.Symbol) 255 return Rule > Other.Rule; 256 assert(*this == Other); 257 return false; 258 } 259 }; 260 261 // The base nodes are the heads after popping the GSS nodes we are reducing. 262 // We don't care which rule yielded each base. If Family.Symbol is S, the 263 // base includes an item X := ... • S ... and since the grammar is 264 // context-free, *all* parses of S are valid here. 265 // FIXME: reuse the queues across calls instead of reallocating. 266 KeyedQueue<Family, const GSS::Node *> Bases; 267 268 // A sequence is the ForestNode payloads of the GSS nodes we are reducing. 269 // These are the RHS of the rule, the RuleID is stored in the Family. 270 // They specify a sequence ForestNode we may build (but we dedup first). 271 using Sequence = llvm::SmallVector<const ForestNode *, Rule::MaxElements>; 272 KeyedQueue<Family, Sequence> Sequences; 273 274 Sequence TempSequence; 275 // Pop walks up the parent chain(s) for a reduction from Head by to Rule. 276 // Once we reach the end, record the bases and sequences. 277 auto Pop = [&](const GSS::Node *Head, RuleID RID) { 278 LLVM_DEBUG(llvm::dbgs() << " Pop " << Params.G.dumpRule(RID) << "\n"); 279 const auto &Rule = Params.G.lookupRule(RID); 280 Family F{/*Start=*/0, /*Symbol=*/Rule.Target, /*Rule=*/RID}; 281 TempSequence.resize_for_overwrite(Rule.Size); 282 auto DFS = [&](const GSS::Node *N, unsigned I, auto &DFS) { 283 if (I == Rule.Size) { 284 F.Start = TempSequence.front()->startTokenIndex(); 285 Bases.emplace(F, N); 286 LLVM_DEBUG(llvm::dbgs() << " --> base at S" << N->State << "\n"); 287 Sequences.emplace(F, TempSequence); 288 return; 289 } 290 TempSequence[Rule.Size - 1 - I] = N->Payload; 291 for (const GSS::Node *Parent : N->parents()) 292 DFS(Parent, I + 1, DFS); 293 }; 294 DFS(Head, 0, DFS); 295 }; 296 auto PopPending = [&] { 297 for (const ParseStep &Pending : PendingReduce) 298 Pop(Pending.Head, Pending.Action.getReduceRule()); 299 PendingReduce.clear(); 300 }; 301 302 std::vector<std::pair</*Goto*/ StateID, const GSS::Node *>> FamilyBases; 303 std::vector<std::pair<RuleID, Sequence>> FamilySequences; 304 305 std::vector<const GSS::Node *> TempGSSNodes; 306 std::vector<const ForestNode *> TempForestNodes; 307 308 // Main reduction loop: 309 // - pop as much as we can 310 // - process one family at a time, forming a forest node 311 // - produces new GSS heads which may enable more pops 312 PopPending(); 313 while (!Bases.empty()) { 314 // We should always have bases and sequences for the same families. 315 Family F = Bases.top().first; 316 assert(!Sequences.empty()); 317 assert(Sequences.top().first == F); 318 319 LLVM_DEBUG(llvm::dbgs() << " Push " << Params.G.symbolName(F.Symbol) 320 << " from token " << F.Start << "\n"); 321 322 // Grab the sequences for this family. 323 FamilySequences.clear(); 324 do { 325 FamilySequences.emplace_back(Sequences.top().first.Rule, 326 Sequences.top().second); 327 Sequences.pop(); 328 } while (!Sequences.empty() && Sequences.top().first == F); 329 // Build a forest node for each unique sequence. 330 sortAndUnique(FamilySequences); 331 auto &SequenceNodes = TempForestNodes; 332 SequenceNodes.clear(); 333 for (const auto &SequenceSpec : FamilySequences) 334 SequenceNodes.push_back(&Params.Forest.createSequence( 335 F.Symbol, SequenceSpec.first, SequenceSpec.second)); 336 // Wrap in an ambiguous node if needed. 337 const ForestNode *Parsed = 338 SequenceNodes.size() == 1 339 ? SequenceNodes.front() 340 : &Params.Forest.createAmbiguous(F.Symbol, SequenceNodes); 341 LLVM_DEBUG(llvm::dbgs() << " --> " << Parsed->dump(Params.G) << "\n"); 342 343 // Grab the bases for this family. 344 // As well as deduplicating them, we'll group by the goto state. 345 FamilyBases.clear(); 346 do { 347 FamilyBases.emplace_back( 348 Params.Table.getGoToState(Bases.top().second->State, F.Symbol), 349 Bases.top().second); 350 Bases.pop(); 351 } while (!Bases.empty() && Bases.top().first == F); 352 sortAndUnique(FamilyBases); 353 // Create a GSS node for each unique goto state. 354 llvm::ArrayRef<decltype(FamilyBases)::value_type> BasesLeft = FamilyBases; 355 while (!BasesLeft.empty()) { 356 StateID NextState = BasesLeft.front().first; 357 auto &Parents = TempGSSNodes; 358 Parents.clear(); 359 for (const auto &Base : BasesLeft) { 360 if (Base.first != NextState) 361 break; 362 Parents.push_back(Base.second); 363 } 364 BasesLeft = BasesLeft.drop_front(Parents.size()); 365 366 // Invoking the callback for new heads, a real GLR parser may add new 367 // reduces to the PendingReduce queue! 368 NewHeadCB(Params.GSStack.addNode(NextState, Parsed, Parents)); 369 } 370 PopPending(); 371 } 372 assert(Sequences.empty()); 373 } 374 375 } // namespace pseudo 376 } // namespace clang 377