Mercurial > hg > CbC > CbC_llvm
diff tools/llvm-rc/ResourceScriptParser.cpp @ 121:803732b1fca8
LLVM 5.0
author | kono |
---|---|
date | Fri, 27 Oct 2017 17:07:41 +0900 |
parents | |
children | 3a76565eade5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/llvm-rc/ResourceScriptParser.cpp Fri Oct 27 17:07:41 2017 +0900 @@ -0,0 +1,716 @@ +//===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// This implements the parser defined in ResourceScriptParser.h. +// +//===---------------------------------------------------------------------===// + +#include "ResourceScriptParser.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" + +// Take an expression returning llvm::Error and forward the error if it exists. +#define RETURN_IF_ERROR(Expr) \ + if (auto Err = (Expr)) \ + return std::move(Err); + +// Take an expression returning llvm::Expected<T> and assign it to Var or +// forward the error out of the function. +#define ASSIGN_OR_RETURN(Var, Expr) \ + auto Var = (Expr); \ + if (!Var) \ + return Var.takeError(); + +namespace llvm { +namespace rc { + +RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc, + const LocIter End) + : ErrorLoc(CurLoc), FileEnd(End) { + CurMessage = "Error parsing file: expected " + Expected.str() + ", got " + + (CurLoc == End ? "<EOF>" : CurLoc->value()).str(); +} + +char RCParser::ParserError::ID = 0; + +RCParser::RCParser(std::vector<RCToken> TokenList) + : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {} + +bool RCParser::isEof() const { return CurLoc == End; } + +RCParser::ParseType RCParser::parseSingleResource() { + // The first thing we read is usually a resource's name. However, in some + // cases (LANGUAGE and STRINGTABLE) the resources don't have their names + // and the first token to be read is the type. + ASSIGN_OR_RETURN(NameToken, readTypeOrName()); + + if (NameToken->equalsLower("LANGUAGE")) + return parseLanguageResource(); + else if (NameToken->equalsLower("STRINGTABLE")) + return parseStringTableResource(); + + // If it's not an unnamed resource, what we've just read is a name. Now, + // read resource type; + ASSIGN_OR_RETURN(TypeToken, readTypeOrName()); + + ParseType Result = std::unique_ptr<RCResource>(); + (void)!Result; + + if (TypeToken->equalsLower("ACCELERATORS")) + Result = parseAcceleratorsResource(); + else if (TypeToken->equalsLower("CURSOR")) + Result = parseCursorResource(); + else if (TypeToken->equalsLower("DIALOG")) + Result = parseDialogResource(false); + else if (TypeToken->equalsLower("DIALOGEX")) + Result = parseDialogResource(true); + else if (TypeToken->equalsLower("ICON")) + Result = parseIconResource(); + else if (TypeToken->equalsLower("HTML")) + Result = parseHTMLResource(); + else if (TypeToken->equalsLower("MENU")) + Result = parseMenuResource(); + else if (TypeToken->equalsLower("VERSIONINFO")) + Result = parseVersionInfoResource(); + else + Result = parseUserDefinedResource(*TypeToken); + + if (Result) + (*Result)->setName(*NameToken); + + return Result; +} + +bool RCParser::isNextTokenKind(Kind TokenKind) const { + return !isEof() && look().kind() == TokenKind; +} + +const RCToken &RCParser::look() const { + assert(!isEof()); + return *CurLoc; +} + +const RCToken &RCParser::read() { + assert(!isEof()); + return *CurLoc++; +} + +void RCParser::consume() { + assert(!isEof()); + CurLoc++; +} + +// An integer description might consist of a single integer or +// an arithmetic expression evaluating to the integer. The expressions +// can contain the following tokens: <int> ( ) + - | & ~. Their meaning +// is the same as in C++. +// The operators in the original RC implementation have the following +// precedence: +// 1) Unary operators (- ~), +// 2) Binary operators (+ - & |), with no precedence. +// +// The following grammar is used to parse the expressions Exp1: +// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2 +// Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1). +// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions, +// separated by binary operators.) +// +// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2 +// is read by parseIntExpr2(). +// +// The original Microsoft tool handles multiple unary operators incorrectly. +// For example, in 16-bit little-endian integers: +// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00; +// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff. +// Our implementation differs from the original one and handles these +// operators correctly: +// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff; +// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff. + +Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); } + +Expected<RCInt> RCParser::parseIntExpr1() { + // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2. + ASSIGN_OR_RETURN(FirstResult, parseIntExpr2()); + RCInt Result = *FirstResult; + + while (!isEof() && look().isBinaryOp()) { + auto OpToken = read(); + ASSIGN_OR_RETURN(NextResult, parseIntExpr2()); + + switch (OpToken.kind()) { + case Kind::Plus: + Result += *NextResult; + break; + + case Kind::Minus: + Result -= *NextResult; + break; + + case Kind::Pipe: + Result |= *NextResult; + break; + + case Kind::Amp: + Result &= *NextResult; + break; + + default: + llvm_unreachable("Already processed all binary ops."); + } + } + + return Result; +} + +Expected<RCInt> RCParser::parseIntExpr2() { + // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1). + static const char ErrorMsg[] = "'-', '~', integer or '('"; + + if (isEof()) + return getExpectedError(ErrorMsg); + + switch (look().kind()) { + case Kind::Minus: { + consume(); + ASSIGN_OR_RETURN(Result, parseIntExpr2()); + return -(*Result); + } + + case Kind::Tilde: { + consume(); + ASSIGN_OR_RETURN(Result, parseIntExpr2()); + return ~(*Result); + } + + case Kind::Int: + return RCInt(read()); + + case Kind::LeftParen: { + consume(); + ASSIGN_OR_RETURN(Result, parseIntExpr1()); + RETURN_IF_ERROR(consumeType(Kind::RightParen)); + return *Result; + } + + default: + return getExpectedError(ErrorMsg); + } +} + +Expected<StringRef> RCParser::readString() { + if (!isNextTokenKind(Kind::String)) + return getExpectedError("string"); + return read().value(); +} + +Expected<StringRef> RCParser::readIdentifier() { + if (!isNextTokenKind(Kind::Identifier)) + return getExpectedError("identifier"); + return read().value(); +} + +Expected<IntOrString> RCParser::readIntOrString() { + if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String)) + return getExpectedError("int or string"); + return IntOrString(read()); +} + +Expected<IntOrString> RCParser::readTypeOrName() { + // We suggest that the correct resource name or type should be either an + // identifier or an integer. The original RC tool is much more liberal. + if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int)) + return getExpectedError("int or identifier"); + return IntOrString(read()); +} + +Error RCParser::consumeType(Kind TokenKind) { + if (isNextTokenKind(TokenKind)) { + consume(); + return Error::success(); + } + + switch (TokenKind) { +#define TOKEN(TokenName) \ + case Kind::TokenName: \ + return getExpectedError(#TokenName); +#define SHORT_TOKEN(TokenName, TokenCh) \ + case Kind::TokenName: \ + return getExpectedError(#TokenCh); +#include "ResourceScriptTokenList.h" +#undef SHORT_TOKEN +#undef TOKEN + } + + llvm_unreachable("All case options exhausted."); +} + +bool RCParser::consumeOptionalType(Kind TokenKind) { + if (isNextTokenKind(TokenKind)) { + consume(); + return true; + } + + return false; +} + +Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount, + size_t MaxCount) { + assert(MinCount <= MaxCount); + + SmallVector<RCInt, 8> Result; + + auto FailureHandler = + [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> { + if (Result.size() < MinCount) + return std::move(Err); + consumeError(std::move(Err)); + return Result; + }; + + for (size_t i = 0; i < MaxCount; ++i) { + // Try to read a comma unless we read the first token. + // Sometimes RC tool requires them and sometimes not. We decide to + // always require them. + if (i >= 1) { + if (auto CommaError = consumeType(Kind::Comma)) + return FailureHandler(std::move(CommaError)); + } + + if (auto IntResult = readInt()) + Result.push_back(*IntResult); + else + return FailureHandler(IntResult.takeError()); + } + + return std::move(Result); +} + +Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc, + ArrayRef<uint32_t> FlagValues) { + assert(!FlagDesc.empty()); + assert(FlagDesc.size() == FlagValues.size()); + + uint32_t Result = 0; + while (isNextTokenKind(Kind::Comma)) { + consume(); + ASSIGN_OR_RETURN(FlagResult, readIdentifier()); + bool FoundFlag = false; + + for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { + if (!FlagResult->equals_lower(FlagDesc[FlagId])) + continue; + + Result |= FlagValues[FlagId]; + FoundFlag = true; + break; + } + + if (!FoundFlag) + return getExpectedError(join(FlagDesc, "/"), true); + } + + return Result; +} + +Expected<OptionalStmtList> +RCParser::parseOptionalStatements(OptStmtType StmtsType) { + OptionalStmtList Result; + + // The last statement is always followed by the start of the block. + while (!isNextTokenKind(Kind::BlockBegin)) { + ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType)); + Result.addStmt(std::move(*SingleParse)); + } + + return std::move(Result); +} + +Expected<std::unique_ptr<OptionalStmt>> +RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) { + ASSIGN_OR_RETURN(TypeToken, readIdentifier()); + if (TypeToken->equals_lower("CHARACTERISTICS")) + return parseCharacteristicsStmt(); + if (TypeToken->equals_lower("LANGUAGE")) + return parseLanguageStmt(); + if (TypeToken->equals_lower("VERSION")) + return parseVersionStmt(); + + if (StmtsType != OptStmtType::BasicStmt) { + if (TypeToken->equals_lower("CAPTION")) + return parseCaptionStmt(); + if (TypeToken->equals_lower("FONT")) + return parseFontStmt(StmtsType); + if (TypeToken->equals_lower("STYLE")) + return parseStyleStmt(); + } + + return getExpectedError("optional statement type, BEGIN or '{'", + /* IsAlreadyRead = */ true); +} + +RCParser::ParseType RCParser::parseLanguageResource() { + // Read LANGUAGE as an optional statement. If it's read correctly, we can + // upcast it to RCResource. + return parseLanguageStmt(); +} + +RCParser::ParseType RCParser::parseAcceleratorsResource() { + ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + + auto Accels = + llvm::make_unique<AcceleratorsResource>(std::move(*OptStatements)); + + while (!consumeOptionalType(Kind::BlockEnd)) { + ASSIGN_OR_RETURN(EventResult, readIntOrString()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ASSIGN_OR_RETURN(IDResult, readInt()); + ASSIGN_OR_RETURN( + FlagsResult, + parseFlags(AcceleratorsResource::Accelerator::OptionsStr, + AcceleratorsResource::Accelerator::OptionsFlags)); + Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult); + } + + return std::move(Accels); +} + +RCParser::ParseType RCParser::parseCursorResource() { + ASSIGN_OR_RETURN(Arg, readString()); + return llvm::make_unique<CursorResource>(*Arg); +} + +RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { + // Dialog resources have the following format of the arguments: + // DIALOG: x, y, width, height [opt stmts...] {controls...} + // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...} + // These are very similar, so we parse them together. + ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4)); + + uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0. + if (IsExtended && consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(HelpIDResult, readInt()); + HelpID = *HelpIDResult; + } + + ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements( + IsExtended ? OptStmtType::DialogExStmt + : OptStmtType::DialogStmt)); + + assert(isNextTokenKind(Kind::BlockBegin) && + "parseOptionalStatements, when successful, halts on BlockBegin."); + consume(); + + auto Dialog = llvm::make_unique<DialogResource>( + (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], + HelpID, std::move(*OptStatements), IsExtended); + + while (!consumeOptionalType(Kind::BlockEnd)) { + ASSIGN_OR_RETURN(ControlDefResult, parseControl()); + Dialog->addControl(std::move(*ControlDefResult)); + } + + return std::move(Dialog); +} + +RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) { + if (isEof()) + return getExpectedError("filename, '{' or BEGIN"); + + // Check if this is a file resource. + if (look().kind() == Kind::String) + return llvm::make_unique<UserDefinedResource>(Type, read().value()); + + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + std::vector<IntOrString> Data; + + // Consume comma before each consecutive token except the first one. + bool ConsumeComma = false; + while (!consumeOptionalType(Kind::BlockEnd)) { + if (ConsumeComma) + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ConsumeComma = true; + + ASSIGN_OR_RETURN(Item, readIntOrString()); + Data.push_back(*Item); + } + + return llvm::make_unique<UserDefinedResource>(Type, std::move(Data)); +} + +RCParser::ParseType RCParser::parseVersionInfoResource() { + ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed()); + ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef())); + return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult), + std::move(*FixedResult)); +} + +Expected<Control> RCParser::parseControl() { + // Each control definition (except CONTROL) follows one of the schemes below + // depending on the control class: + // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID] + // [class] id, x, y, width, height [, style] [, exstyle] [, helpID] + // Note that control ids must be integers. + // Text might be either a string or an integer pointing to resource ID. + ASSIGN_OR_RETURN(ClassResult, readIdentifier()); + std::string ClassUpper = ClassResult->upper(); + auto CtlInfo = Control::SupportedCtls.find(ClassUpper); + if (CtlInfo == Control::SupportedCtls.end()) + return getExpectedError("control type, END or '}'", true); + + // Read caption if necessary. + IntOrString Caption{StringRef()}; + if (CtlInfo->getValue().HasTitle) { + ASSIGN_OR_RETURN(CaptionResult, readIntOrString()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + Caption = *CaptionResult; + } + + ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8)); + + auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> { + return Args->size() > Id ? (uint32_t)(*Args)[Id] : Optional<uint32_t>(); + }; + + return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2], + (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6), + TakeOptArg(7)); +} + +RCParser::ParseType RCParser::parseIconResource() { + ASSIGN_OR_RETURN(Arg, readString()); + return llvm::make_unique<IconResource>(*Arg); +} + +RCParser::ParseType RCParser::parseHTMLResource() { + ASSIGN_OR_RETURN(Arg, readString()); + return llvm::make_unique<HTMLResource>(*Arg); +} + +RCParser::ParseType RCParser::parseMenuResource() { + ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); + ASSIGN_OR_RETURN(Items, parseMenuItemsList()); + return llvm::make_unique<MenuResource>(std::move(*OptStatements), + std::move(*Items)); +} + +Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + + MenuDefinitionList List; + + // Read a set of items. Each item is of one of three kinds: + // MENUITEM SEPARATOR + // MENUITEM caption:String, result:Int [, menu flags]... + // POPUP caption:String [, menu flags]... { items... } + while (!consumeOptionalType(Kind::BlockEnd)) { + ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier()); + + bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM"); + bool IsPopup = ItemTypeResult->equals_lower("POPUP"); + if (!IsMenuItem && !IsPopup) + return getExpectedError("MENUITEM, POPUP, END or '}'", true); + + if (IsMenuItem && isNextTokenKind(Kind::Identifier)) { + // Now, expecting SEPARATOR. + ASSIGN_OR_RETURN(SeparatorResult, readIdentifier()); + if (SeparatorResult->equals_lower("SEPARATOR")) { + List.addDefinition(llvm::make_unique<MenuSeparator>()); + continue; + } + + return getExpectedError("SEPARATOR or string", true); + } + + // Not a separator. Read the caption. + ASSIGN_OR_RETURN(CaptionResult, readString()); + + // If MENUITEM, expect also a comma and an integer. + uint32_t MenuResult = -1; + + if (IsMenuItem) { + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ASSIGN_OR_RETURN(IntResult, readInt()); + MenuResult = *IntResult; + } + + ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr, + MenuDefinition::OptionsFlags)); + + if (IsPopup) { + // If POPUP, read submenu items recursively. + ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList()); + List.addDefinition(llvm::make_unique<PopupItem>( + *CaptionResult, *FlagsResult, std::move(*SubMenuResult))); + continue; + } + + assert(IsMenuItem); + List.addDefinition( + llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult)); + } + + return std::move(List); +} + +RCParser::ParseType RCParser::parseStringTableResource() { + ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + + auto Table = + llvm::make_unique<StringTableResource>(std::move(*OptStatements)); + + // Read strings until we reach the end of the block. + while (!consumeOptionalType(Kind::BlockEnd)) { + // Each definition consists of string's ID (an integer) and a string. + // Some examples in documentation suggest that there might be a comma in + // between, however we strictly adhere to the single statement definition. + ASSIGN_OR_RETURN(IDResult, readInt()); + ASSIGN_OR_RETURN(StrResult, readString()); + Table->addString(*IDResult, *StrResult); + } + + return std::move(Table); +} + +Expected<std::unique_ptr<VersionInfoBlock>> +RCParser::parseVersionInfoBlockContents(StringRef BlockName) { + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + + auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName); + + while (!isNextTokenKind(Kind::BlockEnd)) { + ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt()); + Contents->addStmt(std::move(*Stmt)); + } + + consume(); // Consume BlockEnd. + + return std::move(Contents); +} + +Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() { + // Expect either BLOCK or VALUE, then a name or a key (a string). + ASSIGN_OR_RETURN(TypeResult, readIdentifier()); + + if (TypeResult->equals_lower("BLOCK")) { + ASSIGN_OR_RETURN(NameResult, readString()); + return parseVersionInfoBlockContents(*NameResult); + } + + if (TypeResult->equals_lower("VALUE")) { + ASSIGN_OR_RETURN(KeyResult, readString()); + // Read a non-empty list of strings and/or ints, each + // possibly preceded by a comma. Unfortunately, the tool behavior depends + // on them existing or not, so we need to memorize where we found them. + std::vector<IntOrString> Values; + std::vector<bool> PrecedingCommas; + RETURN_IF_ERROR(consumeType(Kind::Comma)); + while (!isNextTokenKind(Kind::Identifier) && + !isNextTokenKind(Kind::BlockEnd)) { + // Try to eat a comma if it's not the first statement. + bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma); + ASSIGN_OR_RETURN(ValueResult, readIntOrString()); + Values.push_back(*ValueResult); + PrecedingCommas.push_back(HadComma); + } + return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values), + std::move(PrecedingCommas)); + } + + return getExpectedError("BLOCK or VALUE", true); +} + +Expected<VersionInfoResource::VersionInfoFixed> +RCParser::parseVersionInfoFixed() { + using RetType = VersionInfoResource::VersionInfoFixed; + RetType Result; + + // Read until the beginning of the block. + while (!isNextTokenKind(Kind::BlockBegin)) { + ASSIGN_OR_RETURN(TypeResult, readIdentifier()); + auto FixedType = RetType::getFixedType(*TypeResult); + + if (!RetType::isTypeSupported(FixedType)) + return getExpectedError("fixed VERSIONINFO statement type", true); + if (Result.IsTypePresent[FixedType]) + return getExpectedError("yet unread fixed VERSIONINFO statement type", + true); + + // VERSION variations take multiple integers. + size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1; + ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts)); + SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end()); + Result.setValue(FixedType, ArgInts); + } + + return Result; +} + +RCParser::ParseOptionType RCParser::parseLanguageStmt() { + ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2)); + return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]); +} + +RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() { + ASSIGN_OR_RETURN(Arg, readInt()); + return llvm::make_unique<CharacteristicsStmt>(*Arg); +} + +RCParser::ParseOptionType RCParser::parseVersionStmt() { + ASSIGN_OR_RETURN(Arg, readInt()); + return llvm::make_unique<VersionStmt>(*Arg); +} + +RCParser::ParseOptionType RCParser::parseCaptionStmt() { + ASSIGN_OR_RETURN(Arg, readString()); + return llvm::make_unique<CaptionStmt>(*Arg); +} + +RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) { + assert(DialogType != OptStmtType::BasicStmt); + + ASSIGN_OR_RETURN(SizeResult, readInt()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ASSIGN_OR_RETURN(NameResult, readString()); + + // Default values for the optional arguments. + uint32_t FontWeight = 0; + bool FontItalic = false; + uint32_t FontCharset = 1; + if (DialogType == OptStmtType::DialogExStmt) { + if (consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3)); + if (Args->size() >= 1) + FontWeight = (*Args)[0]; + if (Args->size() >= 2) + FontItalic = (*Args)[1] != 0; + if (Args->size() >= 3) + FontCharset = (*Args)[2]; + } + } + return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight, + FontItalic, FontCharset); +} + +RCParser::ParseOptionType RCParser::parseStyleStmt() { + ASSIGN_OR_RETURN(Arg, readInt()); + return llvm::make_unique<StyleStmt>(*Arg); +} + +Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) { + return make_error<ParserError>( + Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); +} + +} // namespace rc +} // namespace llvm