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