llvm.org GIT mirror llvm / 233d2b8
[llvm-rc] Add MENU parsing ability (parser, pt 4/8). This extends llvm-rc parsing tool by MENU resource (msdn.microsoft.com/en-us/library/windows/desktop/aa381025(v=vs.85).aspx). As for now, MENUEX (msdn.microsoft.com/en-us/library/windows/desktop/aa381023(v=vs.85).aspx) seems unnecessary. Thanks for Nico Weber for his original work in this area. Differential Revision: https://reviews.llvm.org/D36898 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@311956 91177308-0d34-0410-b5e6-96231b3b80d8 Marek Sokolowski 2 years ago
10 changed file(s) with 283 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
2828 2, 2, CONTROL, VIRTKEY
2929 3, 3, ALT, CONTROL, SHIFT, NOINVERT, ASCII, VIRTKEY
3030 }
31
32 LLVMTest MENU
33 LANGUAGE 4, 1
34 {
35 POPUP "&OneMenu"
36 {
37 POPUP "Menu&1"
38 {
39 MENUITEM "Item&1", 301, MENUBREAK, CHECKED
40 MENUITEM "Item&2", 302, CHECKED, MENUBARBREAK
41 MENUITEM "Item&3", 303, MENUBREAK, INACTIVE, HELP
42 MENUITEM "Item&4", 304, GRAYED
43 }
44 POPUP "Menu&2"
45 {
46 MENUITEM "&A", 401
47 MENUITEM "&B", 402
48 }
49 }
50 POPUP "&Items"
51 {
52 MENUITEM "&Row", 500
53 MENUITEM "&Column", 501, CHECKED
54 MENUITEM SEPARATOR
55 MENUITEM "&Word", 502
56 }
57 }
0 1 MENU {
1 MENUITEM "&Item", 500, MENUBREAK, ERRONEOUS, HELP
2 }
0 1 MENU {
1 MENUITEM "Hello", A
2 }
0 1 MENU {
1 POPUP "1"
2 POPUP "2" {}
3 }
2222 ; PGOOD-NEXT: Accelerator: 1 1 VIRTKEY CONTROL
2323 ; PGOOD-NEXT: Accelerator: 2 2 VIRTKEY CONTROL
2424 ; PGOOD-NEXT: Accelerator: 3 3 ASCII VIRTKEY NOINVERT ALT SHIFT CONTROL
25 ; PGOOD-NEXT: Menu (LLVMTest):
26 ; PGOOD-NEXT: Option: Language: 4, Sublanguage: 1
27 ; PGOOD-NEXT: Menu list starts
28 ; PGOOD-NEXT: Popup ("&OneMenu"):
29 ; PGOOD-NEXT: Menu list starts
30 ; PGOOD-NEXT: Popup ("Menu&1"):
31 ; PGOOD-NEXT: Menu list starts
32 ; PGOOD-NEXT: MenuItem ("Item&1"), ID = 301 CHECKED MENUBREAK
33 ; PGOOD-NEXT: MenuItem ("Item&2"), ID = 302 CHECKED MENUBARBREAK
34 ; PGOOD-NEXT: MenuItem ("Item&3"), ID = 303 HELP INACTIVE MENUBREAK
35 ; PGOOD-NEXT: MenuItem ("Item&4"), ID = 304 GRAYED
36 ; PGOOD-NEXT: Menu list ends
37 ; PGOOD-NEXT: Popup ("Menu&2"):
38 ; PGOOD-NEXT: Menu list starts
39 ; PGOOD-NEXT: MenuItem ("&A"), ID = 401
40 ; PGOOD-NEXT: MenuItem ("&B"), ID = 402
41 ; PGOOD-NEXT: Menu list ends
42 ; PGOOD-NEXT: Menu list ends
43 ; PGOOD-NEXT: Popup ("&Items"):
44 ; PGOOD-NEXT: Menu list starts
45 ; PGOOD-NEXT: MenuItem ("&Row"), ID = 500
46 ; PGOOD-NEXT: MenuItem ("&Column"), ID = 501 CHECKED
47 ; PGOOD-NEXT: Menu separator
48 ; PGOOD-NEXT: MenuItem ("&Word"), ID = 502
49 ; PGOOD-NEXT: Menu list ends
50 ; PGOOD-NEXT: Menu list ends
2551
2652
2753 ; RUN: not llvm-rc /V %p/Inputs/parser-stringtable-no-string.rc 2>&1 | FileCheck %s --check-prefix PSTRINGTABLE1
97123 ; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-no-comma-2.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS4
98124
99125 ; PACCELERATORS4: llvm-rc: Error parsing file: expected ',', got 10
126
127
128 ; RUN: not llvm-rc /V %p/Inputs/parser-menu-bad-id.rc 2>&1 | FileCheck %s --check-prefix PMENU1
129
130 ; PMENU1: llvm-rc: Error parsing file: expected integer, got A
131
132
133 ; RUN: not llvm-rc /V %p/Inputs/parser-menu-bad-flag.rc 2>&1 | FileCheck %s --check-prefix PMENU2
134
135 ; PMENU2: llvm-rc: Error parsing file: expected CHECKED/GRAYED/HELP/INACTIVE/MENUBARBREAK/MENUBREAK, got ERRONEOUS
136
137
138 ; RUN: not llvm-rc /V %p/Inputs/parser-menu-missing-block.rc 2>&1 | FileCheck %s --check-prefix PMENU3
139
140 ; PMENU3: llvm-rc: Error parsing file: expected '{', got POPUP
141
142
143 ; RUN: not llvm-rc /V %p/Inputs/parser-menu-misspelled-separator.rc 2>&1 | FileCheck %s --check-prefix PMENU4
144
145 ; PMENU4: llvm-rc: Error parsing file: expected SEPARATOR or string, got NOTSEPARATOR
7070 Result = parseIconResource();
7171 else if (TypeToken->equalsLower("HTML"))
7272 Result = parseHTMLResource();
73 else if (TypeToken->equalsLower("MENU"))
74 Result = parseMenuResource();
7375 else
7476 return getExpectedError("resource type", /* IsAlreadyRead = */ true);
7577
284286 return make_unique(*Arg);
285287 }
286288
289 RCParser::ParseType RCParser::parseMenuResource() {
290 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
291 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
292 return make_unique(std::move(*OptStatements),
293 std::move(*Items));
294 }
295
296 Expected RCParser::parseMenuItemsList() {
297 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
298
299 MenuDefinitionList List;
300
301 // Read a set of items. Each item is of one of three kinds:
302 // MENUITEM SEPARATOR
303 // MENUITEM caption:String, result:Int [, menu flags]...
304 // POPUP caption:String [, menu flags]... { items... }
305 while (!consumeOptionalType(Kind::BlockEnd)) {
306 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
307
308 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
309 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
310 if (!IsMenuItem && !IsPopup)
311 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
312
313 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
314 // Now, expecting SEPARATOR.
315 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
316 if (SeparatorResult->equals_lower("SEPARATOR")) {
317 List.addDefinition(make_unique());
318 continue;
319 }
320
321 return getExpectedError("SEPARATOR or string", true);
322 }
323
324 // Not a separator. Read the caption.
325 ASSIGN_OR_RETURN(CaptionResult, readString());
326
327 // If MENUITEM, expect also a comma and an integer.
328 uint32_t MenuResult = -1;
329
330 if (IsMenuItem) {
331 RETURN_IF_ERROR(consumeType(Kind::Comma));
332 ASSIGN_OR_RETURN(IntResult, readInt());
333 MenuResult = *IntResult;
334 }
335
336 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr));
337
338 if (IsPopup) {
339 // If POPUP, read submenu items recursively.
340 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
341 List.addDefinition(make_unique(*CaptionResult, *FlagsResult,
342 std::move(*SubMenuResult)));
343 continue;
344 }
345
346 assert(IsMenuItem);
347 List.addDefinition(
348 make_unique(*CaptionResult, MenuResult, *FlagsResult));
349 }
350
351 return std::move(List);
352 }
353
287354 RCParser::ParseType RCParser::parseStringTableResource() {
288355 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
289356 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
129129 ParseType parseCursorResource();
130130 ParseType parseIconResource();
131131 ParseType parseHTMLResource();
132 ParseType parseMenuResource();
132133 ParseType parseStringTableResource();
134
135 // Helper MENU parser.
136 Expected parseMenuItemsList();
133137
134138 // Optional statement parsers.
135139 ParseOptionType parseLanguageStmt();
6464 return OS << "HTML (" << ResName << "): " << HTMLLoc << "\n";
6565 }
6666
67 StringRef MenuDefinition::OptionsStr[MenuDefinition::NumFlags] = {
68 "CHECKED", "GRAYED", "HELP", "INACTIVE", "MENUBARBREAK", "MENUBREAK"};
69
70 raw_ostream &MenuDefinition::logFlags(raw_ostream &OS, uint8_t Flags) {
71 for (size_t i = 0; i < NumFlags; ++i)
72 if (Flags & (1U << i))
73 OS << " " << OptionsStr[i];
74 return OS;
75 }
76
77 raw_ostream &MenuDefinitionList::log(raw_ostream &OS) const {
78 OS << " Menu list starts\n";
79 for (auto &Item : Definitions)
80 Item->log(OS);
81 return OS << " Menu list ends\n";
82 }
83
84 raw_ostream &MenuItem::log(raw_ostream &OS) const {
85 OS << " MenuItem (" << Name << "), ID = " << Id;
86 logFlags(OS, Flags);
87 return OS << "\n";
88 }
89
90 raw_ostream &MenuSeparator::log(raw_ostream &OS) const {
91 return OS << " Menu separator\n";
92 }
93
94 raw_ostream &PopupItem::log(raw_ostream &OS) const {
95 OS << " Popup (" << Name << ")";
96 logFlags(OS, Flags);
97 OS << ":\n";
98 return SubItems.log(OS);
99 }
100
101 raw_ostream &MenuResource::log(raw_ostream &OS) const {
102 OS << "Menu (" << ResName << "):\n";
103 OptStatements.log(OS);
104 return Elements.log(OS);
105 }
106
67107 raw_ostream &StringTableResource::log(raw_ostream &OS) const {
68108 OS << "StringTable:\n";
69109 OptStatements.log(OS);
165165 raw_ostream &log(raw_ostream &) const override;
166166 };
167167
168 // -- MENU resource and its helper classes --
169 // This resource describes the contents of an application menu
170 // (usually located in the upper part of the dialog.)
171 //
172 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381025(v=vs.85).aspx
173
174 // Description of a single submenu item.
175 class MenuDefinition {
176 public:
177 enum Options {
178 CHECKED = (1 << 0),
179 GRAYED = (1 << 1),
180 HELP = (1 << 2),
181 INACTIVE = (1 << 3),
182 MENUBARBREAK = (1 << 4),
183 MENUBREAK = (1 << 5)
184 };
185
186 static constexpr size_t NumFlags = 6;
187 static StringRef OptionsStr[NumFlags];
188 static raw_ostream &logFlags(raw_ostream &, uint8_t Flags);
189 virtual raw_ostream &log(raw_ostream &OS) const {
190 return OS << "Base menu definition\n";
191 }
192 virtual ~MenuDefinition() {}
193 };
194
195 // Recursive description of a whole submenu.
196 class MenuDefinitionList : public MenuDefinition {
197 std::vector> Definitions;
198
199 public:
200 void addDefinition(std::unique_ptr Def) {
201 Definitions.push_back(std::move(Def));
202 }
203 raw_ostream &log(raw_ostream &) const override;
204 };
205
206 // Separator in MENU definition (MENUITEM SEPARATOR).
207 //
208 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx
209 class MenuSeparator : public MenuDefinition {
210 public:
211 raw_ostream &log(raw_ostream &) const override;
212 };
213
214 // MENUITEM statement definition.
215 //
216 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx
217 class MenuItem : public MenuDefinition {
218 StringRef Name;
219 uint32_t Id;
220 uint8_t Flags;
221
222 public:
223 MenuItem(StringRef Caption, uint32_t ItemId, uint8_t ItemFlags)
224 : Name(Caption), Id(ItemId), Flags(ItemFlags) {}
225 raw_ostream &log(raw_ostream &) const override;
226 };
227
228 // POPUP statement definition.
229 //
230 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381030(v=vs.85).aspx
231 class PopupItem : public MenuDefinition {
232 StringRef Name;
233 uint8_t Flags;
234 MenuDefinitionList SubItems;
235
236 public:
237 PopupItem(StringRef Caption, uint8_t ItemFlags,
238 MenuDefinitionList &&SubItemsList)
239 : Name(Caption), Flags(ItemFlags), SubItems(std::move(SubItemsList)) {}
240 raw_ostream &log(raw_ostream &) const override;
241 };
242
243 // Menu resource definition.
244 class MenuResource : public RCResource {
245 OptionalStmtList OptStatements;
246 MenuDefinitionList Elements;
247
248 public:
249 MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items)
250 : OptStatements(std::move(OptStmts)), Elements(std::move(Items)) {}
251 raw_ostream &log(raw_ostream &) const override;
252 };
253
168254 // STRINGTABLE resource. Contains a list of strings, each having its unique ID.
169255 //
170256 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx