121
|
1 //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
|
|
2 //
|
147
|
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
|
121
|
6 //
|
|
7 //===---------------------------------------------------------------------===//
|
|
8 //
|
|
9 // This implements the parser defined in ResourceScriptParser.h.
|
|
10 //
|
|
11 //===---------------------------------------------------------------------===//
|
|
12
|
|
13 #include "ResourceScriptParser.h"
|
|
14 #include "llvm/Option/ArgList.h"
|
|
15 #include "llvm/Support/FileSystem.h"
|
|
16 #include "llvm/Support/Path.h"
|
|
17 #include "llvm/Support/Process.h"
|
|
18
|
|
19 // Take an expression returning llvm::Error and forward the error if it exists.
|
|
20 #define RETURN_IF_ERROR(Expr) \
|
|
21 if (auto Err = (Expr)) \
|
|
22 return std::move(Err);
|
|
23
|
|
24 // Take an expression returning llvm::Expected<T> and assign it to Var or
|
|
25 // forward the error out of the function.
|
|
26 #define ASSIGN_OR_RETURN(Var, Expr) \
|
|
27 auto Var = (Expr); \
|
|
28 if (!Var) \
|
|
29 return Var.takeError();
|
|
30
|
|
31 namespace llvm {
|
|
32 namespace rc {
|
|
33
|
|
34 RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
|
|
35 const LocIter End)
|
|
36 : ErrorLoc(CurLoc), FileEnd(End) {
|
|
37 CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
|
|
38 (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
|
|
39 }
|
|
40
|
|
41 char RCParser::ParserError::ID = 0;
|
|
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();
|
147
|
68 else if (TypeToken->equalsLower("BITMAP"))
|
|
69 Result = parseBitmapResource();
|
121
|
70 else if (TypeToken->equalsLower("CURSOR"))
|
|
71 Result = parseCursorResource();
|
|
72 else if (TypeToken->equalsLower("DIALOG"))
|
|
73 Result = parseDialogResource(false);
|
|
74 else if (TypeToken->equalsLower("DIALOGEX"))
|
|
75 Result = parseDialogResource(true);
|
147
|
76 else if (TypeToken->equalsLower("HTML"))
|
|
77 Result = parseHTMLResource();
|
121
|
78 else if (TypeToken->equalsLower("ICON"))
|
|
79 Result = parseIconResource();
|
|
80 else if (TypeToken->equalsLower("MENU"))
|
|
81 Result = parseMenuResource();
|
147
|
82 else if (TypeToken->equalsLower("RCDATA"))
|
|
83 Result = parseUserDefinedResource(RkRcData);
|
121
|
84 else if (TypeToken->equalsLower("VERSIONINFO"))
|
|
85 Result = parseVersionInfoResource();
|
|
86 else
|
|
87 Result = parseUserDefinedResource(*TypeToken);
|
|
88
|
|
89 if (Result)
|
|
90 (*Result)->setName(*NameToken);
|
|
91
|
|
92 return Result;
|
|
93 }
|
|
94
|
|
95 bool RCParser::isNextTokenKind(Kind TokenKind) const {
|
|
96 return !isEof() && look().kind() == TokenKind;
|
|
97 }
|
|
98
|
|
99 const RCToken &RCParser::look() const {
|
|
100 assert(!isEof());
|
|
101 return *CurLoc;
|
|
102 }
|
|
103
|
|
104 const RCToken &RCParser::read() {
|
|
105 assert(!isEof());
|
|
106 return *CurLoc++;
|
|
107 }
|
|
108
|
|
109 void RCParser::consume() {
|
|
110 assert(!isEof());
|
|
111 CurLoc++;
|
|
112 }
|
|
113
|
|
114 // An integer description might consist of a single integer or
|
|
115 // an arithmetic expression evaluating to the integer. The expressions
|
147
|
116 // can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning
|
|
117 // is the same as in C++ except for 'not' expression.
|
121
|
118 // The operators in the original RC implementation have the following
|
|
119 // precedence:
|
147
|
120 // 1) Unary operators (- ~ not),
|
121
|
121 // 2) Binary operators (+ - & |), with no precedence.
|
|
122 //
|
147
|
123 // 'not' expression is mostly useful for style values. It evaluates to 0,
|
|
124 // but value given to the operator is stored separately from integer value.
|
|
125 // It's mostly useful for control style expressions and causes bits from
|
|
126 // default control style to be excluded from generated style. For binary
|
|
127 // operators the mask from the right operand is applied to the left operand
|
|
128 // and masks from both operands are combined in operator result.
|
|
129 //
|
121
|
130 // The following grammar is used to parse the expressions Exp1:
|
|
131 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
|
147
|
132 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
|
121
|
133 // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
|
|
134 // separated by binary operators.)
|
|
135 //
|
|
136 // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
|
|
137 // is read by parseIntExpr2().
|
|
138 //
|
|
139 // The original Microsoft tool handles multiple unary operators incorrectly.
|
|
140 // For example, in 16-bit little-endian integers:
|
|
141 // 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
|
|
142 // 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
|
|
143 // Our implementation differs from the original one and handles these
|
|
144 // operators correctly:
|
|
145 // 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
|
|
146 // 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
|
|
147
|
147
|
148 Expected<RCInt> RCParser::readInt() {
|
|
149 ASSIGN_OR_RETURN(Value, parseIntExpr1());
|
|
150 return (*Value).getValue();
|
|
151 }
|
121
|
152
|
147
|
153 Expected<IntWithNotMask> RCParser::parseIntExpr1() {
|
121
|
154 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
|
|
155 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
|
147
|
156 IntWithNotMask Result = *FirstResult;
|
121
|
157
|
|
158 while (!isEof() && look().isBinaryOp()) {
|
|
159 auto OpToken = read();
|
|
160 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
|
|
161
|
|
162 switch (OpToken.kind()) {
|
|
163 case Kind::Plus:
|
|
164 Result += *NextResult;
|
|
165 break;
|
|
166
|
|
167 case Kind::Minus:
|
|
168 Result -= *NextResult;
|
|
169 break;
|
|
170
|
|
171 case Kind::Pipe:
|
|
172 Result |= *NextResult;
|
|
173 break;
|
|
174
|
|
175 case Kind::Amp:
|
|
176 Result &= *NextResult;
|
|
177 break;
|
|
178
|
|
179 default:
|
|
180 llvm_unreachable("Already processed all binary ops.");
|
|
181 }
|
|
182 }
|
|
183
|
|
184 return Result;
|
|
185 }
|
|
186
|
147
|
187 Expected<IntWithNotMask> RCParser::parseIntExpr2() {
|
|
188 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
|
121
|
189 static const char ErrorMsg[] = "'-', '~', integer or '('";
|
|
190
|
|
191 if (isEof())
|
|
192 return getExpectedError(ErrorMsg);
|
|
193
|
|
194 switch (look().kind()) {
|
|
195 case Kind::Minus: {
|
|
196 consume();
|
|
197 ASSIGN_OR_RETURN(Result, parseIntExpr2());
|
|
198 return -(*Result);
|
|
199 }
|
|
200
|
|
201 case Kind::Tilde: {
|
|
202 consume();
|
|
203 ASSIGN_OR_RETURN(Result, parseIntExpr2());
|
|
204 return ~(*Result);
|
|
205 }
|
|
206
|
|
207 case Kind::Int:
|
|
208 return RCInt(read());
|
|
209
|
|
210 case Kind::LeftParen: {
|
|
211 consume();
|
|
212 ASSIGN_OR_RETURN(Result, parseIntExpr1());
|
|
213 RETURN_IF_ERROR(consumeType(Kind::RightParen));
|
|
214 return *Result;
|
|
215 }
|
|
216
|
147
|
217 case Kind::Identifier: {
|
|
218 if (!read().value().equals_lower("not"))
|
|
219 return getExpectedError(ErrorMsg, true);
|
|
220 ASSIGN_OR_RETURN(Result, parseIntExpr2());
|
|
221 return IntWithNotMask(0, (*Result).getValue());
|
|
222 }
|
|
223
|
121
|
224 default:
|
|
225 return getExpectedError(ErrorMsg);
|
|
226 }
|
|
227 }
|
|
228
|
|
229 Expected<StringRef> RCParser::readString() {
|
|
230 if (!isNextTokenKind(Kind::String))
|
|
231 return getExpectedError("string");
|
|
232 return read().value();
|
|
233 }
|
|
234
|
147
|
235 Expected<StringRef> RCParser::readFilename() {
|
|
236 if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
|
|
237 return getExpectedError("string");
|
|
238 return read().value();
|
|
239 }
|
|
240
|
121
|
241 Expected<StringRef> RCParser::readIdentifier() {
|
|
242 if (!isNextTokenKind(Kind::Identifier))
|
|
243 return getExpectedError("identifier");
|
|
244 return read().value();
|
|
245 }
|
|
246
|
|
247 Expected<IntOrString> RCParser::readIntOrString() {
|
|
248 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
|
|
249 return getExpectedError("int or string");
|
|
250 return IntOrString(read());
|
|
251 }
|
|
252
|
|
253 Expected<IntOrString> RCParser::readTypeOrName() {
|
|
254 // We suggest that the correct resource name or type should be either an
|
|
255 // identifier or an integer. The original RC tool is much more liberal.
|
|
256 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
|
|
257 return getExpectedError("int or identifier");
|
|
258 return IntOrString(read());
|
|
259 }
|
|
260
|
|
261 Error RCParser::consumeType(Kind TokenKind) {
|
|
262 if (isNextTokenKind(TokenKind)) {
|
|
263 consume();
|
|
264 return Error::success();
|
|
265 }
|
|
266
|
|
267 switch (TokenKind) {
|
|
268 #define TOKEN(TokenName) \
|
|
269 case Kind::TokenName: \
|
|
270 return getExpectedError(#TokenName);
|
|
271 #define SHORT_TOKEN(TokenName, TokenCh) \
|
|
272 case Kind::TokenName: \
|
|
273 return getExpectedError(#TokenCh);
|
134
|
274 #include "ResourceScriptTokenList.def"
|
121
|
275 }
|
|
276
|
|
277 llvm_unreachable("All case options exhausted.");
|
|
278 }
|
|
279
|
|
280 bool RCParser::consumeOptionalType(Kind TokenKind) {
|
|
281 if (isNextTokenKind(TokenKind)) {
|
|
282 consume();
|
|
283 return true;
|
|
284 }
|
|
285
|
|
286 return false;
|
|
287 }
|
|
288
|
|
289 Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
|
|
290 size_t MaxCount) {
|
|
291 assert(MinCount <= MaxCount);
|
|
292
|
|
293 SmallVector<RCInt, 8> Result;
|
|
294
|
|
295 auto FailureHandler =
|
|
296 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
|
|
297 if (Result.size() < MinCount)
|
|
298 return std::move(Err);
|
|
299 consumeError(std::move(Err));
|
|
300 return Result;
|
|
301 };
|
|
302
|
|
303 for (size_t i = 0; i < MaxCount; ++i) {
|
|
304 // Try to read a comma unless we read the first token.
|
|
305 // Sometimes RC tool requires them and sometimes not. We decide to
|
|
306 // always require them.
|
|
307 if (i >= 1) {
|
|
308 if (auto CommaError = consumeType(Kind::Comma))
|
|
309 return FailureHandler(std::move(CommaError));
|
|
310 }
|
|
311
|
|
312 if (auto IntResult = readInt())
|
|
313 Result.push_back(*IntResult);
|
|
314 else
|
|
315 return FailureHandler(IntResult.takeError());
|
|
316 }
|
|
317
|
|
318 return std::move(Result);
|
|
319 }
|
|
320
|
|
321 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
|
|
322 ArrayRef<uint32_t> FlagValues) {
|
|
323 assert(!FlagDesc.empty());
|
|
324 assert(FlagDesc.size() == FlagValues.size());
|
|
325
|
|
326 uint32_t Result = 0;
|
|
327 while (isNextTokenKind(Kind::Comma)) {
|
|
328 consume();
|
|
329 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
|
|
330 bool FoundFlag = false;
|
|
331
|
|
332 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
|
|
333 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
|
|
334 continue;
|
|
335
|
|
336 Result |= FlagValues[FlagId];
|
|
337 FoundFlag = true;
|
|
338 break;
|
|
339 }
|
|
340
|
|
341 if (!FoundFlag)
|
|
342 return getExpectedError(join(FlagDesc, "/"), true);
|
|
343 }
|
|
344
|
|
345 return Result;
|
|
346 }
|
|
347
|
147
|
348 uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
|
|
349 while (!isEof()) {
|
|
350 const RCToken &Token = look();
|
|
351 if (Token.kind() != Kind::Identifier)
|
|
352 return Flags;
|
|
353 const StringRef Ident = Token.value();
|
|
354 if (Ident.equals_lower("PRELOAD"))
|
|
355 Flags |= MfPreload;
|
|
356 else if (Ident.equals_lower("LOADONCALL"))
|
|
357 Flags &= ~MfPreload;
|
|
358 else if (Ident.equals_lower("FIXED"))
|
|
359 Flags &= ~(MfMoveable | MfDiscardable);
|
|
360 else if (Ident.equals_lower("MOVEABLE"))
|
|
361 Flags |= MfMoveable;
|
|
362 else if (Ident.equals_lower("DISCARDABLE"))
|
|
363 Flags |= MfDiscardable | MfMoveable | MfPure;
|
|
364 else if (Ident.equals_lower("PURE"))
|
|
365 Flags |= MfPure;
|
|
366 else if (Ident.equals_lower("IMPURE"))
|
|
367 Flags &= ~(MfPure | MfDiscardable);
|
|
368 else if (Ident.equals_lower("SHARED"))
|
|
369 Flags |= MfPure;
|
|
370 else if (Ident.equals_lower("NONSHARED"))
|
|
371 Flags &= ~(MfPure | MfDiscardable);
|
|
372 else
|
|
373 return Flags;
|
|
374 consume();
|
|
375 }
|
|
376 return Flags;
|
|
377 }
|
|
378
|
121
|
379 Expected<OptionalStmtList>
|
|
380 RCParser::parseOptionalStatements(OptStmtType StmtsType) {
|
|
381 OptionalStmtList Result;
|
|
382
|
|
383 // The last statement is always followed by the start of the block.
|
|
384 while (!isNextTokenKind(Kind::BlockBegin)) {
|
|
385 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
|
|
386 Result.addStmt(std::move(*SingleParse));
|
|
387 }
|
|
388
|
|
389 return std::move(Result);
|
|
390 }
|
|
391
|
|
392 Expected<std::unique_ptr<OptionalStmt>>
|
|
393 RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
|
|
394 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
|
|
395 if (TypeToken->equals_lower("CHARACTERISTICS"))
|
|
396 return parseCharacteristicsStmt();
|
|
397 if (TypeToken->equals_lower("LANGUAGE"))
|
|
398 return parseLanguageStmt();
|
|
399 if (TypeToken->equals_lower("VERSION"))
|
|
400 return parseVersionStmt();
|
|
401
|
|
402 if (StmtsType != OptStmtType::BasicStmt) {
|
|
403 if (TypeToken->equals_lower("CAPTION"))
|
|
404 return parseCaptionStmt();
|
147
|
405 if (TypeToken->equals_lower("CLASS"))
|
|
406 return parseClassStmt();
|
|
407 if (TypeToken->equals_lower("EXSTYLE"))
|
|
408 return parseExStyleStmt();
|
121
|
409 if (TypeToken->equals_lower("FONT"))
|
|
410 return parseFontStmt(StmtsType);
|
|
411 if (TypeToken->equals_lower("STYLE"))
|
|
412 return parseStyleStmt();
|
|
413 }
|
|
414
|
|
415 return getExpectedError("optional statement type, BEGIN or '{'",
|
|
416 /* IsAlreadyRead = */ true);
|
|
417 }
|
|
418
|
|
419 RCParser::ParseType RCParser::parseLanguageResource() {
|
|
420 // Read LANGUAGE as an optional statement. If it's read correctly, we can
|
|
421 // upcast it to RCResource.
|
|
422 return parseLanguageStmt();
|
|
423 }
|
|
424
|
|
425 RCParser::ParseType RCParser::parseAcceleratorsResource() {
|
147
|
426 uint16_t MemoryFlags =
|
|
427 parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
|
121
|
428 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
|
|
429 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
|
|
430
|
147
|
431 auto Accels = llvm::make_unique<AcceleratorsResource>(
|
|
432 std::move(*OptStatements), MemoryFlags);
|
121
|
433
|
|
434 while (!consumeOptionalType(Kind::BlockEnd)) {
|
|
435 ASSIGN_OR_RETURN(EventResult, readIntOrString());
|
|
436 RETURN_IF_ERROR(consumeType(Kind::Comma));
|
|
437 ASSIGN_OR_RETURN(IDResult, readInt());
|
|
438 ASSIGN_OR_RETURN(
|
|
439 FlagsResult,
|
|
440 parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
|
|
441 AcceleratorsResource::Accelerator::OptionsFlags));
|
|
442 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
|
|
443 }
|
|
444
|
|
445 return std::move(Accels);
|
|
446 }
|
|
447
|
|
448 RCParser::ParseType RCParser::parseCursorResource() {
|
147
|
449 uint16_t MemoryFlags =
|
|
450 parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
|
|
451 ASSIGN_OR_RETURN(Arg, readFilename());
|
|
452 return llvm::make_unique<CursorResource>(*Arg, MemoryFlags);
|
121
|
453 }
|
|
454
|
|
455 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
|
147
|
456 uint16_t MemoryFlags =
|
|
457 parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
|
121
|
458 // Dialog resources have the following format of the arguments:
|
|
459 // DIALOG: x, y, width, height [opt stmts...] {controls...}
|
|
460 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
|
|
461 // These are very similar, so we parse them together.
|
|
462 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
|
|
463
|
|
464 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
|
|
465 if (IsExtended && consumeOptionalType(Kind::Comma)) {
|
|
466 ASSIGN_OR_RETURN(HelpIDResult, readInt());
|
|
467 HelpID = *HelpIDResult;
|
|
468 }
|
|
469
|
|
470 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
|
|
471 IsExtended ? OptStmtType::DialogExStmt
|
|
472 : OptStmtType::DialogStmt));
|
|
473
|
|
474 assert(isNextTokenKind(Kind::BlockBegin) &&
|
|
475 "parseOptionalStatements, when successful, halts on BlockBegin.");
|
|
476 consume();
|
|
477
|
|
478 auto Dialog = llvm::make_unique<DialogResource>(
|
|
479 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
|
147
|
480 HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
|
121
|
481
|
|
482 while (!consumeOptionalType(Kind::BlockEnd)) {
|
|
483 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
|
|
484 Dialog->addControl(std::move(*ControlDefResult));
|
|
485 }
|
|
486
|
|
487 return std::move(Dialog);
|
|
488 }
|
|
489
|
|
490 RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
|
147
|
491 uint16_t MemoryFlags =
|
|
492 parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
|
121
|
493 if (isEof())
|
|
494 return getExpectedError("filename, '{' or BEGIN");
|
|
495
|
|
496 // Check if this is a file resource.
|
147
|
497 switch (look().kind()) {
|
|
498 case Kind::String:
|
|
499 case Kind::Identifier:
|
|
500 return llvm::make_unique<UserDefinedResource>(Type, read().value(),
|
|
501 MemoryFlags);
|
|
502 default:
|
|
503 break;
|
|
504 }
|
121
|
505
|
|
506 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
|
|
507 std::vector<IntOrString> Data;
|
|
508
|
|
509 // Consume comma before each consecutive token except the first one.
|
|
510 bool ConsumeComma = false;
|
|
511 while (!consumeOptionalType(Kind::BlockEnd)) {
|
|
512 if (ConsumeComma)
|
|
513 RETURN_IF_ERROR(consumeType(Kind::Comma));
|
|
514 ConsumeComma = true;
|
|
515
|
|
516 ASSIGN_OR_RETURN(Item, readIntOrString());
|
|
517 Data.push_back(*Item);
|
|
518 }
|
|
519
|
147
|
520 return llvm::make_unique<UserDefinedResource>(Type, std::move(Data),
|
|
521 MemoryFlags);
|
121
|
522 }
|
|
523
|
|
524 RCParser::ParseType RCParser::parseVersionInfoResource() {
|
147
|
525 uint16_t MemoryFlags =
|
|
526 parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
|
121
|
527 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
|
|
528 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
|
147
|
529 return llvm::make_unique<VersionInfoResource>(
|
|
530 std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
|
121
|
531 }
|
|
532
|
|
533 Expected<Control> RCParser::parseControl() {
|
|
534 // Each control definition (except CONTROL) follows one of the schemes below
|
|
535 // depending on the control class:
|
|
536 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
|
|
537 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
|
|
538 // Note that control ids must be integers.
|
|
539 // Text might be either a string or an integer pointing to resource ID.
|
|
540 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
|
|
541 std::string ClassUpper = ClassResult->upper();
|
|
542 auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
|
|
543 if (CtlInfo == Control::SupportedCtls.end())
|
|
544 return getExpectedError("control type, END or '}'", true);
|
|
545
|
|
546 // Read caption if necessary.
|
|
547 IntOrString Caption{StringRef()};
|
|
548 if (CtlInfo->getValue().HasTitle) {
|
|
549 ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
|
|
550 RETURN_IF_ERROR(consumeType(Kind::Comma));
|
|
551 Caption = *CaptionResult;
|
|
552 }
|
|
553
|
147
|
554 ASSIGN_OR_RETURN(ID, readInt());
|
|
555 RETURN_IF_ERROR(consumeType(Kind::Comma));
|
|
556
|
|
557 IntOrString Class;
|
|
558 Optional<IntWithNotMask> Style;
|
|
559 if (ClassUpper == "CONTROL") {
|
|
560 // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
|
|
561 ASSIGN_OR_RETURN(ClassStr, readString());
|
|
562 RETURN_IF_ERROR(consumeType(Kind::Comma));
|
|
563 Class = *ClassStr;
|
|
564 ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());
|
|
565 RETURN_IF_ERROR(consumeType(Kind::Comma));
|
|
566 Style = *StyleVal;
|
|
567 } else {
|
|
568 Class = CtlInfo->getValue().CtlClass;
|
|
569 }
|
|
570
|
|
571 // x, y, width, height
|
|
572 ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
|
121
|
573
|
147
|
574 if (ClassUpper != "CONTROL") {
|
|
575 if (consumeOptionalType(Kind::Comma)) {
|
|
576 ASSIGN_OR_RETURN(Val, parseIntExpr1());
|
|
577 Style = *Val;
|
|
578 }
|
|
579 }
|
121
|
580
|
147
|
581 Optional<uint32_t> ExStyle;
|
|
582 if (consumeOptionalType(Kind::Comma)) {
|
|
583 ASSIGN_OR_RETURN(Val, readInt());
|
|
584 ExStyle = *Val;
|
|
585 }
|
|
586 Optional<uint32_t> HelpID;
|
|
587 if (consumeOptionalType(Kind::Comma)) {
|
|
588 ASSIGN_OR_RETURN(Val, readInt());
|
|
589 HelpID = *Val;
|
|
590 }
|
|
591
|
|
592 return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
|
|
593 (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
|
|
594 }
|
|
595
|
|
596 RCParser::ParseType RCParser::parseBitmapResource() {
|
|
597 uint16_t MemoryFlags =
|
|
598 parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
|
|
599 ASSIGN_OR_RETURN(Arg, readFilename());
|
|
600 return llvm::make_unique<BitmapResource>(*Arg, MemoryFlags);
|
121
|
601 }
|
|
602
|
|
603 RCParser::ParseType RCParser::parseIconResource() {
|
147
|
604 uint16_t MemoryFlags =
|
|
605 parseMemoryFlags(IconResource::getDefaultMemoryFlags());
|
|
606 ASSIGN_OR_RETURN(Arg, readFilename());
|
|
607 return llvm::make_unique<IconResource>(*Arg, MemoryFlags);
|
121
|
608 }
|
|
609
|
|
610 RCParser::ParseType RCParser::parseHTMLResource() {
|
147
|
611 uint16_t MemoryFlags =
|
|
612 parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
|
|
613 ASSIGN_OR_RETURN(Arg, readFilename());
|
|
614 return llvm::make_unique<HTMLResource>(*Arg, MemoryFlags);
|
121
|
615 }
|
|
616
|
|
617 RCParser::ParseType RCParser::parseMenuResource() {
|
147
|
618 uint16_t MemoryFlags =
|
|
619 parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
|
121
|
620 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
|
|
621 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
|
|
622 return llvm::make_unique<MenuResource>(std::move(*OptStatements),
|
147
|
623 std::move(*Items), MemoryFlags);
|
121
|
624 }
|
|
625
|
|
626 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
|
|
627 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
|
|
628
|
|
629 MenuDefinitionList List;
|
|
630
|
|
631 // Read a set of items. Each item is of one of three kinds:
|
|
632 // MENUITEM SEPARATOR
|
|
633 // MENUITEM caption:String, result:Int [, menu flags]...
|
|
634 // POPUP caption:String [, menu flags]... { items... }
|
|
635 while (!consumeOptionalType(Kind::BlockEnd)) {
|
|
636 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
|
|
637
|
|
638 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
|
|
639 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
|
|
640 if (!IsMenuItem && !IsPopup)
|
|
641 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
|
|
642
|
|
643 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
|
|
644 // Now, expecting SEPARATOR.
|
|
645 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
|
|
646 if (SeparatorResult->equals_lower("SEPARATOR")) {
|
|
647 List.addDefinition(llvm::make_unique<MenuSeparator>());
|
|
648 continue;
|
|
649 }
|
|
650
|
|
651 return getExpectedError("SEPARATOR or string", true);
|
|
652 }
|
|
653
|
|
654 // Not a separator. Read the caption.
|
|
655 ASSIGN_OR_RETURN(CaptionResult, readString());
|
|
656
|
|
657 // If MENUITEM, expect also a comma and an integer.
|
|
658 uint32_t MenuResult = -1;
|
|
659
|
|
660 if (IsMenuItem) {
|
|
661 RETURN_IF_ERROR(consumeType(Kind::Comma));
|
|
662 ASSIGN_OR_RETURN(IntResult, readInt());
|
|
663 MenuResult = *IntResult;
|
|
664 }
|
|
665
|
|
666 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
|
|
667 MenuDefinition::OptionsFlags));
|
|
668
|
|
669 if (IsPopup) {
|
|
670 // If POPUP, read submenu items recursively.
|
|
671 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
|
|
672 List.addDefinition(llvm::make_unique<PopupItem>(
|
|
673 *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
|
|
674 continue;
|
|
675 }
|
|
676
|
|
677 assert(IsMenuItem);
|
|
678 List.addDefinition(
|
|
679 llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
|
|
680 }
|
|
681
|
|
682 return std::move(List);
|
|
683 }
|
|
684
|
|
685 RCParser::ParseType RCParser::parseStringTableResource() {
|
147
|
686 uint16_t MemoryFlags =
|
|
687 parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
|
121
|
688 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
|
|
689 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
|
|
690
|
147
|
691 auto Table = llvm::make_unique<StringTableResource>(std::move(*OptStatements),
|
|
692 MemoryFlags);
|
121
|
693
|
|
694 // Read strings until we reach the end of the block.
|
|
695 while (!consumeOptionalType(Kind::BlockEnd)) {
|
|
696 // Each definition consists of string's ID (an integer) and a string.
|
|
697 // Some examples in documentation suggest that there might be a comma in
|
|
698 // between, however we strictly adhere to the single statement definition.
|
|
699 ASSIGN_OR_RETURN(IDResult, readInt());
|
147
|
700 consumeOptionalType(Kind::Comma);
|
121
|
701 ASSIGN_OR_RETURN(StrResult, readString());
|
|
702 Table->addString(*IDResult, *StrResult);
|
|
703 }
|
|
704
|
|
705 return std::move(Table);
|
|
706 }
|
|
707
|
|
708 Expected<std::unique_ptr<VersionInfoBlock>>
|
|
709 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
|
|
710 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
|
|
711
|
|
712 auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
|
|
713
|
|
714 while (!isNextTokenKind(Kind::BlockEnd)) {
|
|
715 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
|
|
716 Contents->addStmt(std::move(*Stmt));
|
|
717 }
|
|
718
|
|
719 consume(); // Consume BlockEnd.
|
|
720
|
|
721 return std::move(Contents);
|
|
722 }
|
|
723
|
|
724 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
|
|
725 // Expect either BLOCK or VALUE, then a name or a key (a string).
|
|
726 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
|
|
727
|
|
728 if (TypeResult->equals_lower("BLOCK")) {
|
|
729 ASSIGN_OR_RETURN(NameResult, readString());
|
|
730 return parseVersionInfoBlockContents(*NameResult);
|
|
731 }
|
|
732
|
|
733 if (TypeResult->equals_lower("VALUE")) {
|
|
734 ASSIGN_OR_RETURN(KeyResult, readString());
|
|
735 // Read a non-empty list of strings and/or ints, each
|
|
736 // possibly preceded by a comma. Unfortunately, the tool behavior depends
|
|
737 // on them existing or not, so we need to memorize where we found them.
|
|
738 std::vector<IntOrString> Values;
|
|
739 std::vector<bool> PrecedingCommas;
|
|
740 RETURN_IF_ERROR(consumeType(Kind::Comma));
|
|
741 while (!isNextTokenKind(Kind::Identifier) &&
|
|
742 !isNextTokenKind(Kind::BlockEnd)) {
|
|
743 // Try to eat a comma if it's not the first statement.
|
|
744 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
|
|
745 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
|
|
746 Values.push_back(*ValueResult);
|
|
747 PrecedingCommas.push_back(HadComma);
|
|
748 }
|
|
749 return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
|
|
750 std::move(PrecedingCommas));
|
|
751 }
|
|
752
|
|
753 return getExpectedError("BLOCK or VALUE", true);
|
|
754 }
|
|
755
|
|
756 Expected<VersionInfoResource::VersionInfoFixed>
|
|
757 RCParser::parseVersionInfoFixed() {
|
|
758 using RetType = VersionInfoResource::VersionInfoFixed;
|
|
759 RetType Result;
|
|
760
|
|
761 // Read until the beginning of the block.
|
|
762 while (!isNextTokenKind(Kind::BlockBegin)) {
|
|
763 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
|
|
764 auto FixedType = RetType::getFixedType(*TypeResult);
|
|
765
|
|
766 if (!RetType::isTypeSupported(FixedType))
|
|
767 return getExpectedError("fixed VERSIONINFO statement type", true);
|
|
768 if (Result.IsTypePresent[FixedType])
|
|
769 return getExpectedError("yet unread fixed VERSIONINFO statement type",
|
|
770 true);
|
|
771
|
|
772 // VERSION variations take multiple integers.
|
|
773 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
|
|
774 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
|
|
775 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
|
|
776 Result.setValue(FixedType, ArgInts);
|
|
777 }
|
|
778
|
|
779 return Result;
|
|
780 }
|
|
781
|
|
782 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
|
|
783 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
|
|
784 return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
|
|
785 }
|
|
786
|
|
787 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
|
|
788 ASSIGN_OR_RETURN(Arg, readInt());
|
|
789 return llvm::make_unique<CharacteristicsStmt>(*Arg);
|
|
790 }
|
|
791
|
|
792 RCParser::ParseOptionType RCParser::parseVersionStmt() {
|
|
793 ASSIGN_OR_RETURN(Arg, readInt());
|
|
794 return llvm::make_unique<VersionStmt>(*Arg);
|
|
795 }
|
|
796
|
|
797 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
|
|
798 ASSIGN_OR_RETURN(Arg, readString());
|
|
799 return llvm::make_unique<CaptionStmt>(*Arg);
|
|
800 }
|
|
801
|
147
|
802 RCParser::ParseOptionType RCParser::parseClassStmt() {
|
|
803 ASSIGN_OR_RETURN(Arg, readIntOrString());
|
|
804 return llvm::make_unique<ClassStmt>(*Arg);
|
|
805 }
|
|
806
|
121
|
807 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
|
|
808 assert(DialogType != OptStmtType::BasicStmt);
|
|
809
|
|
810 ASSIGN_OR_RETURN(SizeResult, readInt());
|
|
811 RETURN_IF_ERROR(consumeType(Kind::Comma));
|
|
812 ASSIGN_OR_RETURN(NameResult, readString());
|
|
813
|
|
814 // Default values for the optional arguments.
|
|
815 uint32_t FontWeight = 0;
|
|
816 bool FontItalic = false;
|
|
817 uint32_t FontCharset = 1;
|
|
818 if (DialogType == OptStmtType::DialogExStmt) {
|
|
819 if (consumeOptionalType(Kind::Comma)) {
|
|
820 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
|
|
821 if (Args->size() >= 1)
|
|
822 FontWeight = (*Args)[0];
|
|
823 if (Args->size() >= 2)
|
|
824 FontItalic = (*Args)[1] != 0;
|
|
825 if (Args->size() >= 3)
|
|
826 FontCharset = (*Args)[2];
|
|
827 }
|
|
828 }
|
|
829 return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
|
|
830 FontItalic, FontCharset);
|
|
831 }
|
|
832
|
|
833 RCParser::ParseOptionType RCParser::parseStyleStmt() {
|
|
834 ASSIGN_OR_RETURN(Arg, readInt());
|
|
835 return llvm::make_unique<StyleStmt>(*Arg);
|
|
836 }
|
|
837
|
147
|
838 RCParser::ParseOptionType RCParser::parseExStyleStmt() {
|
|
839 ASSIGN_OR_RETURN(Arg, readInt());
|
|
840 return llvm::make_unique<ExStyleStmt>(*Arg);
|
|
841 }
|
|
842
|
121
|
843 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
|
|
844 return make_error<ParserError>(
|
|
845 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
|
|
846 }
|
|
847
|
|
848 } // namespace rc
|
|
849 } // namespace llvm
|