xref: /llvm-project/llvm/tools/llvm-rc/ResourceScriptParser.cpp (revision 72aa937ed8cf9a0478bc9908f80ce51e372e8ef4)
1 //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===---------------------------------------------------------------------===//
9 //
10 // This implements the parser defined in ResourceScriptParser.h.
11 //
12 //===---------------------------------------------------------------------===//
13 
14 #include "ResourceScriptParser.h"
15 
16 // Take an expression returning llvm::Error and forward the error if it exists.
17 #define RETURN_IF_ERROR(Expr)                                                  \
18   if (auto Err = (Expr))                                                       \
19     return std::move(Err);
20 
21 // Take an expression returning llvm::Expected<T> and assign it to Var or
22 // forward the error out of the function.
23 #define ASSIGN_OR_RETURN(Var, Expr)                                            \
24   auto Var = (Expr);                                                           \
25   if (!Var)                                                                    \
26     return Var.takeError();
27 
28 namespace llvm {
29 namespace rc {
30 
31 RCParser::ParserError::ParserError(const Twine Expected, const LocIter CurLoc,
32                                    const LocIter End)
33     : ErrorLoc(CurLoc), FileEnd(End) {
34   CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
35                (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
36 }
37 
38 char RCParser::ParserError::ID = 0;
39 
40 RCParser::RCParser(const std::vector<RCToken> &TokenList)
41     : Tokens(TokenList), CurLoc(Tokens.begin()), End(Tokens.end()) {}
42 
43 RCParser::RCParser(std::vector<RCToken> &&TokenList)
44     : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
45 
46 bool RCParser::isEof() const { return CurLoc == End; }
47 
48 RCParser::ParseType RCParser::parseSingleResource() {
49   // The first thing we read is usually a resource's name. However, in some
50   // cases (LANGUAGE and STRINGTABLE) the resources don't have their names
51   // and the first token to be read is the type.
52   ASSIGN_OR_RETURN(NameToken, readTypeOrName());
53 
54   if (NameToken->equalsLower("LANGUAGE"))
55     return parseLanguageResource();
56   else if (NameToken->equalsLower("STRINGTABLE"))
57     return parseStringTableResource();
58 
59   // If it's not an unnamed resource, what we've just read is a name. Now,
60   // read resource type;
61   ASSIGN_OR_RETURN(TypeToken, readTypeOrName());
62 
63   ParseType Result = std::unique_ptr<RCResource>();
64   (void)!Result;
65 
66   if (TypeToken->equalsLower("CURSOR"))
67     Result = parseCursorResource();
68   else if (TypeToken->equalsLower("ICON"))
69     Result = parseIconResource();
70   else if (TypeToken->equalsLower("HTML"))
71     Result = parseHTMLResource();
72   else
73     return getExpectedError("resource type", /* IsAlreadyRead = */ true);
74 
75   if (Result)
76     (*Result)->setName(*NameToken);
77 
78   return Result;
79 }
80 
81 bool RCParser::isNextTokenKind(Kind TokenKind) const {
82   return !isEof() && look().kind() == TokenKind;
83 }
84 
85 const RCToken &RCParser::look() const {
86   assert(!isEof());
87   return *CurLoc;
88 }
89 
90 const RCToken &RCParser::read() {
91   assert(!isEof());
92   return *CurLoc++;
93 }
94 
95 void RCParser::consume() {
96   assert(!isEof());
97   CurLoc++;
98 }
99 
100 Expected<uint32_t> RCParser::readInt() {
101   if (!isNextTokenKind(Kind::Int))
102     return getExpectedError("integer");
103   return read().intValue();
104 }
105 
106 Expected<StringRef> RCParser::readString() {
107   if (!isNextTokenKind(Kind::String))
108     return getExpectedError("string");
109   return read().value();
110 }
111 
112 Expected<StringRef> RCParser::readIdentifier() {
113   if (!isNextTokenKind(Kind::Identifier))
114     return getExpectedError("identifier");
115   return read().value();
116 }
117 
118 Expected<IntOrString> RCParser::readTypeOrName() {
119   // We suggest that the correct resource name or type should be either an
120   // identifier or an integer. The original RC tool is much more liberal.
121   if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
122     return getExpectedError("int or identifier");
123 
124   const RCToken &Tok = read();
125   if (Tok.kind() == Kind::Int)
126     return IntOrString(Tok.intValue());
127   else
128     return IntOrString(Tok.value());
129 }
130 
131 Error RCParser::consumeType(Kind TokenKind) {
132   if (isNextTokenKind(TokenKind)) {
133     consume();
134     return Error::success();
135   }
136 
137   switch (TokenKind) {
138 #define TOKEN(TokenName)                                                       \
139   case Kind::TokenName:                                                        \
140     return getExpectedError(#TokenName);
141 #define SHORT_TOKEN(TokenName, TokenCh)                                        \
142   case Kind::TokenName:                                                        \
143     return getExpectedError(#TokenCh);
144 #include "ResourceScriptTokenList.h"
145 #undef SHORT_TOKEN
146 #undef TOKEN
147   }
148 
149   llvm_unreachable("All case options exhausted.");
150 }
151 
152 bool RCParser::consumeOptionalType(Kind TokenKind) {
153   if (isNextTokenKind(TokenKind)) {
154     consume();
155     return true;
156   }
157 
158   return false;
159 }
160 
161 Expected<SmallVector<uint32_t, 8>>
162 RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) {
163   assert(MinCount <= MaxCount);
164 
165   SmallVector<uint32_t, 8> Result;
166 
167   auto FailureHandler =
168       [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 8>> {
169     if (Result.size() < MinCount)
170       return std::move(Err);
171     consumeError(std::move(Err));
172     return Result;
173   };
174 
175   for (size_t i = 0; i < MaxCount; ++i) {
176     // Try to read a comma unless we read the first token.
177     // Sometimes RC tool requires them and sometimes not. We decide to
178     // always require them.
179     if (i >= 1) {
180       if (auto CommaError = consumeType(Kind::Comma))
181         return FailureHandler(std::move(CommaError));
182     }
183 
184     if (auto IntResult = readInt())
185       Result.push_back(*IntResult);
186     else
187       return FailureHandler(IntResult.takeError());
188   }
189 
190   return std::move(Result);
191 }
192 
193 // As for now, we ignore the extended set of statements.
194 Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) {
195   OptionalStmtList Result;
196 
197   // The last statement is always followed by the start of the block.
198   while (!isNextTokenKind(Kind::BlockBegin)) {
199     ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(IsExtended));
200     Result.addStmt(std::move(*SingleParse));
201   }
202 
203   return std::move(Result);
204 }
205 
206 Expected<std::unique_ptr<OptionalStmt>>
207 RCParser::parseSingleOptionalStatement(bool) {
208   ASSIGN_OR_RETURN(TypeToken, readIdentifier());
209   if (TypeToken->equals_lower("CHARACTERISTICS"))
210     return parseCharacteristicsStmt();
211   else if (TypeToken->equals_lower("LANGUAGE"))
212     return parseLanguageStmt();
213   else if (TypeToken->equals_lower("VERSION"))
214     return parseVersionStmt();
215   else
216     return getExpectedError("optional statement type, BEGIN or '{'",
217                             /* IsAlreadyRead = */ true);
218 }
219 
220 RCParser::ParseType RCParser::parseLanguageResource() {
221   // Read LANGUAGE as an optional statement. If it's read correctly, we can
222   // upcast it to RCResource.
223   return parseLanguageStmt();
224 }
225 
226 RCParser::ParseType RCParser::parseCursorResource() {
227   ASSIGN_OR_RETURN(Arg, readString());
228   return make_unique<CursorResource>(*Arg);
229 }
230 
231 RCParser::ParseType RCParser::parseIconResource() {
232   ASSIGN_OR_RETURN(Arg, readString());
233   return make_unique<IconResource>(*Arg);
234 }
235 
236 RCParser::ParseType RCParser::parseHTMLResource() {
237   ASSIGN_OR_RETURN(Arg, readString());
238   return make_unique<HTMLResource>(*Arg);
239 }
240 
241 RCParser::ParseType RCParser::parseStringTableResource() {
242   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
243   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
244 
245   auto Table = make_unique<StringTableResource>(std::move(*OptStatements));
246 
247   // Read strings until we reach the end of the block.
248   while (!consumeOptionalType(Kind::BlockEnd)) {
249     // Each definition consists of string's ID (an integer) and a string.
250     // Some examples in documentation suggest that there might be a comma in
251     // between, however we strictly adhere to the single statement definition.
252     ASSIGN_OR_RETURN(IDResult, readInt());
253     ASSIGN_OR_RETURN(StrResult, readString());
254     Table->addString(*IDResult, *StrResult);
255   }
256 
257   return std::move(Table);
258 }
259 
260 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
261   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
262   return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
263 }
264 
265 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
266   ASSIGN_OR_RETURN(Arg, readInt());
267   return make_unique<CharacteristicsStmt>(*Arg);
268 }
269 
270 RCParser::ParseOptionType RCParser::parseVersionStmt() {
271   ASSIGN_OR_RETURN(Arg, readInt());
272   return make_unique<VersionStmt>(*Arg);
273 }
274 
275 Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) {
276   return make_error<ParserError>(
277       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
278 }
279 
280 } // namespace rc
281 } // namespace llvm
282