15cd3d5c8SMarek Sokolowski //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
25cd3d5c8SMarek Sokolowski //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65cd3d5c8SMarek Sokolowski //
75cd3d5c8SMarek Sokolowski //===---------------------------------------------------------------------===//
85cd3d5c8SMarek Sokolowski //
95cd3d5c8SMarek Sokolowski // This implements the parser defined in ResourceScriptParser.h.
105cd3d5c8SMarek Sokolowski //
115cd3d5c8SMarek Sokolowski //===---------------------------------------------------------------------===//
125cd3d5c8SMarek Sokolowski 
135cd3d5c8SMarek Sokolowski #include "ResourceScriptParser.h"
14fa0ca6cbSZachary Turner #include "llvm/Option/ArgList.h"
15fa0ca6cbSZachary Turner #include "llvm/Support/FileSystem.h"
16fa0ca6cbSZachary Turner #include "llvm/Support/Path.h"
17fa0ca6cbSZachary Turner #include "llvm/Support/Process.h"
185cd3d5c8SMarek Sokolowski 
195cd3d5c8SMarek Sokolowski // Take an expression returning llvm::Error and forward the error if it exists.
205cd3d5c8SMarek Sokolowski #define RETURN_IF_ERROR(Expr)                                                  \
215cd3d5c8SMarek Sokolowski   if (auto Err = (Expr))                                                       \
22c55cf4afSBill Wendling     return std::move(Err);
235cd3d5c8SMarek Sokolowski 
245cd3d5c8SMarek Sokolowski // Take an expression returning llvm::Expected<T> and assign it to Var or
255cd3d5c8SMarek Sokolowski // forward the error out of the function.
265cd3d5c8SMarek Sokolowski #define ASSIGN_OR_RETURN(Var, Expr)                                            \
275cd3d5c8SMarek Sokolowski   auto Var = (Expr);                                                           \
285cd3d5c8SMarek Sokolowski   if (!Var)                                                                    \
295cd3d5c8SMarek Sokolowski     return Var.takeError();
305cd3d5c8SMarek Sokolowski 
315cd3d5c8SMarek Sokolowski namespace llvm {
325cd3d5c8SMarek Sokolowski namespace rc {
335cd3d5c8SMarek Sokolowski 
ParserError(const Twine & Expected,const LocIter CurLoc,const LocIter End)34514b7105SZachary Turner RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
355cd3d5c8SMarek Sokolowski                                    const LocIter End)
365cd3d5c8SMarek Sokolowski     : ErrorLoc(CurLoc), FileEnd(End) {
375cd3d5c8SMarek Sokolowski   CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
385cd3d5c8SMarek Sokolowski                (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
395cd3d5c8SMarek Sokolowski }
405cd3d5c8SMarek Sokolowski 
415cd3d5c8SMarek Sokolowski char RCParser::ParserError::ID = 0;
425cd3d5c8SMarek Sokolowski 
RCParser(std::vector<RCToken> TokenList)43514b7105SZachary Turner RCParser::RCParser(std::vector<RCToken> TokenList)
445cd3d5c8SMarek Sokolowski     : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
455cd3d5c8SMarek Sokolowski 
isEof() const465cd3d5c8SMarek Sokolowski bool RCParser::isEof() const { return CurLoc == End; }
475cd3d5c8SMarek Sokolowski 
parseSingleResource()485cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseSingleResource() {
495cd3d5c8SMarek Sokolowski   // The first thing we read is usually a resource's name. However, in some
505cd3d5c8SMarek Sokolowski   // cases (LANGUAGE and STRINGTABLE) the resources don't have their names
515cd3d5c8SMarek Sokolowski   // and the first token to be read is the type.
525cd3d5c8SMarek Sokolowski   ASSIGN_OR_RETURN(NameToken, readTypeOrName());
535cd3d5c8SMarek Sokolowski 
545cd3d5c8SMarek Sokolowski   if (NameToken->equalsLower("LANGUAGE"))
555cd3d5c8SMarek Sokolowski     return parseLanguageResource();
565cd3d5c8SMarek Sokolowski   else if (NameToken->equalsLower("STRINGTABLE"))
575cd3d5c8SMarek Sokolowski     return parseStringTableResource();
585cd3d5c8SMarek Sokolowski 
595cd3d5c8SMarek Sokolowski   // If it's not an unnamed resource, what we've just read is a name. Now,
605cd3d5c8SMarek Sokolowski   // read resource type;
615cd3d5c8SMarek Sokolowski   ASSIGN_OR_RETURN(TypeToken, readTypeOrName());
625cd3d5c8SMarek Sokolowski 
635cd3d5c8SMarek Sokolowski   ParseType Result = std::unique_ptr<RCResource>();
645cd3d5c8SMarek Sokolowski   (void)!Result;
655cd3d5c8SMarek Sokolowski 
667f110527SMarek Sokolowski   if (TypeToken->equalsLower("ACCELERATORS"))
677f110527SMarek Sokolowski     Result = parseAcceleratorsResource();
68577b9817SMartin Storsjo   else if (TypeToken->equalsLower("BITMAP"))
69577b9817SMartin Storsjo     Result = parseBitmapResource();
707f110527SMarek Sokolowski   else if (TypeToken->equalsLower("CURSOR"))
7172aa937eSMarek Sokolowski     Result = parseCursorResource();
724ac54d93SMarek Sokolowski   else if (TypeToken->equalsLower("DIALOG"))
734ac54d93SMarek Sokolowski     Result = parseDialogResource(false);
744ac54d93SMarek Sokolowski   else if (TypeToken->equalsLower("DIALOGEX"))
754ac54d93SMarek Sokolowski     Result = parseDialogResource(true);
7672aa937eSMarek Sokolowski   else if (TypeToken->equalsLower("HTML"))
7772aa937eSMarek Sokolowski     Result = parseHTMLResource();
78827ddb24SMartin Storsjo   else if (TypeToken->equalsLower("ICON"))
79827ddb24SMartin Storsjo     Result = parseIconResource();
8099ecb0ebSMarek Sokolowski   else if (TypeToken->equalsLower("MENU"))
8199ecb0ebSMarek Sokolowski     Result = parseMenuResource();
827bc3c582SMartin Storsjo   else if (TypeToken->equalsLower("RCDATA"))
837bc3c582SMartin Storsjo     Result = parseUserDefinedResource(RkRcData);
84fb74cb1eSMarek Sokolowski   else if (TypeToken->equalsLower("VERSIONINFO"))
85fb74cb1eSMarek Sokolowski     Result = parseVersionInfoResource();
865cd3d5c8SMarek Sokolowski   else
87b5f39a05SMarek Sokolowski     Result = parseUserDefinedResource(*TypeToken);
885cd3d5c8SMarek Sokolowski 
895cd3d5c8SMarek Sokolowski   if (Result)
905cd3d5c8SMarek Sokolowski     (*Result)->setName(*NameToken);
915cd3d5c8SMarek Sokolowski 
925cd3d5c8SMarek Sokolowski   return Result;
935cd3d5c8SMarek Sokolowski }
945cd3d5c8SMarek Sokolowski 
isNextTokenKind(Kind TokenKind) const955cd3d5c8SMarek Sokolowski bool RCParser::isNextTokenKind(Kind TokenKind) const {
965cd3d5c8SMarek Sokolowski   return !isEof() && look().kind() == TokenKind;
975cd3d5c8SMarek Sokolowski }
985cd3d5c8SMarek Sokolowski 
look() const995cd3d5c8SMarek Sokolowski const RCToken &RCParser::look() const {
1005cd3d5c8SMarek Sokolowski   assert(!isEof());
1015cd3d5c8SMarek Sokolowski   return *CurLoc;
1025cd3d5c8SMarek Sokolowski }
1035cd3d5c8SMarek Sokolowski 
read()1045cd3d5c8SMarek Sokolowski const RCToken &RCParser::read() {
1055cd3d5c8SMarek Sokolowski   assert(!isEof());
1065cd3d5c8SMarek Sokolowski   return *CurLoc++;
1075cd3d5c8SMarek Sokolowski }
1085cd3d5c8SMarek Sokolowski 
consume()1095cd3d5c8SMarek Sokolowski void RCParser::consume() {
1105cd3d5c8SMarek Sokolowski   assert(!isEof());
1115cd3d5c8SMarek Sokolowski   CurLoc++;
1125cd3d5c8SMarek Sokolowski }
1135cd3d5c8SMarek Sokolowski 
1147e89ee7fSMarek Sokolowski // An integer description might consist of a single integer or
1157e89ee7fSMarek Sokolowski // an arithmetic expression evaluating to the integer. The expressions
116d0afe724SMartin Storsjo // can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning
117d0afe724SMartin Storsjo // is the same as in C++ except for 'not' expression.
1187e89ee7fSMarek Sokolowski // The operators in the original RC implementation have the following
1197e89ee7fSMarek Sokolowski // precedence:
120d0afe724SMartin Storsjo //   1) Unary operators (- ~ not),
1217e89ee7fSMarek Sokolowski //   2) Binary operators (+ - & |), with no precedence.
1227e89ee7fSMarek Sokolowski //
123d0afe724SMartin Storsjo // 'not' expression is mostly useful for style values. It evaluates to 0,
124d0afe724SMartin Storsjo // but value given to the operator is stored separately from integer value.
125d0afe724SMartin Storsjo // It's mostly useful for control style expressions and causes bits from
126d0afe724SMartin Storsjo // default control style to be excluded from generated style. For binary
127d0afe724SMartin Storsjo // operators the mask from the right operand is applied to the left operand
128d0afe724SMartin Storsjo // and masks from both operands are combined in operator result.
129d0afe724SMartin Storsjo //
1307e89ee7fSMarek Sokolowski // The following grammar is used to parse the expressions Exp1:
1317e89ee7fSMarek Sokolowski //   Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
132d0afe724SMartin Storsjo //   Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
1337e89ee7fSMarek Sokolowski // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
1347e89ee7fSMarek Sokolowski // separated by binary operators.)
1357e89ee7fSMarek Sokolowski //
1367e89ee7fSMarek Sokolowski // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
1377e89ee7fSMarek Sokolowski // is read by parseIntExpr2().
1387e89ee7fSMarek Sokolowski //
1397e89ee7fSMarek Sokolowski // The original Microsoft tool handles multiple unary operators incorrectly.
1407e89ee7fSMarek Sokolowski // For example, in 16-bit little-endian integers:
1417e89ee7fSMarek Sokolowski //    1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
1427e89ee7fSMarek Sokolowski //    1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
1437e89ee7fSMarek Sokolowski // Our implementation differs from the original one and handles these
1447e89ee7fSMarek Sokolowski // operators correctly:
1457e89ee7fSMarek Sokolowski //    1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
1467e89ee7fSMarek Sokolowski //    1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
1477e89ee7fSMarek Sokolowski 
readInt()148d0afe724SMartin Storsjo Expected<RCInt> RCParser::readInt() {
149d0afe724SMartin Storsjo   ASSIGN_OR_RETURN(Value, parseIntExpr1());
150d0afe724SMartin Storsjo   return (*Value).getValue();
151d0afe724SMartin Storsjo }
1527e89ee7fSMarek Sokolowski 
parseIntExpr1()153d0afe724SMartin Storsjo Expected<IntWithNotMask> RCParser::parseIntExpr1() {
1547e89ee7fSMarek Sokolowski   // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
1557e89ee7fSMarek Sokolowski   ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
156d0afe724SMartin Storsjo   IntWithNotMask Result = *FirstResult;
1577e89ee7fSMarek Sokolowski 
1587e89ee7fSMarek Sokolowski   while (!isEof() && look().isBinaryOp()) {
1597e89ee7fSMarek Sokolowski     auto OpToken = read();
1607e89ee7fSMarek Sokolowski     ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
1617e89ee7fSMarek Sokolowski 
1627e89ee7fSMarek Sokolowski     switch (OpToken.kind()) {
1637e89ee7fSMarek Sokolowski     case Kind::Plus:
1647e89ee7fSMarek Sokolowski       Result += *NextResult;
1657e89ee7fSMarek Sokolowski       break;
1667e89ee7fSMarek Sokolowski 
1677e89ee7fSMarek Sokolowski     case Kind::Minus:
1687e89ee7fSMarek Sokolowski       Result -= *NextResult;
1697e89ee7fSMarek Sokolowski       break;
1707e89ee7fSMarek Sokolowski 
1717e89ee7fSMarek Sokolowski     case Kind::Pipe:
1727e89ee7fSMarek Sokolowski       Result |= *NextResult;
1737e89ee7fSMarek Sokolowski       break;
1747e89ee7fSMarek Sokolowski 
1757e89ee7fSMarek Sokolowski     case Kind::Amp:
1767e89ee7fSMarek Sokolowski       Result &= *NextResult;
1777e89ee7fSMarek Sokolowski       break;
1787e89ee7fSMarek Sokolowski 
1797e89ee7fSMarek Sokolowski     default:
1807e89ee7fSMarek Sokolowski       llvm_unreachable("Already processed all binary ops.");
1817e89ee7fSMarek Sokolowski     }
1827e89ee7fSMarek Sokolowski   }
1837e89ee7fSMarek Sokolowski 
1847e89ee7fSMarek Sokolowski   return Result;
1857e89ee7fSMarek Sokolowski }
1867e89ee7fSMarek Sokolowski 
parseIntExpr2()187d0afe724SMartin Storsjo Expected<IntWithNotMask> RCParser::parseIntExpr2() {
188d0afe724SMartin Storsjo   // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
1897e89ee7fSMarek Sokolowski   static const char ErrorMsg[] = "'-', '~', integer or '('";
1907e89ee7fSMarek Sokolowski 
1917e89ee7fSMarek Sokolowski   if (isEof())
1927e89ee7fSMarek Sokolowski     return getExpectedError(ErrorMsg);
1937e89ee7fSMarek Sokolowski 
1947e89ee7fSMarek Sokolowski   switch (look().kind()) {
1957e89ee7fSMarek Sokolowski   case Kind::Minus: {
1967e89ee7fSMarek Sokolowski     consume();
1977e89ee7fSMarek Sokolowski     ASSIGN_OR_RETURN(Result, parseIntExpr2());
1987e89ee7fSMarek Sokolowski     return -(*Result);
1997e89ee7fSMarek Sokolowski   }
2007e89ee7fSMarek Sokolowski 
2017e89ee7fSMarek Sokolowski   case Kind::Tilde: {
2027e89ee7fSMarek Sokolowski     consume();
2037e89ee7fSMarek Sokolowski     ASSIGN_OR_RETURN(Result, parseIntExpr2());
2047e89ee7fSMarek Sokolowski     return ~(*Result);
2057e89ee7fSMarek Sokolowski   }
2067e89ee7fSMarek Sokolowski 
2077e89ee7fSMarek Sokolowski   case Kind::Int:
20807bc04ffSZachary Turner     return RCInt(read());
2097e89ee7fSMarek Sokolowski 
2107e89ee7fSMarek Sokolowski   case Kind::LeftParen: {
2117e89ee7fSMarek Sokolowski     consume();
2127e89ee7fSMarek Sokolowski     ASSIGN_OR_RETURN(Result, parseIntExpr1());
2137e89ee7fSMarek Sokolowski     RETURN_IF_ERROR(consumeType(Kind::RightParen));
2147e89ee7fSMarek Sokolowski     return *Result;
2157e89ee7fSMarek Sokolowski   }
2167e89ee7fSMarek Sokolowski 
217d0afe724SMartin Storsjo   case Kind::Identifier: {
21842f74e82SMartin Storsjö     if (!read().value().equals_insensitive("not"))
219d0afe724SMartin Storsjo       return getExpectedError(ErrorMsg, true);
220d0afe724SMartin Storsjo     ASSIGN_OR_RETURN(Result, parseIntExpr2());
221d0afe724SMartin Storsjo     return IntWithNotMask(0, (*Result).getValue());
222d0afe724SMartin Storsjo   }
223d0afe724SMartin Storsjo 
2247e89ee7fSMarek Sokolowski   default:
2257e89ee7fSMarek Sokolowski     return getExpectedError(ErrorMsg);
2267e89ee7fSMarek Sokolowski   }
2275cd3d5c8SMarek Sokolowski }
2285cd3d5c8SMarek Sokolowski 
readString()2295cd3d5c8SMarek Sokolowski Expected<StringRef> RCParser::readString() {
2305cd3d5c8SMarek Sokolowski   if (!isNextTokenKind(Kind::String))
2315cd3d5c8SMarek Sokolowski     return getExpectedError("string");
2325cd3d5c8SMarek Sokolowski   return read().value();
2335cd3d5c8SMarek Sokolowski }
2345cd3d5c8SMarek Sokolowski 
readFilename()2354021cee9SMartin Storsjo Expected<StringRef> RCParser::readFilename() {
2364021cee9SMartin Storsjo   if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
2374021cee9SMartin Storsjo     return getExpectedError("string");
2384021cee9SMartin Storsjo   return read().value();
2394021cee9SMartin Storsjo }
2404021cee9SMartin Storsjo 
readIdentifier()2415cd3d5c8SMarek Sokolowski Expected<StringRef> RCParser::readIdentifier() {
2425cd3d5c8SMarek Sokolowski   if (!isNextTokenKind(Kind::Identifier))
2435cd3d5c8SMarek Sokolowski     return getExpectedError("identifier");
2445cd3d5c8SMarek Sokolowski   return read().value();
2455cd3d5c8SMarek Sokolowski }
2465cd3d5c8SMarek Sokolowski 
readIntOrString()2477f110527SMarek Sokolowski Expected<IntOrString> RCParser::readIntOrString() {
2487f110527SMarek Sokolowski   if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
2497f110527SMarek Sokolowski     return getExpectedError("int or string");
2507f110527SMarek Sokolowski   return IntOrString(read());
2517f110527SMarek Sokolowski }
2527f110527SMarek Sokolowski 
readTypeOrName()2535cd3d5c8SMarek Sokolowski Expected<IntOrString> RCParser::readTypeOrName() {
2545cd3d5c8SMarek Sokolowski   // We suggest that the correct resource name or type should be either an
2555cd3d5c8SMarek Sokolowski   // identifier or an integer. The original RC tool is much more liberal.
2565cd3d5c8SMarek Sokolowski   if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
2575cd3d5c8SMarek Sokolowski     return getExpectedError("int or identifier");
2587f110527SMarek Sokolowski   return IntOrString(read());
2595cd3d5c8SMarek Sokolowski }
2605cd3d5c8SMarek Sokolowski 
consumeType(Kind TokenKind)2615cd3d5c8SMarek Sokolowski Error RCParser::consumeType(Kind TokenKind) {
2625cd3d5c8SMarek Sokolowski   if (isNextTokenKind(TokenKind)) {
2635cd3d5c8SMarek Sokolowski     consume();
2645cd3d5c8SMarek Sokolowski     return Error::success();
2655cd3d5c8SMarek Sokolowski   }
2665cd3d5c8SMarek Sokolowski 
2675cd3d5c8SMarek Sokolowski   switch (TokenKind) {
2685cd3d5c8SMarek Sokolowski #define TOKEN(TokenName)                                                       \
2695cd3d5c8SMarek Sokolowski   case Kind::TokenName:                                                        \
2705cd3d5c8SMarek Sokolowski     return getExpectedError(#TokenName);
2715cd3d5c8SMarek Sokolowski #define SHORT_TOKEN(TokenName, TokenCh)                                        \
2725cd3d5c8SMarek Sokolowski   case Kind::TokenName:                                                        \
2735cd3d5c8SMarek Sokolowski     return getExpectedError(#TokenCh);
274b961d29bSDavid Blaikie #include "ResourceScriptTokenList.def"
2755cd3d5c8SMarek Sokolowski   }
2765cd3d5c8SMarek Sokolowski 
2775cd3d5c8SMarek Sokolowski   llvm_unreachable("All case options exhausted.");
2785cd3d5c8SMarek Sokolowski }
2795cd3d5c8SMarek Sokolowski 
consumeOptionalType(Kind TokenKind)2805cd3d5c8SMarek Sokolowski bool RCParser::consumeOptionalType(Kind TokenKind) {
2815cd3d5c8SMarek Sokolowski   if (isNextTokenKind(TokenKind)) {
2825cd3d5c8SMarek Sokolowski     consume();
2835cd3d5c8SMarek Sokolowski     return true;
2845cd3d5c8SMarek Sokolowski   }
2855cd3d5c8SMarek Sokolowski 
2865cd3d5c8SMarek Sokolowski   return false;
2875cd3d5c8SMarek Sokolowski }
2885cd3d5c8SMarek Sokolowski 
readIntsWithCommas(size_t MinCount,size_t MaxCount)28907bc04ffSZachary Turner Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
29007bc04ffSZachary Turner                                                              size_t MaxCount) {
2915cd3d5c8SMarek Sokolowski   assert(MinCount <= MaxCount);
2925cd3d5c8SMarek Sokolowski 
29307bc04ffSZachary Turner   SmallVector<RCInt, 8> Result;
2945cd3d5c8SMarek Sokolowski 
2955cd3d5c8SMarek Sokolowski   auto FailureHandler =
29607bc04ffSZachary Turner       [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
2975cd3d5c8SMarek Sokolowski     if (Result.size() < MinCount)
298c55cf4afSBill Wendling       return std::move(Err);
2995cd3d5c8SMarek Sokolowski     consumeError(std::move(Err));
3005cd3d5c8SMarek Sokolowski     return Result;
3015cd3d5c8SMarek Sokolowski   };
3025cd3d5c8SMarek Sokolowski 
3035cd3d5c8SMarek Sokolowski   for (size_t i = 0; i < MaxCount; ++i) {
3045cd3d5c8SMarek Sokolowski     // Try to read a comma unless we read the first token.
3055cd3d5c8SMarek Sokolowski     // Sometimes RC tool requires them and sometimes not. We decide to
3065cd3d5c8SMarek Sokolowski     // always require them.
3075cd3d5c8SMarek Sokolowski     if (i >= 1) {
3085cd3d5c8SMarek Sokolowski       if (auto CommaError = consumeType(Kind::Comma))
3095cd3d5c8SMarek Sokolowski         return FailureHandler(std::move(CommaError));
3105cd3d5c8SMarek Sokolowski     }
3115cd3d5c8SMarek Sokolowski 
3125cd3d5c8SMarek Sokolowski     if (auto IntResult = readInt())
3135cd3d5c8SMarek Sokolowski       Result.push_back(*IntResult);
3145cd3d5c8SMarek Sokolowski     else
3155cd3d5c8SMarek Sokolowski       return FailureHandler(IntResult.takeError());
3165cd3d5c8SMarek Sokolowski   }
3175cd3d5c8SMarek Sokolowski 
318c55cf4afSBill Wendling   return std::move(Result);
3195cd3d5c8SMarek Sokolowski }
3205cd3d5c8SMarek Sokolowski 
parseFlags(ArrayRef<StringRef> FlagDesc,ArrayRef<uint32_t> FlagValues)321c75a087cSMarek Sokolowski Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
322c75a087cSMarek Sokolowski                                         ArrayRef<uint32_t> FlagValues) {
3237f110527SMarek Sokolowski   assert(!FlagDesc.empty());
324c75a087cSMarek Sokolowski   assert(FlagDesc.size() == FlagValues.size());
3257f110527SMarek Sokolowski 
3267f110527SMarek Sokolowski   uint32_t Result = 0;
3277f110527SMarek Sokolowski   while (isNextTokenKind(Kind::Comma)) {
3287f110527SMarek Sokolowski     consume();
3297f110527SMarek Sokolowski     ASSIGN_OR_RETURN(FlagResult, readIdentifier());
3307f110527SMarek Sokolowski     bool FoundFlag = false;
3317f110527SMarek Sokolowski 
3327f110527SMarek Sokolowski     for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
33342f74e82SMartin Storsjö       if (!FlagResult->equals_insensitive(FlagDesc[FlagId]))
3347f110527SMarek Sokolowski         continue;
3357f110527SMarek Sokolowski 
336c75a087cSMarek Sokolowski       Result |= FlagValues[FlagId];
3377f110527SMarek Sokolowski       FoundFlag = true;
3387f110527SMarek Sokolowski       break;
3397f110527SMarek Sokolowski     }
3407f110527SMarek Sokolowski 
3417f110527SMarek Sokolowski     if (!FoundFlag)
3427f110527SMarek Sokolowski       return getExpectedError(join(FlagDesc, "/"), true);
3437f110527SMarek Sokolowski   }
3447f110527SMarek Sokolowski 
3457f110527SMarek Sokolowski   return Result;
3467f110527SMarek Sokolowski }
3477f110527SMarek Sokolowski 
parseMemoryFlags(uint16_t Flags)34811adbacaSMartin Storsjo uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
34911adbacaSMartin Storsjo   while (!isEof()) {
35011adbacaSMartin Storsjo     const RCToken &Token = look();
35111adbacaSMartin Storsjo     if (Token.kind() != Kind::Identifier)
35211adbacaSMartin Storsjo       return Flags;
35311adbacaSMartin Storsjo     const StringRef Ident = Token.value();
35442f74e82SMartin Storsjö     if (Ident.equals_insensitive("PRELOAD"))
35511adbacaSMartin Storsjo       Flags |= MfPreload;
35642f74e82SMartin Storsjö     else if (Ident.equals_insensitive("LOADONCALL"))
35711adbacaSMartin Storsjo       Flags &= ~MfPreload;
35842f74e82SMartin Storsjö     else if (Ident.equals_insensitive("FIXED"))
35911adbacaSMartin Storsjo       Flags &= ~(MfMoveable | MfDiscardable);
36042f74e82SMartin Storsjö     else if (Ident.equals_insensitive("MOVEABLE"))
36111adbacaSMartin Storsjo       Flags |= MfMoveable;
36242f74e82SMartin Storsjö     else if (Ident.equals_insensitive("DISCARDABLE"))
36311adbacaSMartin Storsjo       Flags |= MfDiscardable | MfMoveable | MfPure;
36442f74e82SMartin Storsjö     else if (Ident.equals_insensitive("PURE"))
36511adbacaSMartin Storsjo       Flags |= MfPure;
36642f74e82SMartin Storsjö     else if (Ident.equals_insensitive("IMPURE"))
36711adbacaSMartin Storsjo       Flags &= ~(MfPure | MfDiscardable);
36842f74e82SMartin Storsjö     else if (Ident.equals_insensitive("SHARED"))
36911adbacaSMartin Storsjo       Flags |= MfPure;
37042f74e82SMartin Storsjö     else if (Ident.equals_insensitive("NONSHARED"))
37111adbacaSMartin Storsjo       Flags &= ~(MfPure | MfDiscardable);
37211adbacaSMartin Storsjo     else
37311adbacaSMartin Storsjo       return Flags;
37411adbacaSMartin Storsjo     consume();
37511adbacaSMartin Storsjo   }
37611adbacaSMartin Storsjo   return Flags;
37711adbacaSMartin Storsjo }
37811adbacaSMartin Storsjo 
379420090afSZachary Turner Expected<OptionalStmtList>
parseOptionalStatements(OptStmtType StmtsType)380420090afSZachary Turner RCParser::parseOptionalStatements(OptStmtType StmtsType) {
3815cd3d5c8SMarek Sokolowski   OptionalStmtList Result;
3825cd3d5c8SMarek Sokolowski 
3835cd3d5c8SMarek Sokolowski   // The last statement is always followed by the start of the block.
3845cd3d5c8SMarek Sokolowski   while (!isNextTokenKind(Kind::BlockBegin)) {
385420090afSZachary Turner     ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
3865cd3d5c8SMarek Sokolowski     Result.addStmt(std::move(*SingleParse));
3875cd3d5c8SMarek Sokolowski   }
3885cd3d5c8SMarek Sokolowski 
389c55cf4afSBill Wendling   return std::move(Result);
3905cd3d5c8SMarek Sokolowski }
3915cd3d5c8SMarek Sokolowski 
3925cd3d5c8SMarek Sokolowski Expected<std::unique_ptr<OptionalStmt>>
parseSingleOptionalStatement(OptStmtType StmtsType)393420090afSZachary Turner RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
3945cd3d5c8SMarek Sokolowski   ASSIGN_OR_RETURN(TypeToken, readIdentifier());
39542f74e82SMartin Storsjö   if (TypeToken->equals_insensitive("CHARACTERISTICS"))
3965cd3d5c8SMarek Sokolowski     return parseCharacteristicsStmt();
39742f74e82SMartin Storsjö   if (TypeToken->equals_insensitive("LANGUAGE"))
3985cd3d5c8SMarek Sokolowski     return parseLanguageStmt();
39942f74e82SMartin Storsjö   if (TypeToken->equals_insensitive("VERSION"))
4005cd3d5c8SMarek Sokolowski     return parseVersionStmt();
4014ac54d93SMarek Sokolowski 
402420090afSZachary Turner   if (StmtsType != OptStmtType::BasicStmt) {
40342f74e82SMartin Storsjö     if (TypeToken->equals_insensitive("CAPTION"))
4044ac54d93SMarek Sokolowski       return parseCaptionStmt();
40542f74e82SMartin Storsjö     if (TypeToken->equals_insensitive("CLASS"))
406e241ce6fSMartin Storsjo       return parseClassStmt();
40742f74e82SMartin Storsjö     if (TypeToken->equals_insensitive("EXSTYLE"))
408a876b5c0SMartin Storsjo       return parseExStyleStmt();
40942f74e82SMartin Storsjö     if (TypeToken->equals_insensitive("FONT"))
410420090afSZachary Turner       return parseFontStmt(StmtsType);
41142f74e82SMartin Storsjö     if (TypeToken->equals_insensitive("STYLE"))
4124ac54d93SMarek Sokolowski       return parseStyleStmt();
4134ac54d93SMarek Sokolowski   }
4144ac54d93SMarek Sokolowski 
4155cd3d5c8SMarek Sokolowski   return getExpectedError("optional statement type, BEGIN or '{'",
4165cd3d5c8SMarek Sokolowski                           /* IsAlreadyRead = */ true);
4175cd3d5c8SMarek Sokolowski }
4185cd3d5c8SMarek Sokolowski 
parseLanguageResource()4195cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseLanguageResource() {
4205cd3d5c8SMarek Sokolowski   // Read LANGUAGE as an optional statement. If it's read correctly, we can
4215cd3d5c8SMarek Sokolowski   // upcast it to RCResource.
4225cd3d5c8SMarek Sokolowski   return parseLanguageStmt();
4235cd3d5c8SMarek Sokolowski }
4245cd3d5c8SMarek Sokolowski 
parseAcceleratorsResource()4257f110527SMarek Sokolowski RCParser::ParseType RCParser::parseAcceleratorsResource() {
42611adbacaSMartin Storsjo   uint16_t MemoryFlags =
42711adbacaSMartin Storsjo       parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
4287f110527SMarek Sokolowski   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
4297f110527SMarek Sokolowski   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
4307f110527SMarek Sokolowski 
4310eaee545SJonas Devlieghere   auto Accels = std::make_unique<AcceleratorsResource>(
43211adbacaSMartin Storsjo       std::move(*OptStatements), MemoryFlags);
4337f110527SMarek Sokolowski 
4347f110527SMarek Sokolowski   while (!consumeOptionalType(Kind::BlockEnd)) {
4357f110527SMarek Sokolowski     ASSIGN_OR_RETURN(EventResult, readIntOrString());
4367f110527SMarek Sokolowski     RETURN_IF_ERROR(consumeType(Kind::Comma));
4377f110527SMarek Sokolowski     ASSIGN_OR_RETURN(IDResult, readInt());
438c75a087cSMarek Sokolowski     ASSIGN_OR_RETURN(
439c75a087cSMarek Sokolowski         FlagsResult,
440c75a087cSMarek Sokolowski         parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
441c75a087cSMarek Sokolowski                    AcceleratorsResource::Accelerator::OptionsFlags));
4427f110527SMarek Sokolowski     Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
4437f110527SMarek Sokolowski   }
4447f110527SMarek Sokolowski 
445c55cf4afSBill Wendling   return std::move(Accels);
4467f110527SMarek Sokolowski }
4477f110527SMarek Sokolowski 
parseCursorResource()44872aa937eSMarek Sokolowski RCParser::ParseType RCParser::parseCursorResource() {
44911adbacaSMartin Storsjo   uint16_t MemoryFlags =
45011adbacaSMartin Storsjo       parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
4514021cee9SMartin Storsjo   ASSIGN_OR_RETURN(Arg, readFilename());
4520eaee545SJonas Devlieghere   return std::make_unique<CursorResource>(*Arg, MemoryFlags);
45372aa937eSMarek Sokolowski }
45472aa937eSMarek Sokolowski 
parseDialogResource(bool IsExtended)4554ac54d93SMarek Sokolowski RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
45611adbacaSMartin Storsjo   uint16_t MemoryFlags =
45711adbacaSMartin Storsjo       parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
4584ac54d93SMarek Sokolowski   // Dialog resources have the following format of the arguments:
4594ac54d93SMarek Sokolowski   //  DIALOG:   x, y, width, height [opt stmts...] {controls...}
4604ac54d93SMarek Sokolowski   //  DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
4614ac54d93SMarek Sokolowski   // These are very similar, so we parse them together.
4624ac54d93SMarek Sokolowski   ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
4634ac54d93SMarek Sokolowski 
4644ac54d93SMarek Sokolowski   uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
4654ac54d93SMarek Sokolowski   if (IsExtended && consumeOptionalType(Kind::Comma)) {
4664ac54d93SMarek Sokolowski     ASSIGN_OR_RETURN(HelpIDResult, readInt());
4674ac54d93SMarek Sokolowski     HelpID = *HelpIDResult;
4684ac54d93SMarek Sokolowski   }
4694ac54d93SMarek Sokolowski 
470420090afSZachary Turner   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
471420090afSZachary Turner                                       IsExtended ? OptStmtType::DialogExStmt
472420090afSZachary Turner                                                  : OptStmtType::DialogStmt));
4734ac54d93SMarek Sokolowski 
4744ac54d93SMarek Sokolowski   assert(isNextTokenKind(Kind::BlockBegin) &&
4754ac54d93SMarek Sokolowski          "parseOptionalStatements, when successful, halts on BlockBegin.");
4764ac54d93SMarek Sokolowski   consume();
4774ac54d93SMarek Sokolowski 
4780eaee545SJonas Devlieghere   auto Dialog = std::make_unique<DialogResource>(
4794ac54d93SMarek Sokolowski       (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
48011adbacaSMartin Storsjo       HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
4814ac54d93SMarek Sokolowski 
4824ac54d93SMarek Sokolowski   while (!consumeOptionalType(Kind::BlockEnd)) {
4834ac54d93SMarek Sokolowski     ASSIGN_OR_RETURN(ControlDefResult, parseControl());
4844ac54d93SMarek Sokolowski     Dialog->addControl(std::move(*ControlDefResult));
4854ac54d93SMarek Sokolowski   }
4864ac54d93SMarek Sokolowski 
487c55cf4afSBill Wendling   return std::move(Dialog);
4884ac54d93SMarek Sokolowski }
4894ac54d93SMarek Sokolowski 
parseUserDefinedResource(IntOrString Type)490b5f39a05SMarek Sokolowski RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
49111adbacaSMartin Storsjo   uint16_t MemoryFlags =
49211adbacaSMartin Storsjo       parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
493b5f39a05SMarek Sokolowski   if (isEof())
494b5f39a05SMarek Sokolowski     return getExpectedError("filename, '{' or BEGIN");
495b5f39a05SMarek Sokolowski 
496b5f39a05SMarek Sokolowski   // Check if this is a file resource.
4974021cee9SMartin Storsjo   switch (look().kind()) {
4984021cee9SMartin Storsjo   case Kind::String:
4994021cee9SMartin Storsjo   case Kind::Identifier:
5000eaee545SJonas Devlieghere     return std::make_unique<UserDefinedResource>(Type, read().value(),
50111adbacaSMartin Storsjo                                                   MemoryFlags);
5024021cee9SMartin Storsjo   default:
5034021cee9SMartin Storsjo     break;
5044021cee9SMartin Storsjo   }
505b5f39a05SMarek Sokolowski 
506b5f39a05SMarek Sokolowski   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
507b5f39a05SMarek Sokolowski   std::vector<IntOrString> Data;
508b5f39a05SMarek Sokolowski 
509b5f39a05SMarek Sokolowski   while (!consumeOptionalType(Kind::BlockEnd)) {
510b5f39a05SMarek Sokolowski     ASSIGN_OR_RETURN(Item, readIntOrString());
511b5f39a05SMarek Sokolowski     Data.push_back(*Item);
512beb0e7e3SMartin Storsjö 
513beb0e7e3SMartin Storsjö     // There can be zero or more commas after each token (but not before
514beb0e7e3SMartin Storsjö     // the first one).
515beb0e7e3SMartin Storsjö     while (consumeOptionalType(Kind::Comma)) {
516beb0e7e3SMartin Storsjö     }
517b5f39a05SMarek Sokolowski   }
518b5f39a05SMarek Sokolowski 
5190eaee545SJonas Devlieghere   return std::make_unique<UserDefinedResource>(Type, std::move(Data),
52011adbacaSMartin Storsjo                                                 MemoryFlags);
521b5f39a05SMarek Sokolowski }
522b5f39a05SMarek Sokolowski 
parseVersionInfoResource()523fb74cb1eSMarek Sokolowski RCParser::ParseType RCParser::parseVersionInfoResource() {
52411adbacaSMartin Storsjo   uint16_t MemoryFlags =
52511adbacaSMartin Storsjo       parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
526fb74cb1eSMarek Sokolowski   ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
527fb74cb1eSMarek Sokolowski   ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
5280eaee545SJonas Devlieghere   return std::make_unique<VersionInfoResource>(
52911adbacaSMartin Storsjo       std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
530fb74cb1eSMarek Sokolowski }
531fb74cb1eSMarek Sokolowski 
parseControl()5324ac54d93SMarek Sokolowski Expected<Control> RCParser::parseControl() {
5334ac54d93SMarek Sokolowski   // Each control definition (except CONTROL) follows one of the schemes below
5344ac54d93SMarek Sokolowski   // depending on the control class:
5354ac54d93SMarek Sokolowski   //  [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
5364ac54d93SMarek Sokolowski   //  [class]       id, x, y, width, height [, style] [, exstyle] [, helpID]
5374ac54d93SMarek Sokolowski   // Note that control ids must be integers.
5387f7745c0SMarek Sokolowski   // Text might be either a string or an integer pointing to resource ID.
5394ac54d93SMarek Sokolowski   ASSIGN_OR_RETURN(ClassResult, readIdentifier());
54075fa173eSMarek Sokolowski   std::string ClassUpper = ClassResult->upper();
5417f7745c0SMarek Sokolowski   auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
5427f7745c0SMarek Sokolowski   if (CtlInfo == Control::SupportedCtls.end())
5434ac54d93SMarek Sokolowski     return getExpectedError("control type, END or '}'", true);
5444ac54d93SMarek Sokolowski 
5454ac54d93SMarek Sokolowski   // Read caption if necessary.
5467f7745c0SMarek Sokolowski   IntOrString Caption{StringRef()};
5477f7745c0SMarek Sokolowski   if (CtlInfo->getValue().HasTitle) {
5487f7745c0SMarek Sokolowski     ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
5494ac54d93SMarek Sokolowski     RETURN_IF_ERROR(consumeType(Kind::Comma));
5504ac54d93SMarek Sokolowski     Caption = *CaptionResult;
5514ac54d93SMarek Sokolowski   }
5524ac54d93SMarek Sokolowski 
553818bd568SMartin Storsjo   ASSIGN_OR_RETURN(ID, readInt());
554818bd568SMartin Storsjo   RETURN_IF_ERROR(consumeType(Kind::Comma));
5554ac54d93SMarek Sokolowski 
556818bd568SMartin Storsjo   IntOrString Class;
557d0afe724SMartin Storsjo   Optional<IntWithNotMask> Style;
558818bd568SMartin Storsjo   if (ClassUpper == "CONTROL") {
559818bd568SMartin Storsjo     // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
560818bd568SMartin Storsjo     ASSIGN_OR_RETURN(ClassStr, readString());
561818bd568SMartin Storsjo     RETURN_IF_ERROR(consumeType(Kind::Comma));
562818bd568SMartin Storsjo     Class = *ClassStr;
563d0afe724SMartin Storsjo     ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());
564818bd568SMartin Storsjo     RETURN_IF_ERROR(consumeType(Kind::Comma));
565818bd568SMartin Storsjo     Style = *StyleVal;
566818bd568SMartin Storsjo   } else {
567818bd568SMartin Storsjo     Class = CtlInfo->getValue().CtlClass;
568818bd568SMartin Storsjo   }
5694ac54d93SMarek Sokolowski 
570818bd568SMartin Storsjo   // x, y, width, height
571818bd568SMartin Storsjo   ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
572818bd568SMartin Storsjo 
573818bd568SMartin Storsjo   if (ClassUpper != "CONTROL") {
574818bd568SMartin Storsjo     if (consumeOptionalType(Kind::Comma)) {
575d0afe724SMartin Storsjo       ASSIGN_OR_RETURN(Val, parseIntExpr1());
576818bd568SMartin Storsjo       Style = *Val;
577818bd568SMartin Storsjo     }
578818bd568SMartin Storsjo   }
579818bd568SMartin Storsjo 
580818bd568SMartin Storsjo   Optional<uint32_t> ExStyle;
581818bd568SMartin Storsjo   if (consumeOptionalType(Kind::Comma)) {
582818bd568SMartin Storsjo     ASSIGN_OR_RETURN(Val, readInt());
583818bd568SMartin Storsjo     ExStyle = *Val;
584818bd568SMartin Storsjo   }
585818bd568SMartin Storsjo   Optional<uint32_t> HelpID;
586818bd568SMartin Storsjo   if (consumeOptionalType(Kind::Comma)) {
587818bd568SMartin Storsjo     ASSIGN_OR_RETURN(Val, readInt());
588818bd568SMartin Storsjo     HelpID = *Val;
589818bd568SMartin Storsjo   }
590818bd568SMartin Storsjo 
591818bd568SMartin Storsjo   return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
592818bd568SMartin Storsjo                  (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
5934ac54d93SMarek Sokolowski }
5944ac54d93SMarek Sokolowski 
parseBitmapResource()595577b9817SMartin Storsjo RCParser::ParseType RCParser::parseBitmapResource() {
59611adbacaSMartin Storsjo   uint16_t MemoryFlags =
59711adbacaSMartin Storsjo       parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
5984021cee9SMartin Storsjo   ASSIGN_OR_RETURN(Arg, readFilename());
5990eaee545SJonas Devlieghere   return std::make_unique<BitmapResource>(*Arg, MemoryFlags);
600577b9817SMartin Storsjo }
601577b9817SMartin Storsjo 
parseIconResource()6025cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseIconResource() {
60311adbacaSMartin Storsjo   uint16_t MemoryFlags =
60411adbacaSMartin Storsjo       parseMemoryFlags(IconResource::getDefaultMemoryFlags());
6054021cee9SMartin Storsjo   ASSIGN_OR_RETURN(Arg, readFilename());
6060eaee545SJonas Devlieghere   return std::make_unique<IconResource>(*Arg, MemoryFlags);
6075cd3d5c8SMarek Sokolowski }
6085cd3d5c8SMarek Sokolowski 
parseHTMLResource()60972aa937eSMarek Sokolowski RCParser::ParseType RCParser::parseHTMLResource() {
61011adbacaSMartin Storsjo   uint16_t MemoryFlags =
61111adbacaSMartin Storsjo       parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
6124021cee9SMartin Storsjo   ASSIGN_OR_RETURN(Arg, readFilename());
6130eaee545SJonas Devlieghere   return std::make_unique<HTMLResource>(*Arg, MemoryFlags);
61472aa937eSMarek Sokolowski }
61572aa937eSMarek Sokolowski 
parseMenuResource()61699ecb0ebSMarek Sokolowski RCParser::ParseType RCParser::parseMenuResource() {
61711adbacaSMartin Storsjo   uint16_t MemoryFlags =
61811adbacaSMartin Storsjo       parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
61999ecb0ebSMarek Sokolowski   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
62099ecb0ebSMarek Sokolowski   ASSIGN_OR_RETURN(Items, parseMenuItemsList());
6210eaee545SJonas Devlieghere   return std::make_unique<MenuResource>(std::move(*OptStatements),
62211adbacaSMartin Storsjo                                          std::move(*Items), MemoryFlags);
62399ecb0ebSMarek Sokolowski }
62499ecb0ebSMarek Sokolowski 
parseMenuItemsList()62599ecb0ebSMarek Sokolowski Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
62699ecb0ebSMarek Sokolowski   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
62799ecb0ebSMarek Sokolowski 
62899ecb0ebSMarek Sokolowski   MenuDefinitionList List;
62999ecb0ebSMarek Sokolowski 
63099ecb0ebSMarek Sokolowski   // Read a set of items. Each item is of one of three kinds:
63199ecb0ebSMarek Sokolowski   //   MENUITEM SEPARATOR
63299ecb0ebSMarek Sokolowski   //   MENUITEM caption:String, result:Int [, menu flags]...
63399ecb0ebSMarek Sokolowski   //   POPUP caption:String [, menu flags]... { items... }
63499ecb0ebSMarek Sokolowski   while (!consumeOptionalType(Kind::BlockEnd)) {
63599ecb0ebSMarek Sokolowski     ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
63699ecb0ebSMarek Sokolowski 
63742f74e82SMartin Storsjö     bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM");
63842f74e82SMartin Storsjö     bool IsPopup = ItemTypeResult->equals_insensitive("POPUP");
63999ecb0ebSMarek Sokolowski     if (!IsMenuItem && !IsPopup)
64099ecb0ebSMarek Sokolowski       return getExpectedError("MENUITEM, POPUP, END or '}'", true);
64199ecb0ebSMarek Sokolowski 
64299ecb0ebSMarek Sokolowski     if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
64399ecb0ebSMarek Sokolowski       // Now, expecting SEPARATOR.
64499ecb0ebSMarek Sokolowski       ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
64542f74e82SMartin Storsjö       if (SeparatorResult->equals_insensitive("SEPARATOR")) {
6460eaee545SJonas Devlieghere         List.addDefinition(std::make_unique<MenuSeparator>());
64799ecb0ebSMarek Sokolowski         continue;
64899ecb0ebSMarek Sokolowski       }
64999ecb0ebSMarek Sokolowski 
65099ecb0ebSMarek Sokolowski       return getExpectedError("SEPARATOR or string", true);
65199ecb0ebSMarek Sokolowski     }
65299ecb0ebSMarek Sokolowski 
65399ecb0ebSMarek Sokolowski     // Not a separator. Read the caption.
65499ecb0ebSMarek Sokolowski     ASSIGN_OR_RETURN(CaptionResult, readString());
65599ecb0ebSMarek Sokolowski 
65699ecb0ebSMarek Sokolowski     // If MENUITEM, expect also a comma and an integer.
65799ecb0ebSMarek Sokolowski     uint32_t MenuResult = -1;
65899ecb0ebSMarek Sokolowski 
65999ecb0ebSMarek Sokolowski     if (IsMenuItem) {
66099ecb0ebSMarek Sokolowski       RETURN_IF_ERROR(consumeType(Kind::Comma));
66199ecb0ebSMarek Sokolowski       ASSIGN_OR_RETURN(IntResult, readInt());
66299ecb0ebSMarek Sokolowski       MenuResult = *IntResult;
66399ecb0ebSMarek Sokolowski     }
66499ecb0ebSMarek Sokolowski 
665c75a087cSMarek Sokolowski     ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
666c75a087cSMarek Sokolowski                                              MenuDefinition::OptionsFlags));
66799ecb0ebSMarek Sokolowski 
66899ecb0ebSMarek Sokolowski     if (IsPopup) {
66999ecb0ebSMarek Sokolowski       // If POPUP, read submenu items recursively.
67099ecb0ebSMarek Sokolowski       ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
6710eaee545SJonas Devlieghere       List.addDefinition(std::make_unique<PopupItem>(
6724a765da3SMarek Sokolowski           *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
67399ecb0ebSMarek Sokolowski       continue;
67499ecb0ebSMarek Sokolowski     }
67599ecb0ebSMarek Sokolowski 
67699ecb0ebSMarek Sokolowski     assert(IsMenuItem);
67799ecb0ebSMarek Sokolowski     List.addDefinition(
6780eaee545SJonas Devlieghere         std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
67999ecb0ebSMarek Sokolowski   }
68099ecb0ebSMarek Sokolowski 
681c55cf4afSBill Wendling   return std::move(List);
68299ecb0ebSMarek Sokolowski }
68399ecb0ebSMarek Sokolowski 
parseStringTableResource()6845cd3d5c8SMarek Sokolowski RCParser::ParseType RCParser::parseStringTableResource() {
68511adbacaSMartin Storsjo   uint16_t MemoryFlags =
68611adbacaSMartin Storsjo       parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
6875cd3d5c8SMarek Sokolowski   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
6885cd3d5c8SMarek Sokolowski   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
6895cd3d5c8SMarek Sokolowski 
6900eaee545SJonas Devlieghere   auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),
69111adbacaSMartin Storsjo                                                       MemoryFlags);
6925cd3d5c8SMarek Sokolowski 
6935cd3d5c8SMarek Sokolowski   // Read strings until we reach the end of the block.
6945cd3d5c8SMarek Sokolowski   while (!consumeOptionalType(Kind::BlockEnd)) {
6955cd3d5c8SMarek Sokolowski     // Each definition consists of string's ID (an integer) and a string.
6965cd3d5c8SMarek Sokolowski     // Some examples in documentation suggest that there might be a comma in
6975cd3d5c8SMarek Sokolowski     // between, however we strictly adhere to the single statement definition.
6985cd3d5c8SMarek Sokolowski     ASSIGN_OR_RETURN(IDResult, readInt());
6999410276cSMartin Storsjo     consumeOptionalType(Kind::Comma);
700b989fcbaSMartin Storsjö 
701b989fcbaSMartin Storsjö     std::vector<StringRef> Strings;
7025cd3d5c8SMarek Sokolowski     ASSIGN_OR_RETURN(StrResult, readString());
703b989fcbaSMartin Storsjö     Strings.push_back(*StrResult);
704b989fcbaSMartin Storsjö     while (isNextTokenKind(Kind::String))
705b989fcbaSMartin Storsjö       Strings.push_back(read().value());
706b989fcbaSMartin Storsjö 
707b989fcbaSMartin Storsjö     Table->addStrings(*IDResult, std::move(Strings));
7085cd3d5c8SMarek Sokolowski   }
7095cd3d5c8SMarek Sokolowski 
710c55cf4afSBill Wendling   return std::move(Table);
7115cd3d5c8SMarek Sokolowski }
7125cd3d5c8SMarek Sokolowski 
713fb74cb1eSMarek Sokolowski Expected<std::unique_ptr<VersionInfoBlock>>
parseVersionInfoBlockContents(StringRef BlockName)714fb74cb1eSMarek Sokolowski RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
715fb74cb1eSMarek Sokolowski   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
716fb74cb1eSMarek Sokolowski 
7170eaee545SJonas Devlieghere   auto Contents = std::make_unique<VersionInfoBlock>(BlockName);
718fb74cb1eSMarek Sokolowski 
719fb74cb1eSMarek Sokolowski   while (!isNextTokenKind(Kind::BlockEnd)) {
720fb74cb1eSMarek Sokolowski     ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
721fb74cb1eSMarek Sokolowski     Contents->addStmt(std::move(*Stmt));
722fb74cb1eSMarek Sokolowski   }
723fb74cb1eSMarek Sokolowski 
724fb74cb1eSMarek Sokolowski   consume(); // Consume BlockEnd.
725fb74cb1eSMarek Sokolowski 
726c55cf4afSBill Wendling   return std::move(Contents);
727fb74cb1eSMarek Sokolowski }
728fb74cb1eSMarek Sokolowski 
parseVersionInfoStmt()729fb74cb1eSMarek Sokolowski Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
730fb74cb1eSMarek Sokolowski   // Expect either BLOCK or VALUE, then a name or a key (a string).
731fb74cb1eSMarek Sokolowski   ASSIGN_OR_RETURN(TypeResult, readIdentifier());
732fb74cb1eSMarek Sokolowski 
73342f74e82SMartin Storsjö   if (TypeResult->equals_insensitive("BLOCK")) {
734fb74cb1eSMarek Sokolowski     ASSIGN_OR_RETURN(NameResult, readString());
735fb74cb1eSMarek Sokolowski     return parseVersionInfoBlockContents(*NameResult);
736fb74cb1eSMarek Sokolowski   }
737fb74cb1eSMarek Sokolowski 
73842f74e82SMartin Storsjö   if (TypeResult->equals_insensitive("VALUE")) {
739fb74cb1eSMarek Sokolowski     ASSIGN_OR_RETURN(KeyResult, readString());
74007bc04ffSZachary Turner     // Read a non-empty list of strings and/or ints, each
74107bc04ffSZachary Turner     // possibly preceded by a comma. Unfortunately, the tool behavior depends
74207bc04ffSZachary Turner     // on them existing or not, so we need to memorize where we found them.
743fb74cb1eSMarek Sokolowski     std::vector<IntOrString> Values;
744*5f4ae564SJan Svoboda     BitVector PrecedingCommas;
74507bc04ffSZachary Turner     RETURN_IF_ERROR(consumeType(Kind::Comma));
74607bc04ffSZachary Turner     while (!isNextTokenKind(Kind::Identifier) &&
74707bc04ffSZachary Turner            !isNextTokenKind(Kind::BlockEnd)) {
74807bc04ffSZachary Turner       // Try to eat a comma if it's not the first statement.
74907bc04ffSZachary Turner       bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
750fb74cb1eSMarek Sokolowski       ASSIGN_OR_RETURN(ValueResult, readIntOrString());
751fb74cb1eSMarek Sokolowski       Values.push_back(*ValueResult);
75207bc04ffSZachary Turner       PrecedingCommas.push_back(HadComma);
753fb74cb1eSMarek Sokolowski     }
7540eaee545SJonas Devlieghere     return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
75507bc04ffSZachary Turner                                                std::move(PrecedingCommas));
756fb74cb1eSMarek Sokolowski   }
757fb74cb1eSMarek Sokolowski 
758fb74cb1eSMarek Sokolowski   return getExpectedError("BLOCK or VALUE", true);
759fb74cb1eSMarek Sokolowski }
760fb74cb1eSMarek Sokolowski 
761fb74cb1eSMarek Sokolowski Expected<VersionInfoResource::VersionInfoFixed>
parseVersionInfoFixed()762fb74cb1eSMarek Sokolowski RCParser::parseVersionInfoFixed() {
763fb74cb1eSMarek Sokolowski   using RetType = VersionInfoResource::VersionInfoFixed;
764fb74cb1eSMarek Sokolowski   RetType Result;
765fb74cb1eSMarek Sokolowski 
766fb74cb1eSMarek Sokolowski   // Read until the beginning of the block.
767fb74cb1eSMarek Sokolowski   while (!isNextTokenKind(Kind::BlockBegin)) {
768fb74cb1eSMarek Sokolowski     ASSIGN_OR_RETURN(TypeResult, readIdentifier());
769fb74cb1eSMarek Sokolowski     auto FixedType = RetType::getFixedType(*TypeResult);
770fb74cb1eSMarek Sokolowski 
771fb74cb1eSMarek Sokolowski     if (!RetType::isTypeSupported(FixedType))
772fb74cb1eSMarek Sokolowski       return getExpectedError("fixed VERSIONINFO statement type", true);
773fb74cb1eSMarek Sokolowski     if (Result.IsTypePresent[FixedType])
774fb74cb1eSMarek Sokolowski       return getExpectedError("yet unread fixed VERSIONINFO statement type",
775fb74cb1eSMarek Sokolowski                               true);
776fb74cb1eSMarek Sokolowski 
777fb74cb1eSMarek Sokolowski     // VERSION variations take multiple integers.
778fb74cb1eSMarek Sokolowski     size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
779794467b9SMartin Storsjö     ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(1, NumInts));
78007bc04ffSZachary Turner     SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
781794467b9SMartin Storsjö     while (ArgInts.size() < NumInts)
782794467b9SMartin Storsjö       ArgInts.push_back(0);
78307bc04ffSZachary Turner     Result.setValue(FixedType, ArgInts);
784fb74cb1eSMarek Sokolowski   }
785fb74cb1eSMarek Sokolowski 
786fb74cb1eSMarek Sokolowski   return Result;
787fb74cb1eSMarek Sokolowski }
788fb74cb1eSMarek Sokolowski 
parseLanguageStmt()7895cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseLanguageStmt() {
7905cd3d5c8SMarek Sokolowski   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
7910eaee545SJonas Devlieghere   return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
7925cd3d5c8SMarek Sokolowski }
7935cd3d5c8SMarek Sokolowski 
parseCharacteristicsStmt()7945cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
7955cd3d5c8SMarek Sokolowski   ASSIGN_OR_RETURN(Arg, readInt());
7960eaee545SJonas Devlieghere   return std::make_unique<CharacteristicsStmt>(*Arg);
7975cd3d5c8SMarek Sokolowski }
7985cd3d5c8SMarek Sokolowski 
parseVersionStmt()7995cd3d5c8SMarek Sokolowski RCParser::ParseOptionType RCParser::parseVersionStmt() {
8005cd3d5c8SMarek Sokolowski   ASSIGN_OR_RETURN(Arg, readInt());
8010eaee545SJonas Devlieghere   return std::make_unique<VersionStmt>(*Arg);
8025cd3d5c8SMarek Sokolowski }
8035cd3d5c8SMarek Sokolowski 
parseCaptionStmt()8044ac54d93SMarek Sokolowski RCParser::ParseOptionType RCParser::parseCaptionStmt() {
8054ac54d93SMarek Sokolowski   ASSIGN_OR_RETURN(Arg, readString());
8060eaee545SJonas Devlieghere   return std::make_unique<CaptionStmt>(*Arg);
8074ac54d93SMarek Sokolowski }
8084ac54d93SMarek Sokolowski 
parseClassStmt()809e241ce6fSMartin Storsjo RCParser::ParseOptionType RCParser::parseClassStmt() {
810e241ce6fSMartin Storsjo   ASSIGN_OR_RETURN(Arg, readIntOrString());
8110eaee545SJonas Devlieghere   return std::make_unique<ClassStmt>(*Arg);
812e241ce6fSMartin Storsjo }
813e241ce6fSMartin Storsjo 
parseFontStmt(OptStmtType DialogType)814420090afSZachary Turner RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
815420090afSZachary Turner   assert(DialogType != OptStmtType::BasicStmt);
816420090afSZachary Turner 
8174ac54d93SMarek Sokolowski   ASSIGN_OR_RETURN(SizeResult, readInt());
8184ac54d93SMarek Sokolowski   RETURN_IF_ERROR(consumeType(Kind::Comma));
8194ac54d93SMarek Sokolowski   ASSIGN_OR_RETURN(NameResult, readString());
820420090afSZachary Turner 
821420090afSZachary Turner   // Default values for the optional arguments.
822420090afSZachary Turner   uint32_t FontWeight = 0;
823420090afSZachary Turner   bool FontItalic = false;
824420090afSZachary Turner   uint32_t FontCharset = 1;
825420090afSZachary Turner   if (DialogType == OptStmtType::DialogExStmt) {
826420090afSZachary Turner     if (consumeOptionalType(Kind::Comma)) {
827420090afSZachary Turner       ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
828420090afSZachary Turner       if (Args->size() >= 1)
829420090afSZachary Turner         FontWeight = (*Args)[0];
830420090afSZachary Turner       if (Args->size() >= 2)
831420090afSZachary Turner         FontItalic = (*Args)[1] != 0;
832420090afSZachary Turner       if (Args->size() >= 3)
833420090afSZachary Turner         FontCharset = (*Args)[2];
834420090afSZachary Turner     }
835420090afSZachary Turner   }
8360eaee545SJonas Devlieghere   return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
837420090afSZachary Turner                                      FontItalic, FontCharset);
8384ac54d93SMarek Sokolowski }
8394ac54d93SMarek Sokolowski 
parseStyleStmt()8404ac54d93SMarek Sokolowski RCParser::ParseOptionType RCParser::parseStyleStmt() {
8414ac54d93SMarek Sokolowski   ASSIGN_OR_RETURN(Arg, readInt());
8420eaee545SJonas Devlieghere   return std::make_unique<StyleStmt>(*Arg);
8434ac54d93SMarek Sokolowski }
8444ac54d93SMarek Sokolowski 
parseExStyleStmt()845a876b5c0SMartin Storsjo RCParser::ParseOptionType RCParser::parseExStyleStmt() {
846a876b5c0SMartin Storsjo   ASSIGN_OR_RETURN(Arg, readInt());
8470eaee545SJonas Devlieghere   return std::make_unique<ExStyleStmt>(*Arg);
848a876b5c0SMartin Storsjo }
849a876b5c0SMartin Storsjo 
getExpectedError(const Twine & Message,bool IsAlreadyRead)850514b7105SZachary Turner Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
8515cd3d5c8SMarek Sokolowski   return make_error<ParserError>(
8525cd3d5c8SMarek Sokolowski       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
8535cd3d5c8SMarek Sokolowski }
8545cd3d5c8SMarek Sokolowski 
8555cd3d5c8SMarek Sokolowski } // namespace rc
8565cd3d5c8SMarek Sokolowski } // namespace llvm
857