[llvm] r311184 - Reapply: [llvm-rc] Add basic RC scripts parsing ability.

Marek Sokolowski via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 18 11:24:17 PDT 2017


Author: mnbvmar
Date: Fri Aug 18 11:24:17 2017
New Revision: 311184

URL: http://llvm.org/viewvc/llvm-project?rev=311184&view=rev
Log:
Reapply: [llvm-rc] Add basic RC scripts parsing ability.

As for now, the parser supports a limited set of statements and
resources. This will be extended in the following patches.

Thanks to Nico Weber (thakis) for his original work in this area.

This patch was originally submitted as r311175 and got reverted
in r311177 because of the problems with compilation under gcc.

Differential Revision: https://reviews.llvm.org/D36340

Added:
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-correct-everything.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-eof.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-language-no-comma.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-language-too-many-commas.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-no-characteristics-arg.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-token.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-type-eof.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-type.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-stringtable-no-string.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-stringtable-weird-option.rc
    llvm/trunk/test/tools/llvm-rc/parser.test
    llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp
    llvm/trunk/tools/llvm-rc/ResourceScriptParser.h
    llvm/trunk/tools/llvm-rc/ResourceScriptStmt.cpp
    llvm/trunk/tools/llvm-rc/ResourceScriptStmt.h
Modified:
    llvm/trunk/test/tools/llvm-rc/tokenizer.test
    llvm/trunk/tools/llvm-rc/CMakeLists.txt
    llvm/trunk/tools/llvm-rc/llvm-rc.cpp

Added: llvm/trunk/test/tools/llvm-rc/Inputs/parser-correct-everything.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-correct-everything.rc?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-correct-everything.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-correct-everything.rc Fri Aug 18 11:24:17 2017
@@ -0,0 +1,15 @@
+meh IcOn "hello.bmp"
+Icon Icon "Icon"
+
+LANGUAGE 5, 12
+
+STRINGTABLE
+LANGUAGE 1, 1
+CHARACTERISTICS 500
+LANGUAGE 3, 4
+VERSION 14
+{
+  1 "hello"
+  2 "world"
+}
+STRINGTABLE BEGIN END

Added: llvm/trunk/test/tools/llvm-rc/Inputs/parser-eof.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-eof.rc?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-eof.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-eof.rc Fri Aug 18 11:24:17 2017
@@ -0,0 +1 @@
+LANGUAGE

Added: llvm/trunk/test/tools/llvm-rc/Inputs/parser-language-no-comma.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-language-no-comma.rc?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-language-no-comma.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-language-no-comma.rc Fri Aug 18 11:24:17 2017
@@ -0,0 +1 @@
+LANGUAGE 5 7

Added: llvm/trunk/test/tools/llvm-rc/Inputs/parser-language-too-many-commas.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-language-too-many-commas.rc?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-language-too-many-commas.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-language-too-many-commas.rc Fri Aug 18 11:24:17 2017
@@ -0,0 +1 @@
+LANGUAGE 5,, 7

Added: llvm/trunk/test/tools/llvm-rc/Inputs/parser-no-characteristics-arg.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-no-characteristics-arg.rc?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-no-characteristics-arg.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-no-characteristics-arg.rc Fri Aug 18 11:24:17 2017
@@ -0,0 +1,5 @@
+STRINGTABLE
+CHARACTERISTICS
+BEGIN
+  100 "No integer after CHARACTERISTICS."
+END

Added: llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-token.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-token.rc?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-token.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-token.rc Fri Aug 18 11:24:17 2017
@@ -0,0 +1 @@
+& ICON "WeirdResourceName.ico"

Added: llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-type-eof.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-type-eof.rc?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-type-eof.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-type-eof.rc Fri Aug 18 11:24:17 2017
@@ -0,0 +1 @@
+HELLO

Added: llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-type.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-type.rc?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-type.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-nonsense-type.rc Fri Aug 18 11:24:17 2017
@@ -0,0 +1 @@
+HELLO WORLD

Added: llvm/trunk/test/tools/llvm-rc/Inputs/parser-stringtable-no-string.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-stringtable-no-string.rc?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-stringtable-no-string.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-stringtable-no-string.rc Fri Aug 18 11:24:17 2017
@@ -0,0 +1,6 @@
+STRINGTABLE
+VERSION 8
+{
+  1 "hello"
+  2
+}

