1 //===-- Implementation of the main header generation class ----------------===//
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 "Generator.h"
10 
11 #include "IncludeFileCommand.h"
12 #include "PublicAPICommand.h"
13 
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include "llvm/Support/SourceMgr.h"
17 #include "llvm/Support/raw_ostream.h"
18 
19 #include <cstdlib>
20 #include <memory>
21 
22 static const char CommandPrefix[] = "%%";
23 static const size_t CommandPrefixSize = llvm::StringRef(CommandPrefix).size();
24 
25 static const char CommentPrefix[] = "<!>";
26 
27 static const char ParamNamePrefix[] = "${";
28 static const size_t ParamNamePrefixSize =
29     llvm::StringRef(ParamNamePrefix).size();
30 static const char ParamNameSuffix[] = "}";
31 static const size_t ParamNameSuffixSize =
32     llvm::StringRef(ParamNameSuffix).size();
33 
34 namespace llvm_libc {
35 
getCommandHandler(llvm::StringRef CommandName)36 Command *Generator::getCommandHandler(llvm::StringRef CommandName) {
37   if (CommandName == IncludeFileCommand::Name) {
38     if (!IncludeFileCmd)
39       IncludeFileCmd = std::make_unique<IncludeFileCommand>();
40     return IncludeFileCmd.get();
41   } else if (CommandName == PublicAPICommand::Name) {
42     if (!PublicAPICmd)
43       PublicAPICmd = std::make_unique<PublicAPICommand>(EntrypointNameList);
44     return PublicAPICmd.get();
45   } else {
46     return nullptr;
47   }
48 }
49 
parseCommandArgs(llvm::StringRef ArgStr,ArgVector & Args)50 void Generator::parseCommandArgs(llvm::StringRef ArgStr, ArgVector &Args) {
51   if (!ArgStr.contains(',') && ArgStr.trim(' ').trim('\t').size() == 0) {
52     // If it is just space between the parenthesis
53     return;
54   }
55 
56   ArgStr.split(Args, ",");
57   for (llvm::StringRef &A : Args) {
58     A = A.trim(' ');
59     if (A.startswith(ParamNamePrefix) && A.endswith(ParamNameSuffix)) {
60       A = A.drop_front(ParamNamePrefixSize).drop_back(ParamNameSuffixSize);
61       A = ArgMap[std::string(A)];
62     }
63   }
64 }
65 
generate(llvm::raw_ostream & OS,llvm::RecordKeeper & Records)66 void Generator::generate(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {
67   auto DefFileBuffer = llvm::MemoryBuffer::getFile(HeaderDefFile);
68   if (!DefFileBuffer) {
69     llvm::errs() << "Unable to open " << HeaderDefFile << ".\n";
70     std::exit(1);
71   }
72   llvm::SourceMgr SrcMgr;
73   unsigned DefFileID = SrcMgr.AddNewSourceBuffer(
74       std::move(DefFileBuffer.get()), llvm::SMLoc::getFromPointer(nullptr));
75 
76   llvm::StringRef Content = SrcMgr.getMemoryBuffer(DefFileID)->getBuffer();
77   while (true) {
78     std::pair<llvm::StringRef, llvm::StringRef> P = Content.split('\n');
79     Content = P.second;
80 
81     llvm::StringRef Line = P.first.trim(' ');
82     if (Line.startswith(CommandPrefix)) {
83       Line = Line.drop_front(CommandPrefixSize);
84 
85       P = Line.split("(");
86       if (P.second.empty() || P.second[P.second.size() - 1] != ')') {
87         SrcMgr.PrintMessage(llvm::SMLoc::getFromPointer(P.second.data()),
88                             llvm::SourceMgr::DK_Error,
89                             "Command argument list should begin with '(' "
90                             "and end with ')'.");
91         std::exit(1);
92       }
93       llvm::StringRef CommandName = P.first;
94       Command *Cmd = getCommandHandler(CommandName);
95       if (Cmd == nullptr) {
96         SrcMgr.PrintMessage(llvm::SMLoc::getFromPointer(CommandName.data()),
97                             llvm::SourceMgr::DK_Error,
98                             "Unknown command '%%" + CommandName + "'.");
99         std::exit(1);
100       }
101 
102       llvm::StringRef ArgStr = P.second.drop_back(1);
103       ArgVector Args;
104       parseCommandArgs(ArgStr, Args);
105 
106       Command::ErrorReporter Reporter(
107           llvm::SMLoc::getFromPointer(CommandName.data()), SrcMgr);
108       Cmd->run(OS, Args, StdHeader, Records, Reporter);
109     } else if (!Line.startswith(CommentPrefix)) {
110       // There is no comment or command on this line so we just write it as is.
111       OS << P.first << "\n";
112     }
113 
114     if (P.second.empty())
115       break;
116   }
117 }
118 
119 } // namespace llvm_libc
120