xref: /llvm-project/llvm/tools/llvm-rc/ResourceScriptParser.cpp (revision 7e89ee7fdc428900dd362703e80d4644b28058f7)
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     return getExpectedError("resource type", /* IsAlreadyRead = */ true);
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   assert(FlagDesc.size() <= 32 && "More than 32 flags won't fit in result.");
298   assert(!FlagDesc.empty());
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 |= (1U << 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 // As for now, we ignore the extended set of statements.
323 Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) {
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(IsExtended));
329     Result.addStmt(std::move(*SingleParse));
330   }
331 
332   return std::move(Result);
333 }
334 
335 Expected<std::unique_ptr<OptionalStmt>>
336 RCParser::parseSingleOptionalStatement(bool IsExtended) {
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 (IsExtended) {
346     if (TypeToken->equals_lower("CAPTION"))
347       return parseCaptionStmt();
348     if (TypeToken->equals_lower("FONT"))
349       return parseFontStmt();
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 = make_unique<AcceleratorsResource>(std::move(*OptStatements));
369 
370   while (!consumeOptionalType(Kind::BlockEnd)) {
371     ASSIGN_OR_RETURN(EventResult, readIntOrString());
372     RETURN_IF_ERROR(consumeType(Kind::Comma));
373     ASSIGN_OR_RETURN(IDResult, readInt());
374     ASSIGN_OR_RETURN(FlagsResult,
375                      parseFlags(AcceleratorsResource::Accelerator::OptionsStr));
376     Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
377   }
378 
379   return std::move(Accels);
380 }
381 
382 RCParser::ParseType RCParser::parseCursorResource() {
383   ASSIGN_OR_RETURN(Arg, readString());
384   return make_unique<CursorResource>(*Arg);
385 }
386 
387 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
388   // Dialog resources have the following format of the arguments:
389   //  DIALOG:   x, y, width, height [opt stmts...] {controls...}
390   //  DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
391   // These are very similar, so we parse them together.
392   ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
393 
394   uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
395   if (IsExtended && consumeOptionalType(Kind::Comma)) {
396     ASSIGN_OR_RETURN(HelpIDResult, readInt());
397     HelpID = *HelpIDResult;
398   }
399 
400   ASSIGN_OR_RETURN(OptStatements,
401                    parseOptionalStatements(/*UseExtendedStmts = */ true));
402 
403   assert(isNextTokenKind(Kind::BlockBegin) &&
404          "parseOptionalStatements, when successful, halts on BlockBegin.");
405   consume();
406 
407   auto Dialog = make_unique<DialogResource>(
408       (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
409       HelpID, std::move(*OptStatements), IsExtended);
410 
411   while (!consumeOptionalType(Kind::BlockEnd)) {
412     ASSIGN_OR_RETURN(ControlDefResult, parseControl());
413     Dialog->addControl(std::move(*ControlDefResult));
414   }
415 
416   return std::move(Dialog);
417 }
418 
419 RCParser::ParseType RCParser::parseVersionInfoResource() {
420   ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
421   ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
422   return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult),
423                                                 std::move(*FixedResult));
424 }
425 
426 Expected<Control> RCParser::parseControl() {
427   // Each control definition (except CONTROL) follows one of the schemes below
428   // depending on the control class:
429   //  [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
430   //  [class]       id, x, y, width, height [, style] [, exstyle] [, helpID]
431   // Note that control ids must be integers.
432   ASSIGN_OR_RETURN(ClassResult, readIdentifier());
433   std::string ClassUpper = ClassResult->upper();
434   if (Control::SupportedCtls.find(ClassUpper) == Control::SupportedCtls.end())
435     return getExpectedError("control type, END or '}'", true);
436 
437   // Read caption if necessary.
438   StringRef Caption;
439   if (Control::CtlsWithTitle.find(ClassUpper) != Control::CtlsWithTitle.end()) {
440     ASSIGN_OR_RETURN(CaptionResult, readString());
441     RETURN_IF_ERROR(consumeType(Kind::Comma));
442     Caption = *CaptionResult;
443   }
444 
445   ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8));
446 
447   auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> {
448     return Args->size() > Id ? (*Args)[Id] : Optional<uint32_t>();
449   };
450 
451   return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2],
452                  (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6),
453                  TakeOptArg(7));
454 }
455 
456 RCParser::ParseType RCParser::parseIconResource() {
457   ASSIGN_OR_RETURN(Arg, readString());
458   return make_unique<IconResource>(*Arg);
459 }
460 
461 RCParser::ParseType RCParser::parseHTMLResource() {
462   ASSIGN_OR_RETURN(Arg, readString());
463   return make_unique<HTMLResource>(*Arg);
464 }
465 
466 RCParser::ParseType RCParser::parseMenuResource() {
467   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
468   ASSIGN_OR_RETURN(Items, parseMenuItemsList());
469   return make_unique<MenuResource>(std::move(*OptStatements),
470                                    std::move(*Items));
471 }
472 
473 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
474   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
475 
476   MenuDefinitionList List;
477 
478   // Read a set of items. Each item is of one of three kinds:
479   //   MENUITEM SEPARATOR
480   //   MENUITEM caption:String, result:Int [, menu flags]...
481   //   POPUP caption:String [, menu flags]... { items... }
482   while (!consumeOptionalType(Kind::BlockEnd)) {
483     ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
484 
485     bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
486     bool IsPopup = ItemTypeResult->equals_lower("POPUP");
487     if (!IsMenuItem && !IsPopup)
488       return getExpectedError("MENUITEM, POPUP, END or '}'", true);
489 
490     if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
491       // Now, expecting SEPARATOR.
492       ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
493       if (SeparatorResult->equals_lower("SEPARATOR")) {
494         List.addDefinition(make_unique<MenuSeparator>());
495         continue;
496       }
497 
498       return getExpectedError("SEPARATOR or string", true);
499     }
500 
501     // Not a separator. Read the caption.
502     ASSIGN_OR_RETURN(CaptionResult, readString());
503 
504     // If MENUITEM, expect also a comma and an integer.
505     uint32_t MenuResult = -1;
506 
507     if (IsMenuItem) {
508       RETURN_IF_ERROR(consumeType(Kind::Comma));
509       ASSIGN_OR_RETURN(IntResult, readInt());
510       MenuResult = *IntResult;
511     }
512 
513     ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr));
514 
515     if (IsPopup) {
516       // If POPUP, read submenu items recursively.
517       ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
518       List.addDefinition(make_unique<PopupItem>(*CaptionResult, *FlagsResult,
519                                                 std::move(*SubMenuResult)));
520       continue;
521     }
522 
523     assert(IsMenuItem);
524     List.addDefinition(
525         make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
526   }
527 
528   return std::move(List);
529 }
530 
531 RCParser::ParseType RCParser::parseStringTableResource() {
532   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
533   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
534 
535   auto Table = make_unique<StringTableResource>(std::move(*OptStatements));
536 
537   // Read strings until we reach the end of the block.
538   while (!consumeOptionalType(Kind::BlockEnd)) {
539     // Each definition consists of string's ID (an integer) and a string.
540     // Some examples in documentation suggest that there might be a comma in
541     // between, however we strictly adhere to the single statement definition.
542     ASSIGN_OR_RETURN(IDResult, readInt());
543     ASSIGN_OR_RETURN(StrResult, readString());
544     Table->addString(*IDResult, *StrResult);
545   }
546 
547   return std::move(Table);
548 }
549 
550 Expected<std::unique_ptr<VersionInfoBlock>>
551 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
552   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
553 
554   auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
555 
556   while (!isNextTokenKind(Kind::BlockEnd)) {
557     ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
558     Contents->addStmt(std::move(*Stmt));
559   }
560 
561   consume(); // Consume BlockEnd.
562 
563   return std::move(Contents);
564 }
565 
566 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
567   // Expect either BLOCK or VALUE, then a name or a key (a string).
568   ASSIGN_OR_RETURN(TypeResult, readIdentifier());
569 
570   if (TypeResult->equals_lower("BLOCK")) {
571     ASSIGN_OR_RETURN(NameResult, readString());
572     return parseVersionInfoBlockContents(*NameResult);
573   }
574 
575   if (TypeResult->equals_lower("VALUE")) {
576     ASSIGN_OR_RETURN(KeyResult, readString());
577     // Read a (possibly empty) list of strings and/or ints, each preceded by
578     // a comma.
579     std::vector<IntOrString> Values;
580 
581     while (consumeOptionalType(Kind::Comma)) {
582       ASSIGN_OR_RETURN(ValueResult, readIntOrString());
583       Values.push_back(*ValueResult);
584     }
585     return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values));
586   }
587 
588   return getExpectedError("BLOCK or VALUE", true);
589 }
590 
591 Expected<VersionInfoResource::VersionInfoFixed>
592 RCParser::parseVersionInfoFixed() {
593   using RetType = VersionInfoResource::VersionInfoFixed;
594   RetType Result;
595 
596   // Read until the beginning of the block.
597   while (!isNextTokenKind(Kind::BlockBegin)) {
598     ASSIGN_OR_RETURN(TypeResult, readIdentifier());
599     auto FixedType = RetType::getFixedType(*TypeResult);
600 
601     if (!RetType::isTypeSupported(FixedType))
602       return getExpectedError("fixed VERSIONINFO statement type", true);
603     if (Result.IsTypePresent[FixedType])
604       return getExpectedError("yet unread fixed VERSIONINFO statement type",
605                               true);
606 
607     // VERSION variations take multiple integers.
608     size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
609     ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
610     Result.setValue(FixedType, *ArgsResult);
611   }
612 
613   return Result;
614 }
615 
616 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
617   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
618   return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
619 }
620 
621 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
622   ASSIGN_OR_RETURN(Arg, readInt());
623   return make_unique<CharacteristicsStmt>(*Arg);
624 }
625 
626 RCParser::ParseOptionType RCParser::parseVersionStmt() {
627   ASSIGN_OR_RETURN(Arg, readInt());
628   return make_unique<VersionStmt>(*Arg);
629 }
630 
631 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
632   ASSIGN_OR_RETURN(Arg, readString());
633   return make_unique<CaptionStmt>(*Arg);
634 }
635 
636 RCParser::ParseOptionType RCParser::parseFontStmt() {
637   ASSIGN_OR_RETURN(SizeResult, readInt());
638   RETURN_IF_ERROR(consumeType(Kind::Comma));
639   ASSIGN_OR_RETURN(NameResult, readString());
640   return make_unique<FontStmt>(*SizeResult, *NameResult);
641 }
642 
643 RCParser::ParseOptionType RCParser::parseStyleStmt() {
644   ASSIGN_OR_RETURN(Arg, readInt());
645   return make_unique<StyleStmt>(*Arg);
646 }
647 
648 Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) {
649   return make_error<ParserError>(
650       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
651 }
652 
653 } // namespace rc
654 } // namespace llvm
655