Added: llvm/trunk/test/tools/llvm-rc/Inputs/parser-stringtable-weird-option.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-stringtable-weird-option.rc?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-stringtable-weird-option.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-stringtable-weird-option.rc Fri Aug 18 11:24:17 2017
@@ -0,0 +1,4 @@
+STRINGTABLE
+NONSENSETYPE 12 34
+BEGIN
+END

Added: llvm/trunk/test/tools/llvm-rc/parser.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/parser.test?rev=311184&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/parser.test (added)
+++ llvm/trunk/test/tools/llvm-rc/parser.test Fri Aug 18 11:24:17 2017
@@ -0,0 +1,67 @@
+; RUN: llvm-rc /V %p/Inputs/parser-correct-everything.rc | FileCheck %s --check-prefix PGOOD
+
+; PGOOD:  Icon (meh): "hello.bmp"
+; PGOOD-NEXT:  Icon (Icon): "Icon"
+; PGOOD-NEXT:  Language: 5, Sublanguage: 12
+; PGOOD-NEXT:  StringTable:
+; PGOOD-NEXT:    Option: Language: 1, Sublanguage: 1
+; PGOOD-NEXT:    Option: Characteristics: 500
+; PGOOD-NEXT:    Option: Language: 3, Sublanguage: 4
+; PGOOD-NEXT:    Option: Version: 14
+; PGOOD-NEXT:    1 => "hello"
+; PGOOD-NEXT:    2 => "world"
+; PGOOD-NEXT:  StringTable:
+
+
+; RUN: not llvm-rc /V %p/Inputs/parser-stringtable-no-string.rc 2> %t2
+; RUN: FileCheck %s --check-prefix PSTRINGTABLE1 --input-file %t2
+
+; PSTRINGTABLE1:  llvm-rc: Error parsing file: expected string, got }
+
+
+; RUN: not llvm-rc /V %p/Inputs/parser-stringtable-weird-option.rc 2> %t3
+; RUN: FileCheck %s --check-prefix PSTRINGTABLE2 --input-file %t3
+
+; PSTRINGTABLE2:  llvm-rc: Error parsing file: expected optional statement type, BEGIN or '{', got NONSENSETYPE
+
+
+; RUN: not llvm-rc /V %p/Inputs/parser-eof.rc 2> %t4
+; RUN: FileCheck %s --check-prefix PEOF --input-file %t4
+
+; PEOF:  llvm-rc: Error parsing file: expected integer, got <EOF>
+
+
+; RUN: not llvm-rc /V %p/Inputs/parser-no-characteristics-arg.rc 2> %t5
+; RUN: FileCheck %s --check-prefix PCHARACTERISTICS1 --input-file %t5
+
+; PCHARACTERISTICS1:  llvm-rc: Error parsing file: expected integer, got BEGIN
+
+
+; RUN: not llvm-rc /V %p/Inputs/parser-nonsense-token.rc 2> %t6
+; RUN: FileCheck %s --check-prefix PNONSENSE1 --input-file %t6
+
+; PNONSENSE1:  llvm-rc: Error parsing file: expected int or identifier, got &
+
+
+; RUN: not llvm-rc /V %p/Inputs/parser-nonsense-type.rc 2> %t7
+; RUN: FileCheck %s --check-prefix PNONSENSE2 --input-file %t7
+
+; PNONSENSE2:  llvm-rc: Error parsing file: expected resource type, got WORLD
+
+
+; RUN: not llvm-rc /V %p/Inputs/parser-nonsense-type-eof.rc 2> %t8
+; RUN: FileCheck %s --check-prefix PNONSENSE3 --input-file %t8
+
+; PNONSENSE3:  llvm-rc: Error parsing file: expected int or identifier, got <EOF>
+
+
+; RUN: not llvm-rc /V %p/Inputs/parser-language-no-comma.rc 2> %t9
+; RUN: FileCheck %s --check-prefix PLANGUAGE1 --input-file %t9
+
+; PLANGUAGE1:  llvm-rc: Error parsing file: expected ',', got 7
+
+
+; RUN: not llvm-rc /V %p/Inputs/parser-language-too-many-commas.rc 2> %t10
+; RUN: FileCheck %s --check-prefix PLANGUAGE2 --input-file %t10
+
+; PLANGUAGE2:  llvm-rc: Error parsing file: expected integer, got ,

