From: Marek Sokolowski Date: Mon, 28 Aug 2017 22:58:31 +0000 (+0000) Subject: [llvm-rc] Add ACCELERATORS parsing ability. (parser, pt 3/8). X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=66c13b13cafb68702cb39eac604a3fdc74fe2088;p=llvm [llvm-rc] Add ACCELERATORS parsing ability. (parser, pt 3/8). This improves the current llvm-rc parser by the ability of parsing ACCELERATORS statement. Moreover, some small improvements to the original parsing commit were made. Thanks for Nico Weber for his original work in this area. Differential Revision: https://reviews.llvm.org/D36894 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@311946 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/test/tools/llvm-rc/Inputs/parser-accelerators-bad-flag.rc b/test/tools/llvm-rc/Inputs/parser-accelerators-bad-flag.rc new file mode 100644 index 00000000000..a5a5a0d9dc7 --- /dev/null +++ b/test/tools/llvm-rc/Inputs/parser-accelerators-bad-flag.rc @@ -0,0 +1,3 @@ +1 ACCELERATORS { + 5, 7, ALT, CONTROL, HELLO, WORLD +} diff --git a/test/tools/llvm-rc/Inputs/parser-accelerators-bad-int-or-string.rc b/test/tools/llvm-rc/Inputs/parser-accelerators-bad-int-or-string.rc new file mode 100644 index 00000000000..89584e4f693 --- /dev/null +++ b/test/tools/llvm-rc/Inputs/parser-accelerators-bad-int-or-string.rc @@ -0,0 +1,3 @@ +1 ACCELERATORS { + NotIntOrString, 7 +} diff --git a/test/tools/llvm-rc/Inputs/parser-accelerators-no-comma-2.rc b/test/tools/llvm-rc/Inputs/parser-accelerators-no-comma-2.rc new file mode 100644 index 00000000000..7d6ca0eaf8a --- /dev/null +++ b/test/tools/llvm-rc/Inputs/parser-accelerators-no-comma-2.rc @@ -0,0 +1,3 @@ +1 ACCELERATORS { + "^C" 10 +} diff --git a/test/tools/llvm-rc/Inputs/parser-accelerators-no-comma.rc b/test/tools/llvm-rc/Inputs/parser-accelerators-no-comma.rc new file mode 100644 index 00000000000..6cbd6f4ab65 --- /dev/null +++ b/test/tools/llvm-rc/Inputs/parser-accelerators-no-comma.rc @@ -0,0 +1,3 @@ +1 ACCELERATORS { + 5, 10, ASCII CONTROL +} diff --git a/test/tools/llvm-rc/Inputs/parser-correct-everything.rc b/test/tools/llvm-rc/Inputs/parser-correct-everything.rc index 37fc1cfe22c..84ca883b6da 100644 --- a/test/tools/llvm-rc/Inputs/parser-correct-everything.rc +++ b/test/tools/llvm-rc/Inputs/parser-correct-everything.rc @@ -16,3 +16,16 @@ STRINGTABLE BEGIN END 500 HTML "index.html" Name Cursor "hello.ico" + +12 ACCELERATORS +VERSION 5000 +LANGUAGE 0, 2 +{ + "^C", 10 + 14, 11 + 5, 12, VIRTKEY + 0, 0, ASCII + 1, 1, VIRTKEY, CONTROL + 2, 2, CONTROL, VIRTKEY + 3, 3, ALT, CONTROL, SHIFT, NOINVERT, ASCII, VIRTKEY +} diff --git a/test/tools/llvm-rc/parser.test b/test/tools/llvm-rc/parser.test index 9b990a3c543..20071a0ba15 100644 --- a/test/tools/llvm-rc/parser.test +++ b/test/tools/llvm-rc/parser.test @@ -13,6 +13,16 @@ ; PGOOD-NEXT: StringTable: ; PGOOD-NEXT: HTML (500): "index.html" ; PGOOD-NEXT: Cursor (Name): "hello.ico" +; PGOOD-NEXT: Accelerators (12): +; PGOOD-NEXT: Option: Version: 5000 +; PGOOD-NEXT: Option: Language: 0, Sublanguage: 2 +; PGOOD-NEXT: Accelerator: "^C" 10 +; PGOOD-NEXT: Accelerator: 14 11 +; PGOOD-NEXT: Accelerator: 5 12 VIRTKEY +; PGOOD-NEXT: Accelerator: 0 0 ASCII +; PGOOD-NEXT: Accelerator: 1 1 VIRTKEY CONTROL +; PGOOD-NEXT: Accelerator: 2 2 VIRTKEY CONTROL +; PGOOD-NEXT: Accelerator: 3 3 ASCII VIRTKEY NOINVERT ALT SHIFT CONTROL ; RUN: not llvm-rc /V %p/Inputs/parser-stringtable-no-string.rc 2>&1 | FileCheck %s --check-prefix PSTRINGTABLE1 @@ -68,3 +78,23 @@ ; RUN: not llvm-rc /V %p/Inputs/parser-html-extra-comma.rc 2>&1 | FileCheck %s --check-prefix PHTML2 ; PHTML2: llvm-rc: Error parsing file: expected string, got , + + +; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-bad-flag.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS1 + +; PACCELERATORS1: llvm-rc: Error parsing file: expected ASCII/VIRTKEY/NOINVERT/ALT/SHIFT/CONTROL, got HELLO + + +; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-bad-int-or-string.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS2 + +; PACCELERATORS2: llvm-rc: Error parsing file: expected int or string, got NotIntOrString + + +; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-no-comma.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS3 + +; PACCELERATORS3: llvm-rc: Error parsing file: expected int or string, got CONTROL + + +; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-no-comma-2.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS4 + +; PACCELERATORS4: llvm-rc: Error parsing file: expected ',', got 10 diff --git a/tools/llvm-rc/ResourceScriptParser.cpp b/tools/llvm-rc/ResourceScriptParser.cpp index e4c0f2a7f7c..8d08396e615 100644 --- a/tools/llvm-rc/ResourceScriptParser.cpp +++ b/tools/llvm-rc/ResourceScriptParser.cpp @@ -63,7 +63,9 @@ RCParser::ParseType RCParser::parseSingleResource() { ParseType Result = std::unique_ptr(); (void)!Result; - if (TypeToken->equalsLower("CURSOR")) + if (TypeToken->equalsLower("ACCELERATORS")) + Result = parseAcceleratorsResource(); + else if (TypeToken->equalsLower("CURSOR")) Result = parseCursorResource(); else if (TypeToken->equalsLower("ICON")) Result = parseIconResource(); @@ -115,17 +117,18 @@ Expected RCParser::readIdentifier() { return read().value(); } +Expected RCParser::readIntOrString() { + if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String)) + return getExpectedError("int or string"); + return IntOrString(read()); +} + 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()); + return IntOrString(read()); } Error RCParser::consumeType(Kind TokenKind) { @@ -190,6 +193,32 @@ RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) { return std::move(Result); } +Expected RCParser::parseFlags(ArrayRef FlagDesc) { + assert(FlagDesc.size() <= 32 && "More than 32 flags won't fit in result."); + assert(!FlagDesc.empty()); + + uint32_t Result = 0; + while (isNextTokenKind(Kind::Comma)) { + consume(); + ASSIGN_OR_RETURN(FlagResult, readIdentifier()); + bool FoundFlag = false; + + for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { + if (!FlagResult->equals_lower(FlagDesc[FlagId])) + continue; + + Result |= (1U << FlagId); + FoundFlag = true; + break; + } + + if (!FoundFlag) + return getExpectedError(join(FlagDesc, "/"), true); + } + + return Result; +} + // As for now, we ignore the extended set of statements. Expected RCParser::parseOptionalStatements(bool IsExtended) { OptionalStmtList Result; @@ -223,6 +252,24 @@ RCParser::ParseType RCParser::parseLanguageResource() { return parseLanguageStmt(); } +RCParser::ParseType RCParser::parseAcceleratorsResource() { + ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + + auto Accels = make_unique(std::move(*OptStatements)); + + while (!consumeOptionalType(Kind::BlockEnd)) { + ASSIGN_OR_RETURN(EventResult, readIntOrString()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ASSIGN_OR_RETURN(IDResult, readInt()); + ASSIGN_OR_RETURN(FlagsResult, + parseFlags(AcceleratorsResource::Accelerator::OptionsStr)); + Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult); + } + + return std::move(Accels); +} + RCParser::ParseType RCParser::parseCursorResource() { ASSIGN_OR_RETURN(Arg, readString()); return make_unique(*Arg); diff --git a/tools/llvm-rc/ResourceScriptParser.h b/tools/llvm-rc/ResourceScriptParser.h index b217a73ae76..e286f83160c 100644 --- a/tools/llvm-rc/ResourceScriptParser.h +++ b/tools/llvm-rc/ResourceScriptParser.h @@ -77,10 +77,11 @@ private: // 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. + Expected readInt(); // Parse an integer. + Expected readString(); // Parse a string. + Expected readIdentifier(); // Parse an identifier. + Expected readIntOrString(); // Parse an integer or a string. + 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. @@ -97,6 +98,13 @@ private: Expected> readIntsWithCommas(size_t MinCount, size_t MaxCount); + // Read an unknown number of flags preceded by commas. Each correct flag + // has an entry in FlagDesc array of length NumFlags. In case i-th + // flag (0-based) has been read, the i-th bit of the result is set. + // As long as parser has a comma to read, it expects to be fed with + // a correct flag afterwards. + Expected parseFlags(ArrayRef FlagDesc); + // 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. @@ -118,6 +126,7 @@ private: // Top-level resource parsers. ParseType parseLanguageResource(); + ParseType parseAcceleratorsResource(); ParseType parseCursorResource(); ParseType parseIconResource(); ParseType parseHTMLResource(); diff --git a/tools/llvm-rc/ResourceScriptStmt.cpp b/tools/llvm-rc/ResourceScriptStmt.cpp index f008641ff76..150b668bd91 100644 --- a/tools/llvm-rc/ResourceScriptStmt.cpp +++ b/tools/llvm-rc/ResourceScriptStmt.cpp @@ -36,6 +36,23 @@ raw_ostream &LanguageResource::log(raw_ostream &OS) const { return OS << "Language: " << Lang << ", Sublanguage: " << SubLang << "\n"; } +StringRef AcceleratorsResource::Accelerator::OptionsStr + [AcceleratorsResource::Accelerator::NumFlags] = { + "ASCII", "VIRTKEY", "NOINVERT", "ALT", "SHIFT", "CONTROL"}; + +raw_ostream &AcceleratorsResource::log(raw_ostream &OS) const { + OS << "Accelerators (" << ResName << "): \n"; + OptStatements.log(OS); + for (const auto &Acc : Accelerators) { + OS << " Accelerator: " << Acc.Event << " " << Acc.Id; + for (size_t i = 0; i < Accelerator::NumFlags; ++i) + if (Acc.Flags & (1U << i)) + OS << " " << Accelerator::OptionsStr[i]; + OS << "\n"; + } + return OS; +} + raw_ostream &CursorResource::log(raw_ostream &OS) const { return OS << "Cursor (" << ResName << "): " << CursorLoc << "\n"; } diff --git a/tools/llvm-rc/ResourceScriptStmt.h b/tools/llvm-rc/ResourceScriptStmt.h index 8ff0e5bb19c..cf1406e043f 100644 --- a/tools/llvm-rc/ResourceScriptStmt.h +++ b/tools/llvm-rc/ResourceScriptStmt.h @@ -27,7 +27,12 @@ private: StringRef String; Data(uint32_t Value) : Int(Value) {} Data(const StringRef Value) : String(Value) {} - Data(const RCToken &Token); + Data(const RCToken &Token) { + if (Token.kind() == RCToken::Kind::Int) + Int = Token.intValue(); + else + String = Token.value(); + } } Data; bool IsInt; @@ -90,6 +95,42 @@ public: raw_ostream &log(raw_ostream &) const override; }; +// ACCELERATORS resource. Defines a named table of accelerators for the app. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380610(v=vs.85).aspx +class AcceleratorsResource : public RCResource { +public: + class Accelerator { + public: + IntOrString Event; + uint32_t Id; + uint8_t Flags; + + enum Options { + ASCII = (1 << 0), + VIRTKEY = (1 << 1), + NOINVERT = (1 << 2), + ALT = (1 << 3), + SHIFT = (1 << 4), + CONTROL = (1 << 5) + }; + + static constexpr size_t NumFlags = 6; + static StringRef OptionsStr[NumFlags]; + }; + + AcceleratorsResource(OptionalStmtList &&OptStmts) + : OptStatements(std::move(OptStmts)) {} + void addAccelerator(IntOrString Event, uint32_t Id, uint8_t Flags) { + Accelerators.push_back(Accelerator{Event, Id, Flags}); + } + raw_ostream &log(raw_ostream &) const override; + +private: + std::vector Accelerators; + OptionalStmtList OptStatements; +}; + // CURSOR resource. Represents a single cursor (".cur") file. // // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380920(v=vs.85).aspx