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