Modified: llvm/trunk/test/tools/llvm-rc/tokenizer.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/tokenizer.test?rev=311184&r1=311183&r2=311184&view=diff
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/tokenizer.test (original)
+++ llvm/trunk/test/tools/llvm-rc/tokenizer.test Fri Aug 18 11:24:17 2017
@@ -1,4 +1,6 @@
-; RUN: llvm-rc /V %p/Inputs/tokens.rc | FileCheck %s
+; RUN: not llvm-rc /V %p/Inputs/tokens.rc | FileCheck %s
+; llvm-rc fails now on this sample because it is an invalid resource file
+; script. We silence the error message and just analyze the output.
 
 ; CHECK:  Int: 1; int value = 1
 ; CHECK-NEXT:  Plus: +

Modified: llvm/trunk/tools/llvm-rc/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/CMakeLists.txt?rev=311184&r1=311183&r2=311184&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/CMakeLists.txt (original)
+++ llvm/trunk/tools/llvm-rc/CMakeLists.txt Fri Aug 18 11:24:17 2017
@@ -10,5 +10,7 @@ add_public_tablegen_target(RcTableGen)
 
 add_llvm_tool(llvm-rc
   llvm-rc.cpp
+  ResourceScriptParser.cpp
+  ResourceScriptStmt.cpp
   ResourceScriptToken.cpp
   )

Added: llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp?rev=311184&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp (added)
+++ llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp Fri Aug 18 11:24:17 2017
@@ -0,0 +1,267 @@
+//===-- 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"
+
+// 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(const std::vector<RCToken> &TokenList)
+    : Tokens(TokenList), CurLoc(Tokens.begin()), End(Tokens.end()) {}
+
+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("ICON"))
+    Result = parseIconResource();
+  else
+    return getExpectedError("resource type", /* IsAlreadyRead = */ true);
+
+  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++;
+}
+
+Expected<uint32_t> RCParser::readInt() {
+  if (!isNextTokenKind(Kind::Int))
+    return getExpectedError("integer");
+  return read().intValue();
+}
+
+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::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");
+
+  const RCToken &Tok = read();
+  if (Tok.kind() == Kind::Int)
+    return IntOrString(Tok.intValue());
+  else
+    return IntOrString(Tok.value());
+}
+
+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<uint32_t, 8>>
+RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) {
+  assert(MinCount <= MaxCount);
+
+  SmallVector<uint32_t, 8> Result;
+
+  auto FailureHandler =
+      [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 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);
+}
+
+// As for now, we ignore the extended set of statements.
+Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) {
+  OptionalStmtList Result;
+
+  // The last statement is always followed by the start of the block.
+  while (!isNextTokenKind(Kind::BlockBegin)) {
+    ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(IsExtended));
+    Result.addStmt(std::move(*SingleParse));
+  }
+
+  return std::move(Result);
+}
+
+Expected<std::unique_ptr<OptionalStmt>>
+RCParser::parseSingleOptionalStatement(bool) {
+  ASSIGN_OR_RETURN(TypeToken, readIdentifier());
+  if (TypeToken->equals_lower("CHARACTERISTICS"))
+    return parseCharacteristicsStmt();
+  else if (TypeToken->equals_lower("LANGUAGE"))
+    return parseLanguageStmt();
+  else if (TypeToken->equals_lower("VERSION"))
+    return parseVersionStmt();
+  else
+    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::parseIconResource() {
+  ASSIGN_OR_RETURN(Arg, readString());
+  return make_unique<IconResource>(*Arg);
+}
+
+RCParser::ParseType RCParser::parseStringTableResource() {
+  ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
+  RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+  auto Table = 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);
+}
+
+RCParser::ParseOptionType RCParser::parseLanguageStmt() {
+  ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
+  return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
+}
+
+RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
+  ASSIGN_OR_RETURN(Arg, readInt());
+  return make_unique<CharacteristicsStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseVersionStmt() {
+  ASSIGN_OR_RETURN(Arg, readInt());
+  return make_unique<VersionStmt>(*Arg);
+}
+
+Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) {
+  return make_error<ParserError>(
+      Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
+}
+
+} // namespace rc
+} // namespace llvm

