Home | History | Annotate | Download | only in llvm-rc
      1 //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===---------------------------------------------------------------------===//
      9 //
     10 // This implements the parser defined in ResourceScriptParser.h.
     11 //
     12 //===---------------------------------------------------------------------===//
     13 
     14 #include "ResourceScriptParser.h"
     15 #include "llvm/Option/ArgList.h"
     16 #include "llvm/Support/FileSystem.h"
     17 #include "llvm/Support/Path.h"
     18 #include "llvm/Support/Process.h"
     19 
     20 // Take an expression returning llvm::Error and forward the error if it exists.
     21 #define RETURN_IF_ERROR(Expr)                                                  \
     22   if (auto Err = (Expr))                                                       \
     23     return std::move(Err);
     24 
     25 // Take an expression returning llvm::Expected<T> and assign it to Var or
     26 // forward the error out of the function.
     27 #define ASSIGN_OR_RETURN(Var, Expr)                                            \
     28   auto Var = (Expr);                                                           \
     29   if (!Var)                                                                    \
     30     return Var.takeError();
     31 
     32 namespace llvm {
     33 namespace rc {
     34 
     35 RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
     36                                    const LocIter End)
     37     : ErrorLoc(CurLoc), FileEnd(End) {
     38   CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
     39                (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
     40 }
     41 
     42 char RCParser::ParserError::ID = 0;
     43 
     44 RCParser::RCParser(std::vector<RCToken> TokenList)
     45     : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
     46 
     47 bool RCParser::isEof() const { return CurLoc == End; }
     48 
     49 RCParser::ParseType RCParser::parseSingleResource() {
     50   // The first thing we read is usually a resource's name. However, in some
     51   // cases (LANGUAGE and STRINGTABLE) the resources don't have their names
     52   // and the first token to be read is the type.
     53   ASSIGN_OR_RETURN(NameToken, readTypeOrName());
     54 
     55   if (NameToken->equalsLower("LANGUAGE"))
     56     return parseLanguageResource();
     57   else if (NameToken->equalsLower("STRINGTABLE"))
     58     return parseStringTableResource();
     59 
     60   // If it's not an unnamed resource, what we've just read is a name. Now,
     61   // read resource type;
     62   ASSIGN_OR_RETURN(TypeToken, readTypeOrName());
     63 
     64   ParseType Result = std::unique_ptr<RCResource>();
     65   (void)!Result;
     66 
     67   if (TypeToken->equalsLower("ACCELERATORS"))
     68     Result = parseAcceleratorsResource();
     69   else if (TypeToken->equalsLower("BITMAP"))
     70     Result = parseBitmapResource();
     71   else if (TypeToken->equalsLower("CURSOR"))
     72     Result = parseCursorResource();
     73   else if (TypeToken->equalsLower("DIALOG"))
     74     Result = parseDialogResource(false);
     75   else if (TypeToken->equalsLower("DIALOGEX"))
     76     Result = parseDialogResource(true);
     77   else if (TypeToken->equalsLower("HTML"))
     78     Result = parseHTMLResource();
     79   else if (TypeToken->equalsLower("ICON"))
     80     Result = parseIconResource();
     81   else if (TypeToken->equalsLower("MENU"))
     82     Result = parseMenuResource();
     83   else if (TypeToken->equalsLower("RCDATA"))
     84     Result = parseUserDefinedResource(RkRcData);
     85   else if (TypeToken->equalsLower("VERSIONINFO"))
     86     Result = parseVersionInfoResource();
     87   else
     88     Result = parseUserDefinedResource(*TypeToken);
     89 
     90   if (Result)
     91     (*Result)->setName(*NameToken);
     92 
     93   return Result;
     94 }
     95 
     96 bool RCParser::isNextTokenKind(Kind TokenKind) const {
     97   return !isEof() && look().kind() == TokenKind;
     98 }
     99 
    100 const RCToken &RCParser::look() const {
    101   assert(!isEof());
    102   return *CurLoc;
    103 }
    104 
    105 const RCToken &RCParser::read() {
    106   assert(!isEof());
    107   return *CurLoc++;
    108 }
    109 
    110 void RCParser::consume() {
    111   assert(!isEof());
    112   CurLoc++;
    113 }
    114 
    115 // An integer description might consist of a single integer or
    116 // an arithmetic expression evaluating to the integer. The expressions
    117 // can contain the following tokens: <int> ( ) + - | & ~. Their meaning
    118 // is the same as in C++.
    119 // The operators in the original RC implementation have the following
    120 // precedence:
    121 //   1) Unary operators (- ~),
    122 //   2) Binary operators (+ - & |), with no precedence.
    123 //
    124 // The following grammar is used to parse the expressions Exp1:
    125 //   Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
    126 //   Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
    127 // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
    128 // separated by binary operators.)
    129 //
    130 // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
    131 // is read by parseIntExpr2().
    132 //
    133 // The original Microsoft tool handles multiple unary operators incorrectly.
    134 // For example, in 16-bit little-endian integers:
    135 //    1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
    136 //    1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
    137 // Our implementation differs from the original one and handles these
    138 // operators correctly:
    139 //    1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
    140 //    1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
    141 
    142 Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); }
    143 
    144 Expected<RCInt> RCParser::parseIntExpr1() {
    145   // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
    146   ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
    147   RCInt Result = *FirstResult;
    148 
    149   while (!isEof() && look().isBinaryOp()) {
    150     auto OpToken = read();
    151     ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
    152 
    153     switch (OpToken.kind()) {
    154     case Kind::Plus:
    155       Result += *NextResult;
    156       break;
    157 
    158     case Kind::Minus:
    159       Result -= *NextResult;
    160       break;
    161 
    162     case Kind::Pipe:
    163       Result |= *NextResult;
    164       break;
    165 
    166     case Kind::Amp:
    167       Result &= *NextResult;
    168       break;
    169 
    170     default:
    171       llvm_unreachable("Already processed all binary ops.");
    172     }
    173   }
    174 
    175   return Result;
    176 }
    177 
    178 Expected<RCInt> RCParser::parseIntExpr2() {
    179   // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
    180   static const char ErrorMsg[] = "'-', '~', integer or '('";
    181 
    182   if (isEof())
    183     return getExpectedError(ErrorMsg);
    184 
    185   switch (look().kind()) {
    186   case Kind::Minus: {
    187     consume();
    188     ASSIGN_OR_RETURN(Result, parseIntExpr2());
    189     return -(*Result);
    190   }
    191 
    192   case Kind::Tilde: {
    193     consume();
    194     ASSIGN_OR_RETURN(Result, parseIntExpr2());
    195     return ~(*Result);
    196   }
    197 
    198   case Kind::Int:
    199     return RCInt(read());
    200 
    201   case Kind::LeftParen: {
    202     consume();
    203     ASSIGN_OR_RETURN(Result, parseIntExpr1());
    204     RETURN_IF_ERROR(consumeType(Kind::RightParen));
    205     return *Result;
    206   }
    207 
    208   default:
    209     return getExpectedError(ErrorMsg);
    210   }
    211 }
    212 
    213 Expected<StringRef> RCParser::readString() {
    214   if (!isNextTokenKind(Kind::String))
    215     return getExpectedError("string");
    216   return read().value();
    217 }
    218 
    219 Expected<StringRef> RCParser::readFilename() {
    220   if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
    221     return getExpectedError("string");
    222   return read().value();
    223 }
    224 
    225 Expected<StringRef> RCParser::readIdentifier() {
    226   if (!isNextTokenKind(Kind::Identifier))
    227     return getExpectedError("identifier");
    228   return read().value();
    229 }
    230 
    231 Expected<IntOrString> RCParser::readIntOrString() {
    232   if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
    233     return getExpectedError("int or string");
    234   return IntOrString(read());
    235 }
    236 
    237 Expected<IntOrString> RCParser::readTypeOrName() {
    238   // We suggest that the correct resource name or type should be either an
    239   // identifier or an integer. The original RC tool is much more liberal.
    240   if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
    241     return getExpectedError("int or identifier");
    242   return IntOrString(read());
    243 }
    244 
    245 Error RCParser::consumeType(Kind TokenKind) {
    246   if (isNextTokenKind(TokenKind)) {
    247     consume();
    248     return Error::success();
    249   }
    250 
    251   switch (TokenKind) {
    252 #define TOKEN(TokenName)                                                       \
    253   case Kind::TokenName:                                                        \
    254     return getExpectedError(#TokenName);
    255 #define SHORT_TOKEN(TokenName, TokenCh)                                        \
    256   case Kind::TokenName:                                                        \
    257     return getExpectedError(#TokenCh);
    258 #include "ResourceScriptTokenList.def"
    259   }
    260 
    261   llvm_unreachable("All case options exhausted.");
    262 }
    263 
    264 bool RCParser::consumeOptionalType(Kind TokenKind) {
    265   if (isNextTokenKind(TokenKind)) {
    266     consume();
    267     return true;
    268   }
    269 
    270   return false;
    271 }
    272 
    273 Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
    274                                                              size_t MaxCount) {
    275   assert(MinCount <= MaxCount);
    276 
    277   SmallVector<RCInt, 8> Result;
    278 
    279   auto FailureHandler =
    280       [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
    281     if (Result.size() < MinCount)
    282       return std::move(Err);
    283     consumeError(std::move(Err));
    284     return Result;
    285   };
    286 
    287   for (size_t i = 0; i < MaxCount; ++i) {
    288     // Try to read a comma unless we read the first token.
    289     // Sometimes RC tool requires them and sometimes not. We decide to
    290     // always require them.
    291     if (i >= 1) {
    292       if (auto CommaError = consumeType(Kind::Comma))
    293         return FailureHandler(std::move(CommaError));
    294     }
    295 
    296     if (auto IntResult = readInt())
    297       Result.push_back(*IntResult);
    298     else
    299       return FailureHandler(IntResult.takeError());
    300   }
    301 
    302   return std::move(Result);
    303 }
    304 
    305 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
    306                                         ArrayRef<uint32_t> FlagValues) {
    307   assert(!FlagDesc.empty());
    308   assert(FlagDesc.size() == FlagValues.size());
    309 
    310   uint32_t Result = 0;
    311   while (isNextTokenKind(Kind::Comma)) {
    312     consume();
    313     ASSIGN_OR_RETURN(FlagResult, readIdentifier());
    314     bool FoundFlag = false;
    315 
    316     for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
    317       if (!FlagResult->equals_lower(FlagDesc[FlagId]))
    318         continue;
    319 
    320       Result |= FlagValues[FlagId];
    321       FoundFlag = true;
    322       break;
    323     }
    324 
    325     if (!FoundFlag)
    326       return getExpectedError(join(FlagDesc, "/"), true);
    327   }
    328 
    329   return Result;
    330 }
    331 
    332 uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
    333   while (!isEof()) {
    334     const RCToken &Token = look();
    335     if (Token.kind() != Kind::Identifier)
    336       return Flags;
    337     const StringRef Ident = Token.value();
    338     if (Ident.equals_lower("PRELOAD"))
    339       Flags |= MfPreload;
    340     else if (Ident.equals_lower("LOADONCALL"))
    341       Flags &= ~MfPreload;
    342     else if (Ident.equals_lower("FIXED"))
    343       Flags &= ~(MfMoveable | MfDiscardable);
    344     else if (Ident.equals_lower("MOVEABLE"))
    345       Flags |= MfMoveable;
    346     else if (Ident.equals_lower("DISCARDABLE"))
    347       Flags |= MfDiscardable | MfMoveable | MfPure;
    348     else if (Ident.equals_lower("PURE"))
    349       Flags |= MfPure;
    350     else if (Ident.equals_lower("IMPURE"))
    351       Flags &= ~(MfPure | MfDiscardable);
    352     else if (Ident.equals_lower("SHARED"))
    353       Flags |= MfPure;
    354     else if (Ident.equals_lower("NONSHARED"))
    355       Flags &= ~(MfPure | MfDiscardable);
    356     else
    357       return Flags;
    358     consume();
    359   }
    360   return Flags;
    361 }
    362 
    363 Expected<OptionalStmtList>
    364 RCParser::parseOptionalStatements(OptStmtType StmtsType) {
    365   OptionalStmtList Result;
    366 
    367   // The last statement is always followed by the start of the block.
    368   while (!isNextTokenKind(Kind::BlockBegin)) {
    369     ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
    370     Result.addStmt(std::move(*SingleParse));
    371   }
    372 
    373   return std::move(Result);
    374 }
    375 
    376 Expected<std::unique_ptr<OptionalStmt>>
    377 RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
    378   ASSIGN_OR_RETURN(TypeToken, readIdentifier());
    379   if (TypeToken->equals_lower("CHARACTERISTICS"))
    380     return parseCharacteristicsStmt();
    381   if (TypeToken->equals_lower("LANGUAGE"))
    382     return parseLanguageStmt();
    383   if (TypeToken->equals_lower("VERSION"))
    384     return parseVersionStmt();
    385 
    386   if (StmtsType != OptStmtType::BasicStmt) {
    387     if (TypeToken->equals_lower("CAPTION"))
    388       return parseCaptionStmt();
    389     if (TypeToken->equals_lower("CLASS"))
    390       return parseClassStmt();
    391     if (TypeToken->equals_lower("FONT"))
    392       return parseFontStmt(StmtsType);
    393     if (TypeToken->equals_lower("STYLE"))
    394       return parseStyleStmt();
    395   }
    396 
    397   return getExpectedError("optional statement type, BEGIN or '{'",
    398                           /* IsAlreadyRead = */ true);
    399 }
    400 
    401 RCParser::ParseType RCParser::parseLanguageResource() {
    402   // Read LANGUAGE as an optional statement. If it's read correctly, we can
    403   // upcast it to RCResource.
    404   return parseLanguageStmt();
    405 }
    406 
    407 RCParser::ParseType RCParser::parseAcceleratorsResource() {
    408   uint16_t MemoryFlags =
    409       parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
    410   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
    411   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
    412 
    413   auto Accels = llvm::make_unique<AcceleratorsResource>(
    414       std::move(*OptStatements), MemoryFlags);
    415 
    416   while (!consumeOptionalType(Kind::BlockEnd)) {
    417     ASSIGN_OR_RETURN(EventResult, readIntOrString());
    418     RETURN_IF_ERROR(consumeType(Kind::Comma));
    419     ASSIGN_OR_RETURN(IDResult, readInt());
    420     ASSIGN_OR_RETURN(
    421         FlagsResult,
    422         parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
    423                    AcceleratorsResource::Accelerator::OptionsFlags));
    424     Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
    425   }
    426 
    427   return std::move(Accels);
    428 }
    429 
    430 RCParser::ParseType RCParser::parseCursorResource() {
    431   uint16_t MemoryFlags =
    432       parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
    433   ASSIGN_OR_RETURN(Arg, readFilename());
    434   return llvm::make_unique<CursorResource>(*Arg, MemoryFlags);
    435 }
    436 
    437 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
    438   uint16_t MemoryFlags =
    439       parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
    440   // Dialog resources have the following format of the arguments:
    441   //  DIALOG:   x, y, width, height [opt stmts...] {controls...}
    442   //  DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
    443   // These are very similar, so we parse them together.
    444   ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
    445 
    446   uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
    447   if (IsExtended && consumeOptionalType(Kind::Comma)) {
    448     ASSIGN_OR_RETURN(HelpIDResult, readInt());
    449     HelpID = *HelpIDResult;
    450   }
    451 
    452   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
    453                                       IsExtended ? OptStmtType::DialogExStmt
    454                                                  : OptStmtType::DialogStmt));
    455 
    456   assert(isNextTokenKind(Kind::BlockBegin) &&
    457          "parseOptionalStatements, when successful, halts on BlockBegin.");
    458   consume();
    459 
    460   auto Dialog = llvm::make_unique<DialogResource>(
    461       (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
    462       HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
    463 
    464   while (!consumeOptionalType(Kind::BlockEnd)) {
    465     ASSIGN_OR_RETURN(ControlDefResult, parseControl());
    466     Dialog->addControl(std::move(*ControlDefResult));
    467   }
    468 
    469   return std::move(Dialog);
    470 }
    471 
    472 RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
    473   uint16_t MemoryFlags =
    474       parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
    475   if (isEof())
    476     return getExpectedError("filename, '{' or BEGIN");
    477 
    478   // Check if this is a file resource.
    479   switch (look().kind()) {
    480   case Kind::String:
    481   case Kind::Identifier:
    482     return llvm::make_unique<UserDefinedResource>(Type, read().value(),
    483                                                   MemoryFlags);
    484   default:
    485     break;
    486   }
    487 
    488   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
    489   std::vector<IntOrString> Data;
    490 
    491   // Consume comma before each consecutive token except the first one.
    492   bool ConsumeComma = false;
    493   while (!consumeOptionalType(Kind::BlockEnd)) {
    494     if (ConsumeComma)
    495       RETURN_IF_ERROR(consumeType(Kind::Comma));
    496     ConsumeComma = true;
    497 
    498     ASSIGN_OR_RETURN(Item, readIntOrString());
    499     Data.push_back(*Item);
    500   }
    501 
    502   return llvm::make_unique<UserDefinedResource>(Type, std::move(Data),
    503                                                 MemoryFlags);
    504 }
    505 
    506 RCParser::ParseType RCParser::parseVersionInfoResource() {
    507   uint16_t MemoryFlags =
    508       parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
    509   ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
    510   ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
    511   return llvm::make_unique<VersionInfoResource>(
    512       std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
    513 }
    514 
    515 Expected<Control> RCParser::parseControl() {
    516   // Each control definition (except CONTROL) follows one of the schemes below
    517   // depending on the control class:
    518   //  [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
    519   //  [class]       id, x, y, width, height [, style] [, exstyle] [, helpID]
    520   // Note that control ids must be integers.
    521   // Text might be either a string or an integer pointing to resource ID.
    522   ASSIGN_OR_RETURN(ClassResult, readIdentifier());
    523   std::string ClassUpper = ClassResult->upper();
    524   auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
    525   if (CtlInfo == Control::SupportedCtls.end())
    526     return getExpectedError("control type, END or '}'", true);
    527 
    528   // Read caption if necessary.
    529   IntOrString Caption{StringRef()};
    530   if (CtlInfo->getValue().HasTitle) {
    531     ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
    532     RETURN_IF_ERROR(consumeType(Kind::Comma));
    533     Caption = *CaptionResult;
    534   }
    535 
    536   ASSIGN_OR_RETURN(ID, readInt());
    537   RETURN_IF_ERROR(consumeType(Kind::Comma));
    538 
    539   IntOrString Class;
    540   Optional<uint32_t> Style;
    541   if (ClassUpper == "CONTROL") {
    542     // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
    543     ASSIGN_OR_RETURN(ClassStr, readString());
    544     RETURN_IF_ERROR(consumeType(Kind::Comma));
    545     Class = *ClassStr;
    546     ASSIGN_OR_RETURN(StyleVal, readInt());
    547     RETURN_IF_ERROR(consumeType(Kind::Comma));
    548     Style = *StyleVal;
    549   } else {
    550     Class = CtlInfo->getValue().CtlClass;
    551   }
    552 
    553   // x, y, width, height
    554   ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
    555 
    556   if (ClassUpper != "CONTROL") {
    557     if (consumeOptionalType(Kind::Comma)) {
    558       ASSIGN_OR_RETURN(Val, readInt());
    559       Style = *Val;
    560     }
    561   }
    562 
    563   Optional<uint32_t> ExStyle;
    564   if (consumeOptionalType(Kind::Comma)) {
    565     ASSIGN_OR_RETURN(Val, readInt());
    566     ExStyle = *Val;
    567   }
    568   Optional<uint32_t> HelpID;
    569   if (consumeOptionalType(Kind::Comma)) {
    570     ASSIGN_OR_RETURN(Val, readInt());
    571     HelpID = *Val;
    572   }
    573 
    574   return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
    575                  (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
    576 }
    577 
    578 RCParser::ParseType RCParser::parseBitmapResource() {
    579   uint16_t MemoryFlags =
    580       parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
    581   ASSIGN_OR_RETURN(Arg, readFilename());
    582   return llvm::make_unique<BitmapResource>(*Arg, MemoryFlags);
    583 }
    584 
    585 RCParser::ParseType RCParser::parseIconResource() {
    586   uint16_t MemoryFlags =
    587       parseMemoryFlags(IconResource::getDefaultMemoryFlags());
    588   ASSIGN_OR_RETURN(Arg, readFilename());
    589   return llvm::make_unique<IconResource>(*Arg, MemoryFlags);
    590 }
    591 
    592 RCParser::ParseType RCParser::parseHTMLResource() {
    593   uint16_t MemoryFlags =
    594       parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
    595   ASSIGN_OR_RETURN(Arg, readFilename());
    596   return llvm::make_unique<HTMLResource>(*Arg, MemoryFlags);
    597 }
    598 
    599 RCParser::ParseType RCParser::parseMenuResource() {
    600   uint16_t MemoryFlags =
    601       parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
    602   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
    603   ASSIGN_OR_RETURN(Items, parseMenuItemsList());
    604   return llvm::make_unique<MenuResource>(std::move(*OptStatements),
    605                                          std::move(*Items), MemoryFlags);
    606 }
    607 
    608 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
    609   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
    610 
    611   MenuDefinitionList List;
    612 
    613   // Read a set of items. Each item is of one of three kinds:
    614   //   MENUITEM SEPARATOR
    615   //   MENUITEM caption:String, result:Int [, menu flags]...
    616   //   POPUP caption:String [, menu flags]... { items... }
    617   while (!consumeOptionalType(Kind::BlockEnd)) {
    618     ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
    619 
    620     bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
    621     bool IsPopup = ItemTypeResult->equals_lower("POPUP");
    622     if (!IsMenuItem && !IsPopup)
    623       return getExpectedError("MENUITEM, POPUP, END or '}'", true);
    624 
    625     if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
    626       // Now, expecting SEPARATOR.
    627       ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
    628       if (SeparatorResult->equals_lower("SEPARATOR")) {
    629         List.addDefinition(llvm::make_unique<MenuSeparator>());
    630         continue;
    631       }
    632 
    633       return getExpectedError("SEPARATOR or string", true);
    634     }
    635 
    636     // Not a separator. Read the caption.
    637     ASSIGN_OR_RETURN(CaptionResult, readString());
    638 
    639     // If MENUITEM, expect also a comma and an integer.
    640     uint32_t MenuResult = -1;
    641 
    642     if (IsMenuItem) {
    643       RETURN_IF_ERROR(consumeType(Kind::Comma));
    644       ASSIGN_OR_RETURN(IntResult, readInt());
    645       MenuResult = *IntResult;
    646     }
    647 
    648     ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
    649                                              MenuDefinition::OptionsFlags));
    650 
    651     if (IsPopup) {
    652       // If POPUP, read submenu items recursively.
    653       ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
    654       List.addDefinition(llvm::make_unique<PopupItem>(
    655           *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
    656       continue;
    657     }
    658 
    659     assert(IsMenuItem);
    660     List.addDefinition(
    661         llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
    662   }
    663 
    664   return std::move(List);
    665 }
    666 
    667 RCParser::ParseType RCParser::parseStringTableResource() {
    668   uint16_t MemoryFlags =
    669       parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
    670   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
    671   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
    672 
    673   auto Table = llvm::make_unique<StringTableResource>(std::move(*OptStatements),
    674                                                       MemoryFlags);
    675 
    676   // Read strings until we reach the end of the block.
    677   while (!consumeOptionalType(Kind::BlockEnd)) {
    678     // Each definition consists of string's ID (an integer) and a string.
    679     // Some examples in documentation suggest that there might be a comma in
    680     // between, however we strictly adhere to the single statement definition.
    681     ASSIGN_OR_RETURN(IDResult, readInt());
    682     consumeOptionalType(Kind::Comma);
    683     ASSIGN_OR_RETURN(StrResult, readString());
    684     Table->addString(*IDResult, *StrResult);
    685   }
    686 
    687   return std::move(Table);
    688 }
    689 
    690 Expected<std::unique_ptr<VersionInfoBlock>>
    691 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
    692   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
    693 
    694   auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
    695 
    696   while (!isNextTokenKind(Kind::BlockEnd)) {
    697     ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
    698     Contents->addStmt(std::move(*Stmt));
    699   }
    700 
    701   consume(); // Consume BlockEnd.
    702 
    703   return std::move(Contents);
    704 }
    705 
    706 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
    707   // Expect either BLOCK or VALUE, then a name or a key (a string).
    708   ASSIGN_OR_RETURN(TypeResult, readIdentifier());
    709 
    710   if (TypeResult->equals_lower("BLOCK")) {
    711     ASSIGN_OR_RETURN(NameResult, readString());
    712     return parseVersionInfoBlockContents(*NameResult);
    713   }
    714 
    715   if (TypeResult->equals_lower("VALUE")) {
    716     ASSIGN_OR_RETURN(KeyResult, readString());
    717     // Read a non-empty list of strings and/or ints, each
    718     // possibly preceded by a comma. Unfortunately, the tool behavior depends
    719     // on them existing or not, so we need to memorize where we found them.
    720     std::vector<IntOrString> Values;
    721     std::vector<bool> PrecedingCommas;
    722     RETURN_IF_ERROR(consumeType(Kind::Comma));
    723     while (!isNextTokenKind(Kind::Identifier) &&
    724            !isNextTokenKind(Kind::BlockEnd)) {
    725       // Try to eat a comma if it's not the first statement.
    726       bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
    727       ASSIGN_OR_RETURN(ValueResult, readIntOrString());
    728       Values.push_back(*ValueResult);
    729       PrecedingCommas.push_back(HadComma);
    730     }
    731     return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
    732                                                std::move(PrecedingCommas));
    733   }
    734 
    735   return getExpectedError("BLOCK or VALUE", true);
    736 }
    737 
    738 Expected<VersionInfoResource::VersionInfoFixed>
    739 RCParser::parseVersionInfoFixed() {
    740   using RetType = VersionInfoResource::VersionInfoFixed;
    741   RetType Result;
    742 
    743   // Read until the beginning of the block.
    744   while (!isNextTokenKind(Kind::BlockBegin)) {
    745     ASSIGN_OR_RETURN(TypeResult, readIdentifier());
    746     auto FixedType = RetType::getFixedType(*TypeResult);
    747 
    748     if (!RetType::isTypeSupported(FixedType))
    749       return getExpectedError("fixed VERSIONINFO statement type", true);
    750     if (Result.IsTypePresent[FixedType])
    751       return getExpectedError("yet unread fixed VERSIONINFO statement type",
    752                               true);
    753 
    754     // VERSION variations take multiple integers.
    755     size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
    756     ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
    757     SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
    758     Result.setValue(FixedType, ArgInts);
    759   }
    760 
    761   return Result;
    762 }
    763 
    764 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
    765   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
    766   return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
    767 }
    768 
    769 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
    770   ASSIGN_OR_RETURN(Arg, readInt());
    771   return llvm::make_unique<CharacteristicsStmt>(*Arg);
    772 }
    773 
    774 RCParser::ParseOptionType RCParser::parseVersionStmt() {
    775   ASSIGN_OR_RETURN(Arg, readInt());
    776   return llvm::make_unique<VersionStmt>(*Arg);
    777 }
    778 
    779 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
    780   ASSIGN_OR_RETURN(Arg, readString());
    781   return llvm::make_unique<CaptionStmt>(*Arg);
    782 }
    783 
    784 RCParser::ParseOptionType RCParser::parseClassStmt() {
    785   ASSIGN_OR_RETURN(Arg, readIntOrString());
    786   return llvm::make_unique<ClassStmt>(*Arg);
    787 }
    788 
    789 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
    790   assert(DialogType != OptStmtType::BasicStmt);
    791 
    792   ASSIGN_OR_RETURN(SizeResult, readInt());
    793   RETURN_IF_ERROR(consumeType(Kind::Comma));
    794   ASSIGN_OR_RETURN(NameResult, readString());
    795 
    796   // Default values for the optional arguments.
    797   uint32_t FontWeight = 0;
    798   bool FontItalic = false;
    799   uint32_t FontCharset = 1;
    800   if (DialogType == OptStmtType::DialogExStmt) {
    801     if (consumeOptionalType(Kind::Comma)) {
    802       ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
    803       if (Args->size() >= 1)
    804         FontWeight = (*Args)[0];
    805       if (Args->size() >= 2)
    806         FontItalic = (*Args)[1] != 0;
    807       if (Args->size() >= 3)
    808         FontCharset = (*Args)[2];
    809     }
    810   }
    811   return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
    812                                      FontItalic, FontCharset);
    813 }
    814 
    815 RCParser::ParseOptionType RCParser::parseStyleStmt() {
    816   ASSIGN_OR_RETURN(Arg, readInt());
    817   return llvm::make_unique<StyleStmt>(*Arg);
    818 }
    819 
    820 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
    821   return make_error<ParserError>(
    822       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
    823 }
    824 
    825 } // namespace rc
    826 } // namespace llvm
    827