1 //===--- Main.cpp - Compile BNF grammar -----------------------------------===// 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 // This is a tool to compile a BNF grammar, it is used by the build system to 10 // generate a necessary data bits to statically construct core pieces (Grammar, 11 // LRTable etc) of the LR parser. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "clang-pseudo/grammar/Grammar.h" 16 #include "llvm/ADT/StringExtras.h" 17 #include "llvm/Support/CommandLine.h" 18 #include "llvm/Support/FileSystem.h" 19 #include "llvm/Support/FormatVariadic.h" 20 #include "llvm/Support/MemoryBuffer.h" 21 #include "llvm/Support/ToolOutputFile.h" 22 #include <algorithm> 23 24 using llvm::cl::desc; 25 using llvm::cl::init; 26 using llvm::cl::opt; 27 using llvm::cl::Required; 28 using llvm::cl::value_desc; 29 using llvm::cl::values; 30 31 namespace { 32 enum EmitType { 33 EmitSymbolList, 34 EmitGrammarContent, 35 }; 36 37 opt<std::string> Grammar("grammar", desc("Parse a BNF grammar file."), 38 Required); 39 opt<EmitType> 40 Emit(desc("which information to emit:"), 41 values(clEnumValN(EmitSymbolList, "emit-symbol-list", 42 "Print nonterminal symbols (default)"), 43 clEnumValN(EmitGrammarContent, "emit-grammar-content", 44 "Print the BNF grammar content as a string"))); 45 46 opt<std::string> OutputFilename("o", init("-"), desc("Output"), 47 value_desc("file")); 48 49 std::string readOrDie(llvm::StringRef Path) { 50 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text = 51 llvm::MemoryBuffer::getFile(Path); 52 if (std::error_code EC = Text.getError()) { 53 llvm::errs() << "Error: can't read grammar file '" << Path 54 << "': " << EC.message() << "\n"; 55 ::exit(1); 56 } 57 return Text.get()->getBuffer().str(); 58 } 59 } // namespace 60 61 int main(int argc, char *argv[]) { 62 llvm::cl::ParseCommandLineOptions(argc, argv, ""); 63 64 std::string GrammarText = readOrDie(Grammar); 65 std::vector<std::string> Diags; 66 auto G = clang::pseudo::Grammar::parseBNF(GrammarText, Diags); 67 68 if (!Diags.empty()) { 69 llvm::errs() << llvm::join(Diags, "\n"); 70 return 1; 71 } 72 73 std::error_code EC; 74 llvm::ToolOutputFile Out{OutputFilename, EC, llvm::sys::fs::OF_None}; 75 if (EC) { 76 llvm::errs() << EC.message() << '\n'; 77 return 1; 78 } 79 80 switch (Emit) { 81 case EmitSymbolList: 82 Out.os() << R"cpp( 83 #ifndef NONTERMINAL 84 #define NONTERMINAL(X, Y) 85 #endif 86 #ifndef RULE 87 #define RULE(X, Y) 88 #endif 89 #ifndef EXTENSION 90 #define EXTENSION(X, Y) 91 #endif 92 )cpp"; 93 for (clang::pseudo::SymbolID ID = 0; ID < G.table().Nonterminals.size(); 94 ++ID) 95 Out.os() << llvm::formatv("NONTERMINAL({0}, {1})\n", G.mangleSymbol(ID), 96 ID); 97 for (clang::pseudo::RuleID RID = 0; RID < G.table().Rules.size(); ++RID) 98 Out.os() << llvm::formatv("RULE({0}, {1})\n", G.mangleRule(RID), RID); 99 for (clang::pseudo::ExtensionID EID = 1 /*skip the sentinel 0 value*/; 100 EID < G.table().AttributeValues.size(); ++EID) { 101 llvm::StringRef Name = G.table().AttributeValues[EID]; 102 assert(!Name.empty()); 103 Out.os() << llvm::formatv("EXTENSION({0}, {1})\n", Name, EID); 104 } 105 Out.os() << R"cpp( 106 #undef NONTERMINAL 107 #undef RULE 108 #undef EXTENSION 109 )cpp"; 110 break; 111 case EmitGrammarContent: 112 for (llvm::StringRef Line : llvm::split(GrammarText, '\n')) { 113 Out.os() << '"'; 114 Out.os().write_escaped((Line + "\n").str()); 115 Out.os() << "\"\n"; 116 } 117 break; 118 } 119 120 Out.keep(); 121 122 return 0; 123 } 124