Added: llvm/trunk/tools/llvm-rc/ResourceScriptParser.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceScriptParser.h?rev=311184&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceScriptParser.h (added)
+++ llvm/trunk/tools/llvm-rc/ResourceScriptParser.h Fri Aug 18 11:24:17 2017
@@ -0,0 +1,143 @@
+//===-- ResourceScriptParser.h ----------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+//
+// This defines the RC scripts parser. It takes a sequence of RC tokens
+// and then provides the method to parse the resources one by one.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H
+
+#include "ResourceScriptStmt.h"
+#include "ResourceScriptToken.h"
+
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <system_error>
+#include <vector>
+
+namespace llvm {
+namespace rc {
+
+class RCParser {
+public:
+  using LocIter = std::vector<RCToken>::iterator;
+  using ParseType = Expected<std::unique_ptr<RCResource>>;
+  using ParseOptionType = Expected<std::unique_ptr<OptionalStmt>>;
+
+  // Class describing a single failure of parser.
+  class ParserError : public ErrorInfo<ParserError> {
+  public:
+    ParserError(Twine Expected, const LocIter CurLoc, const LocIter End);
+
+    void log(raw_ostream &OS) const override { OS << CurMessage; }
+    std::error_code convertToErrorCode() const override {
+      return std::make_error_code(std::errc::invalid_argument);
+    }
+    const std::string &getMessage() const { return CurMessage; }
+
+    static char ID; // Keep llvm::Error happy.
+
+  private:
+    std::string CurMessage;
+    LocIter ErrorLoc, FileEnd;
+  };
+
+  RCParser(const std::vector<RCToken> &TokenList);
+  RCParser(std::vector<RCToken> &&TokenList);
+
+  // Reads and returns a single resource definition, or error message if any
+  // occurred.
+  ParseType parseSingleResource();
+
+  bool isEof() const;
+
+private:
+  using Kind = RCToken::Kind;
+
+  // Checks if the current parser state points to the token of type TokenKind.
+  bool isNextTokenKind(Kind TokenKind) const;
+
+  // These methods assume that the parser is not in EOF state.
+
+  // Take a look at the current token. Do not fetch it.
+  const RCToken &look() const;
+  // Read the current token and advance the state by one token.
+  const RCToken &read();
+  // Advance the state by one token, discarding the current token.
+  void consume();
+
+  // The following methods try to read a single token, check if it has the
+  // correct type and then parse it.
+  Expected<uint32_t> readInt();           // Parse an integer.
+  Expected<StringRef> readString();       // Parse a string.
+  Expected<StringRef> readIdentifier();   // Parse an identifier.
+  Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier.
+
+  // Advance the state by one, discarding the current token.
+  // If the discarded token had an incorrect type, fail.
+  Error consumeType(Kind TokenKind);
+
+  // Check the current token type. If it's TokenKind, discard it.
+  // Return true if the parser consumed this token successfully.
+  bool consumeOptionalType(Kind TokenKind);
+
+  // Read at least MinCount, and at most MaxCount integers separated by
+  // commas. The parser stops reading after fetching MaxCount integers
+  // or after an error occurs. Whenever the parser reads a comma, it
+  // expects an integer to follow.
+  Expected<SmallVector<uint32_t, 8>> readIntsWithCommas(size_t MinCount,
+                                                        size_t MaxCount);
+
+  // Reads a set of optional statements. These can change the behavior of
+  // a number of resource types (e.g. STRINGTABLE, MENU or DIALOG) if provided
+  // before the main block with the contents of the resource.
+  // Usually, resources use a basic set of optional statements:
+  //    CHARACTERISTICS, LANGUAGE, VERSION
+  // However, DIALOG and DIALOGEX extend this list by the following items:
+  //    CAPTION, CLASS, EXSTYLE, FONT, MENU, STYLE
+  // UseExtendedStatements flag (off by default) allows the parser to read
+  // the additional types of statements.
+  //
+  // Ref (to the list of all optional statements):
+  //    msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx
+  Expected<OptionalStmtList>
+  parseOptionalStatements(bool UseExtendedStatements = false);
+
+  // Read a single optional statement.
+  Expected<std::unique_ptr<OptionalStmt>>
+  parseSingleOptionalStatement(bool UseExtendedStatements = false);
+
+  // Top-level resource parsers.
+  ParseType parseLanguageResource();
+  ParseType parseIconResource();
+  ParseType parseStringTableResource();
+
+  // Optional statement parsers.
+  ParseOptionType parseLanguageStmt();
+  ParseOptionType parseCharacteristicsStmt();
+  ParseOptionType parseVersionStmt();
+
+  // Raises an error. If IsAlreadyRead = false (default), this complains about
+  // the token that couldn't be parsed. If the flag is on, this complains about
+  // the correctly read token that makes no sense (that is, the current parser
+  // state is beyond the erroneous token.)
+  Error getExpectedError(const Twine Message, bool IsAlreadyRead = false);
+
+  std::vector<RCToken> Tokens;
+  LocIter CurLoc;
+  const LocIter End;
+};
+
+} // namespace rc
+} // namespace llvm
+
+#endif

