llvm.org GIT mirror llvm / 66c13b1
[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 Marek Sokolowski 2 years ago
10 changed file(s) with 181 addition(s) and 12 deletion(s). Raw diff Collapse all Expand all
0 1 ACCELERATORS {
1 5, 7, ALT, CONTROL, HELLO, WORLD
2 }
0 1 ACCELERATORS {
1 5, 10, ASCII CONTROL
2 }
1515
1616 500 HTML "index.html"
1717 Name Cursor "hello.ico"
18
19 12 ACCELERATORS
20 VERSION 5000
21 LANGUAGE 0, 2
22 {
23 "^C", 10
24 14, 11
25 5, 12, VIRTKEY
26 0, 0, ASCII
27 1, 1, VIRTKEY, CONTROL
28 2, 2, CONTROL, VIRTKEY
29 3, 3, ALT, CONTROL, SHIFT, NOINVERT, ASCII, VIRTKEY
30 }
1212 ; PGOOD-NEXT: StringTable:
1313 ; PGOOD-NEXT: HTML (500): "index.html"
1414 ; PGOOD-NEXT: Cursor (Name): "hello.ico"
15 ; PGOOD-NEXT: Accelerators (12):
16 ; PGOOD-NEXT: Option: Version: 5000
17 ; PGOOD-NEXT: Option: Language: 0, Sublanguage: 2
18 ; PGOOD-NEXT: Accelerator: "^C" 10
19 ; PGOOD-NEXT: Accelerator: 14 11
20 ; PGOOD-NEXT: Accelerator: 5 12 VIRTKEY
21 ; PGOOD-NEXT: Accelerator: 0 0 ASCII
22 ; PGOOD-NEXT: Accelerator: 1 1 VIRTKEY CONTROL
23 ; PGOOD-NEXT: Accelerator: 2 2 VIRTKEY CONTROL
24 ; PGOOD-NEXT: Accelerator: 3 3 ASCII VIRTKEY NOINVERT ALT SHIFT CONTROL
1525
1626
1727 ; RUN: not llvm-rc /V %p/Inputs/parser-stringtable-no-string.rc 2>&1 | FileCheck %s --check-prefix PSTRINGTABLE1
6777 ; RUN: not llvm-rc /V %p/Inputs/parser-html-extra-comma.rc 2>&1 | FileCheck %s --check-prefix PHTML2
6878
6979 ; PHTML2: llvm-rc: Error parsing file: expected string, got ,
80
81
82 ; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-bad-flag.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS1
83
84 ; PACCELERATORS1: llvm-rc: Error parsing file: expected ASCII/VIRTKEY/NOINVERT/ALT/SHIFT/CONTROL, got HELLO
85
86
87 ; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-bad-int-or-string.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS2
88
89 ; PACCELERATORS2: llvm-rc: Error parsing file: expected int or string, got NotIntOrString
90
91
92 ; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-no-comma.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS3
93
94 ; PACCELERATORS3: llvm-rc: Error parsing file: expected int or string, got CONTROL
95
96
97 ; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-no-comma-2.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS4
98
99 ; PACCELERATORS4: llvm-rc: Error parsing file: expected ',', got 10
6262 ParseType Result = std::unique_ptr();
6363 (void)!Result;
6464
65 if (TypeToken->equalsLower("CURSOR"))
65 if (TypeToken->equalsLower("ACCELERATORS"))
66 Result = parseAcceleratorsResource();
67 else if (TypeToken->equalsLower("CURSOR"))
6668 Result = parseCursorResource();
6769 else if (TypeToken->equalsLower("ICON"))
6870 Result = parseIconResource();
114116 return read().value();
115117 }
116118
119 Expected RCParser::readIntOrString() {
120 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
121 return getExpectedError("int or string");
122 return IntOrString(read());
123 }
124
117125 Expected RCParser::readTypeOrName() {
118126 // We suggest that the correct resource name or type should be either an
119127 // identifier or an integer. The original RC tool is much more liberal.
120128 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
121129 return getExpectedError("int or identifier");
122
123 const RCToken &Tok = read();
124 if (Tok.kind() == Kind::Int)
125 return IntOrString(Tok.intValue());
126 else
127 return IntOrString(Tok.value());
130 return IntOrString(read());
128131 }
129132
130133 Error RCParser::consumeType(Kind TokenKind) {
187190 }
188191
189192 return std::move(Result);
193 }
194
195 Expected RCParser::parseFlags(ArrayRef FlagDesc) {
196 assert(FlagDesc.size() <= 32 && "More than 32 flags won't fit in result.");
197 assert(!FlagDesc.empty());
198
199 uint32_t Result = 0;
200 while (isNextTokenKind(Kind::Comma)) {
201 consume();
202 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
203 bool FoundFlag = false;
204
205 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
206 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
207 continue;
208
209 Result |= (1U << FlagId);
210 FoundFlag = true;
211 break;
212 }
213
214 if (!FoundFlag)
215 return getExpectedError(join(FlagDesc, "/"), true);
216 }
217
218 return Result;
190219 }
191220
192221 // As for now, we ignore the extended set of statements.
222251 return parseLanguageStmt();
223252 }
224253
254 RCParser::ParseType RCParser::parseAcceleratorsResource() {
255 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
256 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
257
258 auto Accels = make_unique(std::move(*OptStatements));
259
260 while (!consumeOptionalType(Kind::BlockEnd)) {
261 ASSIGN_OR_RETURN(EventResult, readIntOrString());
262 RETURN_IF_ERROR(consumeType(Kind::Comma));
263 ASSIGN_OR_RETURN(IDResult, readInt());
264 ASSIGN_OR_RETURN(FlagsResult,
265 parseFlags(AcceleratorsResource::Accelerator::OptionsStr));
266 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
267 }
268
269 return std::move(Accels);
270 }
271
225272 RCParser::ParseType RCParser::parseCursorResource() {
226273 ASSIGN_OR_RETURN(Arg, readString());
227274 return make_unique(*Arg);
7676
7777 // The following methods try to read a single token, check if it has the
7878 // correct type and then parse it.
79 Expected readInt(); // Parse an integer.
80 Expected readString(); // Parse a string.
81 Expected readIdentifier(); // Parse an identifier.
82 Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier.
79 Expected<uint32_t> readInt(); // Parse an integer.
80 Expected readString(); // Parse a string.
81 Expected readIdentifier(); // Parse an identifier.
82 Expected readIntOrString(); // Parse an integer or a string.
83 Expected readTypeOrName(); // Parse an integer or an identifier.
8384
8485 // Advance the state by one, discarding the current token.
8586 // If the discarded token had an incorrect type, fail.
9596 // expects an integer to follow.
9697 Expected> readIntsWithCommas(size_t MinCount,
9798 size_t MaxCount);
99
100 // Read an unknown number of flags preceded by commas. Each correct flag
101 // has an entry in FlagDesc array of length NumFlags. In case i-th
102 // flag (0-based) has been read, the i-th bit of the result is set.
103 // As long as parser has a comma to read, it expects to be fed with
104 // a correct flag afterwards.
105 Expected parseFlags(ArrayRef FlagDesc);
98106
99107 // Reads a set of optional statements. These can change the behavior of
100108 // a number of resource types (e.g. STRINGTABLE, MENU or DIALOG) if provided
117125
118126 // Top-level resource parsers.
119127 ParseType parseLanguageResource();
128 ParseType parseAcceleratorsResource();
120129 ParseType parseCursorResource();
121130 ParseType parseIconResource();
122131 ParseType parseHTMLResource();
3535 return OS << "Language: " << Lang << ", Sublanguage: " << SubLang << "\n";
3636 }
3737
38 StringRef AcceleratorsResource::Accelerator::OptionsStr
39 [AcceleratorsResource::Accelerator::NumFlags] = {
40 "ASCII", "VIRTKEY", "NOINVERT", "ALT", "SHIFT", "CONTROL"};
41
42 raw_ostream &AcceleratorsResource::log(raw_ostream &OS) const {
43 OS << "Accelerators (" << ResName << "): \n";
44 OptStatements.log(OS);
45 for (const auto &Acc : Accelerators) {
46 OS << " Accelerator: " << Acc.Event << " " << Acc.Id;
47 for (size_t i = 0; i < Accelerator::NumFlags; ++i)
48 if (Acc.Flags & (1U << i))
49 OS << " " << Accelerator::OptionsStr[i];
50 OS << "\n";
51 }
52 return OS;
53 }
54
3855 raw_ostream &CursorResource::log(raw_ostream &OS) const {
3956 return OS << "Cursor (" << ResName << "): " << CursorLoc << "\n";
4057 }
2626 StringRef String;
2727 Data(uint32_t Value) : Int(Value) {}
2828 Data(const StringRef Value) : String(Value) {}
29 Data(const RCToken &Token);
29 Data(const RCToken &Token) {
30 if (Token.kind() == RCToken::Kind::Int)
31 Int = Token.intValue();
32 else
33 String = Token.value();
34 }
3035 } Data;
3136 bool IsInt;
3237
8994 raw_ostream &log(raw_ostream &) const override;
9095 };
9196
97 // ACCELERATORS resource. Defines a named table of accelerators for the app.
98 //
99 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380610(v=vs.85).aspx
100 class AcceleratorsResource : public RCResource {
101 public:
102 class Accelerator {
103 public:
104 IntOrString Event;
105 uint32_t Id;
106 uint8_t Flags;
107
108 enum Options {
109 ASCII = (1 << 0),
110 VIRTKEY = (1 << 1),
111 NOINVERT = (1 << 2),
112 ALT = (1 << 3),
113 SHIFT = (1 << 4),
114 CONTROL = (1 << 5)
115 };
116
117 static constexpr size_t NumFlags = 6;
118 static StringRef OptionsStr[NumFlags];
119 };
120
121 AcceleratorsResource(OptionalStmtList &&OptStmts)
122 : OptStatements(std::move(OptStmts)) {}
123 void addAccelerator(IntOrString Event, uint32_t Id, uint8_t Flags) {
124 Accelerators.push_back(Accelerator{Event, Id, Flags});
125 }
126 raw_ostream &log(raw_ostream &) const override;
127
128 private:
129 std::vector Accelerators;
130 OptionalStmtList OptStatements;
131 };
132
92133 // CURSOR resource. Represents a single cursor (".cur") file.
93134 //
94135 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380920(v=vs.85).aspx