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