Added: llvm/trunk/tools/llvm-rc/ResourceScriptStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceScriptStmt.cpp?rev=311184&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceScriptStmt.cpp (added)
+++ llvm/trunk/tools/llvm-rc/ResourceScriptStmt.cpp Fri Aug 18 11:24:17 2017
@@ -0,0 +1,60 @@
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+//
+// This implements methods defined in ResourceScriptStmt.h.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceScriptStmt.h"
+
+namespace llvm {
+namespace rc {
+
+raw_ostream &operator<<(raw_ostream &OS, const IntOrString &Item) {
+  if (Item.IsInt)
+    return OS << Item.Data.Int;
+  else
+    return OS << Item.Data.String;
+}
+
+raw_ostream &OptionalStmtList::log(raw_ostream &OS) const {
+  for (const auto &Stmt : Statements) {
+    OS << "  Option: ";
+    Stmt->log(OS);
+  }
+  return OS;
+}
+
+raw_ostream &LanguageResource::log(raw_ostream &OS) const {
+  return OS << "Language: " << Lang << ", Sublanguage: " << SubLang << "\n";
+}
+
+raw_ostream &IconResource::log(raw_ostream &OS) const {
+  return OS << "Icon (" << ResName << "): " << IconLoc << "\n";
+}
+
+raw_ostream &StringTableResource::log(raw_ostream &OS) const {
+  OS << "StringTable:\n";
+  OptStatements.log(OS);
+  for (const auto &String : Table)
+    OS << "  " << String.first << " => " << String.second << "\n";
+  return OS;
+}
+
+raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const {
+  return OS << "Characteristics: " << Value << "\n";
+}
+
+raw_ostream &VersionStmt::log(raw_ostream &OS) const {
+  return OS << "Version: " << Value << "\n";
+}
+
+} // namespace rc
+} // namespace llvm

