xref: /llvm-project/llvm/tools/llvm-rc/ResourceScriptParser.cpp (revision c75a087c7a9beee1e0c23edf4c6ba173524c4773)
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<uint32_t> RCParser::readInt() { return parseIntExpr1(); }
138 
139 Expected<uint32_t> RCParser::parseIntExpr1() {
140   // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
141   ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
142   uint32_t 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<uint32_t> 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 read().intValue();
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<uint32_t, 8>>
265 RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) {
266   assert(MinCount <= MaxCount);
267 
268   SmallVector<uint32_t, 8> Result;
269 
270   auto FailureHandler =
271       [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 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 // As for now, we ignore the extended set of statements.
324 Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) {
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(IsExtended));
330     Result.addStmt(std::move(*SingleParse));
331   }
332 
333   return std::move(Result);
334 }
335 
336 Expected<std::unique_ptr<OptionalStmt>>
337 RCParser::parseSingleOptionalStatement(bool IsExtended) {
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 (IsExtended) {
347     if (TypeToken->equals_lower("CAPTION"))
348       return parseCaptionStmt();
349     if (TypeToken->equals_lower("FONT"))
350       return parseFontStmt();
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,
405                    parseOptionalStatements(/*UseExtendedStmts = */ true));
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   ASSIGN_OR_RETURN(ClassResult, readIdentifier());
462   std::string ClassUpper = ClassResult->upper();
463   if (Control::SupportedCtls.find(ClassUpper) == Control::SupportedCtls.end())
464     return getExpectedError("control type, END or '}'", true);
465 
466   // Read caption if necessary.
467   StringRef Caption;
468   if (Control::CtlsWithTitle.find(ClassUpper) != Control::CtlsWithTitle.end()) {
469     ASSIGN_OR_RETURN(CaptionResult, readString());
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 ? (*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 (possibly empty) list of strings and/or ints, each preceded by
609     // a comma.
610     std::vector<IntOrString> Values;
611 
612     while (consumeOptionalType(Kind::Comma)) {
613       ASSIGN_OR_RETURN(ValueResult, readIntOrString());
614       Values.push_back(*ValueResult);
615     }
616     return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values));
617   }
618 
619   return getExpectedError("BLOCK or VALUE", true);
620 }
621 
622 Expected<VersionInfoResource::VersionInfoFixed>
623 RCParser::parseVersionInfoFixed() {
624   using RetType = VersionInfoResource::VersionInfoFixed;
625   RetType Result;
626 
627   // Read until the beginning of the block.
628   while (!isNextTokenKind(Kind::BlockBegin)) {
629     ASSIGN_OR_RETURN(TypeResult, readIdentifier());
630     auto FixedType = RetType::getFixedType(*TypeResult);
631 
632     if (!RetType::isTypeSupported(FixedType))
633       return getExpectedError("fixed VERSIONINFO statement type", true);
634     if (Result.IsTypePresent[FixedType])
635       return getExpectedError("yet unread fixed VERSIONINFO statement type",
636                               true);
637 
638     // VERSION variations take multiple integers.
639     size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
640     ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
641     Result.setValue(FixedType, *ArgsResult);
642   }
643 
644   return Result;
645 }
646 
647 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
648   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
649   return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
650 }
651 
652 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
653   ASSIGN_OR_RETURN(Arg, readInt());
654   return llvm::make_unique<CharacteristicsStmt>(*Arg);
655 }
656 
657 RCParser::ParseOptionType RCParser::parseVersionStmt() {
658   ASSIGN_OR_RETURN(Arg, readInt());
659   return llvm::make_unique<VersionStmt>(*Arg);
660 }
661 
662 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
663   ASSIGN_OR_RETURN(Arg, readString());
664   return llvm::make_unique<CaptionStmt>(*Arg);
665 }
666 
667 RCParser::ParseOptionType RCParser::parseFontStmt() {
668   ASSIGN_OR_RETURN(SizeResult, readInt());
669   RETURN_IF_ERROR(consumeType(Kind::Comma));
670   ASSIGN_OR_RETURN(NameResult, readString());
671   return llvm::make_unique<FontStmt>(*SizeResult, *NameResult);
672 }
673 
674 RCParser::ParseOptionType RCParser::parseStyleStmt() {
675   ASSIGN_OR_RETURN(Arg, readInt());
676   return llvm::make_unique<StyleStmt>(*Arg);
677 }
678 
679 Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) {
680   return make_error<ParserError>(
681       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
682 }
683 
684 } // namespace rc
685 } // namespace llvm
686