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