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