--- /dev/null
+1 ACCELERATORS {
+ 5, 7, ALT, CONTROL, HELLO, WORLD
+}
--- /dev/null
+1 ACCELERATORS {
+ NotIntOrString, 7
+}
--- /dev/null
+1 ACCELERATORS {
+ "^C" 10
+}
--- /dev/null
+1 ACCELERATORS {
+ 5, 10, ASCII CONTROL
+}
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
+}
; 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
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();
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) {
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;
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);
// 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.
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.
// Top-level resource parsers.
ParseType parseLanguageResource();
+ ParseType parseAcceleratorsResource();
ParseType parseCursorResource();
ParseType parseIconResource();
ParseType parseHTMLResource();
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";
}
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;
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