Index: llvm/test/tools/llvm-rc/Inputs/parser-correct-everything.rc =================================================================== --- llvm/test/tools/llvm-rc/Inputs/parser-correct-everything.rc +++ llvm/test/tools/llvm-rc/Inputs/parser-correct-everything.rc @@ -111,3 +111,13 @@ END END + +MYNAME MYTYPE "filename" + +500 600 "other filename" + +HELLO INTEGERS {1, 2, 3, 4} + +HELLO STRINGS {"1", "2", "3", "4"} + +4 MIXED {1, "2", 3, "4"} Index: llvm/test/tools/llvm-rc/Inputs/parser-user-invalid-contents.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/parser-user-invalid-contents.rc @@ -0,0 +1,4 @@ +MYNAME MYTYPE +BEGIN + 1, 2, InvalidToken +END Index: llvm/test/tools/llvm-rc/parser.test =================================================================== --- llvm/test/tools/llvm-rc/parser.test +++ llvm/test/tools/llvm-rc/parser.test @@ -91,6 +91,12 @@ ; PGOOD-NEXT: "Translation" => 1033 1252 ; PGOOD-NEXT: End of block ; PGOOD-NEXT: End of block +; PGOOD-NEXT: User-defined (type: MYTYPE, name: MYNAME): "filename" +; PGOOD-NEXT: User-defined (type: 600, name: 500): "other filename" +; PGOOD-NEXT: User-defined (type: INTEGERS, name: HELLO): data = 1 2 3 4 +; PGOOD-NEXT: User-defined (type: STRINGS, name: HELLO): data = "1" "2" "3" "4" +; PGOOD-NEXT: User-defined (type: MIXED, name: 4): data = 1 "2" 3 "4" + ; RUN: not llvm-rc /V %p/Inputs/parser-stringtable-no-string.rc 2> %t2 @@ -126,7 +132,7 @@ ; 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 +; PNONSENSE2: llvm-rc: Error parsing file: expected filename, '{' or BEGIN, got ; RUN: not llvm-rc /V %p/Inputs/parser-nonsense-type-eof.rc 2> %t8 @@ -241,3 +247,9 @@ ; RUN: FileCheck %s --check-prefix PVERSIONINFO5 --input-file %t26 ; PVERSIONINFO5: llvm-rc: Error parsing file: expected BLOCK or VALUE, got INCORRECT + + +; RUN: not llvm-rc /V %p/Inputs/parser-user-invalid-contents.rc 2> %t27 +; RUN: FileCheck %s --check-prefix PUSER1 --input-file %t27 + +; PUSER1: llvm-rc: Error parsing file: expected int or string, got InvalidToken Index: llvm/tools/llvm-rc/ResourceScriptParser.h =================================================================== --- llvm/tools/llvm-rc/ResourceScriptParser.h +++ llvm/tools/llvm-rc/ResourceScriptParser.h @@ -139,6 +139,7 @@ ParseType parseHTMLResource(); ParseType parseMenuResource(); ParseType parseStringTableResource(); + ParseType parseUserDefinedResource(IntOrString Type); ParseType parseVersionInfoResource(); // Helper DIALOG parser - a single control. Index: llvm/tools/llvm-rc/ResourceScriptParser.cpp =================================================================== --- llvm/tools/llvm-rc/ResourceScriptParser.cpp +++ llvm/tools/llvm-rc/ResourceScriptParser.cpp @@ -80,7 +80,7 @@ else if (TypeToken->equalsLower("VERSIONINFO")) Result = parseVersionInfoResource(); else - return getExpectedError("resource type", /* IsAlreadyRead = */ true); + Result = parseUserDefinedResource(*TypeToken); if (Result) (*Result)->setName(*NameToken); @@ -420,6 +420,31 @@ return std::move(Dialog); } +RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) { + if (isEof()) + return getExpectedError("filename, '{' or BEGIN"); + + // Check if this is a file resource. + if (look().kind() == Kind::String) + return make_unique(Type, read().value()); + + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + std::vector Data; + + // Consume comma before each consecutive token except the first one. + bool ConsumeComma = false; + while (!consumeOptionalType(Kind::BlockEnd)) { + if (ConsumeComma) + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ConsumeComma = true; + + ASSIGN_OR_RETURN(Item, readIntOrString()); + Data.push_back(*Item); + } + + return make_unique(Type, std::move(Data)); +} + RCParser::ParseType RCParser::parseVersionInfoResource() { ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed()); ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef())); Index: llvm/tools/llvm-rc/ResourceScriptStmt.h =================================================================== --- llvm/tools/llvm-rc/ResourceScriptStmt.h +++ llvm/tools/llvm-rc/ResourceScriptStmt.h @@ -319,6 +319,24 @@ raw_ostream &log(raw_ostream &) const override; }; +// User-defined resource. It is either: +// * a link to the file, e.g. NAME TYPE "filename", +// * or contains a list of integers and strings, e.g. NAME TYPE {1, "a", 2}. +class UserDefinedResource : public RCResource { + IntOrString Type; + StringRef FileLoc; + std::vector Contents; + bool IsFileResource; + +public: + UserDefinedResource(IntOrString ResourceType, StringRef FileLocation) + : Type(ResourceType), FileLoc(FileLocation), IsFileResource(true) {} + UserDefinedResource(IntOrString ResourceType, std::vector &&Data) + : Type(ResourceType), Contents(std::move(Data)), IsFileResource(false) {} + + raw_ostream &log(raw_ostream &) const override; +}; + // -- VERSIONINFO resource and its helper classes -- // // This resource lists the version information on the executable/library. Index: llvm/tools/llvm-rc/ResourceScriptStmt.cpp =================================================================== --- llvm/tools/llvm-rc/ResourceScriptStmt.cpp +++ llvm/tools/llvm-rc/ResourceScriptStmt.cpp @@ -183,6 +183,16 @@ return MainBlock.log(OS); } +raw_ostream &UserDefinedResource::log(raw_ostream &OS) const { + OS << "User-defined (type: " << Type << ", name: " << ResName << "): "; + if (IsFileResource) + return OS << FileLoc << "\n"; + OS << "data = "; + for (auto &Item : Contents) + OS << Item << " "; + return OS << "\n"; +} + raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const { return OS << "Characteristics: " << Value << "\n"; }