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