1 //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===---------------------------------------------------------------------===//
10 // This implements the parser defined in ResourceScriptParser.h.
12 //===---------------------------------------------------------------------===//
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"
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);
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) \
30 return Var.takeError();
35 RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
37 : ErrorLoc(CurLoc), FileEnd(End) {
38 CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
39 (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
42 char RCParser::ParserError::ID = 0;
44 RCParser::RCParser(std::vector<RCToken> TokenList)
45 : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
47 bool RCParser::isEof() const { return CurLoc == End; }
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());
55 if (NameToken->equalsLower("LANGUAGE"))
56 return parseLanguageResource();
57 else if (NameToken->equalsLower("STRINGTABLE"))
58 return parseStringTableResource();
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());
64 ParseType Result = std::unique_ptr<RCResource>();
67 if (TypeToken->equalsLower("ACCELERATORS"))
68 Result = parseAcceleratorsResource();
69 else if (TypeToken->equalsLower("BITMAP"))
70 Result = parseBitmapResource();
71 else if (TypeToken->equalsLower("CURSOR"))
72 Result = parseCursorResource();
73 else if (TypeToken->equalsLower("DIALOG"))
74 Result = parseDialogResource(false);
75 else if (TypeToken->equalsLower("DIALOGEX"))
76 Result = parseDialogResource(true);
77 else if (TypeToken->equalsLower("HTML"))
78 Result = parseHTMLResource();
79 else if (TypeToken->equalsLower("ICON"))
80 Result = parseIconResource();
81 else if (TypeToken->equalsLower("MENU"))
82 Result = parseMenuResource();
83 else if (TypeToken->equalsLower("VERSIONINFO"))
84 Result = parseVersionInfoResource();
86 Result = parseUserDefinedResource(*TypeToken);
89 (*Result)->setName(*NameToken);
94 bool RCParser::isNextTokenKind(Kind TokenKind) const {
95 return !isEof() && look().kind() == TokenKind;
98 const RCToken &RCParser::look() const {
103 const RCToken &RCParser::read() {
108 void RCParser::consume() {
113 // An integer description might consist of a single integer or
114 // an arithmetic expression evaluating to the integer. The expressions
115 // can contain the following tokens: <int> ( ) + - | & ~. Their meaning
116 // is the same as in C++.
117 // The operators in the original RC implementation have the following
119 // 1) Unary operators (- ~),
120 // 2) Binary operators (+ - & |), with no precedence.
122 // The following grammar is used to parse the expressions Exp1:
123 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
124 // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
125 // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
126 // separated by binary operators.)
128 // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
129 // is read by parseIntExpr2().
131 // The original Microsoft tool handles multiple unary operators incorrectly.
132 // For example, in 16-bit little-endian integers:
133 // 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
134 // 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
135 // Our implementation differs from the original one and handles these
136 // operators correctly:
137 // 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
138 // 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
140 Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); }
142 Expected<RCInt> RCParser::parseIntExpr1() {
143 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
144 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
145 RCInt Result = *FirstResult;
147 while (!isEof() && look().isBinaryOp()) {
148 auto OpToken = read();
149 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
151 switch (OpToken.kind()) {
153 Result += *NextResult;
157 Result -= *NextResult;
161 Result |= *NextResult;
165 Result &= *NextResult;
169 llvm_unreachable("Already processed all binary ops.");
176 Expected<RCInt> RCParser::parseIntExpr2() {
177 // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
178 static const char ErrorMsg[] = "'-', '~', integer or '('";
181 return getExpectedError(ErrorMsg);
183 switch (look().kind()) {
186 ASSIGN_OR_RETURN(Result, parseIntExpr2());
192 ASSIGN_OR_RETURN(Result, parseIntExpr2());
197 return RCInt(read());
199 case Kind::LeftParen: {
201 ASSIGN_OR_RETURN(Result, parseIntExpr1());
202 RETURN_IF_ERROR(consumeType(Kind::RightParen));
207 return getExpectedError(ErrorMsg);
211 Expected<StringRef> RCParser::readString() {
212 if (!isNextTokenKind(Kind::String))
213 return getExpectedError("string");
214 return read().value();
217 Expected<StringRef> RCParser::readFilename() {
218 if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
219 return getExpectedError("string");
220 return read().value();
223 Expected<StringRef> RCParser::readIdentifier() {
224 if (!isNextTokenKind(Kind::Identifier))
225 return getExpectedError("identifier");
226 return read().value();
229 Expected<IntOrString> RCParser::readIntOrString() {
230 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
231 return getExpectedError("int or string");
232 return IntOrString(read());
235 Expected<IntOrString> RCParser::readTypeOrName() {
236 // We suggest that the correct resource name or type should be either an
237 // identifier or an integer. The original RC tool is much more liberal.
238 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
239 return getExpectedError("int or identifier");
240 return IntOrString(read());
243 Error RCParser::consumeType(Kind TokenKind) {
244 if (isNextTokenKind(TokenKind)) {
246 return Error::success();
250 #define TOKEN(TokenName) \
251 case Kind::TokenName: \
252 return getExpectedError(#TokenName);
253 #define SHORT_TOKEN(TokenName, TokenCh) \
254 case Kind::TokenName: \
255 return getExpectedError(#TokenCh);
256 #include "ResourceScriptTokenList.def"
259 llvm_unreachable("All case options exhausted.");
262 bool RCParser::consumeOptionalType(Kind TokenKind) {
263 if (isNextTokenKind(TokenKind)) {
271 Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
273 assert(MinCount <= MaxCount);
275 SmallVector<RCInt, 8> Result;
277 auto FailureHandler =
278 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
279 if (Result.size() < MinCount)
280 return std::move(Err);
281 consumeError(std::move(Err));
285 for (size_t i = 0; i < MaxCount; ++i) {
286 // Try to read a comma unless we read the first token.
287 // Sometimes RC tool requires them and sometimes not. We decide to
288 // always require them.
290 if (auto CommaError = consumeType(Kind::Comma))
291 return FailureHandler(std::move(CommaError));
294 if (auto IntResult = readInt())
295 Result.push_back(*IntResult);
297 return FailureHandler(IntResult.takeError());
300 return std::move(Result);
303 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
304 ArrayRef<uint32_t> FlagValues) {
305 assert(!FlagDesc.empty());
306 assert(FlagDesc.size() == FlagValues.size());
309 while (isNextTokenKind(Kind::Comma)) {
311 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
312 bool FoundFlag = false;
314 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
315 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
318 Result |= FlagValues[FlagId];
324 return getExpectedError(join(FlagDesc, "/"), true);
330 Expected<OptionalStmtList>
331 RCParser::parseOptionalStatements(OptStmtType StmtsType) {
332 OptionalStmtList Result;
334 // The last statement is always followed by the start of the block.
335 while (!isNextTokenKind(Kind::BlockBegin)) {
336 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
337 Result.addStmt(std::move(*SingleParse));
340 return std::move(Result);
343 Expected<std::unique_ptr<OptionalStmt>>
344 RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
345 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
346 if (TypeToken->equals_lower("CHARACTERISTICS"))
347 return parseCharacteristicsStmt();
348 if (TypeToken->equals_lower("LANGUAGE"))
349 return parseLanguageStmt();
350 if (TypeToken->equals_lower("VERSION"))
351 return parseVersionStmt();
353 if (StmtsType != OptStmtType::BasicStmt) {
354 if (TypeToken->equals_lower("CAPTION"))
355 return parseCaptionStmt();
356 if (TypeToken->equals_lower("FONT"))
357 return parseFontStmt(StmtsType);
358 if (TypeToken->equals_lower("STYLE"))
359 return parseStyleStmt();
362 return getExpectedError("optional statement type, BEGIN or '{'",
363 /* IsAlreadyRead = */ true);
366 RCParser::ParseType RCParser::parseLanguageResource() {
367 // Read LANGUAGE as an optional statement. If it's read correctly, we can
368 // upcast it to RCResource.
369 return parseLanguageStmt();
372 RCParser::ParseType RCParser::parseAcceleratorsResource() {
373 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
374 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
377 llvm::make_unique<AcceleratorsResource>(std::move(*OptStatements));
379 while (!consumeOptionalType(Kind::BlockEnd)) {
380 ASSIGN_OR_RETURN(EventResult, readIntOrString());
381 RETURN_IF_ERROR(consumeType(Kind::Comma));
382 ASSIGN_OR_RETURN(IDResult, readInt());
385 parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
386 AcceleratorsResource::Accelerator::OptionsFlags));
387 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
390 return std::move(Accels);
393 RCParser::ParseType RCParser::parseCursorResource() {
394 ASSIGN_OR_RETURN(Arg, readFilename());
395 return llvm::make_unique<CursorResource>(*Arg);
398 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
399 // Dialog resources have the following format of the arguments:
400 // DIALOG: x, y, width, height [opt stmts...] {controls...}
401 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
402 // These are very similar, so we parse them together.
403 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
405 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
406 if (IsExtended && consumeOptionalType(Kind::Comma)) {
407 ASSIGN_OR_RETURN(HelpIDResult, readInt());
408 HelpID = *HelpIDResult;
411 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
412 IsExtended ? OptStmtType::DialogExStmt
413 : OptStmtType::DialogStmt));
415 assert(isNextTokenKind(Kind::BlockBegin) &&
416 "parseOptionalStatements, when successful, halts on BlockBegin.");
419 auto Dialog = llvm::make_unique<DialogResource>(
420 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
421 HelpID, std::move(*OptStatements), IsExtended);
423 while (!consumeOptionalType(Kind::BlockEnd)) {
424 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
425 Dialog->addControl(std::move(*ControlDefResult));
428 return std::move(Dialog);
431 RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
433 return getExpectedError("filename, '{' or BEGIN");
435 // Check if this is a file resource.
436 switch (look().kind()) {
438 case Kind::Identifier:
439 return llvm::make_unique<UserDefinedResource>(Type, read().value());
444 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
445 std::vector<IntOrString> Data;
447 // Consume comma before each consecutive token except the first one.
448 bool ConsumeComma = false;
449 while (!consumeOptionalType(Kind::BlockEnd)) {
451 RETURN_IF_ERROR(consumeType(Kind::Comma));
454 ASSIGN_OR_RETURN(Item, readIntOrString());
455 Data.push_back(*Item);
458 return llvm::make_unique<UserDefinedResource>(Type, std::move(Data));
461 RCParser::ParseType RCParser::parseVersionInfoResource() {
462 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
463 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
464 return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult),
465 std::move(*FixedResult));
468 Expected<Control> RCParser::parseControl() {
469 // Each control definition (except CONTROL) follows one of the schemes below
470 // depending on the control class:
471 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
472 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
473 // Note that control ids must be integers.
474 // Text might be either a string or an integer pointing to resource ID.
475 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
476 std::string ClassUpper = ClassResult->upper();
477 auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
478 if (CtlInfo == Control::SupportedCtls.end())
479 return getExpectedError("control type, END or '}'", true);
481 // Read caption if necessary.
482 IntOrString Caption{StringRef()};
483 if (CtlInfo->getValue().HasTitle) {
484 ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
485 RETURN_IF_ERROR(consumeType(Kind::Comma));
486 Caption = *CaptionResult;
489 ASSIGN_OR_RETURN(ID, readInt());
490 RETURN_IF_ERROR(consumeType(Kind::Comma));
493 Optional<uint32_t> Style;
494 if (ClassUpper == "CONTROL") {
495 // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
496 ASSIGN_OR_RETURN(ClassStr, readString());
497 RETURN_IF_ERROR(consumeType(Kind::Comma));
499 ASSIGN_OR_RETURN(StyleVal, readInt());
500 RETURN_IF_ERROR(consumeType(Kind::Comma));
503 Class = CtlInfo->getValue().CtlClass;
506 // x, y, width, height
507 ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
509 if (ClassUpper != "CONTROL") {
510 if (consumeOptionalType(Kind::Comma)) {
511 ASSIGN_OR_RETURN(Val, readInt());
516 Optional<uint32_t> ExStyle;
517 if (consumeOptionalType(Kind::Comma)) {
518 ASSIGN_OR_RETURN(Val, readInt());
521 Optional<uint32_t> HelpID;
522 if (consumeOptionalType(Kind::Comma)) {
523 ASSIGN_OR_RETURN(Val, readInt());
527 return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
528 (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
531 RCParser::ParseType RCParser::parseBitmapResource() {
532 ASSIGN_OR_RETURN(Arg, readFilename());
533 return llvm::make_unique<BitmapResource>(*Arg);
536 RCParser::ParseType RCParser::parseIconResource() {
537 ASSIGN_OR_RETURN(Arg, readFilename());
538 return llvm::make_unique<IconResource>(*Arg);
541 RCParser::ParseType RCParser::parseHTMLResource() {
542 ASSIGN_OR_RETURN(Arg, readFilename());
543 return llvm::make_unique<HTMLResource>(*Arg);
546 RCParser::ParseType RCParser::parseMenuResource() {
547 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
548 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
549 return llvm::make_unique<MenuResource>(std::move(*OptStatements),
553 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
554 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
556 MenuDefinitionList List;
558 // Read a set of items. Each item is of one of three kinds:
559 // MENUITEM SEPARATOR
560 // MENUITEM caption:String, result:Int [, menu flags]...
561 // POPUP caption:String [, menu flags]... { items... }
562 while (!consumeOptionalType(Kind::BlockEnd)) {
563 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
565 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
566 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
567 if (!IsMenuItem && !IsPopup)
568 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
570 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
571 // Now, expecting SEPARATOR.
572 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
573 if (SeparatorResult->equals_lower("SEPARATOR")) {
574 List.addDefinition(llvm::make_unique<MenuSeparator>());
578 return getExpectedError("SEPARATOR or string", true);
581 // Not a separator. Read the caption.
582 ASSIGN_OR_RETURN(CaptionResult, readString());
584 // If MENUITEM, expect also a comma and an integer.
585 uint32_t MenuResult = -1;
588 RETURN_IF_ERROR(consumeType(Kind::Comma));
589 ASSIGN_OR_RETURN(IntResult, readInt());
590 MenuResult = *IntResult;
593 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
594 MenuDefinition::OptionsFlags));
597 // If POPUP, read submenu items recursively.
598 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
599 List.addDefinition(llvm::make_unique<PopupItem>(
600 *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
606 llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
609 return std::move(List);
612 RCParser::ParseType RCParser::parseStringTableResource() {
613 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
614 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
617 llvm::make_unique<StringTableResource>(std::move(*OptStatements));
619 // Read strings until we reach the end of the block.
620 while (!consumeOptionalType(Kind::BlockEnd)) {
621 // Each definition consists of string's ID (an integer) and a string.
622 // Some examples in documentation suggest that there might be a comma in
623 // between, however we strictly adhere to the single statement definition.
624 ASSIGN_OR_RETURN(IDResult, readInt());
625 consumeOptionalType(Kind::Comma);
626 ASSIGN_OR_RETURN(StrResult, readString());
627 Table->addString(*IDResult, *StrResult);
630 return std::move(Table);
633 Expected<std::unique_ptr<VersionInfoBlock>>
634 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
635 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
637 auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
639 while (!isNextTokenKind(Kind::BlockEnd)) {
640 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
641 Contents->addStmt(std::move(*Stmt));
644 consume(); // Consume BlockEnd.
646 return std::move(Contents);
649 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
650 // Expect either BLOCK or VALUE, then a name or a key (a string).
651 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
653 if (TypeResult->equals_lower("BLOCK")) {
654 ASSIGN_OR_RETURN(NameResult, readString());
655 return parseVersionInfoBlockContents(*NameResult);
658 if (TypeResult->equals_lower("VALUE")) {
659 ASSIGN_OR_RETURN(KeyResult, readString());
660 // Read a non-empty list of strings and/or ints, each
661 // possibly preceded by a comma. Unfortunately, the tool behavior depends
662 // on them existing or not, so we need to memorize where we found them.
663 std::vector<IntOrString> Values;
664 std::vector<bool> PrecedingCommas;
665 RETURN_IF_ERROR(consumeType(Kind::Comma));
666 while (!isNextTokenKind(Kind::Identifier) &&
667 !isNextTokenKind(Kind::BlockEnd)) {
668 // Try to eat a comma if it's not the first statement.
669 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
670 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
671 Values.push_back(*ValueResult);
672 PrecedingCommas.push_back(HadComma);
674 return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
675 std::move(PrecedingCommas));
678 return getExpectedError("BLOCK or VALUE", true);
681 Expected<VersionInfoResource::VersionInfoFixed>
682 RCParser::parseVersionInfoFixed() {
683 using RetType = VersionInfoResource::VersionInfoFixed;
686 // Read until the beginning of the block.
687 while (!isNextTokenKind(Kind::BlockBegin)) {
688 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
689 auto FixedType = RetType::getFixedType(*TypeResult);
691 if (!RetType::isTypeSupported(FixedType))
692 return getExpectedError("fixed VERSIONINFO statement type", true);
693 if (Result.IsTypePresent[FixedType])
694 return getExpectedError("yet unread fixed VERSIONINFO statement type",
697 // VERSION variations take multiple integers.
698 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
699 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
700 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
701 Result.setValue(FixedType, ArgInts);
707 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
708 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
709 return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
712 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
713 ASSIGN_OR_RETURN(Arg, readInt());
714 return llvm::make_unique<CharacteristicsStmt>(*Arg);
717 RCParser::ParseOptionType RCParser::parseVersionStmt() {
718 ASSIGN_OR_RETURN(Arg, readInt());
719 return llvm::make_unique<VersionStmt>(*Arg);
722 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
723 ASSIGN_OR_RETURN(Arg, readString());
724 return llvm::make_unique<CaptionStmt>(*Arg);
727 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
728 assert(DialogType != OptStmtType::BasicStmt);
730 ASSIGN_OR_RETURN(SizeResult, readInt());
731 RETURN_IF_ERROR(consumeType(Kind::Comma));
732 ASSIGN_OR_RETURN(NameResult, readString());
734 // Default values for the optional arguments.
735 uint32_t FontWeight = 0;
736 bool FontItalic = false;
737 uint32_t FontCharset = 1;
738 if (DialogType == OptStmtType::DialogExStmt) {
739 if (consumeOptionalType(Kind::Comma)) {
740 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
741 if (Args->size() >= 1)
742 FontWeight = (*Args)[0];
743 if (Args->size() >= 2)
744 FontItalic = (*Args)[1] != 0;
745 if (Args->size() >= 3)
746 FontCharset = (*Args)[2];
749 return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
750 FontItalic, FontCharset);
753 RCParser::ParseOptionType RCParser::parseStyleStmt() {
754 ASSIGN_OR_RETURN(Arg, readInt());
755 return llvm::make_unique<StyleStmt>(*Arg);
758 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
759 return make_error<ParserError>(
760 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);