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