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,432 @@ +//===-- 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 UnnamedItem>> { +// LanguageStmt(uint32_t, uint32_t); +// } +// +// 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. +// +// Many of the classes defines 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(const 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(StringRef &&Value) : String(std::move(Value)) {} + Data(const RCToken &Token); + } Data; + uint32_t IsInt : 1; + +public: + IntOrString() : IntOrString(0) {} + IntOrString(uint32_t Value) : Data(Value), IsInt(1) {} + IntOrString(StringRef Value) : Data(std::move(Value)), IsInt(0) {} + IntOrString(const IntOrString &Other) = default; + IntOrString(IntOrString &&Other) = default; + IntOrString(const RCToken &Token) + : Data(Token), IsInt(Token.kind() == Kind::Int) {} + // IntOrString + IntOrString &operator=(IntOrString &&Other) = default; + + friend raw_ostream &operator<<(raw_ostream &, const IntOrString &); +}; + +// Base resource. All the parser classes and all statements/resources +// should derive from this base. +class ResourceStmtBase { +protected: + IntOrString ResName; + +public: + void setName(IntOrString &&Name) { ResName = std::move(Name); } + virtual raw_ostream &log(raw_ostream &OS) const { + return OS << "Base statement\n"; + }; + virtual ~ResourceStmtBase(){}; +}; + +// Base *. Return type of many parse() functions. +using BasePtr = std::unique_ptr; + +// Base **. Used as a return type in NodeEither. As return types are +// dereferenced in many parser classes (e.g. in ConcatArgs), using single Base * +// would upcast the returned value to Base and lose information. Therefore, +// we need to return Base **. +using BasePtrPtr = std::unique_ptr; + +// Describes a concatenated sequence of specified tokens. Templated by +// NodeClass, a class deriving from ConcatArgs (CRTP pattern). +// 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. Finally, it returns an encapsulated pointer to +// this constructed object. +// If any of the parser sub-invocations fails, this parser itself fails and +// forwards the error message to the caller. +template +class ConcatArgs : public ResourceStmtBase { + // 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 make_unique( + std::get(std::forward(Children.get()))...); + } + +public: + 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 ResourceStmtBase { +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 ResourceStmtBase { +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 std::move(*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 make_unique(); + } +}; + +// 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 ResourceStmtBase { +public: + using ParseType = std::vector; + + static Expected> parse(LocIter &Location, + const LocIter End) { + LocIter CurLoc = Location; + auto ResultVec = make_unique(); + 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 ResourceStmtBase { +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. +// Returns BasePtrPtr; double pointer is necessary because other wrappers +// (ConcatArgs, NodeList, ...) unpack a single pointer from return +// values. To preserve the dynamic polymorphism, we need this unpacked +// value to remain a pointer. +// +// 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 ResourceStmtBase { +public: + using ParseType = BasePtr; + + 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 ResourceStmtBase { +public: + 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 : public ResourceStmtBase { +protected: + static bool checkEq(LocIter Location) { + return Location->value().equals_lower(ItemName::get()); + } + +public: + 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: + 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 make_unique((Location++)->intValue()); +} + +Expected> +NodeString::parse(LocIter &Location, const LocIter End) { + if (Location == End || Location->kind() != Kind::String) + return getParserError("string", Location, End); + return make_unique((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 make_unique((Location++)->intValue()); + if (LocKind == Kind::String || LocKind == Kind::Identifier) + return make_unique((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,126 @@ +//===-- 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 { + +// --- RC statements. --- // + +// LANGUAGE statement. +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381019(v=vs.85).aspx +ADD_IDENT_NAME(Language); + +class LanguageStmt + : public UnnamedItem>> { + uint32_t Lang, SubLang; + +public: + LanguageStmt(uint32_t LangId, uint32_t SubLangId); + raw_ostream &log(raw_ostream &) const override; +}; + +// CHARACTERISTICS statement. +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx +ADD_IDENT_NAME(Characteristics); + +class CharacteristicsStmt + : public UnnamedItem> { + int32_t Value; + +public: + CharacteristicsStmt(uint32_t ChValue); + raw_ostream &log(raw_ostream &) const override; +}; + +// VERSION statement. +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381059(v=vs.85).aspx +ADD_IDENT_NAME(Version); + +class VersionStmt + : public UnnamedItem> { + uint32_t Version; + +public: + VersionStmt(uint32_t VersionId); + raw_ostream &log(raw_ostream &) const override; +}; + +// 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 StmtBasic = NodeEither; + +// --- RC resources and helper statements. --- // + +// ICON resource. +ADD_IDENT_NAME(Icon); + +class IconRes : public NamedItem> { + StringRef Value; + +public: + IconRes(StringRef &&Name); + raw_ostream &log(raw_ostream &) const override; +}; + +// 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 ConcatArgs { +public: + using ParseType = StringTableStmt; + + int32_t Id; + StringRef String; + + StringTableStmt(int32_t &&SId, StringRef &&SString); +}; + +// STRINGTABLE resource. +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx +ADD_IDENT_NAME(StringTable); + +class StringTableRes + : public UnnamedItem, + NodeBlockList>> { + std::vector Options; + std::vector Statements; + +public: + StringTableRes(std::vector &&Opts, + std::vector &&Stmts); + raw_ostream &log(raw_ostream &OS) const override; +}; + +// A top-level resource definition. +class ResourceStmt : public NodeEither { +}; + +} // namespace node +} // namespace llvm + +#endif Index: llvm/tools/llvm-rc/ResourceScriptStmt.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-rc/ResourceScriptStmt.cpp @@ -0,0 +1,67 @@ +//===-- 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 << itemType() << ": " << Lang << ", Sublanguage: " << SubLang + << "\n"; +} + +CharacteristicsStmt::CharacteristicsStmt(uint32_t ChValue) : Value(ChValue) {} + +raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const { + return OS << itemType() << ": " << Value << "\n"; +} + +VersionStmt::VersionStmt(uint32_t VersionId) : Version(VersionId) {} + +raw_ostream &VersionStmt::log(raw_ostream &OS) const { + return OS << itemType() << ": " << Version << "\n"; +} + +IconRes::IconRes(StringRef &&Name) : Value(std::move(Name)) {} + +raw_ostream &IconRes::log(raw_ostream &OS) const { + return OS << itemType() << " (" << ResName << "): " << Value << "\n"; +} + +StringTableStmt::StringTableStmt(int32_t &&SId, StringRef &&SString) + : Id(std::move(SId)), String(std::move(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 << itemType() << ":\n"; + for (const BasePtr &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 @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "ResourceScriptToken.h" +#include "ResourceScriptStmt.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -133,5 +134,16 @@ } } + if (BeVerbose) { + outs() << "Parsed file:\n"; + outs().flush(); + + auto TokenBegin = Tokens.begin(); + while (TokenBegin != Tokens.end()) { + (*ExitOnErr(node::ResourceStmt::parse(TokenBegin, Tokens.end())))->log(outs()); + outs().flush(); + } + } + return 0; }