]> granicus.if.org Git - llvm/commitdiff
[llvm-rc] Add ACCELERATORS parsing ability. (parser, pt 3/8).
authorMarek Sokolowski <mnbvmar@gmail.com>
Mon, 28 Aug 2017 22:58:31 +0000 (22:58 +0000)
committerMarek Sokolowski <mnbvmar@gmail.com>
Mon, 28 Aug 2017 22:58:31 +0000 (22:58 +0000)
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

test/tools/llvm-rc/Inputs/parser-accelerators-bad-flag.rc [new file with mode: 0644]
test/tools/llvm-rc/Inputs/parser-accelerators-bad-int-or-string.rc [new file with mode: 0644]
test/tools/llvm-rc/Inputs/parser-accelerators-no-comma-2.rc [new file with mode: 0644]
test/tools/llvm-rc/Inputs/parser-accelerators-no-comma.rc [new file with mode: 0644]
test/tools/llvm-rc/Inputs/parser-correct-everything.rc
test/tools/llvm-rc/parser.test
tools/llvm-rc/ResourceScriptParser.cpp
tools/llvm-rc/ResourceScriptParser.h
tools/llvm-rc/ResourceScriptStmt.cpp
tools/llvm-rc/ResourceScriptStmt.h

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 (file)
index 0000000..a5a5a0d
--- /dev/null
@@ -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 (file)
index 0000000..89584e4
--- /dev/null
@@ -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 (file)
index 0000000..7d6ca0e
--- /dev/null
@@ -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 (file)
index 0000000..6cbd6f4
--- /dev/null
@@ -0,0 +1,3 @@
+1 ACCELERATORS {
+  5, 10, ASCII CONTROL
+}
index 37fc1cfe22c23237ca14596434b13db0f669b49a..84ca883b6da25674fcdb918c9d5f0f623cbc8626 100644 (file)
@@ -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
+}
index 9b990a3c54317ff41da6b4e5472f7985356525df..20071a0ba15c1b43aa9f6b02ded5a65a4cdab3be 100644 (file)
 ; 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
 ; 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
index e4c0f2a7f7ca640169c3cebf25e659d34bfaab7d..8d08396e615fa98691b27cbd30bc95bbb4d5cbbe 100644 (file)
@@ -63,7 +63,9 @@ RCParser::ParseType RCParser::parseSingleResource() {
   ParseType Result = std::unique_ptr<RCResource>();
   (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<StringRef> RCParser::readIdentifier() {
   return read().value();
 }
 
+Expected<IntOrString> RCParser::readIntOrString() {
+  if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
+    return getExpectedError("int or string");
+  return IntOrString(read());
+}
+
 Expected<IntOrString> 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<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> 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<OptionalStmtList> 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<AcceleratorsResource>(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<CursorResource>(*Arg);
index b217a73ae765f2b258aa74d974c2da02145e5ad7..e286f83160c298fbf5d51714380d5cfd4d966cd0 100644 (file)
@@ -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<uint32_t> readInt();           // Parse an integer.
-  Expected<StringRef> readString();       // Parse a string.
-  Expected<StringRef> readIdentifier();   // Parse an identifier.
-  Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier.
+  Expected<uint32_t> readInt();            // Parse an integer.
+  Expected<StringRef> readString();        // Parse a string.
+  Expected<StringRef> readIdentifier();    // Parse an identifier.
+  Expected<IntOrString> readIntOrString(); // Parse an integer or a string.
+  Expected<IntOrString> 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<SmallVector<uint32_t, 8>> 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<uint32_t> parseFlags(ArrayRef<StringRef> 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();
index f008641ff76b7219498c390ff3387b51253f7a9b..150b668bd91c171a501d2b90e703bfad9a2b620d 100644 (file)
@@ -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";
 }
index 8ff0e5bb19ccd419f1c9898315621ff3ae7b1576..cf1406e043f360f0fff7a75a783a39c42dfc79d3 100644 (file)
@@ -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<Accelerator> 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