1 //===--- LRGraph.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/grammar/LRGraph.h"
10 #include "clang-pseudo/grammar/Grammar.h"
11 #include "llvm/ADT/DenseSet.h"
12 #include "llvm/ADT/Hashing.h"
13 #include "llvm/ADT/STLExtras.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/Support/FormatVariadic.h"
16 #include "llvm/Support/raw_ostream.h"
17 
18 using ItemSet = std::vector<clang::pseudo::Item>;
19 
20 namespace llvm {
21 // Support clang::pseudo::Item as DenseMap keys.
22 template <> struct DenseMapInfo<ItemSet> {
getEmptyKeyllvm::DenseMapInfo23   static inline ItemSet getEmptyKey() {
24     return {DenseMapInfo<clang::pseudo::Item>::getEmptyKey()};
25   }
getTombstoneKeyllvm::DenseMapInfo26   static inline ItemSet getTombstoneKey() {
27     return {DenseMapInfo<clang::pseudo::Item>::getTombstoneKey()};
28   }
getHashValuellvm::DenseMapInfo29   static unsigned getHashValue(const ItemSet &I) {
30     return llvm::hash_combine_range(I.begin(), I.end());
31   }
isEqualllvm::DenseMapInfo32   static bool isEqual(const ItemSet &LHS, const ItemSet &RHS) {
33     return LHS == RHS;
34   }
35 };
36 } // namespace llvm
37 
38 namespace clang {
39 namespace pseudo {
40 namespace {
41 
42 struct SortByNextSymbol {
SortByNextSymbolclang::pseudo::__anon9c30f6be0111::SortByNextSymbol43   SortByNextSymbol(const Grammar &G) : G(G) {}
operator ()clang::pseudo::__anon9c30f6be0111::SortByNextSymbol44   bool operator()(const Item &L, const Item &R) {
45     if (L.hasNext() && R.hasNext() && L.next(G) != R.next(G))
46       return L.next(G) < R.next(G);
47     if (L.hasNext() != R.hasNext())
48       return L.hasNext() < R.hasNext(); //  a trailing dot is minimal.
49     return L < R;
50   }
51   const Grammar &G;
52 };
53 
54 // Computes a closure of the given item set S:
55 //  - extends the given S to contain all options for parsing next token;
56 //  - nonterminals after a dot are recursively expanded into the begin-state
57 //    of all production rules that produce that nonterminal;
58 //
59 // Given
60 //   Grammar rules = [ _ := E, E := E - T, E := T, T := n, T := ( E ) ]
61 //   Input = [ E := . T ]
62 // returns [ E :=  . T, T := . n, T := . ( E ) ]
closure(ItemSet Queue,const Grammar & G)63 State closure(ItemSet Queue, const Grammar &G) {
64   llvm::DenseSet<Item> InQueue = {Queue.begin(), Queue.end()};
65   // We reuse the passed-by-value Queue as the final result, as it's already
66   // initialized to the right elements.
67   size_t ItIndex = 0;
68   while (ItIndex < Queue.size()) {
69     const Item &ExpandingItem = Queue[ItIndex];
70     ++ItIndex;
71     if (!ExpandingItem.hasNext())
72       continue;
73 
74     SymbolID NextSym = ExpandingItem.next(G);
75     if (pseudo::isToken(NextSym))
76       continue;
77     auto RRange = G.table().Nonterminals[NextSym].RuleRange;
78     for (RuleID RID = RRange.Start; RID < RRange.End; ++RID) {
79       Item NewItem = Item::start(RID, G);
80       if (InQueue.insert(NewItem).second) // new
81         Queue.push_back(std::move(NewItem));
82     }
83   }
84   Queue.shrink_to_fit();
85   llvm::sort(Queue, SortByNextSymbol(G));
86   return {std::move(Queue)};
87 }
88 
89 // Returns all next (with a dot advanced) kernel item sets, partitioned by the
90 // advanced symbol.
91 //
92 // Given
93 //  S = [ E := . a b, E := E . - T ]
94 // returns [
95 //   {id(a), [ E := a . b ]},
96 //   {id(-), [ E := E - . T ]}
97 // ]
98 std::vector<std::pair<SymbolID, ItemSet>>
nextAvailableKernelItems(const State & S,const Grammar & G)99 nextAvailableKernelItems(const State &S, const Grammar &G) {
100   std::vector<std::pair<SymbolID, ItemSet>> Results;
101   llvm::ArrayRef<Item> AllItems = S.Items;
102   AllItems = AllItems.drop_while([](const Item &I) { return !I.hasNext(); });
103   while (!AllItems.empty()) {
104     SymbolID AdvancedSymbol = AllItems.front().next(G);
105     auto Batch = AllItems.take_while([AdvancedSymbol, &G](const Item &I) {
106       assert(I.hasNext());
107       return I.next(G) == AdvancedSymbol;
108     });
109     assert(!Batch.empty());
110     AllItems = AllItems.drop_front(Batch.size());
111 
112     // Advance a dot over the Symbol.
113     ItemSet Next;
114     for (const Item &I : Batch)
115       Next.push_back(I.advance());
116     // sort the set to keep order determinism for hash computation.
117     llvm::sort(Next);
118     Results.push_back({AdvancedSymbol, std::move(Next)});
119   }
120   return Results;
121 }
122 
123 std::vector<std::pair<ExtensionID, SymbolID>>
availableRecovery(const State & S,const Grammar & G)124 availableRecovery(const State &S, const Grammar &G) {
125   std::vector<std::pair<ExtensionID, SymbolID>> Result;
126   for (const Item &I : S.Items) {
127     const auto &Rule = G.lookupRule(I.rule());
128     if (I.dot() != Rule.RecoveryIndex)
129       continue;
130     Result.push_back({Rule.Recovery, Rule.seq()[Rule.RecoveryIndex]});
131   }
132   llvm::sort(Result);
133   Result.erase(std::unique(Result.begin(), Result.end()), Result.end());
134   return Result;
135 }
136 
137 } // namespace
138 
dump(const Grammar & G) const139 std::string Item::dump(const Grammar &G) const {
140   const auto &Rule = G.lookupRule(RID);
141   auto ToNames = [&](llvm::ArrayRef<SymbolID> Syms) {
142     std::vector<llvm::StringRef> Results;
143     for (auto SID : Syms)
144       Results.push_back(G.symbolName(SID));
145     return Results;
146   };
147   return llvm::formatv("{0} := {1} • {2}{3}", G.symbolName(Rule.Target),
148                        llvm::join(ToNames(Rule.seq().take_front(DotPos)), " "),
149                        llvm::join(ToNames(Rule.seq().drop_front(DotPos)), " "),
150                        Rule.RecoveryIndex == DotPos ? " [recovery]" : "")
151       .str();
152 }
153 
dump(const Grammar & G,unsigned Indent) const154 std::string State::dump(const Grammar &G, unsigned Indent) const {
155   std::string Result;
156   llvm::raw_string_ostream OS(Result);
157   for (const auto &Item : Items)
158     OS.indent(Indent) << llvm::formatv("{0}\n", Item.dump(G));
159   return OS.str();
160 }
161 
dumpForTests(const Grammar & G) const162 std::string LRGraph::dumpForTests(const Grammar &G) const {
163   std::string Result;
164   llvm::raw_string_ostream OS(Result);
165   OS << "States:\n";
166   for (StateID ID = 0; ID < States.size(); ++ID) {
167     OS << llvm::formatv("State {0}\n", ID);
168     OS << States[ID].dump(G, /*Indent*/ 4);
169   }
170   for (const auto &E : Edges) {
171     OS << llvm::formatv("{0} ->[{1}] {2}\n", E.Src, G.symbolName(E.Label),
172                         E.Dst);
173   }
174   return OS.str();
175 }
176 
buildLR0(const Grammar & G)177 LRGraph LRGraph::buildLR0(const Grammar &G) {
178   class Builder {
179   public:
180     Builder(const Grammar &G) : G(G) {}
181 
182     // Adds a given state if not existed.
183     std::pair<StateID, /*inserted*/ bool> insert(ItemSet KernelItems) {
184       assert(llvm::is_sorted(KernelItems) &&
185              "Item must be sorted before inserting to a hash map!");
186       auto It = StatesIndex.find(KernelItems);
187       if (It != StatesIndex.end())
188         return {It->second, false};
189       States.push_back(closure(KernelItems, G));
190       StateID NextStateID = States.size() - 1;
191       StatesIndex.insert({std::move(KernelItems), NextStateID});
192       return {NextStateID, true};
193     }
194 
195     void insertEdge(StateID Src, StateID Dst, SymbolID Label) {
196       Edges.push_back({Src, Dst, Label});
197     }
198 
199     void insertRecovery(StateID Src, ExtensionID Strategy, SymbolID Result) {
200       Recoveries.push_back({Src, Strategy, Result});
201     }
202 
203     // Returns a state with the given id.
204     const State &find(StateID ID) const {
205       assert(ID < States.size());
206       return States[ID];
207     }
208 
209     void addStartState(SymbolID Sym, StateID State) {
210       StartStates.push_back({Sym, State});
211     }
212 
213     LRGraph build() && {
214       States.shrink_to_fit();
215       Edges.shrink_to_fit();
216       Recoveries.shrink_to_fit();
217       llvm::sort(StartStates);
218       StartStates.shrink_to_fit();
219       return LRGraph(std::move(States), std::move(Edges), std::move(Recoveries),
220                      std::move(StartStates));
221     }
222 
223   private:
224     // Key is the **kernel** item sets.
225     llvm::DenseMap<ItemSet, /*index of States*/ size_t> StatesIndex;
226     std::vector<State> States;
227     std::vector<Edge> Edges;
228     std::vector<Recovery> Recoveries;
229     const Grammar &G;
230     std::vector<std::pair<SymbolID, StateID>> StartStates;
231   } Builder(G);
232 
233   std::vector<StateID> PendingStates;
234   // Initialize states with the start symbol.
235   auto RRange = G.table().Nonterminals[G.underscore()].RuleRange;
236   for (RuleID RID = RRange.Start; RID < RRange.End; ++RID) {
237     auto StartState = std::vector<Item>{Item::start(RID, G)};
238     auto Result = Builder.insert(std::move(StartState));
239     assert(Result.second && "State must be new");
240     PendingStates.push_back(Result.first);
241 
242     const Rule &StartRule = G.lookupRule(RID);
243     assert(StartRule.Size == 1 &&
244            "Start rule must have exactly one symbol in its body!");
245     Builder.addStartState(StartRule.seq().front(), Result.first);
246   }
247 
248   while (!PendingStates.empty()) {
249     auto StateID = PendingStates.back();
250     PendingStates.pop_back();
251     for (auto Next : nextAvailableKernelItems(Builder.find(StateID), G)) {
252       auto Insert = Builder.insert(Next.second);
253       if (Insert.second) // new state, insert to the pending queue.
254         PendingStates.push_back(Insert.first);
255       Builder.insertEdge(StateID, Insert.first, Next.first);
256     }
257     for (auto Recovery : availableRecovery(Builder.find(StateID), G))
258       Builder.insertRecovery(StateID, Recovery.first, Recovery.second);
259   }
260   return std::move(Builder).build();
261 }
262 
263 } // namespace pseudo
264 } // namespace clang
265