xref: /llvm-project/llvm/tools/llvm-rc/ResourceScriptParser.cpp (revision 827ddb240bf52e3b53d837537445f95e613ed583)
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("HTML"))
76     Result = parseHTMLResource();
77   else if (TypeToken->equalsLower("ICON"))
78     Result = parseIconResource();
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.def"
249   }
250 
251   llvm_unreachable("All case options exhausted.");
252 }
253 
254 bool RCParser::consumeOptionalType(Kind TokenKind) {
255   if (isNextTokenKind(TokenKind)) {
256     consume();
257     return true;
258   }
259 
260   return false;
261 }
262 
263 Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
264                                                              size_t MaxCount) {
265   assert(MinCount <= MaxCount);
266 
267   SmallVector<RCInt, 8> Result;
268 
269   auto FailureHandler =
270       [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
271     if (Result.size() < MinCount)
272       return std::move(Err);
273     consumeError(std::move(Err));
274     return Result;
275   };
276 
277   for (size_t i = 0; i < MaxCount; ++i) {
278     // Try to read a comma unless we read the first token.
279     // Sometimes RC tool requires them and sometimes not. We decide to
280     // always require them.
281     if (i >= 1) {
282       if (auto CommaError = consumeType(Kind::Comma))
283         return FailureHandler(std::move(CommaError));
284     }
285 
286     if (auto IntResult = readInt())
287       Result.push_back(*IntResult);
288     else
289       return FailureHandler(IntResult.takeError());
290   }
291 
292   return std::move(Result);
293 }
294 
295 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
296                                         ArrayRef<uint32_t> FlagValues) {
297   assert(!FlagDesc.empty());
298   assert(FlagDesc.size() == FlagValues.size());
299 
300   uint32_t Result = 0;
301   while (isNextTokenKind(Kind::Comma)) {
302     consume();
303     ASSIGN_OR_RETURN(FlagResult, readIdentifier());
304     bool FoundFlag = false;
305 
306     for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
307       if (!FlagResult->equals_lower(FlagDesc[FlagId]))
308         continue;
309 
310       Result |= FlagValues[FlagId];
311       FoundFlag = true;
312       break;
313     }
314 
315     if (!FoundFlag)
316       return getExpectedError(join(FlagDesc, "/"), true);
317   }
318 
319   return Result;
320 }
321 
322 Expected<OptionalStmtList>
323 RCParser::parseOptionalStatements(OptStmtType StmtsType) {
324   OptionalStmtList Result;
325 
326   // The last statement is always followed by the start of the block.
327   while (!isNextTokenKind(Kind::BlockBegin)) {
328     ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
329     Result.addStmt(std::move(*SingleParse));
330   }
331 
332   return std::move(Result);
333 }
334 
335 Expected<std::unique_ptr<OptionalStmt>>
336 RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
337   ASSIGN_OR_RETURN(TypeToken, readIdentifier());
338   if (TypeToken->equals_lower("CHARACTERISTICS"))
339     return parseCharacteristicsStmt();
340   if (TypeToken->equals_lower("LANGUAGE"))
341     return parseLanguageStmt();
342   if (TypeToken->equals_lower("VERSION"))
343     return parseVersionStmt();
344 
345   if (StmtsType != OptStmtType::BasicStmt) {
346     if (TypeToken->equals_lower("CAPTION"))
347       return parseCaptionStmt();
348     if (TypeToken->equals_lower("FONT"))
349       return parseFontStmt(StmtsType);
350     if (TypeToken->equals_lower("STYLE"))
351       return parseStyleStmt();
352   }
353 
354   return getExpectedError("optional statement type, BEGIN or '{'",
355                           /* IsAlreadyRead = */ true);
356 }
357 
358 RCParser::ParseType RCParser::parseLanguageResource() {
359   // Read LANGUAGE as an optional statement. If it's read correctly, we can
360   // upcast it to RCResource.
361   return parseLanguageStmt();
362 }
363 
364 RCParser::ParseType RCParser::parseAcceleratorsResource() {
365   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
366   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
367 
368   auto Accels =
369       llvm::make_unique<AcceleratorsResource>(std::move(*OptStatements));
370 
371   while (!consumeOptionalType(Kind::BlockEnd)) {
372     ASSIGN_OR_RETURN(EventResult, readIntOrString());
373     RETURN_IF_ERROR(consumeType(Kind::Comma));
374     ASSIGN_OR_RETURN(IDResult, readInt());
375     ASSIGN_OR_RETURN(
376         FlagsResult,
377         parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
378                    AcceleratorsResource::Accelerator::OptionsFlags));
379     Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
380   }
381 
382   return std::move(Accels);
383 }
384 
385 RCParser::ParseType RCParser::parseCursorResource() {
386   ASSIGN_OR_RETURN(Arg, readString());
387   return llvm::make_unique<CursorResource>(*Arg);
388 }
389 
390 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
391   // Dialog resources have the following format of the arguments:
392   //  DIALOG:   x, y, width, height [opt stmts...] {controls...}
393   //  DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
394   // These are very similar, so we parse them together.
395   ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
396 
397   uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
398   if (IsExtended && consumeOptionalType(Kind::Comma)) {
399     ASSIGN_OR_RETURN(HelpIDResult, readInt());
400     HelpID = *HelpIDResult;
401   }
402 
403   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
404                                       IsExtended ? OptStmtType::DialogExStmt
405                                                  : OptStmtType::DialogStmt));
406 
407   assert(isNextTokenKind(Kind::BlockBegin) &&
408          "parseOptionalStatements, when successful, halts on BlockBegin.");
409   consume();
410 
411   auto Dialog = llvm::make_unique<DialogResource>(
412       (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
413       HelpID, std::move(*OptStatements), IsExtended);
414 
415   while (!consumeOptionalType(Kind::BlockEnd)) {
416     ASSIGN_OR_RETURN(ControlDefResult, parseControl());
417     Dialog->addControl(std::move(*ControlDefResult));
418   }
419 
420   return std::move(Dialog);
421 }
422 
423 RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
424   if (isEof())
425     return getExpectedError("filename, '{' or BEGIN");
426 
427   // Check if this is a file resource.
428   if (look().kind() == Kind::String)
429     return llvm::make_unique<UserDefinedResource>(Type, read().value());
430 
431   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
432   std::vector<IntOrString> Data;
433 
434   // Consume comma before each consecutive token except the first one.
435   bool ConsumeComma = false;
436   while (!consumeOptionalType(Kind::BlockEnd)) {
437     if (ConsumeComma)
438       RETURN_IF_ERROR(consumeType(Kind::Comma));
439     ConsumeComma = true;
440 
441     ASSIGN_OR_RETURN(Item, readIntOrString());
442     Data.push_back(*Item);
443   }
444 
445   return llvm::make_unique<UserDefinedResource>(Type, std::move(Data));
446 }
447 
448 RCParser::ParseType RCParser::parseVersionInfoResource() {
449   ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
450   ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
451   return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult),
452                                                 std::move(*FixedResult));
453 }
454 
455 Expected<Control> RCParser::parseControl() {
456   // Each control definition (except CONTROL) follows one of the schemes below
457   // depending on the control class:
458   //  [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
459   //  [class]       id, x, y, width, height [, style] [, exstyle] [, helpID]
460   // Note that control ids must be integers.
461   // Text might be either a string or an integer pointing to resource ID.
462   ASSIGN_OR_RETURN(ClassResult, readIdentifier());
463   std::string ClassUpper = ClassResult->upper();
464   auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
465   if (CtlInfo == Control::SupportedCtls.end())
466     return getExpectedError("control type, END or '}'", true);
467 
468   // Read caption if necessary.
469   IntOrString Caption{StringRef()};
470   if (CtlInfo->getValue().HasTitle) {
471     ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
472     RETURN_IF_ERROR(consumeType(Kind::Comma));
473     Caption = *CaptionResult;
474   }
475 
476   ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8));
477 
478   auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> {
479     return Args->size() > Id ? (uint32_t)(*Args)[Id] : Optional<uint32_t>();
480   };
481 
482   return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2],
483                  (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6),
484                  TakeOptArg(7));
485 }
486 
487 RCParser::ParseType RCParser::parseIconResource() {
488   ASSIGN_OR_RETURN(Arg, readString());
489   return llvm::make_unique<IconResource>(*Arg);
490 }
491 
492 RCParser::ParseType RCParser::parseHTMLResource() {
493   ASSIGN_OR_RETURN(Arg, readString());
494   return llvm::make_unique<HTMLResource>(*Arg);
495 }
496 
497 RCParser::ParseType RCParser::parseMenuResource() {
498   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
499   ASSIGN_OR_RETURN(Items, parseMenuItemsList());
500   return llvm::make_unique<MenuResource>(std::move(*OptStatements),
501                                          std::move(*Items));
502 }
503 
504 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
505   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
506 
507   MenuDefinitionList List;
508 
509   // Read a set of items. Each item is of one of three kinds:
510   //   MENUITEM SEPARATOR
511   //   MENUITEM caption:String, result:Int [, menu flags]...
512   //   POPUP caption:String [, menu flags]... { items... }
513   while (!consumeOptionalType(Kind::BlockEnd)) {
514     ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
515 
516     bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
517     bool IsPopup = ItemTypeResult->equals_lower("POPUP");
518     if (!IsMenuItem && !IsPopup)
519       return getExpectedError("MENUITEM, POPUP, END or '}'", true);
520 
521     if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
522       // Now, expecting SEPARATOR.
523       ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
524       if (SeparatorResult->equals_lower("SEPARATOR")) {
525         List.addDefinition(llvm::make_unique<MenuSeparator>());
526         continue;
527       }
528 
529       return getExpectedError("SEPARATOR or string", true);
530     }
531 
532     // Not a separator. Read the caption.
533     ASSIGN_OR_RETURN(CaptionResult, readString());
534 
535     // If MENUITEM, expect also a comma and an integer.
536     uint32_t MenuResult = -1;
537 
538     if (IsMenuItem) {
539       RETURN_IF_ERROR(consumeType(Kind::Comma));
540       ASSIGN_OR_RETURN(IntResult, readInt());
541       MenuResult = *IntResult;
542     }
543 
544     ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
545                                              MenuDefinition::OptionsFlags));
546 
547     if (IsPopup) {
548       // If POPUP, read submenu items recursively.
549       ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
550       List.addDefinition(llvm::make_unique<PopupItem>(
551           *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
552       continue;
553     }
554 
555     assert(IsMenuItem);
556     List.addDefinition(
557         llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
558   }
559 
560   return std::move(List);
561 }
562 
563 RCParser::ParseType RCParser::parseStringTableResource() {
564   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
565   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
566 
567   auto Table =
568       llvm::make_unique<StringTableResource>(std::move(*OptStatements));
569 
570   // Read strings until we reach the end of the block.
571   while (!consumeOptionalType(Kind::BlockEnd)) {
572     // Each definition consists of string's ID (an integer) and a string.
573     // Some examples in documentation suggest that there might be a comma in
574     // between, however we strictly adhere to the single statement definition.
575     ASSIGN_OR_RETURN(IDResult, readInt());
576     ASSIGN_OR_RETURN(StrResult, readString());
577     Table->addString(*IDResult, *StrResult);
578   }
579 
580   return std::move(Table);
581 }
582 
583 Expected<std::unique_ptr<VersionInfoBlock>>
584 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
585   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
586 
587   auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
588 
589   while (!isNextTokenKind(Kind::BlockEnd)) {
590     ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
591     Contents->addStmt(std::move(*Stmt));
592   }
593 
594   consume(); // Consume BlockEnd.
595 
596   return std::move(Contents);
597 }
598 
599 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
600   // Expect either BLOCK or VALUE, then a name or a key (a string).
601   ASSIGN_OR_RETURN(TypeResult, readIdentifier());
602 
603   if (TypeResult->equals_lower("BLOCK")) {
604     ASSIGN_OR_RETURN(NameResult, readString());
605     return parseVersionInfoBlockContents(*NameResult);
606   }
607 
608   if (TypeResult->equals_lower("VALUE")) {
609     ASSIGN_OR_RETURN(KeyResult, readString());
610     // Read a non-empty list of strings and/or ints, each
611     // possibly preceded by a comma. Unfortunately, the tool behavior depends
612     // on them existing or not, so we need to memorize where we found them.
613     std::vector<IntOrString> Values;
614     std::vector<bool> PrecedingCommas;
615     RETURN_IF_ERROR(consumeType(Kind::Comma));
616     while (!isNextTokenKind(Kind::Identifier) &&
617            !isNextTokenKind(Kind::BlockEnd)) {
618       // Try to eat a comma if it's not the first statement.
619       bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
620       ASSIGN_OR_RETURN(ValueResult, readIntOrString());
621       Values.push_back(*ValueResult);
622       PrecedingCommas.push_back(HadComma);
623     }
624     return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
625                                                std::move(PrecedingCommas));
626   }
627 
628   return getExpectedError("BLOCK or VALUE", true);
629 }
630 
631 Expected<VersionInfoResource::VersionInfoFixed>
632 RCParser::parseVersionInfoFixed() {
633   using RetType = VersionInfoResource::VersionInfoFixed;
634   RetType Result;
635 
636   // Read until the beginning of the block.
637   while (!isNextTokenKind(Kind::BlockBegin)) {
638     ASSIGN_OR_RETURN(TypeResult, readIdentifier());
639     auto FixedType = RetType::getFixedType(*TypeResult);
640 
641     if (!RetType::isTypeSupported(FixedType))
642       return getExpectedError("fixed VERSIONINFO statement type", true);
643     if (Result.IsTypePresent[FixedType])
644       return getExpectedError("yet unread fixed VERSIONINFO statement type",
645                               true);
646 
647     // VERSION variations take multiple integers.
648     size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
649     ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
650     SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
651     Result.setValue(FixedType, ArgInts);
652   }
653 
654   return Result;
655 }
656 
657 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
658   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
659   return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
660 }
661 
662 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
663   ASSIGN_OR_RETURN(Arg, readInt());
664   return llvm::make_unique<CharacteristicsStmt>(*Arg);
665 }
666 
667 RCParser::ParseOptionType RCParser::parseVersionStmt() {
668   ASSIGN_OR_RETURN(Arg, readInt());
669   return llvm::make_unique<VersionStmt>(*Arg);
670 }
671 
672 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
673   ASSIGN_OR_RETURN(Arg, readString());
674   return llvm::make_unique<CaptionStmt>(*Arg);
675 }
676 
677 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
678   assert(DialogType != OptStmtType::BasicStmt);
679 
680   ASSIGN_OR_RETURN(SizeResult, readInt());
681   RETURN_IF_ERROR(consumeType(Kind::Comma));
682   ASSIGN_OR_RETURN(NameResult, readString());
683 
684   // Default values for the optional arguments.
685   uint32_t FontWeight = 0;
686   bool FontItalic = false;
687   uint32_t FontCharset = 1;
688   if (DialogType == OptStmtType::DialogExStmt) {
689     if (consumeOptionalType(Kind::Comma)) {
690       ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
691       if (Args->size() >= 1)
692         FontWeight = (*Args)[0];
693       if (Args->size() >= 2)
694         FontItalic = (*Args)[1] != 0;
695       if (Args->size() >= 3)
696         FontCharset = (*Args)[2];
697     }
698   }
699   return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
700                                      FontItalic, FontCharset);
701 }
702 
703 RCParser::ParseOptionType RCParser::parseStyleStmt() {
704   ASSIGN_OR_RETURN(Arg, readInt());
705   return llvm::make_unique<StyleStmt>(*Arg);
706 }
707 
708 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
709   return make_error<ParserError>(
710       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
711 }
712 
713 } // namespace rc
714 } // namespace llvm
715