diff tools/llvm-rc/ResourceScriptParser.cpp @ 121:803732b1fca8

LLVM 5.0
author kono
date Fri, 27 Oct 2017 17:07:41 +0900
parents
children 3a76565eade5
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/llvm-rc/ResourceScriptParser.cpp	Fri Oct 27 17:07:41 2017 +0900
@@ -0,0 +1,716 @@
+//===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+//
+// This implements the parser defined in ResourceScriptParser.h.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceScriptParser.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+
+// Take an expression returning llvm::Error and forward the error if it exists.
+#define RETURN_IF_ERROR(Expr)                                                  \
+  if (auto Err = (Expr))                                                       \
+    return std::move(Err);
+
+// Take an expression returning llvm::Expected<T> and assign it to Var or
+// forward the error out of the function.
+#define ASSIGN_OR_RETURN(Var, Expr)                                            \
+  auto Var = (Expr);                                                           \
+  if (!Var)                                                                    \
+    return Var.takeError();
+
+namespace llvm {
+namespace rc {
+
+RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
+                                   const LocIter End)
+    : ErrorLoc(CurLoc), FileEnd(End) {
+  CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
+               (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
+}
+
+char RCParser::ParserError::ID = 0;
+
+RCParser::RCParser(std::vector<RCToken> TokenList)
+    : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
+
+bool RCParser::isEof() const { return CurLoc == End; }
+
+RCParser::ParseType RCParser::parseSingleResource() {
+  // The first thing we read is usually a resource's name. However, in some
+  // cases (LANGUAGE and STRINGTABLE) the resources don't have their names
+  // and the first token to be read is the type.
+  ASSIGN_OR_RETURN(NameToken, readTypeOrName());
+
+  if (NameToken->equalsLower("LANGUAGE"))
+    return parseLanguageResource();
+  else if (NameToken->equalsLower("STRINGTABLE"))
+    return parseStringTableResource();
+
+  // If it's not an unnamed resource, what we've just read is a name. Now,
+  // read resource type;
+  ASSIGN_OR_RETURN(TypeToken, readTypeOrName());
+
+  ParseType Result = std::unique_ptr<RCResource>();
+  (void)!Result;
+
+  if (TypeToken->equalsLower("ACCELERATORS"))
+    Result = parseAcceleratorsResource();
+  else if (TypeToken->equalsLower("CURSOR"))
+    Result = parseCursorResource();
+  else if (TypeToken->equalsLower("DIALOG"))
+    Result = parseDialogResource(false);
+  else if (TypeToken->equalsLower("DIALOGEX"))
+    Result = parseDialogResource(true);
+  else if (TypeToken->equalsLower("ICON"))
+    Result = parseIconResource();
+  else if (TypeToken->equalsLower("HTML"))
+    Result = parseHTMLResource();
+  else if (TypeToken->equalsLower("MENU"))
+    Result = parseMenuResource();
+  else if (TypeToken->equalsLower("VERSIONINFO"))
+    Result = parseVersionInfoResource();
+  else
+    Result = parseUserDefinedResource(*TypeToken);
+
+  if (Result)
+    (*Result)->setName(*NameToken);
+
+  return Result;
+}
+
+bool RCParser::isNextTokenKind(Kind TokenKind) const {
+  return !isEof() && look().kind() == TokenKind;
+}
+
+const RCToken &RCParser::look() const {
+  assert(!isEof());
+  return *CurLoc;
+}
+
+const RCToken &RCParser::read() {
+  assert(!isEof());
+  return *CurLoc++;
+}
+
+void RCParser::consume() {
+  assert(!isEof());
+  CurLoc++;
+}
+
+// An integer description might consist of a single integer or
+// an arithmetic expression evaluating to the integer. The expressions
+// can contain the following tokens: <int> ( ) + - | & ~. Their meaning
+// is the same as in C++.
+// The operators in the original RC implementation have the following
+// precedence:
+//   1) Unary operators (- ~),
+//   2) Binary operators (+ - & |), with no precedence.
+//
+// The following grammar is used to parse the expressions Exp1:
+//   Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
+//   Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
+// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
+// separated by binary operators.)
+//
+// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
+// is read by parseIntExpr2().
+//
+// The original Microsoft tool handles multiple unary operators incorrectly.
+// For example, in 16-bit little-endian integers:
+//    1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
+//    1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
+// Our implementation differs from the original one and handles these
+// operators correctly:
+//    1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
+//    1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
+
+Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); }
+
+Expected<RCInt> RCParser::parseIntExpr1() {
+  // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
+  ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
+  RCInt Result = *FirstResult;
+
+  while (!isEof() && look().isBinaryOp()) {
+    auto OpToken = read();
+    ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
+
+    switch (OpToken.kind()) {
+    case Kind::Plus:
+      Result += *NextResult;
+      break;
+
+    case Kind::Minus:
+      Result -= *NextResult;
+      break;
+
+    case Kind::Pipe:
+      Result |= *NextResult;
+      break;
+
+    case Kind::Amp:
+      Result &= *NextResult;
+      break;
+
+    default:
+      llvm_unreachable("Already processed all binary ops.");
+    }
+  }
+
+  return Result;
+}
+
+Expected<RCInt> RCParser::parseIntExpr2() {
+  // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
+  static const char ErrorMsg[] = "'-', '~', integer or '('";
+
+  if (isEof())
+    return getExpectedError(ErrorMsg);
+
+  switch (look().kind()) {
+  case Kind::Minus: {
+    consume();
+    ASSIGN_OR_RETURN(Result, parseIntExpr2());
+    return -(*Result);
+  }
+
+  case Kind::Tilde: {
+    consume();
+    ASSIGN_OR_RETURN(Result, parseIntExpr2());
+    return ~(*Result);
+  }
+
+  case Kind::Int:
+    return RCInt(read());
+
+  case Kind::LeftParen: {
+    consume();
+    ASSIGN_OR_RETURN(Result, parseIntExpr1());
+    RETURN_IF_ERROR(consumeType(Kind::RightParen));
+    return *Result;
+  }
+
+  default:
+    return getExpectedError(ErrorMsg);
+  }
+}
+
+Expected<StringRef> RCParser::readString() {
+  if (!isNextTokenKind(Kind::String))
+    return getExpectedError("string");
+  return read().value();
+}
+
+Expected<StringRef> RCParser::readIdentifier() {
+  if (!isNextTokenKind(Kind::Identifier))
+    return getExpectedError("identifier");
+  return read().value();
+}
+
+Expected<IntOrString> RCParser::readIntOrString() {
+  if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
+    return getExpectedError("int or string");
+  return IntOrString(read());
+}
+
+Expected<IntOrString> RCParser::readTypeOrName() {
+  // We suggest that the correct resource name or type should be either an
+  // identifier or an integer. The original RC tool is much more liberal.
+  if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
+    return getExpectedError("int or identifier");
+  return IntOrString(read());
+}
+
+Error RCParser::consumeType(Kind TokenKind) {
+  if (isNextTokenKind(TokenKind)) {
+    consume();
+    return Error::success();
+  }
+
+  switch (TokenKind) {
+#define TOKEN(TokenName)                                                       \
+  case Kind::TokenName:                                                        \
+    return getExpectedError(#TokenName);
+#define SHORT_TOKEN(TokenName, TokenCh)                                        \
+  case Kind::TokenName:                                                        \
+    return getExpectedError(#TokenCh);
+#include "ResourceScriptTokenList.h"
+#undef SHORT_TOKEN
+#undef TOKEN
+  }
+
+  llvm_unreachable("All case options exhausted.");
+}
+
+bool RCParser::consumeOptionalType(Kind TokenKind) {
+  if (isNextTokenKind(TokenKind)) {
+    consume();
+    return true;
+  }
+
+  return false;
+}
+
+Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
+                                                             size_t MaxCount) {
+  assert(MinCount <= MaxCount);
+
+  SmallVector<RCInt, 8> Result;
+
+  auto FailureHandler =
+      [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
+    if (Result.size() < MinCount)
+      return std::move(Err);
+    consumeError(std::move(Err));
+    return Result;
+  };
+
+  for (size_t i = 0; i < MaxCount; ++i) {
+    // Try to read a comma unless we read the first token.
+    // Sometimes RC tool requires them and sometimes not. We decide to
+    // always require them.
+    if (i >= 1) {
+      if (auto CommaError = consumeType(Kind::Comma))
+        return FailureHandler(std::move(CommaError));
+    }
+
+    if (auto IntResult = readInt())
+      Result.push_back(*IntResult);
+    else
+      return FailureHandler(IntResult.takeError());
+  }
+
+  return std::move(Result);
+}
+
+Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
+                                        ArrayRef<uint32_t> FlagValues) {
+  assert(!FlagDesc.empty());
+  assert(FlagDesc.size() == FlagValues.size());
+
+  uint32_t Result = 0;
+  while (isNextTokenKind(Kind::Comma)) {
+    consume();
+    ASSIGN_OR_RETURN(FlagResult, readIdentifier());
+    bool FoundFlag = false;
+
+    for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
+      if (!FlagResult->equals_lower(FlagDesc[FlagId]))
+        continue;
+
+      Result |= FlagValues[FlagId];
+      FoundFlag = true;
+      break;
+    }
+
+    if (!FoundFlag)
+      return getExpectedError(join(FlagDesc, "/"), true);
+  }
+
+  return Result;
+}
+
+Expected<OptionalStmtList>
+RCParser::parseOptionalStatements(OptStmtType StmtsType) {
+  OptionalStmtList Result;
+
+  // The last statement is always followed by the start of the block.
+  while (!isNextTokenKind(Kind::BlockBegin)) {
+    ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
+    Result.addStmt(std::move(*SingleParse));
+  }
+
+  return std::move(Result);
+}
+
+Expected<std::unique_ptr<OptionalStmt>>
+RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
+  ASSIGN_OR_RETURN(TypeToken, readIdentifier());
+  if (TypeToken->equals_lower("CHARACTERISTICS"))
+    return parseCharacteristicsStmt();
+  if (TypeToken->equals_lower("LANGUAGE"))
+    return parseLanguageStmt();
+  if (TypeToken->equals_lower("VERSION"))
+    return parseVersionStmt();
+
+  if (StmtsType != OptStmtType::BasicStmt) {
+    if (TypeToken->equals_lower("CAPTION"))
+      return parseCaptionStmt();
+    if (TypeToken->equals_lower("FONT"))
+      return parseFontStmt(StmtsType);
+    if (TypeToken->equals_lower("STYLE"))
+      return parseStyleStmt();
+  }
+
+  return getExpectedError("optional statement type, BEGIN or '{'",
+                          /* IsAlreadyRead = */ true);
+}
+
+RCParser::ParseType RCParser::parseLanguageResource() {
+  // Read LANGUAGE as an optional statement. If it's read correctly, we can
+  // upcast it to RCResource.
+  return parseLanguageStmt();
+}
+
+RCParser::ParseType RCParser::parseAcceleratorsResource() {
+  ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
+  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+  auto Accels =
+      llvm::make_unique<AcceleratorsResource>(std::move(*OptStatements));
+
+  while (!consumeOptionalType(Kind::BlockEnd)) {
+    ASSIGN_OR_RETURN(EventResult, readIntOrString());
+    RETURN_IF_ERROR(consumeType(Kind::Comma));
+    ASSIGN_OR_RETURN(IDResult, readInt());
+    ASSIGN_OR_RETURN(
+        FlagsResult,
+        parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
+                   AcceleratorsResource::Accelerator::OptionsFlags));
+    Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
+  }
+
+  return std::move(Accels);
+}
+
+RCParser::ParseType RCParser::parseCursorResource() {
+  ASSIGN_OR_RETURN(Arg, readString());
+  return llvm::make_unique<CursorResource>(*Arg);
+}
+
+RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
+  // Dialog resources have the following format of the arguments:
+  //  DIALOG:   x, y, width, height [opt stmts...] {controls...}
+  //  DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
+  // These are very similar, so we parse them together.
+  ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
+
+  uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
+  if (IsExtended && consumeOptionalType(Kind::Comma)) {
+    ASSIGN_OR_RETURN(HelpIDResult, readInt());
+    HelpID = *HelpIDResult;
+  }
+
+  ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
+                                      IsExtended ? OptStmtType::DialogExStmt
+                                                 : OptStmtType::DialogStmt));
+
+  assert(isNextTokenKind(Kind::BlockBegin) &&
+         "parseOptionalStatements, when successful, halts on BlockBegin.");
+  consume();
+
+  auto Dialog = llvm::make_unique<DialogResource>(
+      (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
+      HelpID, std::move(*OptStatements), IsExtended);
+
+  while (!consumeOptionalType(Kind::BlockEnd)) {
+    ASSIGN_OR_RETURN(ControlDefResult, parseControl());
+    Dialog->addControl(std::move(*ControlDefResult));
+  }
+
+  return std::move(Dialog);
+}
+
+RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
+  if (isEof())
+    return getExpectedError("filename, '{' or BEGIN");
+
+  // Check if this is a file resource.
+  if (look().kind() == Kind::String)
+    return llvm::make_unique<UserDefinedResource>(Type, read().value());
+
+  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+  std::vector<IntOrString> Data;
+
+  // Consume comma before each consecutive token except the first one.
+  bool ConsumeComma = false;
+  while (!consumeOptionalType(Kind::BlockEnd)) {
+    if (ConsumeComma)
+      RETURN_IF_ERROR(consumeType(Kind::Comma));
+    ConsumeComma = true;
+
+    ASSIGN_OR_RETURN(Item, readIntOrString());
+    Data.push_back(*Item);
+  }
+
+  return llvm::make_unique<UserDefinedResource>(Type, std::move(Data));
+}
+
+RCParser::ParseType RCParser::parseVersionInfoResource() {
+  ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
+  ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
+  return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult),
+                                                std::move(*FixedResult));
+}
+
+Expected<Control> RCParser::parseControl() {
+  // Each control definition (except CONTROL) follows one of the schemes below
+  // depending on the control class:
+  //  [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
+  //  [class]       id, x, y, width, height [, style] [, exstyle] [, helpID]
+  // Note that control ids must be integers.
+  // Text might be either a string or an integer pointing to resource ID.
+  ASSIGN_OR_RETURN(ClassResult, readIdentifier());
+  std::string ClassUpper = ClassResult->upper();
+  auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
+  if (CtlInfo == Control::SupportedCtls.end())
+    return getExpectedError("control type, END or '}'", true);
+
+  // Read caption if necessary.
+  IntOrString Caption{StringRef()};
+  if (CtlInfo->getValue().HasTitle) {
+    ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
+    RETURN_IF_ERROR(consumeType(Kind::Comma));
+    Caption = *CaptionResult;
+  }
+
+  ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8));
+
+  auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> {
+    return Args->size() > Id ? (uint32_t)(*Args)[Id] : Optional<uint32_t>();
+  };
+
+  return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2],
+                 (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6),
+                 TakeOptArg(7));
+}
+
+RCParser::ParseType RCParser::parseIconResource() {
+  ASSIGN_OR_RETURN(Arg, readString());
+  return llvm::make_unique<IconResource>(*Arg);
+}
+
+RCParser::ParseType RCParser::parseHTMLResource() {
+  ASSIGN_OR_RETURN(Arg, readString());
+  return llvm::make_unique<HTMLResource>(*Arg);
+}
+
+RCParser::ParseType RCParser::parseMenuResource() {
+  ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
+  ASSIGN_OR_RETURN(Items, parseMenuItemsList());
+  return llvm::make_unique<MenuResource>(std::move(*OptStatements),
+                                         std::move(*Items));
+}
+
+Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
+  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+  MenuDefinitionList List;
+
+  // Read a set of items. Each item is of one of three kinds:
+  //   MENUITEM SEPARATOR
+  //   MENUITEM caption:String, result:Int [, menu flags]...
+  //   POPUP caption:String [, menu flags]... { items... }
+  while (!consumeOptionalType(Kind::BlockEnd)) {
+    ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
+
+    bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
+    bool IsPopup = ItemTypeResult->equals_lower("POPUP");
+    if (!IsMenuItem && !IsPopup)
+      return getExpectedError("MENUITEM, POPUP, END or '}'", true);
+
+    if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
+      // Now, expecting SEPARATOR.
+      ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
+      if (SeparatorResult->equals_lower("SEPARATOR")) {
+        List.addDefinition(llvm::make_unique<MenuSeparator>());
+        continue;
+      }
+
+      return getExpectedError("SEPARATOR or string", true);
+    }
+
+    // Not a separator. Read the caption.
+    ASSIGN_OR_RETURN(CaptionResult, readString());
+
+    // If MENUITEM, expect also a comma and an integer.
+    uint32_t MenuResult = -1;
+
+    if (IsMenuItem) {
+      RETURN_IF_ERROR(consumeType(Kind::Comma));
+      ASSIGN_OR_RETURN(IntResult, readInt());
+      MenuResult = *IntResult;
+    }
+
+    ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
+                                             MenuDefinition::OptionsFlags));
+
+    if (IsPopup) {
+      // If POPUP, read submenu items recursively.
+      ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
+      List.addDefinition(llvm::make_unique<PopupItem>(
+          *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
+      continue;
+    }
+
+    assert(IsMenuItem);
+    List.addDefinition(
+        llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
+  }
+
+  return std::move(List);
+}
+
+RCParser::ParseType RCParser::parseStringTableResource() {
+  ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
+  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+  auto Table =
+      llvm::make_unique<StringTableResource>(std::move(*OptStatements));
+
+  // Read strings until we reach the end of the block.
+  while (!consumeOptionalType(Kind::BlockEnd)) {
+    // Each definition consists of string's ID (an integer) and a string.
+    // Some examples in documentation suggest that there might be a comma in
+    // between, however we strictly adhere to the single statement definition.
+    ASSIGN_OR_RETURN(IDResult, readInt());
+    ASSIGN_OR_RETURN(StrResult, readString());
+    Table->addString(*IDResult, *StrResult);
+  }
+
+  return std::move(Table);
+}
+
+Expected<std::unique_ptr<VersionInfoBlock>>
+RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
+  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+  auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
+
+  while (!isNextTokenKind(Kind::BlockEnd)) {
+    ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
+    Contents->addStmt(std::move(*Stmt));
+  }
+
+  consume(); // Consume BlockEnd.
+
+  return std::move(Contents);
+}
+
+Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
+  // Expect either BLOCK or VALUE, then a name or a key (a string).
+  ASSIGN_OR_RETURN(TypeResult, readIdentifier());
+
+  if (TypeResult->equals_lower("BLOCK")) {
+    ASSIGN_OR_RETURN(NameResult, readString());
+    return parseVersionInfoBlockContents(*NameResult);
+  }
+
+  if (TypeResult->equals_lower("VALUE")) {
+    ASSIGN_OR_RETURN(KeyResult, readString());
+    // Read a non-empty list of strings and/or ints, each
+    // possibly preceded by a comma. Unfortunately, the tool behavior depends
+    // on them existing or not, so we need to memorize where we found them.
+    std::vector<IntOrString> Values;
+    std::vector<bool> PrecedingCommas;
+    RETURN_IF_ERROR(consumeType(Kind::Comma));
+    while (!isNextTokenKind(Kind::Identifier) &&
+           !isNextTokenKind(Kind::BlockEnd)) {
+      // Try to eat a comma if it's not the first statement.
+      bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
+      ASSIGN_OR_RETURN(ValueResult, readIntOrString());
+      Values.push_back(*ValueResult);
+      PrecedingCommas.push_back(HadComma);
+    }
+    return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
+                                               std::move(PrecedingCommas));
+  }
+
+  return getExpectedError("BLOCK or VALUE", true);
+}
+
+Expected<VersionInfoResource::VersionInfoFixed>
+RCParser::parseVersionInfoFixed() {
+  using RetType = VersionInfoResource::VersionInfoFixed;
+  RetType Result;
+
+  // Read until the beginning of the block.
+  while (!isNextTokenKind(Kind::BlockBegin)) {
+    ASSIGN_OR_RETURN(TypeResult, readIdentifier());
+    auto FixedType = RetType::getFixedType(*TypeResult);
+
+    if (!RetType::isTypeSupported(FixedType))
+      return getExpectedError("fixed VERSIONINFO statement type", true);
+    if (Result.IsTypePresent[FixedType])
+      return getExpectedError("yet unread fixed VERSIONINFO statement type",
+                              true);
+
+    // VERSION variations take multiple integers.
+    size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
+    ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
+    SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
+    Result.setValue(FixedType, ArgInts);
+  }
+
+  return Result;
+}
+
+RCParser::ParseOptionType RCParser::parseLanguageStmt() {
+  ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
+  return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
+}
+
+RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
+  ASSIGN_OR_RETURN(Arg, readInt());
+  return llvm::make_unique<CharacteristicsStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseVersionStmt() {
+  ASSIGN_OR_RETURN(Arg, readInt());
+  return llvm::make_unique<VersionStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseCaptionStmt() {
+  ASSIGN_OR_RETURN(Arg, readString());
+  return llvm::make_unique<CaptionStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
+  assert(DialogType != OptStmtType::BasicStmt);
+
+  ASSIGN_OR_RETURN(SizeResult, readInt());
+  RETURN_IF_ERROR(consumeType(Kind::Comma));
+  ASSIGN_OR_RETURN(NameResult, readString());
+
+  // Default values for the optional arguments.
+  uint32_t FontWeight = 0;
+  bool FontItalic = false;
+  uint32_t FontCharset = 1;
+  if (DialogType == OptStmtType::DialogExStmt) {
+    if (consumeOptionalType(Kind::Comma)) {
+      ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
+      if (Args->size() >= 1)
+        FontWeight = (*Args)[0];
+      if (Args->size() >= 2)
+        FontItalic = (*Args)[1] != 0;
+      if (Args->size() >= 3)
+        FontCharset = (*Args)[2];
+    }
+  }
+  return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
+                                     FontItalic, FontCharset);
+}
+
+RCParser::ParseOptionType RCParser::parseStyleStmt() {
+  ASSIGN_OR_RETURN(Arg, readInt());
+  return llvm::make_unique<StyleStmt>(*Arg);
+}
+
+Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
+  return make_error<ParserError>(
+      Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
+}
+
+} // namespace rc
+} // namespace llvm