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