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(); 805cd3d5c8SMarek Sokolowski else 815cd3d5c8SMarek Sokolowski return getExpectedError("resource type", /* IsAlreadyRead = */ true); 825cd3d5c8SMarek Sokolowski 835cd3d5c8SMarek Sokolowski if (Result) 845cd3d5c8SMarek Sokolowski (*Result)->setName(*NameToken); 855cd3d5c8SMarek Sokolowski 865cd3d5c8SMarek Sokolowski return Result; 875cd3d5c8SMarek Sokolowski } 885cd3d5c8SMarek Sokolowski 895cd3d5c8SMarek Sokolowski bool RCParser::isNextTokenKind(Kind TokenKind) const { 905cd3d5c8SMarek Sokolowski return !isEof() && look().kind() == TokenKind; 915cd3d5c8SMarek Sokolowski } 925cd3d5c8SMarek Sokolowski 935cd3d5c8SMarek Sokolowski const RCToken &RCParser::look() const { 945cd3d5c8SMarek Sokolowski assert(!isEof()); 955cd3d5c8SMarek Sokolowski return *CurLoc; 965cd3d5c8SMarek Sokolowski } 975cd3d5c8SMarek Sokolowski 985cd3d5c8SMarek Sokolowski const RCToken &RCParser::read() { 995cd3d5c8SMarek Sokolowski assert(!isEof()); 1005cd3d5c8SMarek Sokolowski return *CurLoc++; 1015cd3d5c8SMarek Sokolowski } 1025cd3d5c8SMarek Sokolowski 1035cd3d5c8SMarek Sokolowski void RCParser::consume() { 1045cd3d5c8SMarek Sokolowski assert(!isEof()); 1055cd3d5c8SMarek Sokolowski CurLoc++; 1065cd3d5c8SMarek Sokolowski } 1075cd3d5c8SMarek Sokolowski 1085cd3d5c8SMarek Sokolowski Expected<uint32_t> RCParser::readInt() { 1095cd3d5c8SMarek Sokolowski if (!isNextTokenKind(Kind::Int)) 1105cd3d5c8SMarek Sokolowski return getExpectedError("integer"); 1115cd3d5c8SMarek Sokolowski return read().intValue(); 1125cd3d5c8SMarek Sokolowski } 1135cd3d5c8SMarek Sokolowski 1145cd3d5c8SMarek Sokolowski Expected<StringRef> RCParser::readString() { 1155cd3d5c8SMarek Sokolowski if (!isNextTokenKind(Kind::String)) 1165cd3d5c8SMarek Sokolowski return getExpectedError("string"); 1175cd3d5c8SMarek Sokolowski return read().value(); 1185cd3d5c8SMarek Sokolowski } 1195cd3d5c8SMarek Sokolowski 1205cd3d5c8SMarek Sokolowski Expected<StringRef> RCParser::readIdentifier() { 1215cd3d5c8SMarek Sokolowski if (!isNextTokenKind(Kind::Identifier)) 1225cd3d5c8SMarek Sokolowski return getExpectedError("identifier"); 1235cd3d5c8SMarek Sokolowski return read().value(); 1245cd3d5c8SMarek Sokolowski } 1255cd3d5c8SMarek Sokolowski 1267f110527SMarek Sokolowski Expected<IntOrString> RCParser::readIntOrString() { 1277f110527SMarek Sokolowski if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String)) 1287f110527SMarek Sokolowski return getExpectedError("int or string"); 1297f110527SMarek Sokolowski return IntOrString(read()); 1307f110527SMarek Sokolowski } 1317f110527SMarek Sokolowski 1325cd3d5c8SMarek Sokolowski Expected<IntOrString> RCParser::readTypeOrName() { 1335cd3d5c8SMarek Sokolowski // We suggest that the correct resource name or type should be either an 1345cd3d5c8SMarek Sokolowski // identifier or an integer. The original RC tool is much more liberal. 1355cd3d5c8SMarek Sokolowski if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int)) 1365cd3d5c8SMarek Sokolowski return getExpectedError("int or identifier"); 1377f110527SMarek Sokolowski return IntOrString(read()); 1385cd3d5c8SMarek Sokolowski } 1395cd3d5c8SMarek Sokolowski 1405cd3d5c8SMarek Sokolowski Error RCParser::consumeType(Kind TokenKind) { 1415cd3d5c8SMarek Sokolowski if (isNextTokenKind(TokenKind)) { 1425cd3d5c8SMarek Sokolowski consume(); 1435cd3d5c8SMarek Sokolowski return Error::success(); 1445cd3d5c8SMarek Sokolowski } 1455cd3d5c8SMarek Sokolowski 1465cd3d5c8SMarek Sokolowski switch (TokenKind) { 1475cd3d5c8SMarek Sokolowski #define TOKEN(TokenName) \ 1485cd3d5c8SMarek Sokolowski case Kind::TokenName: \ 1495cd3d5c8SMarek Sokolowski return getExpectedError(#TokenName); 1505cd3d5c8SMarek Sokolowski #define SHORT_TOKEN(TokenName, TokenCh) \ 1515cd3d5c8SMarek Sokolowski case Kind::TokenName: \ 1525cd3d5c8SMarek Sokolowski return getExpectedError(#TokenCh); 1535cd3d5c8SMarek Sokolowski #include "ResourceScriptTokenList.h" 1545cd3d5c8SMarek Sokolowski #undef SHORT_TOKEN 1555cd3d5c8SMarek Sokolowski #undef TOKEN 1565cd3d5c8SMarek Sokolowski } 1575cd3d5c8SMarek Sokolowski 1585cd3d5c8SMarek Sokolowski llvm_unreachable("All case options exhausted."); 1595cd3d5c8SMarek Sokolowski } 1605cd3d5c8SMarek Sokolowski 1615cd3d5c8SMarek Sokolowski bool RCParser::consumeOptionalType(Kind TokenKind) { 1625cd3d5c8SMarek Sokolowski if (isNextTokenKind(TokenKind)) { 1635cd3d5c8SMarek Sokolowski consume(); 1645cd3d5c8SMarek Sokolowski return true; 1655cd3d5c8SMarek Sokolowski } 1665cd3d5c8SMarek Sokolowski 1675cd3d5c8SMarek Sokolowski return false; 1685cd3d5c8SMarek Sokolowski } 1695cd3d5c8SMarek Sokolowski 1705cd3d5c8SMarek Sokolowski Expected<SmallVector<uint32_t, 8>> 1715cd3d5c8SMarek Sokolowski RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) { 1725cd3d5c8SMarek Sokolowski assert(MinCount <= MaxCount); 1735cd3d5c8SMarek Sokolowski 1745cd3d5c8SMarek Sokolowski SmallVector<uint32_t, 8> Result; 1755cd3d5c8SMarek Sokolowski 1765cd3d5c8SMarek Sokolowski auto FailureHandler = 1775cd3d5c8SMarek Sokolowski [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 8>> { 1785cd3d5c8SMarek Sokolowski if (Result.size() < MinCount) 1795cd3d5c8SMarek Sokolowski return std::move(Err); 1805cd3d5c8SMarek Sokolowski consumeError(std::move(Err)); 1815cd3d5c8SMarek Sokolowski return Result; 1825cd3d5c8SMarek Sokolowski }; 1835cd3d5c8SMarek Sokolowski 1845cd3d5c8SMarek Sokolowski for (size_t i = 0; i < MaxCount; ++i) { 1855cd3d5c8SMarek Sokolowski // Try to read a comma unless we read the first token. 1865cd3d5c8SMarek Sokolowski // Sometimes RC tool requires them and sometimes not. We decide to 1875cd3d5c8SMarek Sokolowski // always require them. 1885cd3d5c8SMarek Sokolowski if (i >= 1) { 1895cd3d5c8SMarek Sokolowski if (auto CommaError = consumeType(Kind::Comma)) 1905cd3d5c8SMarek Sokolowski return FailureHandler(std::move(CommaError)); 1915cd3d5c8SMarek Sokolowski } 1925cd3d5c8SMarek Sokolowski 1935cd3d5c8SMarek Sokolowski if (auto IntResult = readInt()) 1945cd3d5c8SMarek Sokolowski Result.push_back(*IntResult); 1955cd3d5c8SMarek Sokolowski else 1965cd3d5c8SMarek Sokolowski return FailureHandler(IntResult.takeError()); 1975cd3d5c8SMarek Sokolowski } 1985cd3d5c8SMarek Sokolowski 1995cd3d5c8SMarek Sokolowski return std::move(Result); 2005cd3d5c8SMarek Sokolowski } 2015cd3d5c8SMarek Sokolowski 2027f110527SMarek Sokolowski Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc) { 2037f110527SMarek Sokolowski assert(FlagDesc.size() <= 32 && "More than 32 flags won't fit in result."); 2047f110527SMarek Sokolowski assert(!FlagDesc.empty()); 2057f110527SMarek Sokolowski 2067f110527SMarek Sokolowski uint32_t Result = 0; 2077f110527SMarek Sokolowski while (isNextTokenKind(Kind::Comma)) { 2087f110527SMarek Sokolowski consume(); 2097f110527SMarek Sokolowski ASSIGN_OR_RETURN(FlagResult, readIdentifier()); 2107f110527SMarek Sokolowski bool FoundFlag = false; 2117f110527SMarek Sokolowski 2127f110527SMarek Sokolowski for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { 2137f110527SMarek Sokolowski if (!FlagResult->equals_lower(FlagDesc[FlagId])) 2147f110527SMarek Sokolowski continue; 2157f110527SMarek Sokolowski 2167f110527SMarek Sokolowski Result |= (1U << FlagId); 2177f110527SMarek Sokolowski FoundFlag = true; 2187f110527SMarek Sokolowski break; 2197f110527SMarek Sokolowski } 2207f110527SMarek Sokolowski 2217f110527SMarek Sokolowski if (!FoundFlag) 2227f110527SMarek Sokolowski return getExpectedError(join(FlagDesc, "/"), true); 2237f110527SMarek Sokolowski } 2247f110527SMarek Sokolowski 2257f110527SMarek Sokolowski return Result; 2267f110527SMarek Sokolowski } 2277f110527SMarek Sokolowski 2285cd3d5c8SMarek Sokolowski // As for now, we ignore the extended set of statements. 2295cd3d5c8SMarek Sokolowski Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) { 2305cd3d5c8SMarek Sokolowski OptionalStmtList Result; 2315cd3d5c8SMarek Sokolowski 2325cd3d5c8SMarek Sokolowski // The last statement is always followed by the start of the block. 2335cd3d5c8SMarek Sokolowski while (!isNextTokenKind(Kind::BlockBegin)) { 2345cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(IsExtended)); 2355cd3d5c8SMarek Sokolowski Result.addStmt(std::move(*SingleParse)); 2365cd3d5c8SMarek Sokolowski } 2375cd3d5c8SMarek Sokolowski 2385cd3d5c8SMarek Sokolowski return std::move(Result); 2395cd3d5c8SMarek Sokolowski } 2405cd3d5c8SMarek Sokolowski 2415cd3d5c8SMarek Sokolowski Expected<std::unique_ptr<OptionalStmt>> 2424ac54d93SMarek Sokolowski RCParser::parseSingleOptionalStatement(bool IsExtended) { 2435cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(TypeToken, readIdentifier()); 2445cd3d5c8SMarek Sokolowski if (TypeToken->equals_lower("CHARACTERISTICS")) 2455cd3d5c8SMarek Sokolowski return parseCharacteristicsStmt(); 2464ac54d93SMarek Sokolowski if (TypeToken->equals_lower("LANGUAGE")) 2475cd3d5c8SMarek Sokolowski return parseLanguageStmt(); 2484ac54d93SMarek Sokolowski if (TypeToken->equals_lower("VERSION")) 2495cd3d5c8SMarek Sokolowski return parseVersionStmt(); 2504ac54d93SMarek Sokolowski 2514ac54d93SMarek Sokolowski if (IsExtended) { 2524ac54d93SMarek Sokolowski if (TypeToken->equals_lower("CAPTION")) 2534ac54d93SMarek Sokolowski return parseCaptionStmt(); 2544ac54d93SMarek Sokolowski if (TypeToken->equals_lower("FONT")) 2554ac54d93SMarek Sokolowski return parseFontStmt(); 2564ac54d93SMarek Sokolowski if (TypeToken->equals_lower("STYLE")) 2574ac54d93SMarek Sokolowski return parseStyleStmt(); 2584ac54d93SMarek Sokolowski } 2594ac54d93SMarek Sokolowski 2605cd3d5c8SMarek Sokolowski return getExpectedError("optional statement type, BEGIN or '{'", 2615cd3d5c8SMarek Sokolowski /* IsAlreadyRead = */ true); 2625cd3d5c8SMarek Sokolowski } 2635cd3d5c8SMarek Sokolowski 2645cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseLanguageResource() { 2655cd3d5c8SMarek Sokolowski // Read LANGUAGE as an optional statement. If it's read correctly, we can 2665cd3d5c8SMarek Sokolowski // upcast it to RCResource. 2675cd3d5c8SMarek Sokolowski return parseLanguageStmt(); 2685cd3d5c8SMarek Sokolowski } 2695cd3d5c8SMarek Sokolowski 2707f110527SMarek Sokolowski RCParser::ParseType RCParser::parseAcceleratorsResource() { 2717f110527SMarek Sokolowski ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 2727f110527SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 2737f110527SMarek Sokolowski 2747f110527SMarek Sokolowski auto Accels = make_unique<AcceleratorsResource>(std::move(*OptStatements)); 2757f110527SMarek Sokolowski 2767f110527SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 2777f110527SMarek Sokolowski ASSIGN_OR_RETURN(EventResult, readIntOrString()); 2787f110527SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 2797f110527SMarek Sokolowski ASSIGN_OR_RETURN(IDResult, readInt()); 2807f110527SMarek Sokolowski ASSIGN_OR_RETURN(FlagsResult, 2817f110527SMarek Sokolowski parseFlags(AcceleratorsResource::Accelerator::OptionsStr)); 2827f110527SMarek Sokolowski Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult); 2837f110527SMarek Sokolowski } 2847f110527SMarek Sokolowski 2857f110527SMarek Sokolowski return std::move(Accels); 2867f110527SMarek Sokolowski } 2877f110527SMarek Sokolowski 28872aa937eSMarek Sokolowski RCParser::ParseType RCParser::parseCursorResource() { 28972aa937eSMarek Sokolowski ASSIGN_OR_RETURN(Arg, readString()); 29072aa937eSMarek Sokolowski return make_unique<CursorResource>(*Arg); 29172aa937eSMarek Sokolowski } 29272aa937eSMarek Sokolowski 2934ac54d93SMarek Sokolowski RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { 2944ac54d93SMarek Sokolowski // Dialog resources have the following format of the arguments: 2954ac54d93SMarek Sokolowski // DIALOG: x, y, width, height [opt stmts...] {controls...} 2964ac54d93SMarek Sokolowski // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...} 2974ac54d93SMarek Sokolowski // These are very similar, so we parse them together. 2984ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4)); 2994ac54d93SMarek Sokolowski 3004ac54d93SMarek Sokolowski uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0. 3014ac54d93SMarek Sokolowski if (IsExtended && consumeOptionalType(Kind::Comma)) { 3024ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(HelpIDResult, readInt()); 3034ac54d93SMarek Sokolowski HelpID = *HelpIDResult; 3044ac54d93SMarek Sokolowski } 3054ac54d93SMarek Sokolowski 3064ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(OptStatements, 3074ac54d93SMarek Sokolowski parseOptionalStatements(/*UseExtendedStmts = */ true)); 3084ac54d93SMarek Sokolowski 3094ac54d93SMarek Sokolowski assert(isNextTokenKind(Kind::BlockBegin) && 3104ac54d93SMarek Sokolowski "parseOptionalStatements, when successful, halts on BlockBegin."); 3114ac54d93SMarek Sokolowski consume(); 3124ac54d93SMarek Sokolowski 3134ac54d93SMarek Sokolowski auto Dialog = make_unique<DialogResource>( 3144ac54d93SMarek Sokolowski (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], 3154ac54d93SMarek Sokolowski HelpID, std::move(*OptStatements), IsExtended); 3164ac54d93SMarek Sokolowski 3174ac54d93SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 3184ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(ControlDefResult, parseControl()); 3194ac54d93SMarek Sokolowski Dialog->addControl(std::move(*ControlDefResult)); 3204ac54d93SMarek Sokolowski } 3214ac54d93SMarek Sokolowski 3224ac54d93SMarek Sokolowski return std::move(Dialog); 3234ac54d93SMarek Sokolowski } 3244ac54d93SMarek Sokolowski 3254ac54d93SMarek Sokolowski Expected<Control> RCParser::parseControl() { 3264ac54d93SMarek Sokolowski // Each control definition (except CONTROL) follows one of the schemes below 3274ac54d93SMarek Sokolowski // depending on the control class: 3284ac54d93SMarek Sokolowski // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID] 3294ac54d93SMarek Sokolowski // [class] id, x, y, width, height [, style] [, exstyle] [, helpID] 3304ac54d93SMarek Sokolowski // Note that control ids must be integers. 3314ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(ClassResult, readIdentifier()); 332*75fa173eSMarek Sokolowski std::string ClassUpper = ClassResult->upper(); 3334ac54d93SMarek Sokolowski if (Control::SupportedCtls.find(ClassUpper) == Control::SupportedCtls.end()) 3344ac54d93SMarek Sokolowski return getExpectedError("control type, END or '}'", true); 3354ac54d93SMarek Sokolowski 3364ac54d93SMarek Sokolowski // Read caption if necessary. 3374ac54d93SMarek Sokolowski StringRef Caption; 3384ac54d93SMarek Sokolowski if (Control::CtlsWithTitle.find(ClassUpper) != Control::CtlsWithTitle.end()) { 3394ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(CaptionResult, readString()); 3404ac54d93SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 3414ac54d93SMarek Sokolowski Caption = *CaptionResult; 3424ac54d93SMarek Sokolowski } 3434ac54d93SMarek Sokolowski 3444ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8)); 3454ac54d93SMarek Sokolowski 3464ac54d93SMarek Sokolowski auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> { 3474ac54d93SMarek Sokolowski return Args->size() > Id ? (*Args)[Id] : Optional<uint32_t>(); 3484ac54d93SMarek Sokolowski }; 3494ac54d93SMarek Sokolowski 3504ac54d93SMarek Sokolowski return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2], 3514ac54d93SMarek Sokolowski (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6), 3524ac54d93SMarek Sokolowski TakeOptArg(7)); 3534ac54d93SMarek Sokolowski } 3544ac54d93SMarek Sokolowski 3555cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseIconResource() { 3565cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readString()); 3575cd3d5c8SMarek Sokolowski return make_unique<IconResource>(*Arg); 3585cd3d5c8SMarek Sokolowski } 3595cd3d5c8SMarek Sokolowski 36072aa937eSMarek Sokolowski RCParser::ParseType RCParser::parseHTMLResource() { 36172aa937eSMarek Sokolowski ASSIGN_OR_RETURN(Arg, readString()); 36272aa937eSMarek Sokolowski return make_unique<HTMLResource>(*Arg); 36372aa937eSMarek Sokolowski } 36472aa937eSMarek Sokolowski 36599ecb0ebSMarek Sokolowski RCParser::ParseType RCParser::parseMenuResource() { 36699ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 36799ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(Items, parseMenuItemsList()); 36899ecb0ebSMarek Sokolowski return make_unique<MenuResource>(std::move(*OptStatements), 36999ecb0ebSMarek Sokolowski std::move(*Items)); 37099ecb0ebSMarek Sokolowski } 37199ecb0ebSMarek Sokolowski 37299ecb0ebSMarek Sokolowski Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { 37399ecb0ebSMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 37499ecb0ebSMarek Sokolowski 37599ecb0ebSMarek Sokolowski MenuDefinitionList List; 37699ecb0ebSMarek Sokolowski 37799ecb0ebSMarek Sokolowski // Read a set of items. Each item is of one of three kinds: 37899ecb0ebSMarek Sokolowski // MENUITEM SEPARATOR 37999ecb0ebSMarek Sokolowski // MENUITEM caption:String, result:Int [, menu flags]... 38099ecb0ebSMarek Sokolowski // POPUP caption:String [, menu flags]... { items... } 38199ecb0ebSMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 38299ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier()); 38399ecb0ebSMarek Sokolowski 38499ecb0ebSMarek Sokolowski bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM"); 38599ecb0ebSMarek Sokolowski bool IsPopup = ItemTypeResult->equals_lower("POPUP"); 38699ecb0ebSMarek Sokolowski if (!IsMenuItem && !IsPopup) 38799ecb0ebSMarek Sokolowski return getExpectedError("MENUITEM, POPUP, END or '}'", true); 38899ecb0ebSMarek Sokolowski 38999ecb0ebSMarek Sokolowski if (IsMenuItem && isNextTokenKind(Kind::Identifier)) { 39099ecb0ebSMarek Sokolowski // Now, expecting SEPARATOR. 39199ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(SeparatorResult, readIdentifier()); 39299ecb0ebSMarek Sokolowski if (SeparatorResult->equals_lower("SEPARATOR")) { 39399ecb0ebSMarek Sokolowski List.addDefinition(make_unique<MenuSeparator>()); 39499ecb0ebSMarek Sokolowski continue; 39599ecb0ebSMarek Sokolowski } 39699ecb0ebSMarek Sokolowski 39799ecb0ebSMarek Sokolowski return getExpectedError("SEPARATOR or string", true); 39899ecb0ebSMarek Sokolowski } 39999ecb0ebSMarek Sokolowski 40099ecb0ebSMarek Sokolowski // Not a separator. Read the caption. 40199ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(CaptionResult, readString()); 40299ecb0ebSMarek Sokolowski 40399ecb0ebSMarek Sokolowski // If MENUITEM, expect also a comma and an integer. 40499ecb0ebSMarek Sokolowski uint32_t MenuResult = -1; 40599ecb0ebSMarek Sokolowski 40699ecb0ebSMarek Sokolowski if (IsMenuItem) { 40799ecb0ebSMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 40899ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(IntResult, readInt()); 40999ecb0ebSMarek Sokolowski MenuResult = *IntResult; 41099ecb0ebSMarek Sokolowski } 41199ecb0ebSMarek Sokolowski 41299ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr)); 41399ecb0ebSMarek Sokolowski 41499ecb0ebSMarek Sokolowski if (IsPopup) { 41599ecb0ebSMarek Sokolowski // If POPUP, read submenu items recursively. 41699ecb0ebSMarek Sokolowski ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList()); 41799ecb0ebSMarek Sokolowski List.addDefinition(make_unique<PopupItem>(*CaptionResult, *FlagsResult, 41899ecb0ebSMarek Sokolowski std::move(*SubMenuResult))); 41999ecb0ebSMarek Sokolowski continue; 42099ecb0ebSMarek Sokolowski } 42199ecb0ebSMarek Sokolowski 42299ecb0ebSMarek Sokolowski assert(IsMenuItem); 42399ecb0ebSMarek Sokolowski List.addDefinition( 42499ecb0ebSMarek Sokolowski make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult)); 42599ecb0ebSMarek Sokolowski } 42699ecb0ebSMarek Sokolowski 42799ecb0ebSMarek Sokolowski return std::move(List); 42899ecb0ebSMarek Sokolowski } 42999ecb0ebSMarek Sokolowski 4305cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseStringTableResource() { 4315cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 4325cd3d5c8SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 4335cd3d5c8SMarek Sokolowski 4345cd3d5c8SMarek Sokolowski auto Table = make_unique<StringTableResource>(std::move(*OptStatements)); 4355cd3d5c8SMarek Sokolowski 4365cd3d5c8SMarek Sokolowski // Read strings until we reach the end of the block. 4375cd3d5c8SMarek Sokolowski while (!consumeOptionalType(Kind::BlockEnd)) { 4385cd3d5c8SMarek Sokolowski // Each definition consists of string's ID (an integer) and a string. 4395cd3d5c8SMarek Sokolowski // Some examples in documentation suggest that there might be a comma in 4405cd3d5c8SMarek Sokolowski // between, however we strictly adhere to the single statement definition. 4415cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(IDResult, readInt()); 4425cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(StrResult, readString()); 4435cd3d5c8SMarek Sokolowski Table->addString(*IDResult, *StrResult); 4445cd3d5c8SMarek Sokolowski } 4455cd3d5c8SMarek Sokolowski 4465cd3d5c8SMarek Sokolowski return std::move(Table); 4475cd3d5c8SMarek Sokolowski } 4485cd3d5c8SMarek Sokolowski 4495cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseLanguageStmt() { 4505cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2)); 4515cd3d5c8SMarek Sokolowski return make_unique<LanguageResource>((*Args)[0], (*Args)[1]); 4525cd3d5c8SMarek Sokolowski } 4535cd3d5c8SMarek Sokolowski 4545cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() { 4555cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readInt()); 4565cd3d5c8SMarek Sokolowski return make_unique<CharacteristicsStmt>(*Arg); 4575cd3d5c8SMarek Sokolowski } 4585cd3d5c8SMarek Sokolowski 4595cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseVersionStmt() { 4605cd3d5c8SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readInt()); 4615cd3d5c8SMarek Sokolowski return make_unique<VersionStmt>(*Arg); 4625cd3d5c8SMarek Sokolowski } 4635cd3d5c8SMarek Sokolowski 4644ac54d93SMarek Sokolowski RCParser::ParseOptionType RCParser::parseCaptionStmt() { 4654ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readString()); 4664ac54d93SMarek Sokolowski return make_unique<CaptionStmt>(*Arg); 4674ac54d93SMarek Sokolowski } 4684ac54d93SMarek Sokolowski 4694ac54d93SMarek Sokolowski RCParser::ParseOptionType RCParser::parseFontStmt() { 4704ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(SizeResult, readInt()); 4714ac54d93SMarek Sokolowski RETURN_IF_ERROR(consumeType(Kind::Comma)); 4724ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(NameResult, readString()); 4734ac54d93SMarek Sokolowski return make_unique<FontStmt>(*SizeResult, *NameResult); 4744ac54d93SMarek Sokolowski } 4754ac54d93SMarek Sokolowski 4764ac54d93SMarek Sokolowski RCParser::ParseOptionType RCParser::parseStyleStmt() { 4774ac54d93SMarek Sokolowski ASSIGN_OR_RETURN(Arg, readInt()); 4784ac54d93SMarek Sokolowski return make_unique<StyleStmt>(*Arg); 4794ac54d93SMarek Sokolowski } 4804ac54d93SMarek Sokolowski 4815cd3d5c8SMarek Sokolowski Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) { 4825cd3d5c8SMarek Sokolowski return make_error<ParserError>( 4835cd3d5c8SMarek Sokolowski Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); 4845cd3d5c8SMarek Sokolowski } 4855cd3d5c8SMarek Sokolowski 4865cd3d5c8SMarek Sokolowski } // namespace rc 4875cd3d5c8SMarek Sokolowski } // namespace llvm 488