1 //===--- Grammar.cpp - Grammar for clang pseudoparser -----------*- 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/Grammar.h" 10 #include "clang/Basic/TokenKinds.h" 11 #include "llvm/ADT/ArrayRef.h" 12 #include "llvm/ADT/STLExtras.h" 13 #include "llvm/ADT/StringRef.h" 14 #include "llvm/Support/FormatVariadic.h" 15 #include "llvm/Support/raw_ostream.h" 16 17 namespace clang { 18 namespace pseudo { 19 20 Rule::Rule(SymbolID Target, llvm::ArrayRef<SymbolID> Sequence) 21 : Target(Target), Size(static_cast<uint8_t>(Sequence.size())) { 22 assert(Sequence.size() <= Rule::MaxElements); 23 llvm::copy(Sequence, this->Sequence); 24 } 25 26 Grammar::Grammar(std::unique_ptr<GrammarTable> Table) : T(std::move(Table)) { 27 Underscore = *findNonterminal("_"); 28 } 29 30 llvm::ArrayRef<Rule> Grammar::rulesFor(SymbolID SID) const { 31 assert(isNonterminal(SID)); 32 const auto &R = T->Nonterminals[SID].RuleRange; 33 assert(R.End <= T->Rules.size()); 34 return llvm::makeArrayRef(&T->Rules[R.Start], R.End - R.Start); 35 } 36 37 const Rule &Grammar::lookupRule(RuleID RID) const { 38 assert(RID < T->Rules.size()); 39 return T->Rules[RID]; 40 } 41 42 llvm::StringRef Grammar::symbolName(SymbolID SID) const { 43 if (isToken(SID)) 44 return T->Terminals[symbolToToken(SID)]; 45 return T->Nonterminals[SID].Name; 46 } 47 48 std::string Grammar::mangleSymbol(SymbolID SID) const { 49 static const char *const TokNames[] = { 50 #define TOK(X) #X, 51 #define KEYWORD(X, Y) #X, 52 #include "clang/Basic/TokenKinds.def" 53 nullptr}; 54 if (clang::pseudo::isToken(SID)) 55 return TokNames[clang::pseudo::symbolToToken(SID)]; 56 std::string Name = symbolName(SID).str(); 57 // translation-unit -> translation_unit 58 std::replace(Name.begin(), Name.end(), '-', '_'); 59 return Name; 60 } 61 62 std::string Grammar::mangleRule(RuleID RID) const { 63 const auto &R = lookupRule(RID); 64 std::string MangleName = mangleSymbol(R.Target); 65 for (size_t I = 0; I < R.seq().size(); ++I) 66 MangleName += llvm::formatv("_{0}{1}", I, mangleSymbol(R.seq()[I])); 67 return MangleName; 68 } 69 70 llvm::Optional<SymbolID> Grammar::findNonterminal(llvm::StringRef Name) const { 71 auto It = llvm::partition_point( 72 T->Nonterminals, 73 [&](const GrammarTable::Nonterminal &X) { return X.Name < Name; }); 74 if (It != T->Nonterminals.end() && It->Name == Name) 75 return It - T->Nonterminals.begin(); 76 return llvm::None; 77 } 78 79 std::string Grammar::dumpRule(RuleID RID) const { 80 std::string Result; 81 llvm::raw_string_ostream OS(Result); 82 const Rule &R = T->Rules[RID]; 83 OS << symbolName(R.Target) << " :="; 84 for (unsigned I = 0; I < R.Size; ++I) { 85 OS << " " << symbolName(R.Sequence[I]); 86 if (R.RecoveryIndex == I) 87 OS << " [recover=" << T->AttributeValues[R.Recovery] << "]"; 88 } 89 if (R.Guard) 90 OS << " [guard=" << T->AttributeValues[R.Guard] << "]"; 91 return Result; 92 } 93 94 std::string Grammar::dumpRules(SymbolID SID) const { 95 assert(isNonterminal(SID)); 96 std::string Result; 97 const auto &Range = T->Nonterminals[SID].RuleRange; 98 for (RuleID RID = Range.Start; RID < Range.End; ++RID) 99 Result.append(dumpRule(RID)).push_back('\n'); 100 return Result; 101 } 102 103 std::string Grammar::dump() const { 104 std::string Result; 105 llvm::raw_string_ostream OS(Result); 106 OS << "Nonterminals:\n"; 107 for (SymbolID SID = 0; SID < T->Nonterminals.size(); ++SID) 108 OS << llvm::formatv(" {0} {1}\n", SID, symbolName(SID)); 109 OS << "Rules:\n"; 110 for (RuleID RID = 0; RID < T->Rules.size(); ++RID) 111 OS << llvm::formatv(" {0} {1}\n", RID, dumpRule(RID)); 112 return OS.str(); 113 } 114 115 std::vector<llvm::DenseSet<SymbolID>> firstSets(const Grammar &G) { 116 std::vector<llvm::DenseSet<SymbolID>> FirstSets( 117 G.table().Nonterminals.size()); 118 auto ExpandFirstSet = [&FirstSets](SymbolID Target, SymbolID First) { 119 assert(isNonterminal(Target)); 120 if (isToken(First)) 121 return FirstSets[Target].insert(First).second; 122 bool Changed = false; 123 for (SymbolID SID : FirstSets[First]) 124 Changed |= FirstSets[Target].insert(SID).second; 125 return Changed; 126 }; 127 128 // A rule S := T ... implies elements in FIRST(S): 129 // - if T is a terminal, FIRST(S) contains T 130 // - if T is a nonterminal, FIRST(S) contains FIRST(T) 131 // Since FIRST(T) may not have been fully computed yet, FIRST(S) itself may 132 // end up being incomplete. 133 // We iterate until we hit a fixed point. 134 // (This isn't particularly efficient, but table building isn't on the 135 // critical path). 136 bool Changed = true; 137 while (Changed) { 138 Changed = false; 139 for (const auto &R : G.table().Rules) 140 // We only need to consider the first element because symbols are 141 // non-nullable. 142 Changed |= ExpandFirstSet(R.Target, R.seq().front()); 143 } 144 return FirstSets; 145 } 146 147 std::vector<llvm::DenseSet<SymbolID>> followSets(const Grammar &G) { 148 auto FirstSets = firstSets(G); 149 std::vector<llvm::DenseSet<SymbolID>> FollowSets( 150 G.table().Nonterminals.size()); 151 // Expand the follow set of a nonterminal symbol Y by adding all from the 152 // given symbol set. 153 auto ExpandFollowSet = [&FollowSets](SymbolID Y, 154 const llvm::DenseSet<SymbolID> &ToAdd) { 155 assert(isNonterminal(Y)); 156 bool Changed = false; 157 for (SymbolID F : ToAdd) 158 Changed |= FollowSets[Y].insert(F).second; 159 return Changed; 160 }; 161 // Follow sets is computed based on the following 3 rules, the computation 162 // is completed at a fixed point where there is no more new symbols can be 163 // added to any of the follow sets. 164 // 165 // Rule 1: add endmarker to the FOLLOW(S), where S is the start symbol of the 166 // augmented grammar, in our case it is '_'. 167 FollowSets[G.underscore()].insert(tokenSymbol(tok::eof)); 168 bool Changed = true; 169 while (Changed) { 170 Changed = false; 171 for (const auto &R : G.table().Rules) { 172 // Rule 2: for a rule X := ... Y Z, we add all symbols from FIRST(Z) to 173 // FOLLOW(Y). 174 for (size_t I = 0; I + 1 < R.seq().size(); ++I) { 175 if (isToken(R.seq()[I])) 176 continue; 177 // We only need to consider the next symbol because symbols are 178 // non-nullable. 179 SymbolID Next = R.seq()[I + 1]; 180 if (isToken(Next)) 181 // First set for a terminal is itself. 182 Changed |= ExpandFollowSet(R.seq()[I], {Next}); 183 else 184 Changed |= ExpandFollowSet(R.seq()[I], FirstSets[Next]); 185 } 186 // Rule 3: for a rule X := ... Z, we add all symbols from FOLLOW(X) to 187 // FOLLOW(Z). 188 SymbolID Z = R.seq().back(); 189 if (isNonterminal(Z)) 190 Changed |= ExpandFollowSet(Z, FollowSets[R.Target]); 191 } 192 } 193 return FollowSets; 194 } 195 196 static llvm::ArrayRef<std::string> getTerminalNames() { 197 static const auto &TerminalNames = []() { 198 auto TerminalNames = new std::string[NumTerminals]; 199 #define PUNCTUATOR(Tok, Spelling) TerminalNames[tok::Tok] = Spelling; 200 #define KEYWORD(Keyword, Condition) \ 201 TerminalNames[tok::kw_##Keyword] = llvm::StringRef(#Keyword).upper(); 202 #define TOK(Tok) TerminalNames[tok::Tok] = llvm::StringRef(#Tok).upper(); 203 #include "clang/Basic/TokenKinds.def" 204 return llvm::makeArrayRef(TerminalNames, NumTerminals); 205 }(); 206 return TerminalNames; 207 } 208 GrammarTable::GrammarTable() : Terminals(getTerminalNames()) {} 209 210 } // namespace pseudo 211 } // namespace clang 212