15cd3d5c8SMarek Sokolowski //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===// 25cd3d5c8SMarek Sokolowski // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65cd3d5c8SMarek Sokolowski // 75cd3d5c8SMarek Sokolowski //===---------------------------------------------------------------------===// 85cd3d5c8SMarek Sokolowski // 95cd3d5c8SMarek Sokolowski // This implements the parser defined in ResourceScriptParser.h. 105cd3d5c8SMarek Sokolowski // 115cd3d5c8SMarek Sokolowski //===---------------------------------------------------------------------===// 125cd3d5c8SMarek Sokolowski 135cd3d5c8SMarek Sokolowski #include "ResourceScriptParser.h" 14fa0ca6cbSZachary Turner #include "llvm/Option/ArgList.h" 15fa0ca6cbSZachary Turner #include "llvm/Support/FileSystem.h" 16fa0ca6cbSZachary Turner #include "llvm/Support/Path.h" 17fa0ca6cbSZachary Turner #include "llvm/Support/Process.h" 185cd3d5c8SMarek Sokolowski 195cd3d5c8SMarek Sokolowski // Take an expression returning llvm::Error and forward the error if it exists. 205cd3d5c8SMarek Sokolowski #define RETURN_IF_ERROR(Expr) \ 215cd3d5c8SMarek Sokolowski if (auto Err = (Expr)) \ 225cd3d5c8SMarek Sokolowski return std::move(Err); 235cd3d5c8SMarek Sokolowski 245cd3d5c8SMarek Sokolowski // Take an expression returning llvm::Expected<T> and assign it to Var or 255cd3d5c8SMarek Sokolowski // forward the error out of the function. 265cd3d5c8SMarek Sokolowski #define ASSIGN_OR_RETURN(Var, Expr) \ 275cd3d5c8SMarek Sokolowski auto Var = (Expr); \ 285cd3d5c8SMarek Sokolowski if (!Var) \ 295cd3d5c8SMarek Sokolowski return Var.takeError(); 305cd3d5c8SMarek Sokolowski 315cd3d5c8SMarek Sokolowski namespace llvm { 325cd3d5c8SMarek Sokolowski namespace rc { 335cd3d5c8SMarek Sokolowski 34514b7105SZachary Turner RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc, 355cd3d5c8SMarek Sokolowski const LocIter End) 365cd3d5c8SMarek Sokolowski : ErrorLoc(CurLoc), FileEnd(End) { 375cd3d5c8SMarek Sokolowski CurMessage = "Error parsing file: expected " + Expected.str() + ", got " + 385cd3d5c8SMarek Sokolowski (CurLoc == End ? "<EOF>" : CurLoc->value()).str(); 395cd3d5c8SMarek Sokolowski } 405cd3d5c8SMarek Sokolowski 415cd3d5c8SMarek Sokolowski char RCParser::ParserError::ID = 0; 425cd3d5c8SMarek Sokolowski 43514b7105SZachary Turner 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(); 68577b9817SMartin Storsjo else if (TypeToken->equalsLower("BITMAP")) 69577b9817SMartin Storsjo Result = parseBitmapResource(); 707f110527SMarek Sokolowski else if (TypeToken->equalsLower("CURSOR")) 7172aa937eSMarek Sokolowski Result = parseCursorResource(); 724ac54d93SMarek Sokolowski else if (TypeToken->equalsLower("DIALOG")) 734ac54d93SMarek Sokolowski Result = parseDialogResource(false); 744ac54d93SMarek Sokolowski else if (TypeToken->equalsLower("DIALOGEX")) 754ac54d93SMarek Sokolowski Result = parseDialogResource(true); 7672aa937eSMarek Sokolowski else if (TypeToken->equalsLower("HTML")) 7772aa937eSMarek Sokolowski Result = parseHTMLResource(); 78827ddb24SMartin Storsjo else if (TypeToken->equalsLower("ICON")) 79827ddb24SMartin Storsjo Result = parseIconResource(); 8099ecb0ebSMarek Sokolowski else if (TypeToken->equalsLower("MENU")) 8199ecb0ebSMarek Sokolowski Result = parseMenuResource(); 827bc3c582SMartin Storsjo else if (TypeToken->equalsLower("RCDATA")) 837bc3c582SMartin Storsjo Result = parseUserDefinedResource(RkRcData); 84fb74cb1eSMarek Sokolowski else if (TypeToken->equalsLower("VERSIONINFO")) 85fb74cb1eSMarek Sokolowski Result = parseVersionInfoResource(); 865cd3d5c8SMarek Sokolowski else 87b5f39a05SMarek Sokolowski Result = parseUserDefinedResource(*TypeToken); 885cd3d5c8SMarek Sokolowski 895cd3d5c8SMarek Sokolowski if (Result) 905cd3d5c8SMarek Sokolowski (*Result)->setName(*NameToken); 915cd3d5c8SMarek Sokolowski 925cd3d5c8SMarek Sokolowski return Result; 935cd3d5c8SMarek Sokolowski } 945cd3d5c8SMarek Sokolowski 955cd3d5c8SMarek Sokolowski bool RCParser::isNextTokenKind(Kind TokenKind) const { 965cd3d5c8SMarek Sokolowski return !isEof() && look().kind() == TokenKind; 975cd3d5c8SMarek Sokolowski } 985cd3d5c8SMarek Sokolowski 995cd3d5c8SMarek Sokolowski const RCToken &RCParser::look() const { 1005cd3d5c8SMarek Sokolowski assert(!isEof()); 1015cd3d5c8SMarek Sokolowski return *CurLoc; 1025cd3d5c8SMarek Sokolowski } 1035cd3d5c8SMarek Sokolowski 1045cd3d5c8SMarek Sokolowski const RCToken &RCParser::read() { 1055cd3d5c8SMarek Sokolowski assert(!isEof()); 1065cd3d5c8SMarek Sokolowski return *CurLoc++; 1075cd3d5c8SMarek Sokolowski } 1085cd3d5c8SMarek Sokolowski 1095cd3d5c8SMarek Sokolowski void RCParser::consume() { 1105cd3d5c8SMarek Sokolowski assert(!isEof()); 1115cd3d5c8SMarek Sokolowski CurLoc++; 1125cd3d5c8SMarek Sokolowski } 1135cd3d5c8SMarek Sokolowski 1147e89ee7fSMarek Sokolowski // An integer description might consist of a single integer or 1157e89ee7fSMarek Sokolowski // an arithmetic expression evaluating to the integer. The expressions 116d0afe724SMartin Storsjo // can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning 117d0afe724SMartin Storsjo // is the same as in C++ except for 'not' expression. 1187e89ee7fSMarek Sokolowski // The operators in the original RC implementation have the following 1197e89ee7fSMarek Sokolowski // precedence: 120d0afe724SMartin Storsjo // 1) Unary operators (- ~ not), 1217e89ee7fSMarek Sokolowski // 2) Binary operators (+ - & |), with no precedence. 1227e89ee7fSMarek Sokolowski // 123d0afe724SMartin Storsjo // 'not' expression is mostly useful for style values. It evaluates to 0, 124d0afe724SMartin Storsjo // but value given to the operator is stored separately from integer value. 125d0afe724SMartin Storsjo // It's mostly useful for control style expressions and causes bits from 126d0afe724SMartin Storsjo // default control style to be excluded from generated style. For binary 127d0afe724SMartin Storsjo // operators the mask from the right operand is applied to the left operand 128d0afe724SMartin Storsjo // and masks from both operands are combined in operator result. 129d0afe724SMartin Storsjo // 1307e89ee7fSMarek Sokolowski // The following grammar is used to parse the expressions Exp1: 1317e89ee7fSMarek Sokolowski // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2 132d0afe724SMartin Storsjo // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). 1337e89ee7fSMarek Sokolowski // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions, 1347e89ee7fSMarek Sokolowski // separated by binary operators.) 1357e89ee7fSMarek Sokolowski // 1367e89ee7fSMarek Sokolowski // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2 1377e89ee7fSMarek Sokolowski // is read by parseIntExpr2(). 1387e89ee7fSMarek Sokolowski // 1397e89ee7fSMarek Sokolowski // The original Microsoft tool handles multiple unary operators incorrectly. 1407e89ee7fSMarek Sokolowski // For example, in 16-bit little-endian integers: 1417e89ee7fSMarek Sokolowski // 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00; 1427e89ee7fSMarek Sokolowski // 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff. 1437e89ee7fSMarek Sokolowski // Our implementation differs from the original one and handles these 1447e89ee7fSMarek Sokolowski // operators correctly: 1457e89ee7fSMarek Sokolowski // 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff; 1467e89ee7fSMarek Sokolowski // 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff. 1477e89ee7fSMarek Sokolowski 148d0afe724SMartin Storsjo Expected<RCInt> RCParser::readInt() { 149d0afe724SMartin Storsjo ASSIGN_OR_RETURN(Value, parseIntExpr1()); 150d0afe724SMartin Storsjo return (*Value).getValue(); 151d0afe724SMartin Storsjo } 1527e89ee7fSMarek Sokolowski 153d0afe724SMartin Storsjo Expected<IntWithNotMask> RCParser::parseIntExpr1() { 1547e89ee7fSMarek Sokolowski // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2. 1557e89ee7fSMarek Sokolowski ASSIGN_OR_RETURN(FirstResult, parseIntExpr2()); 156d0afe724SMartin Storsjo IntWithNotMask Result = *FirstResult; 1577e89ee7fSMarek Sokolowski 1587e89ee7fSMarek Sokolowski while (!isEof() && look().isBinaryOp()) { 1597e89ee7fSMarek Sokolowski auto OpToken = read(); 1607e89ee7fSMarek Sokolowski ASSIGN_OR_RETURN(NextResult, parseIntExpr2()); 1617e89ee7fSMarek Sokolowski 1627e89ee7fSMarek Sokolowski switch (OpToken.kind()) { 1637e89ee7fSMarek Sokolowski case Kind::Plus: 1647e89ee7fSMarek Sokolowski Result += *NextResult; 1657e89ee7fSMarek Sokolowski break; 1667e89ee7fSMarek Sokolowski 1677e89ee7fSMarek Sokolowski case Kind::Minus: 1687e89ee7fSMarek Sokolowski Result -= *NextResult; 1697e89ee7fSMarek Sokolowski break; 1707e89ee7fSMarek Sokolowski 1717e89ee7fSMarek Sokolowski case Kind::Pipe: 1727e89ee7fSMarek Sokolowski Result |= *NextResult; 1737e89ee7fSMarek Sokolowski break; 1747e89ee7fSMarek Sokolowski 1757e89ee7fSMarek Sokolowski case Kind::Amp: 1767e89ee7fSMarek Sokolowski Result &= *NextResult; 1777e89ee7fSMarek Sokolowski break; 1787e89ee7fSMarek Sokolowski 1797e89ee7fSMarek Sokolowski default: 1807e89ee7fSMarek Sokolowski llvm_unreachable("Already processed all binary ops."); 1817e89ee7fSMarek Sokolowski } 1827e89ee7fSMarek Sokolowski } 1837e89ee7fSMarek Sokolowski 1847e89ee7fSMarek Sokolowski return Result; 1857e89ee7fSMarek Sokolowski } 1867e89ee7fSMarek Sokolowski 187d0afe724SMartin Storsjo Expected<IntWithNotMask> RCParser::parseIntExpr2() { 188d0afe724SMartin Storsjo // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). 1897e89ee7fSMarek Sokolowski static const char ErrorMsg[] = "'-', '~', integer or '('"; 1907e89ee7fSMarek Sokolowski 1917e89ee7fSMarek Sokolowski if (isEof()) 1927e89ee7fSMarek Sokolowski return getExpectedError(ErrorMsg); 1937e89ee7fSMarek Sokolowski 1947e89ee7fSMarek Sokolowski switch (look().kind()) { 1957e89ee7fSMarek Sokolowski case Kind::Minus: { 1967e89ee7fSMarek Sokolowski consume(); 1977e89ee7fSMarek Sokolowski ASSIGN_OR_RETURN(Result, parseIntExpr2()); 1987e89ee7fSMarek Sokolowski return -(*Result); 1997e89ee7fSMarek Sokolowski } 2007e89ee7fSMarek Sokolowski 2017e89ee7fSMarek Sokolowski case Kind::Tilde: { 2027e89ee7fSMarek Sokolowski consume(); 2037e89ee7fSMarek Sokolowski ASSIGN_OR_RETURN(Result, parseIntExpr2()); 2047e89ee7fSMarek Sokolowski return ~(*Result); 2057e89ee7fSMarek Sokolowski } 2067e89ee7fSMarek Sokolowski 2077e89ee7fSMarek Sokolowski case Kind::Int: 20807bc04ffSZachary Turner return RCInt(read()); 2097e89ee7fSMarek Sokolowski 2107e89ee7fSMarek Sokolowski case Kind::LeftParen: { 2117e89ee7fSMarek Sokolowski consume(); 2127e89ee7fSMarek Sokolowski ASSIGN_OR_RETURN(Result, parseIntExpr1()); 2137e89ee7fSMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::RightParen)); 2147e89ee7fSMarek Sokolowski return *Result; 2157e89ee7fSMarek Sokolowski } 2167e89ee7fSMarek Sokolowski 217d0afe724SMartin Storsjo case Kind::Identifier: { 218d0afe724SMartin Storsjo if (!read().value().equals_lower("not")) 219d0afe724SMartin Storsjo return getExpectedError(ErrorMsg, true); 220d0afe724SMartin Storsjo ASSIGN_OR_RETURN(Result, parseIntExpr2()); 221d0afe724SMartin Storsjo return IntWithNotMask(0, (*Result).getValue()); 222d0afe724SMartin Storsjo } 223d0afe724SMartin Storsjo 2247e89ee7fSMarek Sokolowski default: 2257e89ee7fSMarek Sokolowski return getExpectedError(ErrorMsg); 2267e89ee7fSMarek Sokolowski } 2275cd3d5c8SMarek Sokolowski } 2285cd3d5c8SMarek Sokolowski 2295cd3d5c8SMarek Sokolowski Expected<StringRef> RCParser::readString() { 2305cd3d5c8SMarek Sokolowski if (!isNextTokenKind(Kind::String)) 2315cd3d5c8SMarek Sokolowski return getExpectedError("string"); 2325cd3d5c8SMarek Sokolowski return read().value(); 2335cd3d5c8SMarek Sokolowski } 2345cd3d5c8SMarek Sokolowski 2354021cee9SMartin Storsjo Expected<StringRef> RCParser::readFilename() { 2364021cee9SMartin Storsjo if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier)) 2374021cee9SMartin Storsjo return getExpectedError("string"); 2384021cee9SMartin Storsjo return read().value(); 2394021cee9SMartin Storsjo } 2404021cee9SMartin Storsjo 2415cd3d5c8SMarek Sokolowski Expected<StringRef> RCParser::readIdentifier() { 2425cd3d5c8SMarek Sokolowski if (!isNextTokenKind(Kind::Identifier)) 2435cd3d5c8SMarek Sokolowski return getExpectedError("identifier"); 2445cd3d5c8SMarek Sokolowski return read().value(); 2455cd3d5c8SMarek Sokolowski } 2465cd3d5c8SMarek Sokolowski 2477f110527SMarek Sokolowski Expected<IntOrString> RCParser::readIntOrString() { 2487f110527SMarek Sokolowski if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String)) 2497f110527SMarek Sokolowski return getExpectedError("int or string"); 2507f110527SMarek Sokolowski return IntOrString(read()); 2517f110527SMarek Sokolowski } 2527f110527SMarek Sokolowski 2535cd3d5c8SMarek Sokolowski Expected<IntOrString> RCParser::readTypeOrName() { 2545cd3d5c8SMarek Sokolowski // We suggest that the correct resource name or type should be either an 2555cd3d5c8SMarek Sokolowski // identifier or an integer. The original RC tool is much more liberal. 2565cd3d5c8SMarek Sokolowski if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int)) 2575cd3d5c8SMarek Sokolowski return getExpectedError("int or identifier"); 2587f110527SMarek Sokolowski return IntOrString(read()); 2595cd3d5c8SMarek Sokolowski } 2605cd3d5c8SMarek Sokolowski 2615cd3d5c8SMarek Sokolowski Error RCParser::consumeType(Kind TokenKind) { 2625cd3d5c8SMarek Sokolowski if (isNextTokenKind(TokenKind)) { 2635cd3d5c8SMarek Sokolowski consume(); 2645cd3d5c8SMarek Sokolowski return Error::success(); 2655cd3d5c8SMarek Sokolowski } 2665cd3d5c8SMarek Sokolowski 2675cd3d5c8SMarek Sokolowski switch (TokenKind) { 2685cd3d5c8SMarek Sokolowski #define TOKEN(TokenName) \ 2695cd3d5c8SMarek Sokolowski case Kind::TokenName: \ 2705cd3d5c8SMarek Sokolowski return getExpectedError(#TokenName); 2715cd3d5c8SMarek Sokolowski #define SHORT_TOKEN(TokenName, TokenCh) \ 2725cd3d5c8SMarek Sokolowski case Kind::TokenName: \ 2735cd3d5c8SMarek Sokolowski return getExpectedError(#TokenCh); 274b961d29bSDavid Blaikie #include "ResourceScriptTokenList.def" 2755cd3d5c8SMarek Sokolowski } 2765cd3d5c8SMarek Sokolowski 2775cd3d5c8SMarek Sokolowski llvm_unreachable("All case options exhausted."); 2785cd3d5c8SMarek Sokolowski } 2795cd3d5c8SMarek Sokolowski 2805cd3d5c8SMarek Sokolowski bool RCParser::consumeOptionalType(Kind TokenKind) { 2815cd3d5c8SMarek Sokolowski if (isNextTokenKind(TokenKind)) { 2825cd3d5c8SMarek Sokolowski consume(); 2835cd3d5c8SMarek Sokolowski return true; 2845cd3d5c8SMarek Sokolowski } 2855cd3d5c8SMarek Sokolowski 2865cd3d5c8SMarek Sokolowski return false; 2875cd3d5c8SMarek Sokolowski } 2885cd3d5c8SMarek Sokolowski 28907bc04ffSZachary Turner Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount, 29007bc04ffSZachary Turner size_t MaxCount) { 2915cd3d5c8SMarek Sokolowski assert(MinCount <= MaxCount); 2925cd3d5c8SMarek Sokolowski 29307bc04ffSZachary Turner SmallVector<RCInt, 8> Result; 2945cd3d5c8SMarek Sokolowski 2955cd3d5c8SMarek Sokolowski auto FailureHandler = 29607bc04ffSZachary Turner [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> { 2975cd3d5c8SMarek Sokolowski if (Result.size() < MinCount) 2985cd3d5c8SMarek Sokolowski return std::move(Err); 2995cd3d5c8SMarek Sokolowski consumeError(std::move(Err)); 3005cd3d5c8SMarek Sokolowski return Result; 3015cd3d5c8SMarek Sokolowski }; 3025cd3d5c8SMarek Sokolowski 3035cd3d5c8SMarek Sokolowski for (size_t i = 0; i < MaxCount; ++i) { 3045cd3d5c8SMarek Sokolowski // Try to read a comma unless we read the first token. 3055cd3d5c8SMarek Sokolowski // Sometimes RC tool requires them and sometimes not. We decide to 3065cd3d5c8SMarek Sokolowski // always require them. 3075cd3d5c8SMarek Sokolowski if (i >= 1) { 3085cd3d5c8SMarek Sokolowski if (auto CommaError = consumeType(Kind::Comma)) 3095cd3d5c8SMarek Sokolowski return FailureHandler(std::move(CommaError)); 3105cd3d5c8SMarek Sokolowski } 3115cd3d5c8SMarek Sokolowski 3125cd3d5c8SMarek Sokolowski if (auto IntResult = readInt()) 3135cd3d5c8SMarek Sokolowski Result.push_back(*IntResult); 3145cd3d5c8SMarek Sokolowski else 3155cd3d5c8SMarek Sokolowski return FailureHandler(IntResult.takeError()); 3165cd3d5c8SMarek Sokolowski } 3175cd3d5c8SMarek Sokolowski 3185cd3d5c8SMarek Sokolowski return std::move(Result); 3195cd3d5c8SMarek Sokolowski } 3205cd3d5c8SMarek Sokolowski 321c75a087cSMarek Sokolowski Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc, 322c75a087cSMarek Sokolowski ArrayRef<uint32_t> FlagValues) { 3237f110527SMarek Sokolowski assert(!FlagDesc.empty()); 324c75a087cSMarek Sokolowski assert(FlagDesc.size() == FlagValues.size()); 3257f110527SMarek Sokolowski 3267f110527SMarek Sokolowski uint32_t Result = 0; 3277f110527SMarek Sokolowski while (isNextTokenKind(Kind::Comma)) { 3287f110527SMarek Sokolowski consume(); 3297f110527SMarek Sokolowski ASSIGN_OR_RETURN(FlagResult, readIdentifier()); 3307f110527SMarek Sokolowski bool FoundFlag = false; 3317f110527SMarek Sokolowski 3327f110527SMarek Sokolowski for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { 3337f110527SMarek Sokolowski if (!FlagResult->equals_lower(FlagDesc[FlagId])) 3347f110527SMarek Sokolowski continue; 3357f110527SMarek Sokolowski 336c75a087cSMarek Sokolowski Result |= FlagValues[FlagId]; 3377f110527SMarek Sokolowski FoundFlag = true; 3387f110527SMarek Sokolowski break; 3397f110527SMarek Sokolowski } 3407f110527SMarek Sokolowski 3417f110527SMarek Sokolowski if (!FoundFlag) 3427f110527SMarek Sokolowski return getExpectedError(join(FlagDesc, "/"), true); 3437f110527SMarek Sokolowski } 3447f110527SMarek Sokolowski 3457f110527SMarek Sokolowski return Result; 3467f110527SMarek Sokolowski } 3477f110527SMarek Sokolowski 34811adbacaSMartin Storsjo uint16_t RCParser::parseMemoryFlags(uint16_t Flags) { 34911adbacaSMartin Storsjo while (!isEof()) { 35011adbacaSMartin Storsjo const RCToken &Token = look(); 35111adbacaSMartin Storsjo if (Token.kind() != Kind::Identifier) 35211adbacaSMartin Storsjo return Flags; 35311adbacaSMartin Storsjo const StringRef Ident = Token.value(); 35411adbacaSMartin Storsjo if (Ident.equals_lower("PRELOAD")) 35511adbacaSMartin Storsjo Flags |= MfPreload; 35611adbacaSMartin Storsjo else if (Ident.equals_lower("LOADONCALL")) 35711adbacaSMartin Storsjo Flags &= ~MfPreload; 35811adbacaSMartin Storsjo else if (Ident.equals_lower("FIXED")) 35911adbacaSMartin Storsjo Flags &= ~(MfMoveable | MfDiscardable); 36011adbacaSMartin Storsjo else if (Ident.equals_lower("MOVEABLE")) 36111adbacaSMartin Storsjo Flags |= MfMoveable; 36211adbacaSMartin Storsjo else if (Ident.equals_lower("DISCARDABLE")) 36311adbacaSMartin Storsjo Flags |= MfDiscardable | MfMoveable | MfPure; 36411adbacaSMartin Storsjo else if (Ident.equals_lower("PURE")) 36511adbacaSMartin Storsjo Flags |= MfPure; 36611adbacaSMartin Storsjo else if (Ident.equals_lower("IMPURE")) 36711adbacaSMartin Storsjo Flags &= ~(MfPure | MfDiscardable); 36811adbacaSMartin Storsjo else if (Ident.equals_lower("SHARED")) 36911adbacaSMartin Storsjo Flags |= MfPure; 37011adbacaSMartin Storsjo else if (Ident.equals_lower("NONSHARED")) 37111adbacaSMartin Storsjo Flags &= ~(MfPure | MfDiscardable); 37211adbacaSMartin Storsjo else 37311adbacaSMartin Storsjo return Flags; 37411adbacaSMartin Storsjo consume(); 37511adbacaSMartin Storsjo } 37611adbacaSMartin Storsjo return Flags; 37711adbacaSMartin Storsjo } 37811adbacaSMartin Storsjo 379420090afSZachary Turner Expected<OptionalStmtList> 380420090afSZachary Turner RCParser::parseOptionalStatements(OptStmtType StmtsType) { 3815cd3d5c8SMarek Sokolowski OptionalStmtList Result; 3825cd3d5c8SMarek Sokolowski 3835cd3d5c8SMarek Sokolowski // The last statement is always followed by the start of the block. 3845cd3d5c8SMarek Sokolowski while (!isNextTokenKind(Kind::BlockBegin)) { 385420090afSZachary Turner ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType)); 3865cd3d5c8SMarek Sokolowski Result.addStmt(std::move(*SingleParse)); 3875cd3d5c8SMarek Sokolowski } 3885cd3d5c8SMarek Sokolowski 3895cd3d5c8SMarek Sokolowski return std::move(Result); 3905cd3d5c8SMarek Sokolowski } 3915cd3d5c8SMarek Sokolowski 3925cd3d5c8SMarek Sokolowski Expected<std::unique_ptr<OptionalStmt>> 393420090afSZachary Turner RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) { 3945cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(TypeToken, readIdentifier()); 3955cd3d5c8SMarek Sokolowski if (TypeToken->equals_lower("CHARACTERISTICS")) 3965cd3d5c8SMarek Sokolowski return parseCharacteristicsStmt(); 3974ac54d93SMarek Sokolowski if (TypeToken->equals_lower("LANGUAGE")) 3985cd3d5c8SMarek Sokolowski return parseLanguageStmt(); 3994ac54d93SMarek Sokolowski if (TypeToken->equals_lower("VERSION")) 4005cd3d5c8SMarek Sokolowski return parseVersionStmt(); 4014ac54d93SMarek Sokolowski 402420090afSZachary Turner if (StmtsType != OptStmtType::BasicStmt) { 4034ac54d93SMarek Sokolowski if (TypeToken->equals_lower("CAPTION")) 4044ac54d93SMarek Sokolowski return parseCaptionStmt(); 405e241ce6fSMartin Storsjo if (TypeToken->equals_lower("CLASS")) 406e241ce6fSMartin Storsjo return parseClassStmt(); 407a876b5c0SMartin Storsjo if (TypeToken->equals_lower("EXSTYLE")) 408a876b5c0SMartin Storsjo return parseExStyleStmt(); 4094ac54d93SMarek Sokolowski if (TypeToken->equals_lower("FONT")) 410420090afSZachary Turner return parseFontStmt(StmtsType); 4114ac54d93SMarek Sokolowski if (TypeToken->equals_lower("STYLE")) 4124ac54d93SMarek Sokolowski return parseStyleStmt(); 4134ac54d93SMarek Sokolowski } 4144ac54d93SMarek Sokolowski 4155cd3d5c8SMarek Sokolowski return getExpectedError("optional statement type, BEGIN or '{'", 4165cd3d5c8SMarek Sokolowski /* IsAlreadyRead = */ true); 4175cd3d5c8SMarek Sokolowski } 4185cd3d5c8SMarek Sokolowski 4195cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseLanguageResource() { 4205cd3d5c8SMarek Sokolowski // Read LANGUAGE as an optional statement. If it's read correctly, we can 4215cd3d5c8SMarek Sokolowski // upcast it to RCResource. 4225cd3d5c8SMarek Sokolowski return parseLanguageStmt(); 4235cd3d5c8SMarek Sokolowski } 4245cd3d5c8SMarek Sokolowski 4257f110527SMarek Sokolowski RCParser::ParseType RCParser::parseAcceleratorsResource() { 42611adbacaSMartin Storsjo uint16_t MemoryFlags = 42711adbacaSMartin Storsjo parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags()); 4287f110527SMarek Sokolowski ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 4297f110527SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 4307f110527SMarek Sokolowski 431*0eaee545SJonas Devlieghere auto Accels = std::make_unique<AcceleratorsResource>( 43211adbacaSMartin Storsjo std::move(*OptStatements), MemoryFlags); 4337f110527SMarek Sokolowski 4347f110527SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 4357f110527SMarek Sokolowski ASSIGN_OR_RETURN(EventResult, readIntOrString()); 4367f110527SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 4377f110527SMarek Sokolowski ASSIGN_OR_RETURN(IDResult, readInt()); 438c75a087cSMarek Sokolowski ASSIGN_OR_RETURN( 439c75a087cSMarek Sokolowski FlagsResult, 440c75a087cSMarek Sokolowski parseFlags(AcceleratorsResource::Accelerator::OptionsStr, 441c75a087cSMarek Sokolowski AcceleratorsResource::Accelerator::OptionsFlags)); 4427f110527SMarek Sokolowski Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult); 4437f110527SMarek Sokolowski } 4447f110527SMarek Sokolowski 4457f110527SMarek Sokolowski return std::move(Accels); 4467f110527SMarek Sokolowski } 4477f110527SMarek Sokolowski 44872aa937eSMarek Sokolowski RCParser::ParseType RCParser::parseCursorResource() { 44911adbacaSMartin Storsjo uint16_t MemoryFlags = 45011adbacaSMartin Storsjo parseMemoryFlags(CursorResource::getDefaultMemoryFlags()); 4514021cee9SMartin Storsjo ASSIGN_OR_RETURN(Arg, readFilename()); 452*0eaee545SJonas Devlieghere return std::make_unique<CursorResource>(*Arg, MemoryFlags); 45372aa937eSMarek Sokolowski } 45472aa937eSMarek Sokolowski 4554ac54d93SMarek Sokolowski RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { 45611adbacaSMartin Storsjo uint16_t MemoryFlags = 45711adbacaSMartin Storsjo parseMemoryFlags(DialogResource::getDefaultMemoryFlags()); 4584ac54d93SMarek Sokolowski // Dialog resources have the following format of the arguments: 4594ac54d93SMarek Sokolowski // DIALOG: x, y, width, height [opt stmts...] {controls...} 4604ac54d93SMarek Sokolowski // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...} 4614ac54d93SMarek Sokolowski // These are very similar, so we parse them together. 4624ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4)); 4634ac54d93SMarek Sokolowski 4644ac54d93SMarek Sokolowski uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0. 4654ac54d93SMarek Sokolowski if (IsExtended && consumeOptionalType(Kind::Comma)) { 4664ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(HelpIDResult, readInt()); 4674ac54d93SMarek Sokolowski HelpID = *HelpIDResult; 4684ac54d93SMarek Sokolowski } 4694ac54d93SMarek Sokolowski 470420090afSZachary Turner ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements( 471420090afSZachary Turner IsExtended ? OptStmtType::DialogExStmt 472420090afSZachary Turner : OptStmtType::DialogStmt)); 4734ac54d93SMarek Sokolowski 4744ac54d93SMarek Sokolowski assert(isNextTokenKind(Kind::BlockBegin) && 4754ac54d93SMarek Sokolowski "parseOptionalStatements, when successful, halts on BlockBegin."); 4764ac54d93SMarek Sokolowski consume(); 4774ac54d93SMarek Sokolowski 478*0eaee545SJonas Devlieghere auto Dialog = std::make_unique<DialogResource>( 4794ac54d93SMarek Sokolowski (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], 48011adbacaSMartin Storsjo HelpID, std::move(*OptStatements), IsExtended, MemoryFlags); 4814ac54d93SMarek Sokolowski 4824ac54d93SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 4834ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(ControlDefResult, parseControl()); 4844ac54d93SMarek Sokolowski Dialog->addControl(std::move(*ControlDefResult)); 4854ac54d93SMarek Sokolowski } 4864ac54d93SMarek Sokolowski 4874ac54d93SMarek Sokolowski return std::move(Dialog); 4884ac54d93SMarek Sokolowski } 4894ac54d93SMarek Sokolowski 490b5f39a05SMarek Sokolowski RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) { 49111adbacaSMartin Storsjo uint16_t MemoryFlags = 49211adbacaSMartin Storsjo parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags()); 493b5f39a05SMarek Sokolowski if (isEof()) 494b5f39a05SMarek Sokolowski return getExpectedError("filename, '{' or BEGIN"); 495b5f39a05SMarek Sokolowski 496b5f39a05SMarek Sokolowski // Check if this is a file resource. 4974021cee9SMartin Storsjo switch (look().kind()) { 4984021cee9SMartin Storsjo case Kind::String: 4994021cee9SMartin Storsjo case Kind::Identifier: 500*0eaee545SJonas Devlieghere return std::make_unique<UserDefinedResource>(Type, read().value(), 50111adbacaSMartin Storsjo MemoryFlags); 5024021cee9SMartin Storsjo default: 5034021cee9SMartin Storsjo break; 5044021cee9SMartin Storsjo } 505b5f39a05SMarek Sokolowski 506b5f39a05SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 507b5f39a05SMarek Sokolowski std::vector<IntOrString> Data; 508b5f39a05SMarek Sokolowski 509b5f39a05SMarek Sokolowski // Consume comma before each consecutive token except the first one. 510b5f39a05SMarek Sokolowski bool ConsumeComma = false; 511b5f39a05SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 512b5f39a05SMarek Sokolowski if (ConsumeComma) 513b5f39a05SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 514b5f39a05SMarek Sokolowski ConsumeComma = true; 515b5f39a05SMarek Sokolowski 516b5f39a05SMarek Sokolowski ASSIGN_OR_RETURN(Item, readIntOrString()); 517b5f39a05SMarek Sokolowski Data.push_back(*Item); 518b5f39a05SMarek Sokolowski } 519b5f39a05SMarek Sokolowski 520*0eaee545SJonas Devlieghere return std::make_unique<UserDefinedResource>(Type, std::move(Data), 52111adbacaSMartin Storsjo MemoryFlags); 522b5f39a05SMarek Sokolowski } 523b5f39a05SMarek Sokolowski 524fb74cb1eSMarek Sokolowski RCParser::ParseType RCParser::parseVersionInfoResource() { 52511adbacaSMartin Storsjo uint16_t MemoryFlags = 52611adbacaSMartin Storsjo parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags()); 527fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed()); 528fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef())); 529*0eaee545SJonas Devlieghere return std::make_unique<VersionInfoResource>( 53011adbacaSMartin Storsjo std::move(**BlockResult), std::move(*FixedResult), MemoryFlags); 531fb74cb1eSMarek Sokolowski } 532fb74cb1eSMarek Sokolowski 5334ac54d93SMarek Sokolowski Expected<Control> RCParser::parseControl() { 5344ac54d93SMarek Sokolowski // Each control definition (except CONTROL) follows one of the schemes below 5354ac54d93SMarek Sokolowski // depending on the control class: 5364ac54d93SMarek Sokolowski // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID] 5374ac54d93SMarek Sokolowski // [class] id, x, y, width, height [, style] [, exstyle] [, helpID] 5384ac54d93SMarek Sokolowski // Note that control ids must be integers. 5397f7745c0SMarek Sokolowski // Text might be either a string or an integer pointing to resource ID. 5404ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(ClassResult, readIdentifier()); 54175fa173eSMarek Sokolowski std::string ClassUpper = ClassResult->upper(); 5427f7745c0SMarek Sokolowski auto CtlInfo = Control::SupportedCtls.find(ClassUpper); 5437f7745c0SMarek Sokolowski if (CtlInfo == Control::SupportedCtls.end()) 5444ac54d93SMarek Sokolowski return getExpectedError("control type, END or '}'", true); 5454ac54d93SMarek Sokolowski 5464ac54d93SMarek Sokolowski // Read caption if necessary. 5477f7745c0SMarek Sokolowski IntOrString Caption{StringRef()}; 5487f7745c0SMarek Sokolowski if (CtlInfo->getValue().HasTitle) { 5497f7745c0SMarek Sokolowski ASSIGN_OR_RETURN(CaptionResult, readIntOrString()); 5504ac54d93SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 5514ac54d93SMarek Sokolowski Caption = *CaptionResult; 5524ac54d93SMarek Sokolowski } 5534ac54d93SMarek Sokolowski 554818bd568SMartin Storsjo ASSIGN_OR_RETURN(ID, readInt()); 555818bd568SMartin Storsjo RETURN_IF_ERROR(consumeType(Kind::Comma)); 5564ac54d93SMarek Sokolowski 557818bd568SMartin Storsjo IntOrString Class; 558d0afe724SMartin Storsjo Optional<IntWithNotMask> Style; 559818bd568SMartin Storsjo if (ClassUpper == "CONTROL") { 560818bd568SMartin Storsjo // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID] 561818bd568SMartin Storsjo ASSIGN_OR_RETURN(ClassStr, readString()); 562818bd568SMartin Storsjo RETURN_IF_ERROR(consumeType(Kind::Comma)); 563818bd568SMartin Storsjo Class = *ClassStr; 564d0afe724SMartin Storsjo ASSIGN_OR_RETURN(StyleVal, parseIntExpr1()); 565818bd568SMartin Storsjo RETURN_IF_ERROR(consumeType(Kind::Comma)); 566818bd568SMartin Storsjo Style = *StyleVal; 567818bd568SMartin Storsjo } else { 568818bd568SMartin Storsjo Class = CtlInfo->getValue().CtlClass; 569818bd568SMartin Storsjo } 5704ac54d93SMarek Sokolowski 571818bd568SMartin Storsjo // x, y, width, height 572818bd568SMartin Storsjo ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4)); 573818bd568SMartin Storsjo 574818bd568SMartin Storsjo if (ClassUpper != "CONTROL") { 575818bd568SMartin Storsjo if (consumeOptionalType(Kind::Comma)) { 576d0afe724SMartin Storsjo ASSIGN_OR_RETURN(Val, parseIntExpr1()); 577818bd568SMartin Storsjo Style = *Val; 578818bd568SMartin Storsjo } 579818bd568SMartin Storsjo } 580818bd568SMartin Storsjo 581818bd568SMartin Storsjo Optional<uint32_t> ExStyle; 582818bd568SMartin Storsjo if (consumeOptionalType(Kind::Comma)) { 583818bd568SMartin Storsjo ASSIGN_OR_RETURN(Val, readInt()); 584818bd568SMartin Storsjo ExStyle = *Val; 585818bd568SMartin Storsjo } 586818bd568SMartin Storsjo Optional<uint32_t> HelpID; 587818bd568SMartin Storsjo if (consumeOptionalType(Kind::Comma)) { 588818bd568SMartin Storsjo ASSIGN_OR_RETURN(Val, readInt()); 589818bd568SMartin Storsjo HelpID = *Val; 590818bd568SMartin Storsjo } 591818bd568SMartin Storsjo 592818bd568SMartin Storsjo return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1], 593818bd568SMartin Storsjo (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class); 5944ac54d93SMarek Sokolowski } 5954ac54d93SMarek Sokolowski 596577b9817SMartin Storsjo RCParser::ParseType RCParser::parseBitmapResource() { 59711adbacaSMartin Storsjo uint16_t MemoryFlags = 59811adbacaSMartin Storsjo parseMemoryFlags(BitmapResource::getDefaultMemoryFlags()); 5994021cee9SMartin Storsjo ASSIGN_OR_RETURN(Arg, readFilename()); 600*0eaee545SJonas Devlieghere return std::make_unique<BitmapResource>(*Arg, MemoryFlags); 601577b9817SMartin Storsjo } 602577b9817SMartin Storsjo 6035cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseIconResource() { 60411adbacaSMartin Storsjo uint16_t MemoryFlags = 60511adbacaSMartin Storsjo parseMemoryFlags(IconResource::getDefaultMemoryFlags()); 6064021cee9SMartin Storsjo ASSIGN_OR_RETURN(Arg, readFilename()); 607*0eaee545SJonas Devlieghere return std::make_unique<IconResource>(*Arg, MemoryFlags); 6085cd3d5c8SMarek Sokolowski } 6095cd3d5c8SMarek Sokolowski 61072aa937eSMarek Sokolowski RCParser::ParseType RCParser::parseHTMLResource() { 61111adbacaSMartin Storsjo uint16_t MemoryFlags = 61211adbacaSMartin Storsjo parseMemoryFlags(HTMLResource::getDefaultMemoryFlags()); 6134021cee9SMartin Storsjo ASSIGN_OR_RETURN(Arg, readFilename()); 614*0eaee545SJonas Devlieghere return std::make_unique<HTMLResource>(*Arg, MemoryFlags); 61572aa937eSMarek Sokolowski } 61672aa937eSMarek Sokolowski 61799ecb0ebSMarek Sokolowski RCParser::ParseType RCParser::parseMenuResource() { 61811adbacaSMartin Storsjo uint16_t MemoryFlags = 61911adbacaSMartin Storsjo parseMemoryFlags(MenuResource::getDefaultMemoryFlags()); 62099ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 62199ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(Items, parseMenuItemsList()); 622*0eaee545SJonas Devlieghere return std::make_unique<MenuResource>(std::move(*OptStatements), 62311adbacaSMartin Storsjo std::move(*Items), MemoryFlags); 62499ecb0ebSMarek Sokolowski } 62599ecb0ebSMarek Sokolowski 62699ecb0ebSMarek Sokolowski Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { 62799ecb0ebSMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 62899ecb0ebSMarek Sokolowski 62999ecb0ebSMarek Sokolowski MenuDefinitionList List; 63099ecb0ebSMarek Sokolowski 63199ecb0ebSMarek Sokolowski // Read a set of items. Each item is of one of three kinds: 63299ecb0ebSMarek Sokolowski // MENUITEM SEPARATOR 63399ecb0ebSMarek Sokolowski // MENUITEM caption:String, result:Int [, menu flags]... 63499ecb0ebSMarek Sokolowski // POPUP caption:String [, menu flags]... { items... } 63599ecb0ebSMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 63699ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier()); 63799ecb0ebSMarek Sokolowski 63899ecb0ebSMarek Sokolowski bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM"); 63999ecb0ebSMarek Sokolowski bool IsPopup = ItemTypeResult->equals_lower("POPUP"); 64099ecb0ebSMarek Sokolowski if (!IsMenuItem && !IsPopup) 64199ecb0ebSMarek Sokolowski return getExpectedError("MENUITEM, POPUP, END or '}'", true); 64299ecb0ebSMarek Sokolowski 64399ecb0ebSMarek Sokolowski if (IsMenuItem && isNextTokenKind(Kind::Identifier)) { 64499ecb0ebSMarek Sokolowski // Now, expecting SEPARATOR. 64599ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(SeparatorResult, readIdentifier()); 64699ecb0ebSMarek Sokolowski if (SeparatorResult->equals_lower("SEPARATOR")) { 647*0eaee545SJonas Devlieghere List.addDefinition(std::make_unique<MenuSeparator>()); 64899ecb0ebSMarek Sokolowski continue; 64999ecb0ebSMarek Sokolowski } 65099ecb0ebSMarek Sokolowski 65199ecb0ebSMarek Sokolowski return getExpectedError("SEPARATOR or string", true); 65299ecb0ebSMarek Sokolowski } 65399ecb0ebSMarek Sokolowski 65499ecb0ebSMarek Sokolowski // Not a separator. Read the caption. 65599ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(CaptionResult, readString()); 65699ecb0ebSMarek Sokolowski 65799ecb0ebSMarek Sokolowski // If MENUITEM, expect also a comma and an integer. 65899ecb0ebSMarek Sokolowski uint32_t MenuResult = -1; 65999ecb0ebSMarek Sokolowski 66099ecb0ebSMarek Sokolowski if (IsMenuItem) { 66199ecb0ebSMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 66299ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(IntResult, readInt()); 66399ecb0ebSMarek Sokolowski MenuResult = *IntResult; 66499ecb0ebSMarek Sokolowski } 66599ecb0ebSMarek Sokolowski 666c75a087cSMarek Sokolowski ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr, 667c75a087cSMarek Sokolowski MenuDefinition::OptionsFlags)); 66899ecb0ebSMarek Sokolowski 66999ecb0ebSMarek Sokolowski if (IsPopup) { 67099ecb0ebSMarek Sokolowski // If POPUP, read submenu items recursively. 67199ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList()); 672*0eaee545SJonas Devlieghere List.addDefinition(std::make_unique<PopupItem>( 6734a765da3SMarek Sokolowski *CaptionResult, *FlagsResult, std::move(*SubMenuResult))); 67499ecb0ebSMarek Sokolowski continue; 67599ecb0ebSMarek Sokolowski } 67699ecb0ebSMarek Sokolowski 67799ecb0ebSMarek Sokolowski assert(IsMenuItem); 67899ecb0ebSMarek Sokolowski List.addDefinition( 679*0eaee545SJonas Devlieghere std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult)); 68099ecb0ebSMarek Sokolowski } 68199ecb0ebSMarek Sokolowski 68299ecb0ebSMarek Sokolowski return std::move(List); 68399ecb0ebSMarek Sokolowski } 68499ecb0ebSMarek Sokolowski 6855cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseStringTableResource() { 68611adbacaSMartin Storsjo uint16_t MemoryFlags = 68711adbacaSMartin Storsjo parseMemoryFlags(StringTableResource::getDefaultMemoryFlags()); 6885cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 6895cd3d5c8SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 6905cd3d5c8SMarek Sokolowski 691*0eaee545SJonas Devlieghere auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements), 69211adbacaSMartin Storsjo MemoryFlags); 6935cd3d5c8SMarek Sokolowski 6945cd3d5c8SMarek Sokolowski // Read strings until we reach the end of the block. 6955cd3d5c8SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 6965cd3d5c8SMarek Sokolowski // Each definition consists of string's ID (an integer) and a string. 6975cd3d5c8SMarek Sokolowski // Some examples in documentation suggest that there might be a comma in 6985cd3d5c8SMarek Sokolowski // between, however we strictly adhere to the single statement definition. 6995cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(IDResult, readInt()); 7009410276cSMartin Storsjo consumeOptionalType(Kind::Comma); 7015cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(StrResult, readString()); 7025cd3d5c8SMarek Sokolowski Table->addString(*IDResult, *StrResult); 7035cd3d5c8SMarek Sokolowski } 7045cd3d5c8SMarek Sokolowski 7055cd3d5c8SMarek Sokolowski return std::move(Table); 7065cd3d5c8SMarek Sokolowski } 7075cd3d5c8SMarek Sokolowski 708fb74cb1eSMarek Sokolowski Expected<std::unique_ptr<VersionInfoBlock>> 709fb74cb1eSMarek Sokolowski RCParser::parseVersionInfoBlockContents(StringRef BlockName) { 710fb74cb1eSMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 711fb74cb1eSMarek Sokolowski 712*0eaee545SJonas Devlieghere auto Contents = std::make_unique<VersionInfoBlock>(BlockName); 713fb74cb1eSMarek Sokolowski 714fb74cb1eSMarek Sokolowski while (!isNextTokenKind(Kind::BlockEnd)) { 715fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt()); 716fb74cb1eSMarek Sokolowski Contents->addStmt(std::move(*Stmt)); 717fb74cb1eSMarek Sokolowski } 718fb74cb1eSMarek Sokolowski 719fb74cb1eSMarek Sokolowski consume(); // Consume BlockEnd. 720fb74cb1eSMarek Sokolowski 721fb74cb1eSMarek Sokolowski return std::move(Contents); 722fb74cb1eSMarek Sokolowski } 723fb74cb1eSMarek Sokolowski 724fb74cb1eSMarek Sokolowski Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() { 725fb74cb1eSMarek Sokolowski // Expect either BLOCK or VALUE, then a name or a key (a string). 726fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(TypeResult, readIdentifier()); 727fb74cb1eSMarek Sokolowski 728fb74cb1eSMarek Sokolowski if (TypeResult->equals_lower("BLOCK")) { 729fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(NameResult, readString()); 730fb74cb1eSMarek Sokolowski return parseVersionInfoBlockContents(*NameResult); 731fb74cb1eSMarek Sokolowski } 732fb74cb1eSMarek Sokolowski 733fb74cb1eSMarek Sokolowski if (TypeResult->equals_lower("VALUE")) { 734fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(KeyResult, readString()); 73507bc04ffSZachary Turner // Read a non-empty list of strings and/or ints, each 73607bc04ffSZachary Turner // possibly preceded by a comma. Unfortunately, the tool behavior depends 73707bc04ffSZachary Turner // on them existing or not, so we need to memorize where we found them. 738fb74cb1eSMarek Sokolowski std::vector<IntOrString> Values; 73907bc04ffSZachary Turner std::vector<bool> PrecedingCommas; 74007bc04ffSZachary Turner RETURN_IF_ERROR(consumeType(Kind::Comma)); 74107bc04ffSZachary Turner while (!isNextTokenKind(Kind::Identifier) && 74207bc04ffSZachary Turner !isNextTokenKind(Kind::BlockEnd)) { 74307bc04ffSZachary Turner // Try to eat a comma if it's not the first statement. 74407bc04ffSZachary Turner bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma); 745fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(ValueResult, readIntOrString()); 746fb74cb1eSMarek Sokolowski Values.push_back(*ValueResult); 74707bc04ffSZachary Turner PrecedingCommas.push_back(HadComma); 748fb74cb1eSMarek Sokolowski } 749*0eaee545SJonas Devlieghere return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values), 75007bc04ffSZachary Turner std::move(PrecedingCommas)); 751fb74cb1eSMarek Sokolowski } 752fb74cb1eSMarek Sokolowski 753fb74cb1eSMarek Sokolowski return getExpectedError("BLOCK or VALUE", true); 754fb74cb1eSMarek Sokolowski } 755fb74cb1eSMarek Sokolowski 756fb74cb1eSMarek Sokolowski Expected<VersionInfoResource::VersionInfoFixed> 757fb74cb1eSMarek Sokolowski RCParser::parseVersionInfoFixed() { 758fb74cb1eSMarek Sokolowski using RetType = VersionInfoResource::VersionInfoFixed; 759fb74cb1eSMarek Sokolowski RetType Result; 760fb74cb1eSMarek Sokolowski 761fb74cb1eSMarek Sokolowski // Read until the beginning of the block. 762fb74cb1eSMarek Sokolowski while (!isNextTokenKind(Kind::BlockBegin)) { 763fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(TypeResult, readIdentifier()); 764fb74cb1eSMarek Sokolowski auto FixedType = RetType::getFixedType(*TypeResult); 765fb74cb1eSMarek Sokolowski 766fb74cb1eSMarek Sokolowski if (!RetType::isTypeSupported(FixedType)) 767fb74cb1eSMarek Sokolowski return getExpectedError("fixed VERSIONINFO statement type", true); 768fb74cb1eSMarek Sokolowski if (Result.IsTypePresent[FixedType]) 769fb74cb1eSMarek Sokolowski return getExpectedError("yet unread fixed VERSIONINFO statement type", 770fb74cb1eSMarek Sokolowski true); 771fb74cb1eSMarek Sokolowski 772fb74cb1eSMarek Sokolowski // VERSION variations take multiple integers. 773fb74cb1eSMarek Sokolowski size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1; 774fb74cb1eSMarek Sokolowski ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts)); 77507bc04ffSZachary Turner SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end()); 77607bc04ffSZachary Turner Result.setValue(FixedType, ArgInts); 777fb74cb1eSMarek Sokolowski } 778fb74cb1eSMarek Sokolowski 779fb74cb1eSMarek Sokolowski return Result; 780fb74cb1eSMarek Sokolowski } 781fb74cb1eSMarek Sokolowski 7825cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseLanguageStmt() { 7835cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2)); 784*0eaee545SJonas Devlieghere return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]); 7855cd3d5c8SMarek Sokolowski } 7865cd3d5c8SMarek Sokolowski 7875cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() { 7885cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readInt()); 789*0eaee545SJonas Devlieghere return std::make_unique<CharacteristicsStmt>(*Arg); 7905cd3d5c8SMarek Sokolowski } 7915cd3d5c8SMarek Sokolowski 7925cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseVersionStmt() { 7935cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readInt()); 794*0eaee545SJonas Devlieghere return std::make_unique<VersionStmt>(*Arg); 7955cd3d5c8SMarek Sokolowski } 7965cd3d5c8SMarek Sokolowski 7974ac54d93SMarek Sokolowski RCParser::ParseOptionType RCParser::parseCaptionStmt() { 7984ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readString()); 799*0eaee545SJonas Devlieghere return std::make_unique<CaptionStmt>(*Arg); 8004ac54d93SMarek Sokolowski } 8014ac54d93SMarek Sokolowski 802e241ce6fSMartin Storsjo RCParser::ParseOptionType RCParser::parseClassStmt() { 803e241ce6fSMartin Storsjo ASSIGN_OR_RETURN(Arg, readIntOrString()); 804*0eaee545SJonas Devlieghere return std::make_unique<ClassStmt>(*Arg); 805e241ce6fSMartin Storsjo } 806e241ce6fSMartin Storsjo 807420090afSZachary Turner RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) { 808420090afSZachary Turner assert(DialogType != OptStmtType::BasicStmt); 809420090afSZachary Turner 8104ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(SizeResult, readInt()); 8114ac54d93SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 8124ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(NameResult, readString()); 813420090afSZachary Turner 814420090afSZachary Turner // Default values for the optional arguments. 815420090afSZachary Turner uint32_t FontWeight = 0; 816420090afSZachary Turner bool FontItalic = false; 817420090afSZachary Turner uint32_t FontCharset = 1; 818420090afSZachary Turner if (DialogType == OptStmtType::DialogExStmt) { 819420090afSZachary Turner if (consumeOptionalType(Kind::Comma)) { 820420090afSZachary Turner ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3)); 821420090afSZachary Turner if (Args->size() >= 1) 822420090afSZachary Turner FontWeight = (*Args)[0]; 823420090afSZachary Turner if (Args->size() >= 2) 824420090afSZachary Turner FontItalic = (*Args)[1] != 0; 825420090afSZachary Turner if (Args->size() >= 3) 826420090afSZachary Turner FontCharset = (*Args)[2]; 827420090afSZachary Turner } 828420090afSZachary Turner } 829*0eaee545SJonas Devlieghere return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight, 830420090afSZachary Turner FontItalic, FontCharset); 8314ac54d93SMarek Sokolowski } 8324ac54d93SMarek Sokolowski 8334ac54d93SMarek Sokolowski RCParser::ParseOptionType RCParser::parseStyleStmt() { 8344ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readInt()); 835*0eaee545SJonas Devlieghere return std::make_unique<StyleStmt>(*Arg); 8364ac54d93SMarek Sokolowski } 8374ac54d93SMarek Sokolowski 838a876b5c0SMartin Storsjo RCParser::ParseOptionType RCParser::parseExStyleStmt() { 839a876b5c0SMartin Storsjo ASSIGN_OR_RETURN(Arg, readInt()); 840*0eaee545SJonas Devlieghere return std::make_unique<ExStyleStmt>(*Arg); 841a876b5c0SMartin Storsjo } 842a876b5c0SMartin Storsjo 843514b7105SZachary Turner Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) { 8445cd3d5c8SMarek Sokolowski return make_error<ParserError>( 8455cd3d5c8SMarek Sokolowski Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); 8465cd3d5c8SMarek Sokolowski } 8475cd3d5c8SMarek Sokolowski 8485cd3d5c8SMarek Sokolowski } // namespace rc 8495cd3d5c8SMarek Sokolowski } // namespace llvm 850