xref: /llvm-project/llvm/tools/llvm-rc/ResourceScriptParser.cpp (revision 7f110527415edc30c2abd027736347a80708cb98)
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("ACCELERATORS"))
67     Result = parseAcceleratorsResource();
68   else if (TypeToken->equalsLower("CURSOR"))
69     Result = parseCursorResource();
70   else if (TypeToken->equalsLower("ICON"))
71     Result = parseIconResource();
72   else if (TypeToken->equalsLower("HTML"))
73     Result = parseHTMLResource();
74   else
75     return getExpectedError("resource type", /* IsAlreadyRead = */ true);
76 
77   if (Result)
78     (*Result)->setName(*NameToken);
79 
80   return Result;
81 }
82 
83 bool RCParser::isNextTokenKind(Kind TokenKind) const {
84   return !isEof() && look().kind() == TokenKind;
85 }
86 
87 const RCToken &RCParser::look() const {
88   assert(!isEof());
89   return *CurLoc;
90 }
91 
92 const RCToken &RCParser::read() {
93   assert(!isEof());
94   return *CurLoc++;
95 }
96 
97 void RCParser::consume() {
98   assert(!isEof());
99   CurLoc++;
100 }
101 
102 Expected<uint32_t> RCParser::readInt() {
103   if (!isNextTokenKind(Kind::Int))
104     return getExpectedError("integer");
105   return read().intValue();
106 }
107 
108 Expected<StringRef> RCParser::readString() {
109   if (!isNextTokenKind(Kind::String))
110     return getExpectedError("string");
111   return read().value();
112 }
113 
114 Expected<StringRef> RCParser::readIdentifier() {
115   if (!isNextTokenKind(Kind::Identifier))
116     return getExpectedError("identifier");
117   return read().value();
118 }
119 
120 Expected<IntOrString> RCParser::readIntOrString() {
121   if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
122     return getExpectedError("int or string");
123   return IntOrString(read());
124 }
125 
126 Expected<IntOrString> RCParser::readTypeOrName() {
127   // We suggest that the correct resource name or type should be either an
128   // identifier or an integer. The original RC tool is much more liberal.
129   if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
130     return getExpectedError("int or identifier");
131   return IntOrString(read());
132 }
133 
134 Error RCParser::consumeType(Kind TokenKind) {
135   if (isNextTokenKind(TokenKind)) {
136     consume();
137     return Error::success();
138   }
139 
140   switch (TokenKind) {
141 #define TOKEN(TokenName)                                                       \
142   case Kind::TokenName:                                                        \
143     return getExpectedError(#TokenName);
144 #define SHORT_TOKEN(TokenName, TokenCh)                                        \
145   case Kind::TokenName:                                                        \
146     return getExpectedError(#TokenCh);
147 #include "ResourceScriptTokenList.h"
148 #undef SHORT_TOKEN
149 #undef TOKEN
150   }
151 
152   llvm_unreachable("All case options exhausted.");
153 }
154 
155 bool RCParser::consumeOptionalType(Kind TokenKind) {
156   if (isNextTokenKind(TokenKind)) {
157     consume();
158     return true;
159   }
160 
161   return false;
162 }
163 
164 Expected<SmallVector<uint32_t, 8>>
165 RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) {
166   assert(MinCount <= MaxCount);
167 
168   SmallVector<uint32_t, 8> Result;
169 
170   auto FailureHandler =
171       [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 8>> {
172     if (Result.size() < MinCount)
173       return std::move(Err);
174     consumeError(std::move(Err));
175     return Result;
176   };
177 
178   for (size_t i = 0; i < MaxCount; ++i) {
179     // Try to read a comma unless we read the first token.
180     // Sometimes RC tool requires them and sometimes not. We decide to
181     // always require them.
182     if (i >= 1) {
183       if (auto CommaError = consumeType(Kind::Comma))
184         return FailureHandler(std::move(CommaError));
185     }
186 
187     if (auto IntResult = readInt())
188       Result.push_back(*IntResult);
189     else
190       return FailureHandler(IntResult.takeError());
191   }
192 
193   return std::move(Result);
194 }
195 
196 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc) {
197   assert(FlagDesc.size() <= 32 && "More than 32 flags won't fit in result.");
198   assert(!FlagDesc.empty());
199 
200   uint32_t Result = 0;
201   while (isNextTokenKind(Kind::Comma)) {
202     consume();
203     ASSIGN_OR_RETURN(FlagResult, readIdentifier());
204     bool FoundFlag = false;
205 
206     for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
207       if (!FlagResult->equals_lower(FlagDesc[FlagId]))
208         continue;
209 
210       Result |= (1U << FlagId);
211       FoundFlag = true;
212       break;
213     }
214 
215     if (!FoundFlag)
216       return getExpectedError(join(FlagDesc, "/"), true);
217   }
218 
219   return Result;
220 }
221 
222 // As for now, we ignore the extended set of statements.
223 Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) {
224   OptionalStmtList Result;
225 
226   // The last statement is always followed by the start of the block.
227   while (!isNextTokenKind(Kind::BlockBegin)) {
228     ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(IsExtended));
229     Result.addStmt(std::move(*SingleParse));
230   }
231 
232   return std::move(Result);
233 }
234 
235 Expected<std::unique_ptr<OptionalStmt>>
236 RCParser::parseSingleOptionalStatement(bool) {
237   ASSIGN_OR_RETURN(TypeToken, readIdentifier());
238   if (TypeToken->equals_lower("CHARACTERISTICS"))
239     return parseCharacteristicsStmt();
240   else if (TypeToken->equals_lower("LANGUAGE"))
241     return parseLanguageStmt();
242   else if (TypeToken->equals_lower("VERSION"))
243     return parseVersionStmt();
244   else
245     return getExpectedError("optional statement type, BEGIN or '{'",
246                             /* IsAlreadyRead = */ true);
247 }
248 
249 RCParser::ParseType RCParser::parseLanguageResource() {
250   // Read LANGUAGE as an optional statement. If it's read correctly, we can
251   // upcast it to RCResource.
252   return parseLanguageStmt();
253 }
254 
255 RCParser::ParseType RCParser::parseAcceleratorsResource() {
256   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
257   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
258 
259   auto Accels = make_unique<AcceleratorsResource>(std::move(*OptStatements));
260 
261   while (!consumeOptionalType(Kind::BlockEnd)) {
262     ASSIGN_OR_RETURN(EventResult, readIntOrString());
263     RETURN_IF_ERROR(consumeType(Kind::Comma));
264     ASSIGN_OR_RETURN(IDResult, readInt());
265     ASSIGN_OR_RETURN(FlagsResult,
266                      parseFlags(AcceleratorsResource::Accelerator::OptionsStr));
267     Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
268   }
269 
270   return std::move(Accels);
271 }
272 
273 RCParser::ParseType RCParser::parseCursorResource() {
274   ASSIGN_OR_RETURN(Arg, readString());
275   return make_unique<CursorResource>(*Arg);
276 }
277 
278 RCParser::ParseType RCParser::parseIconResource() {
279   ASSIGN_OR_RETURN(Arg, readString());
280   return make_unique<IconResource>(*Arg);
281 }
282 
283 RCParser::ParseType RCParser::parseHTMLResource() {
284   ASSIGN_OR_RETURN(Arg, readString());
285   return make_unique<HTMLResource>(*Arg);
286 }
287 
288 RCParser::ParseType RCParser::parseStringTableResource() {
289   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
290   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
291 
292   auto Table = make_unique<StringTableResource>(std::move(*OptStatements));
293 
294   // Read strings until we reach the end of the block.
295   while (!consumeOptionalType(Kind::BlockEnd)) {
296     // Each definition consists of string's ID (an integer) and a string.
297     // Some examples in documentation suggest that there might be a comma in
298     // between, however we strictly adhere to the single statement definition.
299     ASSIGN_OR_RETURN(IDResult, readInt());
300     ASSIGN_OR_RETURN(StrResult, readString());
301     Table->addString(*IDResult, *StrResult);
302   }
303 
304   return std::move(Table);
305 }
306 
307 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
308   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
309   return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
310 }
311 
312 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
313   ASSIGN_OR_RETURN(Arg, readInt());
314   return make_unique<CharacteristicsStmt>(*Arg);
315 }
316 
317 RCParser::ParseOptionType RCParser::parseVersionStmt() {
318   ASSIGN_OR_RETURN(Arg, readInt());
319   return make_unique<VersionStmt>(*Arg);
320 }
321 
322 Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) {
323   return make_error<ParserError>(
324       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
325 }
326 
327 } // namespace rc
328 } // namespace llvm
329