Index: llvm/test/tools/llvm-rc/Inputs/parser-correct-everything.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-correct-everything.rc @@ -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 Index: llvm/test/tools/llvm-rc/Inputs/parser-eof.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-eof.rc @@ -0,0 +1 @@ +LANGUAGE Index: llvm/test/tools/llvm-rc/Inputs/parser-language-no-comma.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-language-no-comma.rc @@ -0,0 +1 @@ +LANGUAGE 5 7 Index: llvm/test/tools/llvm-rc/Inputs/parser-language-too-many-commas.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-language-too-many-commas.rc @@ -0,0 +1 @@ +LANGUAGE 5,, 7 Index: llvm/test/tools/llvm-rc/Inputs/parser-no-characteristics-arg.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-no-characteristics-arg.rc @@ -0,0 +1,5 @@ +STRINGTABLE +CHARACTERISTICS +BEGIN + 100 "No integer after CHARACTERISTICS." +END Index: llvm/test/tools/llvm-rc/Inputs/parser-nonsense-token.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-nonsense-token.rc @@ -0,0 +1 @@ +& ICON "WeirdResourceName.ico" Index: llvm/test/tools/llvm-rc/Inputs/parser-nonsense-type-eof.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-nonsense-type-eof.rc @@ -0,0 +1 @@ +HELLO Index: llvm/test/tools/llvm-rc/Inputs/parser-nonsense-type.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-nonsense-type.rc @@ -0,0 +1 @@ +HELLO WORLD Index: llvm/test/tools/llvm-rc/Inputs/parser-stringtable-no-string.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-stringtable-no-string.rc @@ -0,0 +1,6 @@ +STRINGTABLE +VERSION 8 +{ + 1 "hello" + 2 +} Index: llvm/test/tools/llvm-rc/Inputs/parser-stringtable-weird-option.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-stringtable-weird-option.rc @@ -0,0 +1,4 @@ +STRINGTABLE +NONSENSETYPE 12 34 +BEGIN +END Index: llvm/test/tools/llvm-rc/parser.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/parser.test @@ -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: llvm-rc /V %p/Inputs/parser-stringtable-no-string.rc 2> %t2 || true +; RUN: FileCheck %s --check-prefix PSTRINGTABLE1 --input-file %t2 + +; PSTRINGTABLE1: llvm-rc: Error parsing file: expected string, got } + + +; RUN: llvm-rc /V %p/Inputs/parser-stringtable-weird-option.rc 2> %t3 || true +; RUN: FileCheck %s --check-prefix PSTRINGTABLE2 --input-file %t3 + +; PSTRINGTABLE2: llvm-rc: Error parsing file: expected optional statement type, BEGIN or '{', got NONSENSETYPE + + +; RUN: llvm-rc /V %p/Inputs/parser-eof.rc 2> %t4 || true +; RUN: FileCheck %s --check-prefix PEOF --input-file %t4 + +; PEOF: llvm-rc: Error parsing file: expected integer, got + + +; RUN: llvm-rc /V %p/Inputs/parser-no-characteristics-arg.rc 2> %t5 || true +; RUN: FileCheck %s --check-prefix PCHARACTERISTICS1 --input-file %t5 + +; PCHARACTERISTICS1: llvm-rc: Error parsing file: expected integer, got BEGIN + + +; RUN: llvm-rc /V %p/Inputs/parser-nonsense-token.rc 2> %t6 || true +; RUN: FileCheck %s --check-prefix PNONSENSE1 --input-file %t6 + +; PNONSENSE1: llvm-rc: Error parsing file: expected int or identifier, got & + + +; RUN: llvm-rc /V %p/Inputs/parser-nonsense-type.rc 2> %t7 || true +; RUN: FileCheck %s --check-prefix PNONSENSE2 --input-file %t7 + +; PNONSENSE2: llvm-rc: Error parsing file: expected resource type, got WORLD + + +; RUN: llvm-rc /V %p/Inputs/parser-nonsense-type-eof.rc 2> %t8 || true +; RUN: FileCheck %s --check-prefix PNONSENSE3 --input-file %t8 + +; PNONSENSE3: llvm-rc: Error parsing file: expected int or identifier, got + + +; RUN: llvm-rc /V %p/Inputs/parser-language-no-comma.rc 2> %t9 || true +; RUN: FileCheck %s --check-prefix PLANGUAGE1 --input-file %t9 + +; PLANGUAGE1: llvm-rc: Error parsing file: expected ',', got 7 + + +; RUN: llvm-rc /V %p/Inputs/parser-language-too-many-commas.rc 2> %t10 || true +; RUN: FileCheck %s --check-prefix PLANGUAGE2 --input-file %t10 + +; PLANGUAGE2: llvm-rc: Error parsing file: expected integer, got , Index: llvm/test/tools/llvm-rc/tokenizer.test =================================================================== --- llvm/test/tools/llvm-rc/tokenizer.test +++ llvm/test/tools/llvm-rc/tokenizer.test @@ -1,4 +1,6 @@ -; RUN: llvm-rc /V %p/Inputs/tokens.rc | FileCheck %s +; RUN: (llvm-rc /V %p/Inputs/tokens.rc || true) | 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: + Index: llvm/tools/llvm-rc/CMakeLists.txt =================================================================== --- llvm/tools/llvm-rc/CMakeLists.txt +++ llvm/tools/llvm-rc/CMakeLists.txt @@ -10,5 +10,7 @@ add_llvm_tool(llvm-rc llvm-rc.cpp + ResourceScriptParser.cpp + ResourceScriptStmt.cpp ResourceScriptToken.cpp ) Index: llvm/tools/llvm-rc/ResourceScriptParser.h =================================================================== --- /dev/null +++ llvm/tools/llvm-rc/ResourceScriptParser.h @@ -0,0 +1,138 @@ +//===-- 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 +#include + +namespace llvm { +namespace rc { + +class RCParser { +public: + using LocIter = std::vector::iterator; + using ParseType = Expected>; + using ParseOptionType = Expected>; + + // Class describing a single failure of parser. + class ParserError : public ErrorInfo { + 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 &TokenList); + RCParser(std::vector &&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 readInt(); // Parse an integer. + Expected readString(); // Parse a string. + Expected readIdentifier(); // Parse an identifier. + Expected 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. + Expected> consumeType(Kind TokenKind); + + // Check the current token type. If it's TokenKind, discard it. + void 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> 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 + parseOptionalStatements(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 Tokens; + LocIter CurLoc; + const LocIter End; +}; + +} // namespace rc +} // namespace llvm + +#endif Index: llvm/tools/llvm-rc/ResourceScriptParser.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-rc/ResourceScriptParser.cpp @@ -0,0 +1,283 @@ +//===-- 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" + +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 ? "" : CurLoc->value()).str(); +} + +char RCParser::ParserError::ID = 0; + +RCParser::RCParser(const std::vector &TokenList) + : Tokens(TokenList), CurLoc(Tokens.begin()), End(Tokens.end()) {} + +RCParser::RCParser(std::vector &&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. + auto NameToken = readTypeOrName(); + + if (!NameToken) + return NameToken.takeError(); + + 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; + auto TypeToken = readTypeOrName(); + + if (!TypeToken) + return TypeToken.takeError(); + + ParseType Result = std::unique_ptr(); + (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 RCParser::readInt() { + if (!isNextTokenKind(Kind::Int)) + return getExpectedError("integer"); + return read().intValue(); +} + +Expected RCParser::readString() { + if (!isNextTokenKind(Kind::String)) + return getExpectedError("string"); + return read().value(); +} + +Expected RCParser::readIdentifier() { + if (!isNextTokenKind(Kind::Identifier)) + return getExpectedError("identifier"); + return read().value(); +} + +Expected 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()); +} + +Expected> RCParser::consumeType(Kind TokenKind) { + if (isNextTokenKind(TokenKind)) { + consume(); + return std::tuple<>(); + } + + 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 + } +} + +void RCParser::consumeOptionalType(Kind TokenKind) { + if (isNextTokenKind(TokenKind)) + consume(); +} + +Expected> +RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) { + assert(MinCount <= MaxCount); + + SmallVector Result; + + auto FailureHandler = + [&](llvm::Error &&Err) -> Expected> { + 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) { + auto CommaResult = consumeType(Kind::Comma); + if (!CommaResult) + return FailureHandler(CommaResult.takeError()); + } + + if (auto IntResult = readInt()) + Result.push_back(*IntResult); + else + return FailureHandler(IntResult.takeError()); + } + + return Result; +} + +// As for now, we ignore the extended set of statements. +Expected RCParser::parseOptionalStatements(bool) { + OptionalStmtList Result; + + // The last statement is always followed by the start of the block. + while (!isNextTokenKind(Kind::BlockBegin)) { + auto TypeToken = readIdentifier(); + if (!TypeToken) + return TypeToken.takeError(); + + auto ParseSingleStmt = [&TypeToken, + this]() -> Expected> { + 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); + }; + + if (auto SingleParse = ParseSingleStmt()) + Result.addStmt(std::move(*SingleParse)); + else + return SingleParse.takeError(); + } + + return Result; +} + +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() { + if (auto Arg = readString()) + return make_unique(*Arg); + else + return Arg.takeError(); +} + +RCParser::ParseType RCParser::parseStringTableResource() { + auto OptStatements = parseOptionalStatements(); + if (!OptStatements) + return OptStatements.takeError(); + + auto BlockResult = consumeType(Kind::BlockBegin); + if (!BlockResult) + return BlockResult.takeError(); + + auto Table = make_unique(std::move(*OptStatements)); + + // Read strings until we reach the end of the block. + while (!isNextTokenKind(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. + auto IDResult = readInt(); + if (!IDResult) + return IDResult.takeError(); + + auto StrResult = readString(); + if (!StrResult) + return StrResult.takeError(); + + Table->addString(*IDResult, *StrResult); + } + + // We're sure to read BlockEnd now. + consume(); + + return Table; +} + +RCParser::ParseOptionType RCParser::parseLanguageStmt() { + if (auto Args = readIntsWithCommas(/* min = */ 2, /* max = */ 2)) + return make_unique((*Args)[0], (*Args)[1]); + else + return Args.takeError(); +} + +RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() { + if (auto Arg = readInt()) + return make_unique(*Arg); + else + return Arg.takeError(); +} + +RCParser::ParseOptionType RCParser::parseVersionStmt() { + if (auto Arg = readInt()) + return make_unique(*Arg); + else + return Arg.takeError(); +} + +Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) { + return make_error( + Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); +} + +} // namespace rc +} // namespace llvm Index: llvm/tools/llvm-rc/ResourceScriptStmt.h =================================================================== --- /dev/null +++ llvm/tools/llvm-rc/ResourceScriptStmt.h @@ -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> Statements; + +public: + OptionalStmtList() {} + virtual raw_ostream &log(raw_ostream &OS) const; + + void addStmt(std::unique_ptr &&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> 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 Index: llvm/tools/llvm-rc/ResourceScriptStmt.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-rc/ResourceScriptStmt.cpp @@ -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 Index: llvm/tools/llvm-rc/llvm-rc.cpp =================================================================== --- llvm/tools/llvm-rc/llvm-rc.cpp +++ llvm/tools/llvm-rc/llvm-rc.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "ResourceScriptToken.h" +#include "ResourceScriptParser.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -133,5 +134,12 @@ } } + rc::RCParser Parser{std::move(Tokens)}; + while (!Parser.isEof()) { + auto Resource = ExitOnErr(Parser.parseSingleResource()); + if (BeVerbose) + Resource->log(outs()); + } + return 0; }