Added: llvm/trunk/tools/llvm-rc/ResourceScriptStmt.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceScriptStmt.h?rev=311184&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceScriptStmt.h (added)
+++ llvm/trunk/tools/llvm-rc/ResourceScriptStmt.h Fri Aug 18 11:24:17 2017
@@ -0,0 +1,145 @@
+//===-- ResourceScriptStmt.h ------------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+//
+// This lists all the resource and statement types occurring in RC scripts.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H
+
+#include "ResourceScriptToken.h"
+
+namespace llvm {
+namespace rc {
+
+// A class holding a name - either an integer or a reference to the string.
+class IntOrString {
+private:
+  union Data {
+    uint32_t Int;
+    StringRef String;
+    Data(uint32_t Value) : Int(Value) {}
+    Data(const StringRef Value) : String(Value) {}
+    Data(const RCToken &Token);
+  } Data;
+  bool IsInt;
+
+public:
+  IntOrString() : IntOrString(0) {}
+  IntOrString(uint32_t Value) : Data(Value), IsInt(1) {}
+  IntOrString(StringRef Value) : Data(Value), IsInt(0) {}
+  IntOrString(const RCToken &Token)
+      : Data(Token), IsInt(Token.kind() == RCToken::Kind::Int) {}
+
+  bool equalsLower(const char *Str) {
+    return !IsInt && Data.String.equals_lower(Str);
+  }
+
+  friend raw_ostream &operator<<(raw_ostream &, const IntOrString &);
+};
+
+// Base resource. All the resources should derive from this base.
+class RCResource {
+protected:
+  IntOrString ResName;
+
+public:
+  RCResource() = default;
+  RCResource(RCResource &&) = default;
+  void setName(const IntOrString &Name) { ResName = Name; }
+  virtual raw_ostream &log(raw_ostream &OS) const {
+    return OS << "Base statement\n";
+  };
+  virtual ~RCResource() {}
+};
+
+// Optional statement base. All such statements should derive from this base.
+class OptionalStmt : public RCResource {};
+
+class OptionalStmtList : public OptionalStmt {
+  std::vector<std::unique_ptr<OptionalStmt>> Statements;
+
+public:
+  OptionalStmtList() {}
+  virtual raw_ostream &log(raw_ostream &OS) const;
+
+  void addStmt(std::unique_ptr<OptionalStmt> Stmt) {
+    Statements.push_back(std::move(Stmt));
+  }
+};
+
+// LANGUAGE statement. It can occur both as a top-level statement (in such
+// a situation, it changes the default language until the end of the file)
+// and as an optional resource statement (then it changes the language
+// of a single resource).
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381019(v=vs.85).aspx
+class LanguageResource : public OptionalStmt {
+  uint32_t Lang, SubLang;
+
+public:
+  LanguageResource(uint32_t LangId, uint32_t SubLangId)
+      : Lang(LangId), SubLang(SubLangId) {}
+  raw_ostream &log(raw_ostream &) const override;
+};
+
+// ICON resource. Represents a single ".ico" file containing a group of icons.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381018(v=vs.85).aspx
+class IconResource : public RCResource {
+  StringRef IconLoc;
+
+public:
+  IconResource(StringRef Location) : IconLoc(Location) {}
+  raw_ostream &log(raw_ostream &) const override;
+};
+
+// STRINGTABLE resource. Contains a list of strings, each having its unique ID.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx
+class StringTableResource : public RCResource {
+  OptionalStmtList OptStatements;
+  std::vector<std::pair<uint32_t, StringRef>> Table;
+
+public:
+  StringTableResource(OptionalStmtList &&OptStmts)
+      : OptStatements(std::move(OptStmts)) {}
+  void addString(uint32_t ID, StringRef String) {
+    Table.emplace_back(ID, String);
+  }
+  raw_ostream &log(raw_ostream &) const override;
+};
+
+// CHARACTERISTICS optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx
+class CharacteristicsStmt : public OptionalStmt {
+  uint32_t Value;
+
+public:
+  CharacteristicsStmt(uint32_t Characteristic) : Value(Characteristic) {}
+  raw_ostream &log(raw_ostream &) const override;
+};
+
+// VERSION optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381059(v=vs.85).aspx
+class VersionStmt : public OptionalStmt {
+  uint32_t Value;
+
+public:
+  VersionStmt(uint32_t Version) : Value(Version) {}
+  raw_ostream &log(raw_ostream &) const override;
+};
+
+} // namespace rc
+} // namespace llvm
+
+#endif

Modified: llvm/trunk/tools/llvm-rc/llvm-rc.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/llvm-rc.cpp?rev=311184&r1=311183&r2=311184&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/llvm-rc.cpp (original)
+++ llvm/trunk/tools/llvm-rc/llvm-rc.cpp Fri Aug 18 11:24:17 2017
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "ResourceScriptToken.h"
+#include "ResourceScriptParser.h"
 
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
@@ -133,5 +134,12 @@ int main(int argc_, const char *argv_[])
     }
   }
 
+  rc::RCParser Parser{std::move(Tokens)};
+  while (!Parser.isEof()) {
+    auto Resource = ExitOnErr(Parser.parseSingleResource());
+    if (BeVerbose)
+      Resource->log(outs());
+  }
+
   return 0;
 }




More information about the llvm-commits mailing list