Index: llvm/test/tools/llvm-rc/Inputs/parser1.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser1.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/parser2.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser2.rc @@ -0,0 +1,6 @@ +STRINGTABLE +VERSION 8 +{ + 1 "hello" + 2 +} Index: llvm/test/tools/llvm-rc/Inputs/parser3.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser3.rc @@ -0,0 +1 @@ +LANGUAGE Index: llvm/test/tools/llvm-rc/Inputs/parser4.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser4.rc @@ -0,0 +1,5 @@ +STRINGTABLE +CHARACTERISTICS +BEGIN + 100 "No integer after CHARACTERISTICS." +END Index: llvm/test/tools/llvm-rc/Inputs/parser5.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser5.rc @@ -0,0 +1 @@ +& ICON "WeirdResourceName.ico" Index: llvm/test/tools/llvm-rc/Inputs/parser6.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser6.rc @@ -0,0 +1 @@ +LANGUAGE 5 7 Index: llvm/test/tools/llvm-rc/Inputs/parser7.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser7.rc @@ -0,0 +1 @@ +LANGUAGE 5,, 7 Index: llvm/test/tools/llvm-rc/parser.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/parser.test @@ -0,0 +1,50 @@ +; RUN: llvm-rc /V %p/Inputs/parser1.rc | FileCheck %s --check-prefix P1 + +; P1: Parsed file: +; P1-NEXT: Icon ("meh"): "hello.bmp" +; P1-NEXT: Icon (Icon): "Icon" +; P1-NEXT: Language: 5, Sublanguage: 12 +; P1-NEXT: StringTable: +; P1-NEXT: Option: Language: 1, Sublanguage: 1 +; P1-NEXT: Option: Characteristics: 500 +; P1-NEXT: Option: Language: 3, Sublanguage: 4 +; P1-NEXT: Option: Version: 14 +; P1-NEXT: 1 => "hello" +; P1-NEXT: 2 => "world" +; P1-NEXT: StringTable: + + +; RUN: llvm-rc /V %p/Inputs/parser2.rc 2> %t2 || true +; RUN: FileCheck %s --check-prefix P2 --input-file %t2 + +; P2: llvm-rc: Error parsing file: expected string, got } + + +; RUN: llvm-rc /V %p/Inputs/parser3.rc 2> %t3 || true +; RUN: FileCheck %s --check-prefix P3 --input-file %t3 + +; P3: llvm-rc: Error parsing file: expected integer, got + + +; RUN: llvm-rc /V %p/Inputs/parser4.rc 2> %t4 || true +; RUN: FileCheck %s --check-prefix P4 --input-file %t4 + +; P4: llvm-rc: Error parsing file: expected integer, got BEGIN + + +; RUN: llvm-rc /V %p/Inputs/parser5.rc 2> %t5 || true +; RUN: FileCheck %s --check-prefix P5 --input-file %t5 + +; P5: llvm-rc: Error parsing file: expected data/resource, got & + + +; RUN: llvm-rc /V %p/Inputs/parser6.rc 2> %t6 || true +; RUN: FileCheck %s --check-prefix P6 --input-file %t6 + +; P6: llvm-rc: Error parsing file: expected comma, got 7 + + +; RUN: llvm-rc /V %p/Inputs/parser7.rc 2> %t7 || true +; RUN: FileCheck %s --check-prefix P7 --input-file %t7 + +; P7: 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 > %t1 2> %t2 || echo "Errorcode $?" >> %t2 +; RUN: FileCheck %s --input-file %t1 --check-prefix CHECK +; RUN: FileCheck %s --input-file %t2 --check-prefix PARSE ; CHECK: Int: 1; int value = 1 ; CHECK-NEXT: Plus: + @@ -33,3 +35,6 @@ ; CHECK-NEXT: Comma: , ; CHECK-NEXT: Int: 100; int value = 100 ; CHECK-NEXT: String: ":))" + +; PARSE: llvm-rc: Error parsing file: expected data/resource, got 1 +; PARSE-NEXT: Errorcode 1 Index: llvm/tools/llvm-rc/CMakeLists.txt =================================================================== --- llvm/tools/llvm-rc/CMakeLists.txt +++ llvm/tools/llvm-rc/CMakeLists.txt @@ -11,4 +11,6 @@ add_llvm_tool(llvm-rc llvm-rc.cpp ResourceScriptToken.cpp + ResourceScriptParser.cpp + ResourceScriptStmt.cpp ) Index: llvm/tools/llvm-rc/ResourceScriptParser.h =================================================================== --- /dev/null +++ llvm/tools/llvm-rc/ResourceScriptParser.h @@ -0,0 +1,402 @@ +//===-- 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 is an implementation of .rc scripts parser. It accepts the tokenized +// input and produces single statement/resource definitions. This parser allows +// to define the statements as the composition of simple elements. For example, +// LANGUAGE statement has no assigned name; it starts by 'LANGUAGE' identifier, +// and its parameters are: an integer and an integer followed by a comma: +// +// ADD_IDENT_NAME(Language); /* Introduces class 'LanguageName'. */ +// class LanguageStmt : public Resource { +// public: +// LanguageStmt(uint32_t, uint32_t); +// }; +// +// using NodeLanguageStmt = +// UnnamedItem>>; +// +// Each of the parser classes defines a static method parse(&Start, End), +// where Start is an iterator to the beginning of the parsed token, and End +// defines the end of the text. The method returns Expected> +// which contains either an error description or a pointer to the read token. +// Moreover, it advances the 'Start' iterator so that it points right after +// the read token. Using pointers is necessary to maintain the polymorphism. +// +// All the parsers should define ParseType. This defines a type returned by the +// parse() method. In fact, the returned type is llvm::Expected. +// +// Sometimes a parser class has multiple ways to parse a sequence of items +// (e.g. NodeList might read any number of items and needs to know when to end; +// NodeEither can choose one of ways of parsing the input etc.) Thus, we +// assume the following algorithm: +// 1. Pick a way of parsing a sequence of items (e.g. in case of NodeList, +// try parsing the next item.) +// 2. If the chosen method consumes some tokens, commit to it. If it parses +// successfully, take the result and continue/return. If it fails, +// the whole class should fail. +// 3. If the chosen method doesn't consume any tokens, try another method. +// Note that this method might look at the future tokens before trying +// to consume anything. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H +#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H + +#include "ResourceScriptToken.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include + +namespace llvm { +namespace node { + +using Kind = RCToken::Kind; + +using LocIter = std::vector::iterator; + +// 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; +}; + +Error getParserError(const Twine &Expected, const LocIter CurLoc, + const LocIter End); + +// 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(std::move(Value)), IsInt(0) {} + IntOrString(const RCToken &Token) + : Data(Token), IsInt(Token.kind() == Kind::Int) {} + + friend raw_ostream &operator<<(raw_ostream &, const IntOrString &); +}; + +// Describes a concatenated sequence of specified tokens. Templated by +// NodeClass, a class deriving from ConcatArgs. +// This class tries to parse all arguments (ArgTs...) sequentially. After it +// succeeds, it constructs an object of type ConcatArgs, providing to it +// all the parsed arguments. +// If any of the parser sub-invocations fails, this parser itself fails and +// forwards the error message to the caller. +template class ConcatArgs { + // The tuple that stores the parsed arguments. + using ChildrenTuple = std::tuple; + + // Parses a single argument of type Type at position ParamId to tuple + // Children. If the result tuple is already in erroneous state, do nothing. + // If any error occurs when parsing, leave this error in Children variable. + template + static void parseOne(LocIter &Location, const LocIter End, + Expected &Children) { + if (!Children) + return; + + auto ParseResult = Type::parse(Location, End); + if (ParseResult) + std::get(*Children) = std::move(ParseResult.get()); + else + Children = ParseResult.takeError(); + } + + // Main body of parsing function. Has a parameter pack describing the + // sequence of integers from 0 to sizeof...(ArgTs) - 1. + template + static Expected makePtr(LocIter &Location, const LocIter End, + integer_sequence) { + // Construct a tuple of results. + Expected Children{ChildrenTuple{}}; + // Parse the sub-expressions one-by-one. If any of them fails, the error + // is unchanged in subsequent evaluations and returned in Children variable. + LLVM_ATTRIBUTE_UNUSED + int Temporary[] = {(parseOne(Location, End, Children), 0)...}; + + if (!Children) + return Children.takeError(); + return Expected{std::move(NodeClass{ + std::get(std::forward(Children.get()))...})}; + } + +public: + using ParseType = NodeClass; + + static Expected parse(LocIter &Location, const LocIter End) { + return makePtr(Location, End, index_sequence_for{}); + } +}; + +// Describes a Node that comes right after the comma. The parser tries +// to read the comma. If it succeeds, proceeds to parsing the templated token +// and returns it. +template class AfterComma { +public: + using ParseType = typename Node::ParseType; + + static Expected parse(LocIter &Location, const LocIter End) { + if (Location == End || Location->kind() != Kind::Comma) + return getParserError("comma", Location, End); + ++Location; + return Node::parse(Location, End); + } +}; + +// Describes a Node that might possibly appear in the parsed sequence. +// Parsing this returns llvm::Optional<(result of parsing of Node)>. +// Note that consuming at least one token when parsing Node implies committing +// to it. In such situaion, when an error occurs, Optional fails, too. +template class NodeOptional { +public: + using ParseType = Optional; + + static Expected parse(LocIter &Location, const LocIter End) { + const LocIter OldLoc = Location; + auto Result = Node::parse(Location, End); + if (Result) + return Result.get(); + else if (Location != OldLoc) + return Result.takeError(); + + // If parsing of Node failed without advancing Location, we just didn't + // read anything. + consumeError(Result.takeError()); + return ParseType{}; + } +}; + +// Describes a variable-length list of Nodes. +// Returns std::vector<(result of parsing of Node)>. +// The class reads Nodes until one of the parsing attempts fails. If any +// token has been consumed during this single attempt, the whole List fails. +// In the opposite case, already read Nodes (possibly none) are returned. +template class NodeList { +public: + using ParseType = std::vector; + + static Expected parse(LocIter &Location, const LocIter End) { + LocIter CurLoc = Location; + ParseType ResultVec; + while (true) { + auto Result = Node::parse(Location, End); + if (!Result) { + auto Err = Result.takeError(); + if (Location != CurLoc) { + return std::move(Err); + } else { + consumeError(std::move(Err)); + break; + } + } + CurLoc = Location; + ResultVec.push_back(std::move(Result.get())); + } + Location = CurLoc; + return ResultVec; + } +}; + +// Describes a variable-length list of Nodes representing a single block +// of script: BlockBegin [Node]* BlockEnd. +// Returns std::vector<(result of parsing of Node)>. +// The class is analoguous to NodeList in terms of its behavior. +template class NodeBlockList { +public: + using ParseType = typename NodeList::ParseType; + + static Expected parse(LocIter &Location, const LocIter End) { + if (Location == End || Location->kind() != Kind::BlockBegin) + return getParserError("{ or BEGIN", Location, End); + Location++; + + auto Result = NodeList::parse(Location, End); + if (!Result) + return std::move(Result); + + if (Location == End || Location->kind() != Kind::BlockEnd) + return getParserError("} or END", Location, End); + Location++; + + return std::move(Result); + } +}; + +// Describes a node that might be one of EitherTypes. +// Its ParseType is a pointer to ReturnType as we want to preserve the +// polymorphism between ReturnType and each parsed type. That is, each +// of the parsed types should derive, directly or indirectly, after +// ReturnType. +// +// This tries matching the subsequent types one by one. If any of them +// succeeds or consumes any token, the search is terminated and the +// return value is forwarded. If no type matches, the search fails. +template class NodeEither; + +template +class NodeEither { +public: + using ParseType = std::unique_ptr; + + static Expected parse(LocIter &Location, const LocIter End) { + const LocIter StartLoc = Location; + auto Result = Head::parse(Location, End); + if (Result) + return make_unique(std::move(Result.get())); + if (Location != StartLoc) + return Result.takeError(); + + consumeError(Result.takeError()); + return NodeEither::parse(Location, End); + } +}; + +template class NodeEither { +public: + using ParseType = std::unique_ptr; + + static Expected parse(LocIter &Location, const LocIter End) { + return getParserError("data/resource", Location, End); + } +}; + +// Describes an integer node. For now, it expects to read an integer. +// In the future, it should read arithmetic expressions consisting of +// tokens ( ) + - | & ~. +class NodeInt { +public: + using ParseType = uint32_t; + + static Expected parse(LocIter &Location, const LocIter End); +}; + +// Describes a node parsing string tokens. +class NodeString { +public: + using ParseType = StringRef; + + static Expected parse(LocIter &Location, const LocIter End); +}; + +// Describes a node parsing tokens which might be either an integer, +// string or identifier. This kind of nodes is used when naming resources: +// msdn.microsoft.com/en-us/library/windows/desktop/aa380680(v=vs.85).aspx +class NodeResourceName { +public: + using ParseType = IntOrString; + + static Expected parse(LocIter &Location, const LocIter End); +}; + +// Describes a resource or statement of the following format: +// FIXED_STRING Arguments... +// where FIXED_STRING == ItemName::get() and arguments are parsed by ItemNode. +// FIXED_STRING is compared in a case-insensitive manner. +// An example of such item is LANGUAGE: +// msdn.microsoft.com/en-us/library/windows/desktop/aa381019(v=vs.85).aspx +// +// As soon as FIXED_STRING (name of resource) is matched, the token is +// consumed in order to prevent other resource types from trying to parse +// (that is, we commit to this type of item). +template class UnnamedItem { +protected: + static bool checkEq(LocIter Location) { + return Location->value().equals_lower(ItemName::get()); + } + +public: + using ParseType = typename ItemNode::ParseType; + + static Expected parse(LocIter &Location, const LocIter End) { + if (Location == End || !checkEq(Location)) + return getParserError(ItemName::get(), Location, End); + return ItemNode::parse(++Location, End); + } + + static const char *itemType() { return ItemName::get(); } +}; + +// Describes a resource or statement of the following format: +// Name FIXED_STRING Arguments... +// where Name is parsed by NodeResourceName, +// FIXED_STRING == ItemName::get() (case-insensitive comparison), and +// Arguments are parsed by ItemNode. +// +// As soon as Name and FIXED_STRING are matched, the two tokens are consumed +// to disallow other resource types from trying to parse. +template +class NamedItem : public UnnamedItem { +public: + using ParseType = typename ItemNode::ParseType; + + static Expected parse(LocIter &Location, const LocIter End) { + if (Location == End) + return getParserError("resource name", Location, End); + if (Location->kind() != Kind::Int && Location->kind() != Kind::String && + Location->kind() != Kind::Identifier) + return getParserError("resource name", Location, End); + if (!UnnamedItem::checkEq(Location + 1)) + return getParserError(ItemName::get(), Location, End); + IntOrString Name{*Location}; + + Location += 2; + auto Result = ItemNode::parse(Location, End); + + if (Result) + Result->setName(std::move(Name)); + + return std::move(Result); + } +}; + +// A macro emitting IdentName class declaring its name to be Ident. This allows +// the classes to be used in UnnamedItem and NamedItem parsers. +#define ADD_IDENT_NAME(Ident) \ + class Ident##Name { \ + public: \ + static const char *get() { return #Ident; } \ + }; + +} // namespace node +} // namespace llvm + +#endif Index: llvm/tools/llvm-rc/ResourceScriptParser.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-rc/ResourceScriptParser.cpp @@ -0,0 +1,77 @@ +//===-- 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 non-templated functions from ResourceScriptParser.h. +// +//===---------------------------------------------------------------------===// + +#include "ResourceScriptParser.h" + +namespace llvm { +namespace node { + +char ParserError::ID = 0; + +IntOrString::Data::Data(const RCToken &Token) { + if (Token.kind() == Kind::Int) + Int = Token.intValue(); + else if (Token.kind() == Kind::String || Token.kind() == Kind::Identifier) + String = Token.value(); + else + assert(false && "Tried to get IntOrString from neither int, string " + "or identifier."); +} + +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(); +} + +Error getParserError(const Twine &Message, const LocIter CurLoc, + const LocIter End) { + return make_error(Message, CurLoc, End); +} + +raw_ostream &operator<<(raw_ostream &OS, const IntOrString &Item) { + if (Item.IsInt) + return OS << Item.Data.Int; + else + return OS << Item.Data.String; +} + +Expected NodeInt::parse(LocIter &Location, + const LocIter End) { + if (Location == End || Location->kind() != Kind::Int) + return getParserError("integer", Location, End); + return (Location++)->intValue(); +} + +Expected NodeString::parse(LocIter &Location, + const LocIter End) { + if (Location == End || Location->kind() != Kind::String) + return getParserError("string", Location, End); + return (Location++)->value(); +} + +Expected +NodeResourceName::parse(LocIter &Location, const LocIter End) { + if (Location == End) + return getParserError("resource name", Location, End); + const Kind LocKind = Location->kind(); + if (LocKind == Kind::Int) + return (Location++)->intValue(); + if (LocKind == Kind::String || LocKind == Kind::Identifier) + return (Location++)->value(); + return getParserError("resource name", Location, End); +} + +} // namespace node +} // namespace llvm Index: llvm/tools/llvm-rc/ResourceScriptStmt.h =================================================================== --- /dev/null +++ llvm/tools/llvm-rc/ResourceScriptStmt.h @@ -0,0 +1,175 @@ +//===-- 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 contains the definition of types of statements and resources occurring +// in RC scripts. For now, only a very limited set of resources is supported. +// However, it will be extended in the future. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H +#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H + +#include "ResourceScriptParser.h" +#include "ResourceScriptToken.h" + +namespace llvm { +namespace node { + +// Base resource. All the resources should derive from this base. +class Resource { +protected: + IntOrString ResName; + +public: + Resource() = default; + Resource(Resource &&) = default; + void setName(const IntOrString &Name) { ResName = Name; } + virtual raw_ostream &log(raw_ostream &OS) const { + return OS << "Base statement\n"; + }; + virtual ~Resource() {} +}; + +// --- RC statements. --- // + +class OptionStatement { +public: + virtual ~OptionStatement() {} + virtual raw_ostream &log(raw_ostream &X) const { return X; } +}; + +class BasicOptionStatement : public OptionStatement {}; + +// LANGUAGE statement. +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381019(v=vs.85).aspx +ADD_IDENT_NAME(Language); + +class LanguageStmt : public BasicOptionStatement { + uint32_t Lang, SubLang; + +public: + LanguageStmt(uint32_t LangId, uint32_t SubLangId); + raw_ostream &log(raw_ostream &) const override; +}; + +using NodeLanguageStmt = + UnnamedItem>>; + +// CHARACTERISTICS statement. +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx +ADD_IDENT_NAME(Characteristics); + +class CharacteristicsStmt : public BasicOptionStatement { + int32_t Value; + +public: + CharacteristicsStmt(uint32_t ChValue); + raw_ostream &log(raw_ostream &) const override; +}; + +using NodeCharacteristicsStmt = + UnnamedItem>; + +// VERSION statement. +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381059(v=vs.85).aspx +ADD_IDENT_NAME(Version); + +class VersionStmt : public BasicOptionStatement { + uint32_t Version; + +public: + VersionStmt(uint32_t VersionId); + raw_ostream &log(raw_ostream &) const override; +}; + +using NodeVersionStmt = + UnnamedItem>; + +// A basic set of optional resource-specific statements. +// Occurs e.g. in STRINGTABLE resource: +// msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx +using NodeBasicOption = + NodeEither; + +// --- RC resources and helper statements. --- // + +/*class Resource { + virtual raw_ostream &log(raw_ostream &) const; +};*/ + +// LANGUAGE resource. +class LanguageRes : public Resource { + uint32_t Lang, SubLang; + +public: + LanguageRes(uint32_t LangId, uint32_t SubLangId); + raw_ostream &log(raw_ostream &) const override; +}; + +using NodeLanguageRes = + UnnamedItem>>; + +// ICON resource. +ADD_IDENT_NAME(Icon); + +class IconRes : public Resource { + StringRef Value; + +public: + IconRes(StringRef Name); + raw_ostream &log(raw_ostream &) const override; +}; + +using NodeIconRes = NamedItem>; + +// Single statement in STRINGTABLE resource block. +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx +// It is a single "stringID string" statement. +class StringTableStmt : public Resource { +public: + uint32_t Id; + StringRef String; + StringTableStmt(uint32_t SId, StringRef SString); +}; + +using NodeStringTableStmt = ConcatArgs; + +// STRINGTABLE resource. +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx +ADD_IDENT_NAME(StringTable); + +class StringTableRes : public Resource { + std::vector> Options; + std::vector Statements; + +public: + StringTableRes(std::vector> &&Opts, + std::vector &&Stmts); + raw_ostream &log(raw_ostream &OS) const override; +}; + +using NodeStringTableRes = + UnnamedItem, + NodeBlockList>>; + +// A top-level resource definition. +using NodeResource = + NodeEither; + +} // namespace node +} // namespace llvm + +#endif Index: llvm/tools/llvm-rc/ResourceScriptStmt.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-rc/ResourceScriptStmt.cpp @@ -0,0 +1,74 @@ +//===-- ResourceScriptStmt.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 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 node { + +LanguageStmt::LanguageStmt(uint32_t LangId, uint32_t SubLangId) + : Lang(LangId), SubLang(SubLangId) {} + +raw_ostream &LanguageStmt::log(raw_ostream &OS) const { + return OS << "Language: " << Lang << ", Sublanguage: " << SubLang << "\n"; +} + +CharacteristicsStmt::CharacteristicsStmt(uint32_t ChValue) : Value(ChValue) {} + +raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const { + return OS << "Characteristics: " << Value << "\n"; +} + +VersionStmt::VersionStmt(uint32_t VersionId) : Version(VersionId) {} + +raw_ostream &VersionStmt::log(raw_ostream &OS) const { + return OS << "Version: " << Version << "\n"; +} + +LanguageRes::LanguageRes(uint32_t LangId, uint32_t SubLangId) + : Lang(LangId), SubLang(SubLangId) {} + +raw_ostream &LanguageRes::log(raw_ostream &OS) const { + return OS << "Language: " << Lang << ", Sublanguage: " << SubLang << "\n"; +} + +IconRes::IconRes(StringRef Name) : Value(Name) {} + +raw_ostream &IconRes::log(raw_ostream &OS) const { + return OS << "Icon (" << ResName << "): " << Value << "\n"; +} + +StringTableStmt::StringTableStmt(uint32_t SId, StringRef SString) + : Id(SId), String(SString) {} + +StringTableRes::StringTableRes( + std::vector> &&Opts, + std::vector &&Stmts) + : Options(std::move(Opts)), Statements(std::move(Stmts)) {} + +raw_ostream &StringTableRes::log(raw_ostream &OS) const { + OS << "StringTable:\n"; + for (const auto &Option : Options) { + OS << " Option: "; + Option->log(OS); + } + for (const StringTableStmt &Stmt : Statements) { + OS << " " << Stmt.Id << " => " << Stmt.String << "\n"; + } + return OS; +} + +} // namespace node +} // namespace llvm Index: llvm/tools/llvm-rc/llvm-rc.cpp =================================================================== --- llvm/tools/llvm-rc/llvm-rc.cpp +++ llvm/tools/llvm-rc/llvm-rc.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "ResourceScriptStmt.h" #include "ResourceScriptToken.h" #include "llvm/Option/Arg.h" @@ -133,5 +134,17 @@ } } + if (BeVerbose) { + outs() << "Parsed file:\n"; + outs().flush(); + + auto TokenBegin = Tokens.begin(); + while (TokenBegin != Tokens.end()) { + ExitOnErr(node::NodeResource::parse(TokenBegin, Tokens.end())) + ->log(outs()); + outs().flush(); + } + } + return 0; }