15cd3d5c8SMarek Sokolowski //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===// 25cd3d5c8SMarek Sokolowski // 35cd3d5c8SMarek Sokolowski // The LLVM Compiler Infrastructure 45cd3d5c8SMarek Sokolowski // 55cd3d5c8SMarek Sokolowski // This file is distributed under the University of Illinois Open Source 65cd3d5c8SMarek Sokolowski // License. See LICENSE.TXT for details. 75cd3d5c8SMarek Sokolowski // 85cd3d5c8SMarek Sokolowski //===---------------------------------------------------------------------===// 95cd3d5c8SMarek Sokolowski // 105cd3d5c8SMarek Sokolowski // This implements the parser defined in ResourceScriptParser.h. 115cd3d5c8SMarek Sokolowski // 125cd3d5c8SMarek Sokolowski //===---------------------------------------------------------------------===// 135cd3d5c8SMarek Sokolowski 145cd3d5c8SMarek Sokolowski #include "ResourceScriptParser.h" 155cd3d5c8SMarek Sokolowski 165cd3d5c8SMarek Sokolowski // Take an expression returning llvm::Error and forward the error if it exists. 175cd3d5c8SMarek Sokolowski #define RETURN_IF_ERROR(Expr) \ 185cd3d5c8SMarek Sokolowski if (auto Err = (Expr)) \ 195cd3d5c8SMarek Sokolowski return std::move(Err); 205cd3d5c8SMarek Sokolowski 215cd3d5c8SMarek Sokolowski // Take an expression returning llvm::Expected<T> and assign it to Var or 225cd3d5c8SMarek Sokolowski // forward the error out of the function. 235cd3d5c8SMarek Sokolowski #define ASSIGN_OR_RETURN(Var, Expr) \ 245cd3d5c8SMarek Sokolowski auto Var = (Expr); \ 255cd3d5c8SMarek Sokolowski if (!Var) \ 265cd3d5c8SMarek Sokolowski return Var.takeError(); 275cd3d5c8SMarek Sokolowski 285cd3d5c8SMarek Sokolowski namespace llvm { 295cd3d5c8SMarek Sokolowski namespace rc { 305cd3d5c8SMarek Sokolowski 315cd3d5c8SMarek Sokolowski RCParser::ParserError::ParserError(const Twine Expected, const LocIter CurLoc, 325cd3d5c8SMarek Sokolowski const LocIter End) 335cd3d5c8SMarek Sokolowski : ErrorLoc(CurLoc), FileEnd(End) { 345cd3d5c8SMarek Sokolowski CurMessage = "Error parsing file: expected " + Expected.str() + ", got " + 355cd3d5c8SMarek Sokolowski (CurLoc == End ? "<EOF>" : CurLoc->value()).str(); 365cd3d5c8SMarek Sokolowski } 375cd3d5c8SMarek Sokolowski 385cd3d5c8SMarek Sokolowski char RCParser::ParserError::ID = 0; 395cd3d5c8SMarek Sokolowski 405cd3d5c8SMarek Sokolowski RCParser::RCParser(const std::vector<RCToken> &TokenList) 415cd3d5c8SMarek Sokolowski : Tokens(TokenList), CurLoc(Tokens.begin()), End(Tokens.end()) {} 425cd3d5c8SMarek Sokolowski 435cd3d5c8SMarek Sokolowski RCParser::RCParser(std::vector<RCToken> &&TokenList) 445cd3d5c8SMarek Sokolowski : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {} 455cd3d5c8SMarek Sokolowski 465cd3d5c8SMarek Sokolowski bool RCParser::isEof() const { return CurLoc == End; } 475cd3d5c8SMarek Sokolowski 485cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseSingleResource() { 495cd3d5c8SMarek Sokolowski // The first thing we read is usually a resource's name. However, in some 505cd3d5c8SMarek Sokolowski // cases (LANGUAGE and STRINGTABLE) the resources don't have their names 515cd3d5c8SMarek Sokolowski // and the first token to be read is the type. 525cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(NameToken, readTypeOrName()); 535cd3d5c8SMarek Sokolowski 545cd3d5c8SMarek Sokolowski if (NameToken->equalsLower("LANGUAGE")) 555cd3d5c8SMarek Sokolowski return parseLanguageResource(); 565cd3d5c8SMarek Sokolowski else if (NameToken->equalsLower("STRINGTABLE")) 575cd3d5c8SMarek Sokolowski return parseStringTableResource(); 585cd3d5c8SMarek Sokolowski 595cd3d5c8SMarek Sokolowski // If it's not an unnamed resource, what we've just read is a name. Now, 605cd3d5c8SMarek Sokolowski // read resource type; 615cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(TypeToken, readTypeOrName()); 625cd3d5c8SMarek Sokolowski 635cd3d5c8SMarek Sokolowski ParseType Result = std::unique_ptr<RCResource>(); 645cd3d5c8SMarek Sokolowski (void)!Result; 655cd3d5c8SMarek Sokolowski 667f110527SMarek Sokolowski if (TypeToken->equalsLower("ACCELERATORS")) 677f110527SMarek Sokolowski Result = parseAcceleratorsResource(); 687f110527SMarek Sokolowski else if (TypeToken->equalsLower("CURSOR")) 6972aa937eSMarek Sokolowski Result = parseCursorResource(); 704ac54d93SMarek Sokolowski else if (TypeToken->equalsLower("DIALOG")) 714ac54d93SMarek Sokolowski Result = parseDialogResource(false); 724ac54d93SMarek Sokolowski else if (TypeToken->equalsLower("DIALOGEX")) 734ac54d93SMarek Sokolowski Result = parseDialogResource(true); 7472aa937eSMarek Sokolowski else if (TypeToken->equalsLower("ICON")) 755cd3d5c8SMarek Sokolowski Result = parseIconResource(); 7672aa937eSMarek Sokolowski else if (TypeToken->equalsLower("HTML")) 7772aa937eSMarek Sokolowski Result = parseHTMLResource(); 7899ecb0ebSMarek Sokolowski else if (TypeToken->equalsLower("MENU")) 7999ecb0ebSMarek Sokolowski Result = parseMenuResource(); 80fb74cb1eSMarek Sokolowski else if (TypeToken->equalsLower("VERSIONINFO")) 81fb74cb1eSMarek Sokolowski Result = parseVersionInfoResource(); 825cd3d5c8SMarek Sokolowski else 83b5f39a05SMarek Sokolowski Result = parseUserDefinedResource(*TypeToken); 845cd3d5c8SMarek Sokolowski 855cd3d5c8SMarek Sokolowski if (Result) 865cd3d5c8SMarek Sokolowski (*Result)->setName(*NameToken); 875cd3d5c8SMarek Sokolowski 885cd3d5c8SMarek Sokolowski return Result; 895cd3d5c8SMarek Sokolowski } 905cd3d5c8SMarek Sokolowski 915cd3d5c8SMarek Sokolowski bool RCParser::isNextTokenKind(Kind TokenKind) const { 925cd3d5c8SMarek Sokolowski return !isEof() && look().kind() == TokenKind; 935cd3d5c8SMarek Sokolowski } 945cd3d5c8SMarek Sokolowski 955cd3d5c8SMarek Sokolowski const RCToken &RCParser::look() const { 965cd3d5c8SMarek Sokolowski assert(!isEof()); 975cd3d5c8SMarek Sokolowski return *CurLoc; 985cd3d5c8SMarek Sokolowski } 995cd3d5c8SMarek Sokolowski 1005cd3d5c8SMarek Sokolowski const RCToken &RCParser::read() { 1015cd3d5c8SMarek Sokolowski assert(!isEof()); 1025cd3d5c8SMarek Sokolowski return *CurLoc++; 1035cd3d5c8SMarek Sokolowski } 1045cd3d5c8SMarek Sokolowski 1055cd3d5c8SMarek Sokolowski void RCParser::consume() { 1065cd3d5c8SMarek Sokolowski assert(!isEof()); 1075cd3d5c8SMarek Sokolowski CurLoc++; 1085cd3d5c8SMarek Sokolowski } 1095cd3d5c8SMarek Sokolowski 1107e89ee7fSMarek Sokolowski // An integer description might consist of a single integer or 1117e89ee7fSMarek Sokolowski // an arithmetic expression evaluating to the integer. The expressions 1127e89ee7fSMarek Sokolowski // can contain the following tokens: <int> ( ) + - | & ~. Their meaning 1137e89ee7fSMarek Sokolowski // is the same as in C++. 1147e89ee7fSMarek Sokolowski // The operators in the original RC implementation have the following 1157e89ee7fSMarek Sokolowski // precedence: 1167e89ee7fSMarek Sokolowski // 1) Unary operators (- ~), 1177e89ee7fSMarek Sokolowski // 2) Binary operators (+ - & |), with no precedence. 1187e89ee7fSMarek Sokolowski // 1197e89ee7fSMarek Sokolowski // The following grammar is used to parse the expressions Exp1: 1207e89ee7fSMarek Sokolowski // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2 1217e89ee7fSMarek Sokolowski // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1). 1227e89ee7fSMarek Sokolowski // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions, 1237e89ee7fSMarek Sokolowski // separated by binary operators.) 1247e89ee7fSMarek Sokolowski // 1257e89ee7fSMarek Sokolowski // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2 1267e89ee7fSMarek Sokolowski // is read by parseIntExpr2(). 1277e89ee7fSMarek Sokolowski // 1287e89ee7fSMarek Sokolowski // The original Microsoft tool handles multiple unary operators incorrectly. 1297e89ee7fSMarek Sokolowski // For example, in 16-bit little-endian integers: 1307e89ee7fSMarek Sokolowski // 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00; 1317e89ee7fSMarek Sokolowski // 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff. 1327e89ee7fSMarek Sokolowski // Our implementation differs from the original one and handles these 1337e89ee7fSMarek Sokolowski // operators correctly: 1347e89ee7fSMarek Sokolowski // 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff; 1357e89ee7fSMarek Sokolowski // 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff. 1367e89ee7fSMarek Sokolowski 137*07bc04ffSZachary Turner Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); } 1387e89ee7fSMarek Sokolowski 139*07bc04ffSZachary Turner Expected<RCInt> RCParser::parseIntExpr1() { 1407e89ee7fSMarek Sokolowski // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2. 1417e89ee7fSMarek Sokolowski ASSIGN_OR_RETURN(FirstResult, parseIntExpr2()); 142*07bc04ffSZachary Turner RCInt Result = *FirstResult; 1437e89ee7fSMarek Sokolowski 1447e89ee7fSMarek Sokolowski while (!isEof() && look().isBinaryOp()) { 1457e89ee7fSMarek Sokolowski auto OpToken = read(); 1467e89ee7fSMarek Sokolowski ASSIGN_OR_RETURN(NextResult, parseIntExpr2()); 1477e89ee7fSMarek Sokolowski 1487e89ee7fSMarek Sokolowski switch (OpToken.kind()) { 1497e89ee7fSMarek Sokolowski case Kind::Plus: 1507e89ee7fSMarek Sokolowski Result += *NextResult; 1517e89ee7fSMarek Sokolowski break; 1527e89ee7fSMarek Sokolowski 1537e89ee7fSMarek Sokolowski case Kind::Minus: 1547e89ee7fSMarek Sokolowski Result -= *NextResult; 1557e89ee7fSMarek Sokolowski break; 1567e89ee7fSMarek Sokolowski 1577e89ee7fSMarek Sokolowski case Kind::Pipe: 1587e89ee7fSMarek Sokolowski Result |= *NextResult; 1597e89ee7fSMarek Sokolowski break; 1607e89ee7fSMarek Sokolowski 1617e89ee7fSMarek Sokolowski case Kind::Amp: 1627e89ee7fSMarek Sokolowski Result &= *NextResult; 1637e89ee7fSMarek Sokolowski break; 1647e89ee7fSMarek Sokolowski 1657e89ee7fSMarek Sokolowski default: 1667e89ee7fSMarek Sokolowski llvm_unreachable("Already processed all binary ops."); 1677e89ee7fSMarek Sokolowski } 1687e89ee7fSMarek Sokolowski } 1697e89ee7fSMarek Sokolowski 1707e89ee7fSMarek Sokolowski return Result; 1717e89ee7fSMarek Sokolowski } 1727e89ee7fSMarek Sokolowski 173*07bc04ffSZachary Turner Expected<RCInt> RCParser::parseIntExpr2() { 1747e89ee7fSMarek Sokolowski // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1). 1757e89ee7fSMarek Sokolowski static const char ErrorMsg[] = "'-', '~', integer or '('"; 1767e89ee7fSMarek Sokolowski 1777e89ee7fSMarek Sokolowski if (isEof()) 1787e89ee7fSMarek Sokolowski return getExpectedError(ErrorMsg); 1797e89ee7fSMarek Sokolowski 1807e89ee7fSMarek Sokolowski switch (look().kind()) { 1817e89ee7fSMarek Sokolowski case Kind::Minus: { 1827e89ee7fSMarek Sokolowski consume(); 1837e89ee7fSMarek Sokolowski ASSIGN_OR_RETURN(Result, parseIntExpr2()); 1847e89ee7fSMarek Sokolowski return -(*Result); 1857e89ee7fSMarek Sokolowski } 1867e89ee7fSMarek Sokolowski 1877e89ee7fSMarek Sokolowski case Kind::Tilde: { 1887e89ee7fSMarek Sokolowski consume(); 1897e89ee7fSMarek Sokolowski ASSIGN_OR_RETURN(Result, parseIntExpr2()); 1907e89ee7fSMarek Sokolowski return ~(*Result); 1917e89ee7fSMarek Sokolowski } 1927e89ee7fSMarek Sokolowski 1937e89ee7fSMarek Sokolowski case Kind::Int: 194*07bc04ffSZachary Turner return RCInt(read()); 1957e89ee7fSMarek Sokolowski 1967e89ee7fSMarek Sokolowski case Kind::LeftParen: { 1977e89ee7fSMarek Sokolowski consume(); 1987e89ee7fSMarek Sokolowski ASSIGN_OR_RETURN(Result, parseIntExpr1()); 1997e89ee7fSMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::RightParen)); 2007e89ee7fSMarek Sokolowski return *Result; 2017e89ee7fSMarek Sokolowski } 2027e89ee7fSMarek Sokolowski 2037e89ee7fSMarek Sokolowski default: 2047e89ee7fSMarek Sokolowski return getExpectedError(ErrorMsg); 2057e89ee7fSMarek Sokolowski } 2065cd3d5c8SMarek Sokolowski } 2075cd3d5c8SMarek Sokolowski 2085cd3d5c8SMarek Sokolowski Expected<StringRef> RCParser::readString() { 2095cd3d5c8SMarek Sokolowski if (!isNextTokenKind(Kind::String)) 2105cd3d5c8SMarek Sokolowski return getExpectedError("string"); 2115cd3d5c8SMarek Sokolowski return read().value(); 2125cd3d5c8SMarek Sokolowski } 2135cd3d5c8SMarek Sokolowski 2145cd3d5c8SMarek Sokolowski Expected<StringRef> RCParser::readIdentifier() { 2155cd3d5c8SMarek Sokolowski if (!isNextTokenKind(Kind::Identifier)) 2165cd3d5c8SMarek Sokolowski return getExpectedError("identifier"); 2175cd3d5c8SMarek Sokolowski return read().value(); 2185cd3d5c8SMarek Sokolowski } 2195cd3d5c8SMarek Sokolowski 2207f110527SMarek Sokolowski Expected<IntOrString> RCParser::readIntOrString() { 2217f110527SMarek Sokolowski if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String)) 2227f110527SMarek Sokolowski return getExpectedError("int or string"); 2237f110527SMarek Sokolowski return IntOrString(read()); 2247f110527SMarek Sokolowski } 2257f110527SMarek Sokolowski 2265cd3d5c8SMarek Sokolowski Expected<IntOrString> RCParser::readTypeOrName() { 2275cd3d5c8SMarek Sokolowski // We suggest that the correct resource name or type should be either an 2285cd3d5c8SMarek Sokolowski // identifier or an integer. The original RC tool is much more liberal. 2295cd3d5c8SMarek Sokolowski if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int)) 2305cd3d5c8SMarek Sokolowski return getExpectedError("int or identifier"); 2317f110527SMarek Sokolowski return IntOrString(read()); 2325cd3d5c8SMarek Sokolowski } 2335cd3d5c8SMarek Sokolowski 2345cd3d5c8SMarek Sokolowski Error RCParser::consumeType(Kind TokenKind) { 2355cd3d5c8SMarek Sokolowski if (isNextTokenKind(TokenKind)) { 2365cd3d5c8SMarek Sokolowski consume(); 2375cd3d5c8SMarek Sokolowski return Error::success(); 2385cd3d5c8SMarek Sokolowski } 2395cd3d5c8SMarek Sokolowski 2405cd3d5c8SMarek Sokolowski switch (TokenKind) { 2415cd3d5c8SMarek Sokolowski #define TOKEN(TokenName) \ 2425cd3d5c8SMarek Sokolowski case Kind::TokenName: \ 2435cd3d5c8SMarek Sokolowski return getExpectedError(#TokenName); 2445cd3d5c8SMarek Sokolowski #define SHORT_TOKEN(TokenName, TokenCh) \ 2455cd3d5c8SMarek Sokolowski case Kind::TokenName: \ 2465cd3d5c8SMarek Sokolowski return getExpectedError(#TokenCh); 2475cd3d5c8SMarek Sokolowski #include "ResourceScriptTokenList.h" 2485cd3d5c8SMarek Sokolowski #undef SHORT_TOKEN 2495cd3d5c8SMarek Sokolowski #undef TOKEN 2505cd3d5c8SMarek Sokolowski } 2515cd3d5c8SMarek Sokolowski 2525cd3d5c8SMarek Sokolowski llvm_unreachable("All case options exhausted."); 2535cd3d5c8SMarek Sokolowski } 2545cd3d5c8SMarek Sokolowski 2555cd3d5c8SMarek Sokolowski bool RCParser::consumeOptionalType(Kind TokenKind) { 2565cd3d5c8SMarek Sokolowski if (isNextTokenKind(TokenKind)) { 2575cd3d5c8SMarek Sokolowski consume(); 2585cd3d5c8SMarek Sokolowski return true; 2595cd3d5c8SMarek Sokolowski } 2605cd3d5c8SMarek Sokolowski 2615cd3d5c8SMarek Sokolowski return false; 2625cd3d5c8SMarek Sokolowski } 2635cd3d5c8SMarek Sokolowski 264*07bc04ffSZachary Turner Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount, 265*07bc04ffSZachary Turner size_t MaxCount) { 2665cd3d5c8SMarek Sokolowski assert(MinCount <= MaxCount); 2675cd3d5c8SMarek Sokolowski 268*07bc04ffSZachary Turner SmallVector<RCInt, 8> Result; 2695cd3d5c8SMarek Sokolowski 2705cd3d5c8SMarek Sokolowski auto FailureHandler = 271*07bc04ffSZachary Turner [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> { 2725cd3d5c8SMarek Sokolowski if (Result.size() < MinCount) 2735cd3d5c8SMarek Sokolowski return std::move(Err); 2745cd3d5c8SMarek Sokolowski consumeError(std::move(Err)); 2755cd3d5c8SMarek Sokolowski return Result; 2765cd3d5c8SMarek Sokolowski }; 2775cd3d5c8SMarek Sokolowski 2785cd3d5c8SMarek Sokolowski for (size_t i = 0; i < MaxCount; ++i) { 2795cd3d5c8SMarek Sokolowski // Try to read a comma unless we read the first token. 2805cd3d5c8SMarek Sokolowski // Sometimes RC tool requires them and sometimes not. We decide to 2815cd3d5c8SMarek Sokolowski // always require them. 2825cd3d5c8SMarek Sokolowski if (i >= 1) { 2835cd3d5c8SMarek Sokolowski if (auto CommaError = consumeType(Kind::Comma)) 2845cd3d5c8SMarek Sokolowski return FailureHandler(std::move(CommaError)); 2855cd3d5c8SMarek Sokolowski } 2865cd3d5c8SMarek Sokolowski 2875cd3d5c8SMarek Sokolowski if (auto IntResult = readInt()) 2885cd3d5c8SMarek Sokolowski Result.push_back(*IntResult); 2895cd3d5c8SMarek Sokolowski else 2905cd3d5c8SMarek Sokolowski return FailureHandler(IntResult.takeError()); 2915cd3d5c8SMarek Sokolowski } 2925cd3d5c8SMarek Sokolowski 2935cd3d5c8SMarek Sokolowski return std::move(Result); 2945cd3d5c8SMarek Sokolowski } 2955cd3d5c8SMarek Sokolowski 296c75a087cSMarek Sokolowski Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc, 297c75a087cSMarek Sokolowski ArrayRef<uint32_t> FlagValues) { 2987f110527SMarek Sokolowski assert(!FlagDesc.empty()); 299c75a087cSMarek Sokolowski assert(FlagDesc.size() == FlagValues.size()); 3007f110527SMarek Sokolowski 3017f110527SMarek Sokolowski uint32_t Result = 0; 3027f110527SMarek Sokolowski while (isNextTokenKind(Kind::Comma)) { 3037f110527SMarek Sokolowski consume(); 3047f110527SMarek Sokolowski ASSIGN_OR_RETURN(FlagResult, readIdentifier()); 3057f110527SMarek Sokolowski bool FoundFlag = false; 3067f110527SMarek Sokolowski 3077f110527SMarek Sokolowski for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { 3087f110527SMarek Sokolowski if (!FlagResult->equals_lower(FlagDesc[FlagId])) 3097f110527SMarek Sokolowski continue; 3107f110527SMarek Sokolowski 311c75a087cSMarek Sokolowski Result |= FlagValues[FlagId]; 3127f110527SMarek Sokolowski FoundFlag = true; 3137f110527SMarek Sokolowski break; 3147f110527SMarek Sokolowski } 3157f110527SMarek Sokolowski 3167f110527SMarek Sokolowski if (!FoundFlag) 3177f110527SMarek Sokolowski return getExpectedError(join(FlagDesc, "/"), true); 3187f110527SMarek Sokolowski } 3197f110527SMarek Sokolowski 3207f110527SMarek Sokolowski return Result; 3217f110527SMarek Sokolowski } 3227f110527SMarek Sokolowski 323420090afSZachary Turner Expected<OptionalStmtList> 324420090afSZachary Turner RCParser::parseOptionalStatements(OptStmtType StmtsType) { 3255cd3d5c8SMarek Sokolowski OptionalStmtList Result; 3265cd3d5c8SMarek Sokolowski 3275cd3d5c8SMarek Sokolowski // The last statement is always followed by the start of the block. 3285cd3d5c8SMarek Sokolowski while (!isNextTokenKind(Kind::BlockBegin)) { 329420090afSZachary Turner ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType)); 3305cd3d5c8SMarek Sokolowski Result.addStmt(std::move(*SingleParse)); 3315cd3d5c8SMarek Sokolowski } 3325cd3d5c8SMarek Sokolowski 3335cd3d5c8SMarek Sokolowski return std::move(Result); 3345cd3d5c8SMarek Sokolowski } 3355cd3d5c8SMarek Sokolowski 3365cd3d5c8SMarek Sokolowski Expected<std::unique_ptr<OptionalStmt>> 337420090afSZachary Turner RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) { 3385cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(TypeToken, readIdentifier()); 3395cd3d5c8SMarek Sokolowski if (TypeToken->equals_lower("CHARACTERISTICS")) 3405cd3d5c8SMarek Sokolowski return parseCharacteristicsStmt(); 3414ac54d93SMarek Sokolowski if (TypeToken->equals_lower("LANGUAGE")) 3425cd3d5c8SMarek Sokolowski return parseLanguageStmt(); 3434ac54d93SMarek Sokolowski if (TypeToken->equals_lower("VERSION")) 3445cd3d5c8SMarek Sokolowski return parseVersionStmt(); 3454ac54d93SMarek Sokolowski 346420090afSZachary Turner if (StmtsType != OptStmtType::BasicStmt) { 3474ac54d93SMarek Sokolowski if (TypeToken->equals_lower("CAPTION")) 3484ac54d93SMarek Sokolowski return parseCaptionStmt(); 3494ac54d93SMarek Sokolowski if (TypeToken->equals_lower("FONT")) 350420090afSZachary Turner return parseFontStmt(StmtsType); 3514ac54d93SMarek Sokolowski if (TypeToken->equals_lower("STYLE")) 3524ac54d93SMarek Sokolowski return parseStyleStmt(); 3534ac54d93SMarek Sokolowski } 3544ac54d93SMarek Sokolowski 3555cd3d5c8SMarek Sokolowski return getExpectedError("optional statement type, BEGIN or '{'", 3565cd3d5c8SMarek Sokolowski /* IsAlreadyRead = */ true); 3575cd3d5c8SMarek Sokolowski } 3585cd3d5c8SMarek Sokolowski 3595cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseLanguageResource() { 3605cd3d5c8SMarek Sokolowski // Read LANGUAGE as an optional statement. If it's read correctly, we can 3615cd3d5c8SMarek Sokolowski // upcast it to RCResource. 3625cd3d5c8SMarek Sokolowski return parseLanguageStmt(); 3635cd3d5c8SMarek Sokolowski } 3645cd3d5c8SMarek Sokolowski 3657f110527SMarek Sokolowski RCParser::ParseType RCParser::parseAcceleratorsResource() { 3667f110527SMarek Sokolowski ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 3677f110527SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 3687f110527SMarek Sokolowski 3694a765da3SMarek Sokolowski auto Accels = 3704a765da3SMarek Sokolowski llvm::make_unique<AcceleratorsResource>(std::move(*OptStatements)); 3717f110527SMarek Sokolowski 3727f110527SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 3737f110527SMarek Sokolowski ASSIGN_OR_RETURN(EventResult, readIntOrString()); 3747f110527SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 3757f110527SMarek Sokolowski ASSIGN_OR_RETURN(IDResult, readInt()); 376c75a087cSMarek Sokolowski ASSIGN_OR_RETURN( 377c75a087cSMarek Sokolowski FlagsResult, 378c75a087cSMarek Sokolowski parseFlags(AcceleratorsResource::Accelerator::OptionsStr, 379c75a087cSMarek Sokolowski AcceleratorsResource::Accelerator::OptionsFlags)); 3807f110527SMarek Sokolowski Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult); 3817f110527SMarek Sokolowski } 3827f110527SMarek Sokolowski 3837f110527SMarek Sokolowski return std::move(Accels); 3847f110527SMarek Sokolowski } 3857f110527SMarek Sokolowski 38672aa937eSMarek Sokolowski RCParser::ParseType RCParser::parseCursorResource() { 38772aa937eSMarek Sokolowski ASSIGN_OR_RETURN(Arg, readString()); 3884a765da3SMarek Sokolowski return llvm::make_unique<CursorResource>(*Arg); 38972aa937eSMarek Sokolowski } 39072aa937eSMarek Sokolowski 3914ac54d93SMarek Sokolowski RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { 3924ac54d93SMarek Sokolowski // Dialog resources have the following format of the arguments: 3934ac54d93SMarek Sokolowski // DIALOG: x, y, width, height [opt stmts...] {controls...} 3944ac54d93SMarek Sokolowski // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...} 3954ac54d93SMarek Sokolowski // These are very similar, so we parse them together. 3964ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4)); 3974ac54d93SMarek Sokolowski 3984ac54d93SMarek Sokolowski uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0. 3994ac54d93SMarek Sokolowski if (IsExtended && consumeOptionalType(Kind::Comma)) { 4004ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(HelpIDResult, readInt()); 4014ac54d93SMarek Sokolowski HelpID = *HelpIDResult; 4024ac54d93SMarek Sokolowski } 4034ac54d93SMarek Sokolowski 404420090afSZachary Turner ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements( 405420090afSZachary Turner IsExtended ? OptStmtType::DialogExStmt 406420090afSZachary Turner : OptStmtType::DialogStmt)); 4074ac54d93SMarek Sokolowski 4084ac54d93SMarek Sokolowski assert(isNextTokenKind(Kind::BlockBegin) && 4094ac54d93SMarek Sokolowski "parseOptionalStatements, when successful, halts on BlockBegin."); 4104ac54d93SMarek Sokolowski consume(); 4114ac54d93SMarek Sokolowski 4124a765da3SMarek Sokolowski auto Dialog = llvm::make_unique<DialogResource>( 4134ac54d93SMarek Sokolowski (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], 4144ac54d93SMarek Sokolowski HelpID, std::move(*OptStatements), IsExtended); 4154ac54d93SMarek Sokolowski 4164ac54d93SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 4174ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(ControlDefResult, parseControl()); 4184ac54d93SMarek Sokolowski Dialog->addControl(std::move(*ControlDefResult)); 4194ac54d93SMarek Sokolowski } 4204ac54d93SMarek Sokolowski 4214ac54d93SMarek Sokolowski return std::move(Dialog); 4224ac54d93SMarek Sokolowski } 4234ac54d93SMarek Sokolowski 424b5f39a05SMarek Sokolowski RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) { 425b5f39a05SMarek Sokolowski if (isEof()) 426b5f39a05SMarek Sokolowski return getExpectedError("filename, '{' or BEGIN"); 427b5f39a05SMarek Sokolowski 428b5f39a05SMarek Sokolowski // Check if this is a file resource. 429b5f39a05SMarek Sokolowski if (look().kind() == Kind::String) 4304a765da3SMarek Sokolowski return llvm::make_unique<UserDefinedResource>(Type, read().value()); 431b5f39a05SMarek Sokolowski 432b5f39a05SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 433b5f39a05SMarek Sokolowski std::vector<IntOrString> Data; 434b5f39a05SMarek Sokolowski 435b5f39a05SMarek Sokolowski // Consume comma before each consecutive token except the first one. 436b5f39a05SMarek Sokolowski bool ConsumeComma = false; 437b5f39a05SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 438b5f39a05SMarek Sokolowski if (ConsumeComma) 439b5f39a05SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 440b5f39a05SMarek Sokolowski ConsumeComma = true; 441b5f39a05SMarek Sokolowski 442b5f39a05SMarek Sokolowski ASSIGN_OR_RETURN(Item, readIntOrString()); 443b5f39a05SMarek Sokolowski Data.push_back(*Item); 444b5f39a05SMarek Sokolowski } 445b5f39a05SMarek Sokolowski 4464a765da3SMarek Sokolowski return llvm::make_unique<UserDefinedResource>(Type, std::move(Data)); 447b5f39a05SMarek Sokolowski } 448b5f39a05SMarek Sokolowski 449fb74cb1eSMarek Sokolowski RCParser::ParseType RCParser::parseVersionInfoResource() { 450fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed()); 451fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef())); 45299ead70fSMarek Sokolowski return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult), 453fb74cb1eSMarek Sokolowski std::move(*FixedResult)); 454fb74cb1eSMarek Sokolowski } 455fb74cb1eSMarek Sokolowski 4564ac54d93SMarek Sokolowski Expected<Control> RCParser::parseControl() { 4574ac54d93SMarek Sokolowski // Each control definition (except CONTROL) follows one of the schemes below 4584ac54d93SMarek Sokolowski // depending on the control class: 4594ac54d93SMarek Sokolowski // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID] 4604ac54d93SMarek Sokolowski // [class] id, x, y, width, height [, style] [, exstyle] [, helpID] 4614ac54d93SMarek Sokolowski // Note that control ids must be integers. 4627f7745c0SMarek Sokolowski // Text might be either a string or an integer pointing to resource ID. 4634ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(ClassResult, readIdentifier()); 46475fa173eSMarek Sokolowski std::string ClassUpper = ClassResult->upper(); 4657f7745c0SMarek Sokolowski auto CtlInfo = Control::SupportedCtls.find(ClassUpper); 4667f7745c0SMarek Sokolowski if (CtlInfo == Control::SupportedCtls.end()) 4674ac54d93SMarek Sokolowski return getExpectedError("control type, END or '}'", true); 4684ac54d93SMarek Sokolowski 4694ac54d93SMarek Sokolowski // Read caption if necessary. 4707f7745c0SMarek Sokolowski IntOrString Caption{StringRef()}; 4717f7745c0SMarek Sokolowski if (CtlInfo->getValue().HasTitle) { 4727f7745c0SMarek Sokolowski ASSIGN_OR_RETURN(CaptionResult, readIntOrString()); 4734ac54d93SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 4744ac54d93SMarek Sokolowski Caption = *CaptionResult; 4754ac54d93SMarek Sokolowski } 4764ac54d93SMarek Sokolowski 4774ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8)); 4784ac54d93SMarek Sokolowski 4794ac54d93SMarek Sokolowski auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> { 480*07bc04ffSZachary Turner return Args->size() > Id ? (uint32_t)(*Args)[Id] : Optional<uint32_t>(); 4814ac54d93SMarek Sokolowski }; 4824ac54d93SMarek Sokolowski 4834ac54d93SMarek Sokolowski return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2], 4844ac54d93SMarek Sokolowski (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6), 4854ac54d93SMarek Sokolowski TakeOptArg(7)); 4864ac54d93SMarek Sokolowski } 4874ac54d93SMarek Sokolowski 4885cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseIconResource() { 4895cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readString()); 4904a765da3SMarek Sokolowski return llvm::make_unique<IconResource>(*Arg); 4915cd3d5c8SMarek Sokolowski } 4925cd3d5c8SMarek Sokolowski 49372aa937eSMarek Sokolowski RCParser::ParseType RCParser::parseHTMLResource() { 49472aa937eSMarek Sokolowski ASSIGN_OR_RETURN(Arg, readString()); 4954a765da3SMarek Sokolowski return llvm::make_unique<HTMLResource>(*Arg); 49672aa937eSMarek Sokolowski } 49772aa937eSMarek Sokolowski 49899ecb0ebSMarek Sokolowski RCParser::ParseType RCParser::parseMenuResource() { 49999ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 50099ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(Items, parseMenuItemsList()); 5014a765da3SMarek Sokolowski return llvm::make_unique<MenuResource>(std::move(*OptStatements), 50299ecb0ebSMarek Sokolowski std::move(*Items)); 50399ecb0ebSMarek Sokolowski } 50499ecb0ebSMarek Sokolowski 50599ecb0ebSMarek Sokolowski Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { 50699ecb0ebSMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 50799ecb0ebSMarek Sokolowski 50899ecb0ebSMarek Sokolowski MenuDefinitionList List; 50999ecb0ebSMarek Sokolowski 51099ecb0ebSMarek Sokolowski // Read a set of items. Each item is of one of three kinds: 51199ecb0ebSMarek Sokolowski // MENUITEM SEPARATOR 51299ecb0ebSMarek Sokolowski // MENUITEM caption:String, result:Int [, menu flags]... 51399ecb0ebSMarek Sokolowski // POPUP caption:String [, menu flags]... { items... } 51499ecb0ebSMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 51599ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier()); 51699ecb0ebSMarek Sokolowski 51799ecb0ebSMarek Sokolowski bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM"); 51899ecb0ebSMarek Sokolowski bool IsPopup = ItemTypeResult->equals_lower("POPUP"); 51999ecb0ebSMarek Sokolowski if (!IsMenuItem && !IsPopup) 52099ecb0ebSMarek Sokolowski return getExpectedError("MENUITEM, POPUP, END or '}'", true); 52199ecb0ebSMarek Sokolowski 52299ecb0ebSMarek Sokolowski if (IsMenuItem && isNextTokenKind(Kind::Identifier)) { 52399ecb0ebSMarek Sokolowski // Now, expecting SEPARATOR. 52499ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(SeparatorResult, readIdentifier()); 52599ecb0ebSMarek Sokolowski if (SeparatorResult->equals_lower("SEPARATOR")) { 5264a765da3SMarek Sokolowski List.addDefinition(llvm::make_unique<MenuSeparator>()); 52799ecb0ebSMarek Sokolowski continue; 52899ecb0ebSMarek Sokolowski } 52999ecb0ebSMarek Sokolowski 53099ecb0ebSMarek Sokolowski return getExpectedError("SEPARATOR or string", true); 53199ecb0ebSMarek Sokolowski } 53299ecb0ebSMarek Sokolowski 53399ecb0ebSMarek Sokolowski // Not a separator. Read the caption. 53499ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(CaptionResult, readString()); 53599ecb0ebSMarek Sokolowski 53699ecb0ebSMarek Sokolowski // If MENUITEM, expect also a comma and an integer. 53799ecb0ebSMarek Sokolowski uint32_t MenuResult = -1; 53899ecb0ebSMarek Sokolowski 53999ecb0ebSMarek Sokolowski if (IsMenuItem) { 54099ecb0ebSMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 54199ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(IntResult, readInt()); 54299ecb0ebSMarek Sokolowski MenuResult = *IntResult; 54399ecb0ebSMarek Sokolowski } 54499ecb0ebSMarek Sokolowski 545c75a087cSMarek Sokolowski ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr, 546c75a087cSMarek Sokolowski MenuDefinition::OptionsFlags)); 54799ecb0ebSMarek Sokolowski 54899ecb0ebSMarek Sokolowski if (IsPopup) { 54999ecb0ebSMarek Sokolowski // If POPUP, read submenu items recursively. 55099ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList()); 5514a765da3SMarek Sokolowski List.addDefinition(llvm::make_unique<PopupItem>( 5524a765da3SMarek Sokolowski *CaptionResult, *FlagsResult, std::move(*SubMenuResult))); 55399ecb0ebSMarek Sokolowski continue; 55499ecb0ebSMarek Sokolowski } 55599ecb0ebSMarek Sokolowski 55699ecb0ebSMarek Sokolowski assert(IsMenuItem); 55799ecb0ebSMarek Sokolowski List.addDefinition( 5584a765da3SMarek Sokolowski llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult)); 55999ecb0ebSMarek Sokolowski } 56099ecb0ebSMarek Sokolowski 56199ecb0ebSMarek Sokolowski return std::move(List); 56299ecb0ebSMarek Sokolowski } 56399ecb0ebSMarek Sokolowski 5645cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseStringTableResource() { 5655cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 5665cd3d5c8SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 5675cd3d5c8SMarek Sokolowski 5684a765da3SMarek Sokolowski auto Table = 5694a765da3SMarek Sokolowski llvm::make_unique<StringTableResource>(std::move(*OptStatements)); 5705cd3d5c8SMarek Sokolowski 5715cd3d5c8SMarek Sokolowski // Read strings until we reach the end of the block. 5725cd3d5c8SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 5735cd3d5c8SMarek Sokolowski // Each definition consists of string's ID (an integer) and a string. 5745cd3d5c8SMarek Sokolowski // Some examples in documentation suggest that there might be a comma in 5755cd3d5c8SMarek Sokolowski // between, however we strictly adhere to the single statement definition. 5765cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(IDResult, readInt()); 5775cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(StrResult, readString()); 5785cd3d5c8SMarek Sokolowski Table->addString(*IDResult, *StrResult); 5795cd3d5c8SMarek Sokolowski } 5805cd3d5c8SMarek Sokolowski 5815cd3d5c8SMarek Sokolowski return std::move(Table); 5825cd3d5c8SMarek Sokolowski } 5835cd3d5c8SMarek Sokolowski 584fb74cb1eSMarek Sokolowski Expected<std::unique_ptr<VersionInfoBlock>> 585fb74cb1eSMarek Sokolowski RCParser::parseVersionInfoBlockContents(StringRef BlockName) { 586fb74cb1eSMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 587fb74cb1eSMarek Sokolowski 58899ead70fSMarek Sokolowski auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName); 589fb74cb1eSMarek Sokolowski 590fb74cb1eSMarek Sokolowski while (!isNextTokenKind(Kind::BlockEnd)) { 591fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt()); 592fb74cb1eSMarek Sokolowski Contents->addStmt(std::move(*Stmt)); 593fb74cb1eSMarek Sokolowski } 594fb74cb1eSMarek Sokolowski 595fb74cb1eSMarek Sokolowski consume(); // Consume BlockEnd. 596fb74cb1eSMarek Sokolowski 597fb74cb1eSMarek Sokolowski return std::move(Contents); 598fb74cb1eSMarek Sokolowski } 599fb74cb1eSMarek Sokolowski 600fb74cb1eSMarek Sokolowski Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() { 601fb74cb1eSMarek Sokolowski // Expect either BLOCK or VALUE, then a name or a key (a string). 602fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(TypeResult, readIdentifier()); 603fb74cb1eSMarek Sokolowski 604fb74cb1eSMarek Sokolowski if (TypeResult->equals_lower("BLOCK")) { 605fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(NameResult, readString()); 606fb74cb1eSMarek Sokolowski return parseVersionInfoBlockContents(*NameResult); 607fb74cb1eSMarek Sokolowski } 608fb74cb1eSMarek Sokolowski 609fb74cb1eSMarek Sokolowski if (TypeResult->equals_lower("VALUE")) { 610fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(KeyResult, readString()); 611*07bc04ffSZachary Turner // Read a non-empty list of strings and/or ints, each 612*07bc04ffSZachary Turner // possibly preceded by a comma. Unfortunately, the tool behavior depends 613*07bc04ffSZachary Turner // on them existing or not, so we need to memorize where we found them. 614fb74cb1eSMarek Sokolowski std::vector<IntOrString> Values; 615*07bc04ffSZachary Turner std::vector<bool> PrecedingCommas; 616*07bc04ffSZachary Turner RETURN_IF_ERROR(consumeType(Kind::Comma)); 617*07bc04ffSZachary Turner while (!isNextTokenKind(Kind::Identifier) && 618*07bc04ffSZachary Turner !isNextTokenKind(Kind::BlockEnd)) { 619*07bc04ffSZachary Turner // Try to eat a comma if it's not the first statement. 620*07bc04ffSZachary Turner bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma); 621fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(ValueResult, readIntOrString()); 622fb74cb1eSMarek Sokolowski Values.push_back(*ValueResult); 623*07bc04ffSZachary Turner PrecedingCommas.push_back(HadComma); 624fb74cb1eSMarek Sokolowski } 625*07bc04ffSZachary Turner return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values), 626*07bc04ffSZachary Turner std::move(PrecedingCommas)); 627fb74cb1eSMarek Sokolowski } 628fb74cb1eSMarek Sokolowski 629fb74cb1eSMarek Sokolowski return getExpectedError("BLOCK or VALUE", true); 630fb74cb1eSMarek Sokolowski } 631fb74cb1eSMarek Sokolowski 632fb74cb1eSMarek Sokolowski Expected<VersionInfoResource::VersionInfoFixed> 633fb74cb1eSMarek Sokolowski RCParser::parseVersionInfoFixed() { 634fb74cb1eSMarek Sokolowski using RetType = VersionInfoResource::VersionInfoFixed; 635fb74cb1eSMarek Sokolowski RetType Result; 636fb74cb1eSMarek Sokolowski 637fb74cb1eSMarek Sokolowski // Read until the beginning of the block. 638fb74cb1eSMarek Sokolowski while (!isNextTokenKind(Kind::BlockBegin)) { 639fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(TypeResult, readIdentifier()); 640fb74cb1eSMarek Sokolowski auto FixedType = RetType::getFixedType(*TypeResult); 641fb74cb1eSMarek Sokolowski 642fb74cb1eSMarek Sokolowski if (!RetType::isTypeSupported(FixedType)) 643fb74cb1eSMarek Sokolowski return getExpectedError("fixed VERSIONINFO statement type", true); 644fb74cb1eSMarek Sokolowski if (Result.IsTypePresent[FixedType]) 645fb74cb1eSMarek Sokolowski return getExpectedError("yet unread fixed VERSIONINFO statement type", 646fb74cb1eSMarek Sokolowski true); 647fb74cb1eSMarek Sokolowski 648fb74cb1eSMarek Sokolowski // VERSION variations take multiple integers. 649fb74cb1eSMarek Sokolowski size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1; 650fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts)); 651*07bc04ffSZachary Turner SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end()); 652*07bc04ffSZachary Turner Result.setValue(FixedType, ArgInts); 653fb74cb1eSMarek Sokolowski } 654fb74cb1eSMarek Sokolowski 655fb74cb1eSMarek Sokolowski return Result; 656fb74cb1eSMarek Sokolowski } 657fb74cb1eSMarek Sokolowski 6585cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseLanguageStmt() { 6595cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2)); 6604a765da3SMarek Sokolowski return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]); 6615cd3d5c8SMarek Sokolowski } 6625cd3d5c8SMarek Sokolowski 6635cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() { 6645cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readInt()); 6654a765da3SMarek Sokolowski return llvm::make_unique<CharacteristicsStmt>(*Arg); 6665cd3d5c8SMarek Sokolowski } 6675cd3d5c8SMarek Sokolowski 6685cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseVersionStmt() { 6695cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readInt()); 6704a765da3SMarek Sokolowski return llvm::make_unique<VersionStmt>(*Arg); 6715cd3d5c8SMarek Sokolowski } 6725cd3d5c8SMarek Sokolowski 6734ac54d93SMarek Sokolowski RCParser::ParseOptionType RCParser::parseCaptionStmt() { 6744ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readString()); 6754a765da3SMarek Sokolowski return llvm::make_unique<CaptionStmt>(*Arg); 6764ac54d93SMarek Sokolowski } 6774ac54d93SMarek Sokolowski 678420090afSZachary Turner RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) { 679420090afSZachary Turner assert(DialogType != OptStmtType::BasicStmt); 680420090afSZachary Turner 6814ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(SizeResult, readInt()); 6824ac54d93SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 6834ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(NameResult, readString()); 684420090afSZachary Turner 685420090afSZachary Turner // Default values for the optional arguments. 686420090afSZachary Turner uint32_t FontWeight = 0; 687420090afSZachary Turner bool FontItalic = false; 688420090afSZachary Turner uint32_t FontCharset = 1; 689420090afSZachary Turner if (DialogType == OptStmtType::DialogExStmt) { 690420090afSZachary Turner if (consumeOptionalType(Kind::Comma)) { 691420090afSZachary Turner ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3)); 692420090afSZachary Turner if (Args->size() >= 1) 693420090afSZachary Turner FontWeight = (*Args)[0]; 694420090afSZachary Turner if (Args->size() >= 2) 695420090afSZachary Turner FontItalic = (*Args)[1] != 0; 696420090afSZachary Turner if (Args->size() >= 3) 697420090afSZachary Turner FontCharset = (*Args)[2]; 698420090afSZachary Turner } 699420090afSZachary Turner } 700420090afSZachary Turner return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight, 701420090afSZachary Turner FontItalic, FontCharset); 7024ac54d93SMarek Sokolowski } 7034ac54d93SMarek Sokolowski 7044ac54d93SMarek Sokolowski RCParser::ParseOptionType RCParser::parseStyleStmt() { 7054ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readInt()); 7064a765da3SMarek Sokolowski return llvm::make_unique<StyleStmt>(*Arg); 7074ac54d93SMarek Sokolowski } 7084ac54d93SMarek Sokolowski 7095cd3d5c8SMarek Sokolowski Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) { 7105cd3d5c8SMarek Sokolowski return make_error<ParserError>( 7115cd3d5c8SMarek Sokolowski Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); 7125cd3d5c8SMarek Sokolowski } 7135cd3d5c8SMarek Sokolowski 7145cd3d5c8SMarek Sokolowski } // namespace rc 7155cd3d5c8SMarek Sokolowski } // namespace llvm 716