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