brintos

brintos / llvm-project-archived public Read only

0
0
Text · 32.9 KiB · e4efc83 Raw
1009 lines · cpp
1//===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===---------------------------------------------------------------------===//8//9// This implements the parser defined in ResourceScriptParser.h.10//11//===---------------------------------------------------------------------===//12 13#include "ResourceScriptParser.h"14#include "llvm/ADT/StringExtras.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 or26// 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 32namespace llvm {33namespace rc {34 35RCParser::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 42char RCParser::ParserError::ID = 0;43 44RCParser::RCParser(std::vector<RCToken> TokenList)45    : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}46 47bool RCParser::isEof() const { return CurLoc == End; }48 49RCParser::ParseType RCParser::parseSingleResource() {50  // The first thing we read is usually a resource's name. However, in some51  // cases (LANGUAGE and STRINGTABLE) the resources don't have their names52  // 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("MENUEX"))84    Result = parseMenuExResource();85  else if (TypeToken->equalsLower("RCDATA"))86    Result = parseUserDefinedResource(RkRcData);87  else if (TypeToken->equalsLower("VERSIONINFO"))88    Result = parseVersionInfoResource();89  else90    Result = parseUserDefinedResource(*TypeToken);91 92  if (Result)93    (*Result)->setName(*NameToken);94 95  return Result;96}97 98bool RCParser::isNextTokenKind(Kind TokenKind) const {99  return !isEof() && look().kind() == TokenKind;100}101 102const RCToken &RCParser::look() const {103  assert(!isEof());104  return *CurLoc;105}106 107const RCToken &RCParser::read() {108  assert(!isEof());109  return *CurLoc++;110}111 112void RCParser::consume() {113  assert(!isEof());114  CurLoc++;115}116 117// An integer description might consist of a single integer or118// an arithmetic expression evaluating to the integer. The expressions119// can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning120// is the same as in C++ except for 'not' expression.121// The operators in the original RC implementation have the following122// precedence:123//   1) Unary operators (- ~ not),124//   2) Binary operators (+ - & |), with no precedence.125//126// 'not' expression is mostly useful for style values. It evaluates to 0,127// but value given to the operator is stored separately from integer value.128// It's mostly useful for control style expressions and causes bits from129// default control style to be excluded from generated style. For binary130// operators the mask from the right operand is applied to the left operand131// and masks from both operands are combined in operator result.132//133// The following grammar is used to parse the expressions Exp1:134//   Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2135//   Exp2 ::= Exp3 || Exp3 * Exp3 || Exp3 / Exp3136//   Exp3 ::= -Exp3 || ~Exp3 || not Expr3 || Int || (Exp1)137// (More conveniently, Exp1 and Exp2 are non-empty sequences of Exp3138// expressions, separated by binary operators.)139//140// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, Exp2141// is read by parseIntExpr2() and Exp3 is read by parseIntExpr3().142//143// The original Microsoft tool handles multiple unary operators incorrectly.144// For example, in 16-bit little-endian integers:145//    1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;146//    1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.147// Our implementation differs from the original one and handles these148// operators correctly:149//    1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;150//    1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.151 152Expected<RCInt> RCParser::readInt() {153  ASSIGN_OR_RETURN(Value, parseIntExpr1());154  return (*Value).getValue();155}156 157Expected<IntWithNotMask> RCParser::parseIntExpr1() {158  // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.159  ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());160  IntWithNotMask Result = *FirstResult;161 162  while (!isEof() && look().isLowPrecedenceBinaryOp()) {163    auto OpToken = read();164    ASSIGN_OR_RETURN(NextResult, parseIntExpr2());165 166    switch (OpToken.kind()) {167    case Kind::Plus:168      Result += *NextResult;169      break;170 171    case Kind::Minus:172      Result -= *NextResult;173      break;174 175    case Kind::Pipe:176      Result |= *NextResult;177      break;178 179    case Kind::Amp:180      Result &= *NextResult;181      break;182 183    default:184      llvm_unreachable("Already processed all low precedence binary ops.");185    }186  }187 188  return Result;189}190 191Expected<IntWithNotMask> RCParser::parseIntExpr2() {192  // Exp2 ::= Exp3 || Exp3 * Exp3 || Exp3 / Exp3.193  ASSIGN_OR_RETURN(FirstResult, parseIntExpr3());194  IntWithNotMask Result = *FirstResult;195 196  while (!isEof() && look().isHighPrecedenceBinaryOp()) {197    auto OpToken = read();198    ASSIGN_OR_RETURN(NextResult, parseIntExpr3());199 200    switch (OpToken.kind()) {201    case Kind::Asterisk:202      Result *= *NextResult;203      break;204 205    case Kind::Slash:206      Result /= *NextResult;207      break;208 209    default:210      llvm_unreachable("Already processed all high precedence binary ops.");211    }212  }213 214  return Result;215}216 217Expected<IntWithNotMask> RCParser::parseIntExpr3() {218  // Exp3 ::= -Exp3 || ~Exp3 || not Expr3 || Int || (Exp1).219  static const char ErrorMsg[] = "'-', '~', integer or '('";220 221  if (isEof())222    return getExpectedError(ErrorMsg);223 224  switch (look().kind()) {225  case Kind::Minus: {226    consume();227    ASSIGN_OR_RETURN(Result, parseIntExpr3());228    return -(*Result);229  }230 231  case Kind::Tilde: {232    consume();233    ASSIGN_OR_RETURN(Result, parseIntExpr3());234    return ~(*Result);235  }236 237  case Kind::Int:238    return RCInt(read());239 240  case Kind::LeftParen: {241    consume();242    ASSIGN_OR_RETURN(Result, parseIntExpr1());243    RETURN_IF_ERROR(consumeType(Kind::RightParen));244    return *Result;245  }246 247  case Kind::Identifier: {248    if (!read().value().equals_insensitive("not"))249      return getExpectedError(ErrorMsg, true);250    ASSIGN_OR_RETURN(Result, parseIntExpr3());251    return IntWithNotMask(0, (*Result).getValue());252  }253 254  default:255    return getExpectedError(ErrorMsg);256  }257}258 259Expected<StringRef> RCParser::readString() {260  if (!isNextTokenKind(Kind::String))261    return getExpectedError("string");262  return read().value();263}264 265Expected<StringRef> RCParser::readFilename() {266  if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))267    return getExpectedError("string");268  const RCToken &Token = read();269  StringRef Str = Token.value();270  if (Token.kind() != Kind::String)271    return Str;272  while (isNextTokenKind(Kind::String)) {273    const RCToken &NextToken = read();274    StringRef Next = NextToken.value();275    bool IsWide = Str.consume_front_insensitive("L");276    Next.consume_front_insensitive("L");277    bool StrUnquoted = Str.consume_front("\"") && Str.consume_back("\"");278    bool NextUnquoted = Next.consume_front("\"") && Next.consume_back("\"");279    assert(StrUnquoted && NextUnquoted);280    (void)StrUnquoted;281    (void)NextUnquoted;282 283    Str = Saver.save(Twine(IsWide ? "L" : "") + "\"" + Str + Next + "\"");284  }285  return Str;286}287 288Expected<StringRef> RCParser::readIdentifier() {289  if (!isNextTokenKind(Kind::Identifier))290    return getExpectedError("identifier");291  return read().value();292}293 294Expected<IntOrString> RCParser::readIntOrString() {295  if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))296    return getExpectedError("int or string");297  return IntOrString(read());298}299 300Expected<IntOrString> RCParser::readTypeOrName() {301  // We suggest that the correct resource name or type should be either an302  // identifier or an integer. The original RC tool is much more liberal.303  if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))304    return getExpectedError("int or identifier");305  return IntOrString(read());306}307 308Error RCParser::consumeType(Kind TokenKind) {309  if (isNextTokenKind(TokenKind)) {310    consume();311    return Error::success();312  }313 314  switch (TokenKind) {315#define TOKEN(TokenName)                                                       \316  case Kind::TokenName:                                                        \317    return getExpectedError(#TokenName);318#define SHORT_TOKEN(TokenName, TokenCh)                                        \319  case Kind::TokenName:                                                        \320    return getExpectedError(#TokenCh);321#include "ResourceScriptTokenList.def"322  }323 324  llvm_unreachable("All case options exhausted.");325}326 327bool RCParser::consumeOptionalType(Kind TokenKind) {328  if (isNextTokenKind(TokenKind)) {329    consume();330    return true;331  }332 333  return false;334}335 336Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,337                                                             size_t MaxCount) {338  assert(MinCount <= MaxCount);339 340  SmallVector<RCInt, 8> Result;341 342  auto FailureHandler =343      [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {344    if (Result.size() < MinCount)345      return std::move(Err);346    consumeError(std::move(Err));347    return Result;348  };349 350  for (size_t i = 0; i < MaxCount; ++i) {351    // Try to read a comma unless we read the first token.352    // Sometimes RC tool requires them and sometimes not. We decide to353    // always require them.354    if (i >= 1) {355      if (auto CommaError = consumeType(Kind::Comma))356        return FailureHandler(std::move(CommaError));357    }358 359    if (auto IntResult = readInt())360      Result.push_back(*IntResult);361    else362      return FailureHandler(IntResult.takeError());363  }364 365  return std::move(Result);366}367 368Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,369                                        ArrayRef<uint32_t> FlagValues) {370  assert(!FlagDesc.empty());371  assert(FlagDesc.size() == FlagValues.size());372 373  uint32_t Result = 0;374  while (isNextTokenKind(Kind::Comma)) {375    consume();376    ASSIGN_OR_RETURN(FlagResult, readIdentifier());377    bool FoundFlag = false;378 379    for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {380      if (!FlagResult->equals_insensitive(FlagDesc[FlagId]))381        continue;382 383      Result |= FlagValues[FlagId];384      FoundFlag = true;385      break;386    }387 388    if (!FoundFlag)389      return getExpectedError(join(FlagDesc, "/"), true);390  }391 392  return Result;393}394 395uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {396  while (!isEof()) {397    const RCToken &Token = look();398    if (Token.kind() != Kind::Identifier)399      return Flags;400    const StringRef Ident = Token.value();401    if (Ident.equals_insensitive("PRELOAD"))402      Flags |= MfPreload;403    else if (Ident.equals_insensitive("LOADONCALL"))404      Flags &= ~MfPreload;405    else if (Ident.equals_insensitive("FIXED"))406      Flags &= ~(MfMoveable | MfDiscardable);407    else if (Ident.equals_insensitive("MOVEABLE"))408      Flags |= MfMoveable;409    else if (Ident.equals_insensitive("DISCARDABLE"))410      Flags |= MfDiscardable | MfMoveable | MfPure;411    else if (Ident.equals_insensitive("PURE"))412      Flags |= MfPure;413    else if (Ident.equals_insensitive("IMPURE"))414      Flags &= ~(MfPure | MfDiscardable);415    else if (Ident.equals_insensitive("SHARED"))416      Flags |= MfPure;417    else if (Ident.equals_insensitive("NONSHARED"))418      Flags &= ~(MfPure | MfDiscardable);419    else420      return Flags;421    consume();422  }423  return Flags;424}425 426Expected<OptionalStmtList>427RCParser::parseOptionalStatements(OptStmtType StmtsType) {428  OptionalStmtList Result;429 430  // The last statement is always followed by the start of the block.431  while (!isNextTokenKind(Kind::BlockBegin)) {432    ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));433    Result.addStmt(std::move(*SingleParse));434  }435 436  return std::move(Result);437}438 439Expected<std::unique_ptr<OptionalStmt>>440RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {441  ASSIGN_OR_RETURN(TypeToken, readIdentifier());442  if (TypeToken->equals_insensitive("CHARACTERISTICS"))443    return parseCharacteristicsStmt();444  if (TypeToken->equals_insensitive("LANGUAGE"))445    return parseLanguageStmt();446  if (TypeToken->equals_insensitive("VERSION"))447    return parseVersionStmt();448 449  if (StmtsType != OptStmtType::BasicStmt) {450    if (TypeToken->equals_insensitive("CAPTION"))451      return parseCaptionStmt();452    if (TypeToken->equals_insensitive("CLASS"))453      return parseClassStmt();454    if (TypeToken->equals_insensitive("EXSTYLE"))455      return parseExStyleStmt();456    if (TypeToken->equals_insensitive("FONT"))457      return parseFontStmt(StmtsType);458    if (TypeToken->equals_insensitive("STYLE"))459      return parseStyleStmt();460    if (TypeToken->equals_insensitive("MENU"))461      return parseMenuStmt();462  }463 464  return getExpectedError("optional statement type, BEGIN or '{'",465                          /* IsAlreadyRead = */ true);466}467 468RCParser::ParseType RCParser::parseLanguageResource() {469  // Read LANGUAGE as an optional statement. If it's read correctly, we can470  // upcast it to RCResource.471  return parseLanguageStmt();472}473 474RCParser::ParseType RCParser::parseAcceleratorsResource() {475  uint16_t MemoryFlags =476      parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());477  ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());478  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));479 480  auto Accels = std::make_unique<AcceleratorsResource>(481      std::move(*OptStatements), MemoryFlags);482 483  while (!consumeOptionalType(Kind::BlockEnd)) {484    ASSIGN_OR_RETURN(EventResult, readIntOrString());485    RETURN_IF_ERROR(consumeType(Kind::Comma));486    ASSIGN_OR_RETURN(IDResult, readInt());487    ASSIGN_OR_RETURN(488        FlagsResult,489        parseFlags(AcceleratorsResource::Accelerator::OptionsStr,490                   AcceleratorsResource::Accelerator::OptionsFlags));491    Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);492  }493 494  return std::move(Accels);495}496 497RCParser::ParseType RCParser::parseCursorResource() {498  uint16_t MemoryFlags =499      parseMemoryFlags(CursorResource::getDefaultMemoryFlags());500  ASSIGN_OR_RETURN(Arg, readFilename());501  return std::make_unique<CursorResource>(*Arg, MemoryFlags);502}503 504RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {505  uint16_t MemoryFlags =506      parseMemoryFlags(DialogResource::getDefaultMemoryFlags());507  // Dialog resources have the following format of the arguments:508  //  DIALOG:   x, y, width, height [opt stmts...] {controls...}509  //  DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}510  // These are very similar, so we parse them together.511  ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));512 513  uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.514  if (IsExtended && consumeOptionalType(Kind::Comma)) {515    ASSIGN_OR_RETURN(HelpIDResult, readInt());516    HelpID = *HelpIDResult;517  }518 519  ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(520                                      IsExtended ? OptStmtType::DialogExStmt521                                                 : OptStmtType::DialogStmt));522 523  assert(isNextTokenKind(Kind::BlockBegin) &&524         "parseOptionalStatements, when successful, halts on BlockBegin.");525  consume();526 527  auto Dialog = std::make_unique<DialogResource>(528      (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],529      HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);530 531  while (!consumeOptionalType(Kind::BlockEnd)) {532    ASSIGN_OR_RETURN(ControlDefResult, parseControl());533    Dialog->addControl(std::move(*ControlDefResult));534  }535 536  return std::move(Dialog);537}538 539RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {540  uint16_t MemoryFlags =541      parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());542  if (isEof())543    return getExpectedError("filename, '{' or BEGIN");544 545  // Check if this is a file resource.546  switch (look().kind()) {547  case Kind::String:548  case Kind::Identifier: {549    ASSIGN_OR_RETURN(Filename, readFilename());550    return std::make_unique<UserDefinedResource>(Type, *Filename, MemoryFlags);551  }552  default:553    break;554  }555 556  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));557  std::vector<IntOrString> Data;558 559  while (!consumeOptionalType(Kind::BlockEnd)) {560    ASSIGN_OR_RETURN(Item, readIntOrString());561    Data.push_back(*Item);562 563    // There can be zero or more commas after each token (but not before564    // the first one).565    while (consumeOptionalType(Kind::Comma)) {566    }567  }568 569  return std::make_unique<UserDefinedResource>(Type, std::move(Data),570                                               MemoryFlags);571}572 573RCParser::ParseType RCParser::parseVersionInfoResource() {574  uint16_t MemoryFlags =575      parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());576  ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());577  ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));578  return std::make_unique<VersionInfoResource>(579      std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);580}581 582Expected<Control> RCParser::parseControl() {583  // Each control definition (except CONTROL) follows one of the schemes below584  // depending on the control class:585  //  [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]586  //  [class]       id, x, y, width, height [, style] [, exstyle] [, helpID]587  // Note that control ids must be integers.588  // Text might be either a string or an integer pointing to resource ID.589  ASSIGN_OR_RETURN(ClassResult, readIdentifier());590  std::string ClassUpper = ClassResult->upper();591  auto CtlInfo = Control::SupportedCtls.find(ClassUpper);592  if (CtlInfo == Control::SupportedCtls.end())593    return getExpectedError("control type, END or '}'", true);594 595  // Read caption if necessary.596  IntOrString Caption{StringRef()};597  if (CtlInfo->getValue().HasTitle) {598    ASSIGN_OR_RETURN(CaptionResult, readIntOrString());599    RETURN_IF_ERROR(consumeType(Kind::Comma));600    Caption = *CaptionResult;601  }602 603  ASSIGN_OR_RETURN(ID, readInt());604  RETURN_IF_ERROR(consumeType(Kind::Comma));605 606  IntOrString Class;607  std::optional<IntWithNotMask> Style;608  if (ClassUpper == "CONTROL") {609    // CONTROL text, id, class, style, x, y, width, height [, exstyle] [,610    // helpID]611    ASSIGN_OR_RETURN(ClassStr, readString());612    RETURN_IF_ERROR(consumeType(Kind::Comma));613    Class = *ClassStr;614    ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());615    RETURN_IF_ERROR(consumeType(Kind::Comma));616    Style = *StyleVal;617  } else {618    Class = CtlInfo->getValue().CtlClass;619  }620 621  // x, y, width, height622  ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));623 624  if (ClassUpper != "CONTROL") {625    if (consumeOptionalType(Kind::Comma)) {626      ASSIGN_OR_RETURN(Val, parseIntExpr1());627      Style = *Val;628    }629  }630 631  std::optional<uint32_t> ExStyle;632  if (consumeOptionalType(Kind::Comma)) {633    ASSIGN_OR_RETURN(Val, readInt());634    ExStyle = *Val;635  }636  std::optional<uint32_t> HelpID;637  if (consumeOptionalType(Kind::Comma)) {638    ASSIGN_OR_RETURN(Val, readInt());639    HelpID = *Val;640  }641 642  return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1], (*Args)[2],643                 (*Args)[3], Style, ExStyle, HelpID, Class);644}645 646RCParser::ParseType RCParser::parseBitmapResource() {647  uint16_t MemoryFlags =648      parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());649  ASSIGN_OR_RETURN(Arg, readFilename());650  return std::make_unique<BitmapResource>(*Arg, MemoryFlags);651}652 653RCParser::ParseType RCParser::parseIconResource() {654  uint16_t MemoryFlags =655      parseMemoryFlags(IconResource::getDefaultMemoryFlags());656  ASSIGN_OR_RETURN(Arg, readFilename());657  return std::make_unique<IconResource>(*Arg, MemoryFlags);658}659 660RCParser::ParseType RCParser::parseHTMLResource() {661  uint16_t MemoryFlags =662      parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());663  ASSIGN_OR_RETURN(Arg, readFilename());664  return std::make_unique<HTMLResource>(*Arg, MemoryFlags);665}666 667RCParser::ParseType RCParser::parseMenuResource() {668  uint16_t MemoryFlags =669      parseMemoryFlags(MenuResource::getDefaultMemoryFlags());670  ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());671  ASSIGN_OR_RETURN(Items, parseMenuItemsList());672  return std::make_unique<MenuResource>(std::move(*OptStatements),673                                        std::move(*Items), MemoryFlags);674}675 676RCParser::ParseType RCParser::parseMenuExResource() {677  uint16_t MemoryFlags =678      parseMemoryFlags(MenuExResource::getDefaultMemoryFlags());679  ASSIGN_OR_RETURN(Items, parseMenuExItemsList());680  return std::make_unique<MenuExResource>(std::move(*Items), MemoryFlags);681}682 683Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {684  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));685 686  MenuDefinitionList List;687 688  // Read a set of items. Each item is of one of three kinds:689  //   MENUITEM SEPARATOR690  //   MENUITEM caption:String, result:Int [, menu flags]...691  //   POPUP caption:String [, menu flags]... { items... }692  while (!consumeOptionalType(Kind::BlockEnd)) {693    ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());694 695    bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM");696    bool IsPopup = ItemTypeResult->equals_insensitive("POPUP");697    if (!IsMenuItem && !IsPopup)698      return getExpectedError("MENUITEM, POPUP, END or '}'", true);699 700    if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {701      // Now, expecting SEPARATOR.702      ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());703      if (SeparatorResult->equals_insensitive("SEPARATOR")) {704        List.addDefinition(std::make_unique<MenuSeparator>());705        continue;706      }707 708      return getExpectedError("SEPARATOR or string", true);709    }710 711    // Not a separator. Read the caption.712    ASSIGN_OR_RETURN(CaptionResult, readString());713 714    // If MENUITEM, expect also a comma and an integer.715    uint32_t MenuResult = -1;716 717    if (IsMenuItem) {718      RETURN_IF_ERROR(consumeType(Kind::Comma));719      ASSIGN_OR_RETURN(IntResult, readInt());720      MenuResult = *IntResult;721    }722 723    ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,724                                             MenuDefinition::OptionsFlags));725 726    if (IsPopup) {727      // If POPUP, read submenu items recursively.728      ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());729      List.addDefinition(std::make_unique<PopupItem>(730          *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));731      continue;732    }733 734    assert(IsMenuItem);735    List.addDefinition(736        std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));737  }738 739  return std::move(List);740}741 742Expected<MenuDefinitionList> RCParser::parseMenuExItemsList() {743  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));744 745  MenuDefinitionList List;746 747  // Read a set of items. Each item is of one of two kinds:748  //   MENUITEM caption:String [,[id][, [type][, state]]]]749  //   POPUP caption:String [,[id][, [type][, [state][, helpID]]]] { popupBody }750  while (!consumeOptionalType(Kind::BlockEnd)) {751    ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());752 753    bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM");754    bool IsPopup = ItemTypeResult->equals_insensitive("POPUP");755    if (!IsMenuItem && !IsPopup)756      return getExpectedError("MENUITEM, POPUP, END or '}'", true);757 758    // Not a separator. Read the caption.759    ASSIGN_OR_RETURN(CaptionResult, readString());760 761    // If MENUITEM, expect [,[id][, [type][, state]]]]762    if (IsMenuItem) {763      uint32_t MenuId = 0;764      uint32_t MenuType = 0;765      uint32_t MenuState = 0;766 767      if (consumeOptionalType(Kind::Comma)) {768        auto IntId = readInt();769        if (IntId) {770          MenuId = *IntId;771        }772        if (consumeOptionalType(Kind::Comma)) {773          auto IntType = readInt();774          if (IntType) {775            MenuType = *IntType;776          }777          if (consumeOptionalType(Kind::Comma)) {778            auto IntState = readInt();779            if (IntState) {780              MenuState = *IntState;781            }782          }783        }784      }785      List.addDefinition(std::make_unique<MenuExItem>(*CaptionResult, MenuId,786                                                      MenuType, MenuState));787      continue;788    }789 790    assert(IsPopup);791 792    uint32_t PopupId = 0;793    uint32_t PopupType = 0;794    uint32_t PopupState = 0;795    uint32_t PopupHelpID = 0;796 797    if (consumeOptionalType(Kind::Comma)) {798      auto IntId = readInt();799      if (IntId) {800        PopupId = *IntId;801      }802      if (consumeOptionalType(Kind::Comma)) {803        auto IntType = readInt();804        if (IntType) {805          PopupType = *IntType;806        }807        if (consumeOptionalType(Kind::Comma)) {808          auto IntState = readInt();809          if (IntState) {810            PopupState = *IntState;811          }812          if (consumeOptionalType(Kind::Comma)) {813            auto IntHelpID = readInt();814            if (IntHelpID) {815              PopupHelpID = *IntHelpID;816            }817          }818        }819      }820    }821    // If POPUP, read submenu items recursively.822    ASSIGN_OR_RETURN(SubMenuResult, parseMenuExItemsList());823    List.addDefinition(std::make_unique<PopupExItem>(824        *CaptionResult, PopupId, PopupType, PopupState, PopupHelpID,825        std::move(*SubMenuResult)));826  }827 828  return std::move(List);829}830 831RCParser::ParseType RCParser::parseStringTableResource() {832  uint16_t MemoryFlags =833      parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());834  ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());835  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));836 837  auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),838                                                     MemoryFlags);839 840  // Read strings until we reach the end of the block.841  while (!consumeOptionalType(Kind::BlockEnd)) {842    // Each definition consists of string's ID (an integer) and a string.843    // Some examples in documentation suggest that there might be a comma in844    // between, however we strictly adhere to the single statement definition.845    ASSIGN_OR_RETURN(IDResult, readInt());846    consumeOptionalType(Kind::Comma);847 848    std::vector<StringRef> Strings;849    ASSIGN_OR_RETURN(StrResult, readString());850    Strings.push_back(*StrResult);851    while (isNextTokenKind(Kind::String))852      Strings.push_back(read().value());853 854    Table->addStrings(*IDResult, std::move(Strings));855  }856 857  return std::move(Table);858}859 860Expected<std::unique_ptr<VersionInfoBlock>>861RCParser::parseVersionInfoBlockContents(StringRef BlockName) {862  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));863 864  auto Contents = std::make_unique<VersionInfoBlock>(BlockName);865 866  while (!isNextTokenKind(Kind::BlockEnd)) {867    ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());868    Contents->addStmt(std::move(*Stmt));869  }870 871  consume(); // Consume BlockEnd.872 873  return std::move(Contents);874}875 876Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {877  // Expect either BLOCK or VALUE, then a name or a key (a string).878  ASSIGN_OR_RETURN(TypeResult, readIdentifier());879 880  if (TypeResult->equals_insensitive("BLOCK")) {881    ASSIGN_OR_RETURN(NameResult, readString());882    return parseVersionInfoBlockContents(*NameResult);883  }884 885  if (TypeResult->equals_insensitive("VALUE")) {886    ASSIGN_OR_RETURN(KeyResult, readString());887    // Read a non-empty list of strings and/or ints, each888    // possibly preceded by a comma. Unfortunately, the tool behavior depends889    // on them existing or not, so we need to memorize where we found them.890    std::vector<IntOrString> Values;891    BitVector PrecedingCommas;892    RETURN_IF_ERROR(consumeType(Kind::Comma));893    while (!isNextTokenKind(Kind::Identifier) &&894           !isNextTokenKind(Kind::BlockEnd)) {895      // Try to eat a comma if it's not the first statement.896      bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);897      ASSIGN_OR_RETURN(ValueResult, readIntOrString());898      Values.push_back(*ValueResult);899      PrecedingCommas.push_back(HadComma);900    }901    return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),902                                              std::move(PrecedingCommas));903  }904 905  return getExpectedError("BLOCK or VALUE", true);906}907 908Expected<VersionInfoResource::VersionInfoFixed>909RCParser::parseVersionInfoFixed() {910  using RetType = VersionInfoResource::VersionInfoFixed;911  RetType Result;912 913  // Read until the beginning of the block.914  while (!isNextTokenKind(Kind::BlockBegin)) {915    ASSIGN_OR_RETURN(TypeResult, readIdentifier());916    auto FixedType = RetType::getFixedType(*TypeResult);917 918    if (!RetType::isTypeSupported(FixedType))919      return getExpectedError("fixed VERSIONINFO statement type", true);920    if (Result.IsTypePresent[FixedType])921      return getExpectedError("yet unread fixed VERSIONINFO statement type",922                              true);923 924    // VERSION variations take multiple integers.925    size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;926    ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(1, NumInts));927    SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());928    while (ArgInts.size() < NumInts)929      ArgInts.push_back(0);930    Result.setValue(FixedType, ArgInts);931  }932 933  return Result;934}935 936RCParser::ParseOptionType RCParser::parseLanguageStmt() {937  ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));938  return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);939}940 941RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {942  ASSIGN_OR_RETURN(Arg, readInt());943  return std::make_unique<CharacteristicsStmt>(*Arg);944}945 946RCParser::ParseOptionType RCParser::parseVersionStmt() {947  ASSIGN_OR_RETURN(Arg, readInt());948  return std::make_unique<VersionStmt>(*Arg);949}950 951RCParser::ParseOptionType RCParser::parseCaptionStmt() {952  ASSIGN_OR_RETURN(Arg, readString());953  return std::make_unique<CaptionStmt>(*Arg);954}955 956RCParser::ParseOptionType RCParser::parseClassStmt() {957  ASSIGN_OR_RETURN(Arg, readIntOrString());958  return std::make_unique<ClassStmt>(*Arg);959}960 961RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {962  assert(DialogType != OptStmtType::BasicStmt);963 964  ASSIGN_OR_RETURN(SizeResult, readInt());965  RETURN_IF_ERROR(consumeType(Kind::Comma));966  ASSIGN_OR_RETURN(NameResult, readString());967 968  // Default values for the optional arguments.969  uint32_t FontWeight = 0;970  bool FontItalic = false;971  uint32_t FontCharset = 1;972  if (DialogType == OptStmtType::DialogExStmt) {973    if (consumeOptionalType(Kind::Comma)) {974      ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));975      if (Args->size() >= 1)976        FontWeight = (*Args)[0];977      if (Args->size() >= 2)978        FontItalic = (*Args)[1] != 0;979      if (Args->size() >= 3)980        FontCharset = (*Args)[2];981    }982  }983  return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,984                                    FontItalic, FontCharset);985}986 987RCParser::ParseOptionType RCParser::parseStyleStmt() {988  ASSIGN_OR_RETURN(Arg, readInt());989  return std::make_unique<StyleStmt>(*Arg);990}991 992RCParser::ParseOptionType RCParser::parseExStyleStmt() {993  ASSIGN_OR_RETURN(Arg, readInt());994  return std::make_unique<ExStyleStmt>(*Arg);995}996 997RCParser::ParseOptionType RCParser::parseMenuStmt() {998  ASSIGN_OR_RETURN(Arg, readIntOrString());999  return std::make_unique<MenuStmt>(*Arg);1000}1001 1002Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {1003  return make_error<ParserError>(1004      Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);1005}1006 1007} // namespace rc1008} // namespace llvm1009