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 EXTENSION 87 #define EXTENSION(X, Y) 88 #endif 89 )cpp"; 90 for (clang::pseudo::SymbolID ID = 0; ID < G.table().Nonterminals.size(); 91 ++ID) { 92 std::string Name = G.symbolName(ID).str(); 93 // translation-unit -> translation_unit 94 std::replace(Name.begin(), Name.end(), '-', '_'); 95 Out.os() << llvm::formatv("NONTERMINAL({0}, {1})\n", Name, ID); 96 } 97 for (clang::pseudo::ExtensionID EID = 1 /*skip the sentinel 0 value*/; 98 EID < G.table().AttributeValues.size(); ++EID) { 99 llvm::StringRef Name = G.table().AttributeValues[EID]; 100 assert(!Name.empty()); 101 Out.os() << llvm::formatv("EXTENSION({0}, {1})\n", Name, EID); 102 } 103 Out.os() << R"cpp( 104 #undef NONTERMINAL 105 #undef EXTENSION 106 )cpp"; 107 break; 108 case EmitGrammarContent: 109 for (llvm::StringRef Line : llvm::split(GrammarText, '\n')) { 110 Out.os() << '"'; 111 Out.os().write_escaped((Line + "\n").str()); 112 Out.os() << "\"\n"; 113 } 114 break; 115 } 116 117 Out.keep(); 118 119 return 0; 120 } 121