xref: /llvm-project/llvm/tools/llvm-rc/ResourceScriptParser.cpp (revision fb74cb1edfa64424fb5204d10a16892f972435ed)
1 //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===---------------------------------------------------------------------===//
9 //
10 // This implements the parser defined in ResourceScriptParser.h.
11 //
12 //===---------------------------------------------------------------------===//
13 
14 #include "ResourceScriptParser.h"
15 
16 // Take an expression returning llvm::Error and forward the error if it exists.
17 #define RETURN_IF_ERROR(Expr)                                                  \
18   if (auto Err = (Expr))                                                       \
19     return std::move(Err);
20 
21 // Take an expression returning llvm::Expected<T> and assign it to Var or
22 // forward the error out of the function.
23 #define ASSIGN_OR_RETURN(Var, Expr)                                            \
24   auto Var = (Expr);                                                           \
25   if (!Var)                                                                    \
26     return Var.takeError();
27 
28 namespace llvm {
29 namespace rc {
30 
31 RCParser::ParserError::ParserError(const Twine Expected, const LocIter CurLoc,
32                                    const LocIter End)
33     : ErrorLoc(CurLoc), FileEnd(End) {
34   CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
35                (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
36 }
37 
38 char RCParser::ParserError::ID = 0;
39 
40 RCParser::RCParser(const std::vector<RCToken> &TokenList)
41     : Tokens(TokenList), CurLoc(Tokens.begin()), End(Tokens.end()) {}
42 
43 RCParser::RCParser(std::vector<RCToken> &&TokenList)
44     : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
45 
46 bool RCParser::isEof() const { return CurLoc == End; }
47 
48 RCParser::ParseType RCParser::parseSingleResource() {
49   // The first thing we read is usually a resource's name. However, in some
50   // cases (LANGUAGE and STRINGTABLE) the resources don't have their names
51   // and the first token to be read is the type.
52   ASSIGN_OR_RETURN(NameToken, readTypeOrName());
53 
54   if (NameToken->equalsLower("LANGUAGE"))
55     return parseLanguageResource();
56   else if (NameToken->equalsLower("STRINGTABLE"))
57     return parseStringTableResource();
58 
59   // If it's not an unnamed resource, what we've just read is a name. Now,
60   // read resource type;
61   ASSIGN_OR_RETURN(TypeToken, readTypeOrName());
62 
63   ParseType Result = std::unique_ptr<RCResource>();
64   (void)!Result;
65 
66   if (TypeToken->equalsLower("ACCELERATORS"))
67     Result = parseAcceleratorsResource();
68   else if (TypeToken->equalsLower("CURSOR"))
69     Result = parseCursorResource();
70   else if (TypeToken->equalsLower("DIALOG"))
71     Result = parseDialogResource(false);
72   else if (TypeToken->equalsLower("DIALOGEX"))
73     Result = parseDialogResource(true);
74   else if (TypeToken->equalsLower("ICON"))
75     Result = parseIconResource();
76   else if (TypeToken->equalsLower("HTML"))
77     Result = parseHTMLResource();
78   else if (TypeToken->equalsLower("MENU"))
79     Result = parseMenuResource();
80   else if (TypeToken->equalsLower("VERSIONINFO"))
81     Result = parseVersionInfoResource();
82   else
83     return getExpectedError("resource type", /* IsAlreadyRead = */ true);
84 
85   if (Result)
86     (*Result)->setName(*NameToken);
87 
88   return Result;
89 }
90 
91 bool RCParser::isNextTokenKind(Kind TokenKind) const {
92   return !isEof() && look().kind() == TokenKind;
93 }
94 
95 const RCToken &RCParser::look() const {
96   assert(!isEof());
97   return *CurLoc;
98 }
99 
100 const RCToken &RCParser::read() {
101   assert(!isEof());
102   return *CurLoc++;
103 }
104 
105 void RCParser::consume() {
106   assert(!isEof());
107   CurLoc++;
108 }
109 
110 Expected<uint32_t> RCParser::readInt() {
111   if (!isNextTokenKind(Kind::Int))
112     return getExpectedError("integer");
113   return read().intValue();
114 }
115 
116 Expected<StringRef> RCParser::readString() {
117   if (!isNextTokenKind(Kind::String))
118     return getExpectedError("string");
119   return read().value();
120 }
121 
122 Expected<StringRef> RCParser::readIdentifier() {
123   if (!isNextTokenKind(Kind::Identifier))
124     return getExpectedError("identifier");
125   return read().value();
126 }
127 
128 Expected<IntOrString> RCParser::readIntOrString() {
129   if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
130     return getExpectedError("int or string");
131   return IntOrString(read());
132 }
133 
134 Expected<IntOrString> RCParser::readTypeOrName() {
135   // We suggest that the correct resource name or type should be either an
136   // identifier or an integer. The original RC tool is much more liberal.
137   if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
138     return getExpectedError("int or identifier");
139   return IntOrString(read());
140 }
141 
142 Error RCParser::consumeType(Kind TokenKind) {
143   if (isNextTokenKind(TokenKind)) {
144     consume();
145     return Error::success();
146   }
147 
148   switch (TokenKind) {
149 #define TOKEN(TokenName)                                                       \
150   case Kind::TokenName:                                                        \
151     return getExpectedError(#TokenName);
152 #define SHORT_TOKEN(TokenName, TokenCh)                                        \
153   case Kind::TokenName:                                                        \
154     return getExpectedError(#TokenCh);
155 #include "ResourceScriptTokenList.h"
156 #undef SHORT_TOKEN
157 #undef TOKEN
158   }
159 
160   llvm_unreachable("All case options exhausted.");
161 }
162 
163 bool RCParser::consumeOptionalType(Kind TokenKind) {
164   if (isNextTokenKind(TokenKind)) {
165     consume();
166     return true;
167   }
168 
169   return false;
170 }
171 
172 Expected<SmallVector<uint32_t, 8>>
173 RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) {
174   assert(MinCount <= MaxCount);
175 
176   SmallVector<uint32_t, 8> Result;
177 
178   auto FailureHandler =
179       [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 8>> {
180     if (Result.size() < MinCount)
181       return std::move(Err);
182     consumeError(std::move(Err));
183     return Result;
184   };
185 
186   for (size_t i = 0; i < MaxCount; ++i) {
187     // Try to read a comma unless we read the first token.
188     // Sometimes RC tool requires them and sometimes not. We decide to
189     // always require them.
190     if (i >= 1) {
191       if (auto CommaError = consumeType(Kind::Comma))
192         return FailureHandler(std::move(CommaError));
193     }
194 
195     if (auto IntResult = readInt())
196       Result.push_back(*IntResult);
197     else
198       return FailureHandler(IntResult.takeError());
199   }
200 
201   return std::move(Result);
202 }
203 
204 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc) {
205   assert(FlagDesc.size() <= 32 && "More than 32 flags won't fit in result.");
206   assert(!FlagDesc.empty());
207 
208   uint32_t Result = 0;
209   while (isNextTokenKind(Kind::Comma)) {
210     consume();
211     ASSIGN_OR_RETURN(FlagResult, readIdentifier());
212     bool FoundFlag = false;
213 
214     for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
215       if (!FlagResult->equals_lower(FlagDesc[FlagId]))
216         continue;
217 
218       Result |= (1U << FlagId);
219       FoundFlag = true;
220       break;
221     }
222 
223     if (!FoundFlag)
224       return getExpectedError(join(FlagDesc, "/"), true);
225   }
226 
227   return Result;
228 }
229 
230 // As for now, we ignore the extended set of statements.
231 Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) {
232   OptionalStmtList Result;
233 
234   // The last statement is always followed by the start of the block.
235   while (!isNextTokenKind(Kind::BlockBegin)) {
236     ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(IsExtended));
237     Result.addStmt(std::move(*SingleParse));
238   }
239 
240   return std::move(Result);
241 }
242 
243 Expected<std::unique_ptr<OptionalStmt>>
244 RCParser::parseSingleOptionalStatement(bool IsExtended) {
245   ASSIGN_OR_RETURN(TypeToken, readIdentifier());
246   if (TypeToken->equals_lower("CHARACTERISTICS"))
247     return parseCharacteristicsStmt();
248   if (TypeToken->equals_lower("LANGUAGE"))
249     return parseLanguageStmt();
250   if (TypeToken->equals_lower("VERSION"))
251     return parseVersionStmt();
252 
253   if (IsExtended) {
254     if (TypeToken->equals_lower("CAPTION"))
255       return parseCaptionStmt();
256     if (TypeToken->equals_lower("FONT"))
257       return parseFontStmt();
258     if (TypeToken->equals_lower("STYLE"))
259       return parseStyleStmt();
260   }
261 
262   return getExpectedError("optional statement type, BEGIN or '{'",
263                           /* IsAlreadyRead = */ true);
264 }
265 
266 RCParser::ParseType RCParser::parseLanguageResource() {
267   // Read LANGUAGE as an optional statement. If it's read correctly, we can
268   // upcast it to RCResource.
269   return parseLanguageStmt();
270 }
271 
272 RCParser::ParseType RCParser::parseAcceleratorsResource() {
273   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
274   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
275 
276   auto Accels = make_unique<AcceleratorsResource>(std::move(*OptStatements));
277 
278   while (!consumeOptionalType(Kind::BlockEnd)) {
279     ASSIGN_OR_RETURN(EventResult, readIntOrString());
280     RETURN_IF_ERROR(consumeType(Kind::Comma));
281     ASSIGN_OR_RETURN(IDResult, readInt());
282     ASSIGN_OR_RETURN(FlagsResult,
283                      parseFlags(AcceleratorsResource::Accelerator::OptionsStr));
284     Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
285   }
286 
287   return std::move(Accels);
288 }
289 
290 RCParser::ParseType RCParser::parseCursorResource() {
291   ASSIGN_OR_RETURN(Arg, readString());
292   return make_unique<CursorResource>(*Arg);
293 }
294 
295 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
296   // Dialog resources have the following format of the arguments:
297   //  DIALOG:   x, y, width, height [opt stmts...] {controls...}
298   //  DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
299   // These are very similar, so we parse them together.
300   ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
301 
302   uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
303   if (IsExtended && consumeOptionalType(Kind::Comma)) {
304     ASSIGN_OR_RETURN(HelpIDResult, readInt());
305     HelpID = *HelpIDResult;
306   }
307 
308   ASSIGN_OR_RETURN(OptStatements,
309                    parseOptionalStatements(/*UseExtendedStmts = */ true));
310 
311   assert(isNextTokenKind(Kind::BlockBegin) &&
312          "parseOptionalStatements, when successful, halts on BlockBegin.");
313   consume();
314 
315   auto Dialog = make_unique<DialogResource>(
316       (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
317       HelpID, std::move(*OptStatements), IsExtended);
318 
319   while (!consumeOptionalType(Kind::BlockEnd)) {
320     ASSIGN_OR_RETURN(ControlDefResult, parseControl());
321     Dialog->addControl(std::move(*ControlDefResult));
322   }
323 
324   return std::move(Dialog);
325 }
326 
327 RCParser::ParseType RCParser::parseVersionInfoResource() {
328   ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
329   ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
330   return make_unique<VersionInfoResource>(std::move(**BlockResult),
331                                           std::move(*FixedResult));
332 }
333 
334 Expected<Control> RCParser::parseControl() {
335   // Each control definition (except CONTROL) follows one of the schemes below
336   // depending on the control class:
337   //  [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
338   //  [class]       id, x, y, width, height [, style] [, exstyle] [, helpID]
339   // Note that control ids must be integers.
340   ASSIGN_OR_RETURN(ClassResult, readIdentifier());
341   std::string ClassUpper = ClassResult->upper();
342   if (Control::SupportedCtls.find(ClassUpper) == Control::SupportedCtls.end())
343     return getExpectedError("control type, END or '}'", true);
344 
345   // Read caption if necessary.
346   StringRef Caption;
347   if (Control::CtlsWithTitle.find(ClassUpper) != Control::CtlsWithTitle.end()) {
348     ASSIGN_OR_RETURN(CaptionResult, readString());
349     RETURN_IF_ERROR(consumeType(Kind::Comma));
350     Caption = *CaptionResult;
351   }
352 
353   ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8));
354 
355   auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> {
356     return Args->size() > Id ? (*Args)[Id] : Optional<uint32_t>();
357   };
358 
359   return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2],
360                  (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6),
361                  TakeOptArg(7));
362 }
363 
364 RCParser::ParseType RCParser::parseIconResource() {
365   ASSIGN_OR_RETURN(Arg, readString());
366   return make_unique<IconResource>(*Arg);
367 }
368 
369 RCParser::ParseType RCParser::parseHTMLResource() {
370   ASSIGN_OR_RETURN(Arg, readString());
371   return make_unique<HTMLResource>(*Arg);
372 }
373 
374 RCParser::ParseType RCParser::parseMenuResource() {
375   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
376   ASSIGN_OR_RETURN(Items, parseMenuItemsList());
377   return make_unique<MenuResource>(std::move(*OptStatements),
378                                    std::move(*Items));
379 }
380 
381 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
382   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
383 
384   MenuDefinitionList List;
385 
386   // Read a set of items. Each item is of one of three kinds:
387   //   MENUITEM SEPARATOR
388   //   MENUITEM caption:String, result:Int [, menu flags]...
389   //   POPUP caption:String [, menu flags]... { items... }
390   while (!consumeOptionalType(Kind::BlockEnd)) {
391     ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
392 
393     bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
394     bool IsPopup = ItemTypeResult->equals_lower("POPUP");
395     if (!IsMenuItem && !IsPopup)
396       return getExpectedError("MENUITEM, POPUP, END or '}'", true);
397 
398     if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
399       // Now, expecting SEPARATOR.
400       ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
401       if (SeparatorResult->equals_lower("SEPARATOR")) {
402         List.addDefinition(make_unique<MenuSeparator>());
403         continue;
404       }
405 
406       return getExpectedError("SEPARATOR or string", true);
407     }
408 
409     // Not a separator. Read the caption.
410     ASSIGN_OR_RETURN(CaptionResult, readString());
411 
412     // If MENUITEM, expect also a comma and an integer.
413     uint32_t MenuResult = -1;
414 
415     if (IsMenuItem) {
416       RETURN_IF_ERROR(consumeType(Kind::Comma));
417       ASSIGN_OR_RETURN(IntResult, readInt());
418       MenuResult = *IntResult;
419     }
420 
421     ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr));
422 
423     if (IsPopup) {
424       // If POPUP, read submenu items recursively.
425       ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
426       List.addDefinition(make_unique<PopupItem>(*CaptionResult, *FlagsResult,
427                                                 std::move(*SubMenuResult)));
428       continue;
429     }
430 
431     assert(IsMenuItem);
432     List.addDefinition(
433         make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
434   }
435 
436   return std::move(List);
437 }
438 
439 RCParser::ParseType RCParser::parseStringTableResource() {
440   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
441   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
442 
443   auto Table = make_unique<StringTableResource>(std::move(*OptStatements));
444 
445   // Read strings until we reach the end of the block.
446   while (!consumeOptionalType(Kind::BlockEnd)) {
447     // Each definition consists of string's ID (an integer) and a string.
448     // Some examples in documentation suggest that there might be a comma in
449     // between, however we strictly adhere to the single statement definition.
450     ASSIGN_OR_RETURN(IDResult, readInt());
451     ASSIGN_OR_RETURN(StrResult, readString());
452     Table->addString(*IDResult, *StrResult);
453   }
454 
455   return std::move(Table);
456 }
457 
458 Expected<std::unique_ptr<VersionInfoBlock>>
459 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
460   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
461 
462   auto Contents = make_unique<VersionInfoBlock>(BlockName);
463 
464   while (!isNextTokenKind(Kind::BlockEnd)) {
465     ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
466     Contents->addStmt(std::move(*Stmt));
467   }
468 
469   consume(); // Consume BlockEnd.
470 
471   return std::move(Contents);
472 }
473 
474 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
475   // Expect either BLOCK or VALUE, then a name or a key (a string).
476   ASSIGN_OR_RETURN(TypeResult, readIdentifier());
477 
478   if (TypeResult->equals_lower("BLOCK")) {
479     ASSIGN_OR_RETURN(NameResult, readString());
480     return parseVersionInfoBlockContents(*NameResult);
481   }
482 
483   if (TypeResult->equals_lower("VALUE")) {
484     ASSIGN_OR_RETURN(KeyResult, readString());
485     // Read a (possibly empty) list of strings and/or ints, each preceded by
486     // a comma.
487     std::vector<IntOrString> Values;
488 
489     while (consumeOptionalType(Kind::Comma)) {
490       ASSIGN_OR_RETURN(ValueResult, readIntOrString());
491       Values.push_back(*ValueResult);
492     }
493     return make_unique<VersionInfoValue>(*KeyResult, std::move(Values));
494   }
495 
496   return getExpectedError("BLOCK or VALUE", true);
497 }
498 
499 Expected<VersionInfoResource::VersionInfoFixed>
500 RCParser::parseVersionInfoFixed() {
501   using RetType = VersionInfoResource::VersionInfoFixed;
502   RetType Result;
503 
504   // Read until the beginning of the block.
505   while (!isNextTokenKind(Kind::BlockBegin)) {
506     ASSIGN_OR_RETURN(TypeResult, readIdentifier());
507     auto FixedType = RetType::getFixedType(*TypeResult);
508 
509     if (!RetType::isTypeSupported(FixedType))
510       return getExpectedError("fixed VERSIONINFO statement type", true);
511     if (Result.IsTypePresent[FixedType])
512       return getExpectedError("yet unread fixed VERSIONINFO statement type",
513                               true);
514 
515     // VERSION variations take multiple integers.
516     size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
517     ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
518     Result.setValue(FixedType, *ArgsResult);
519   }
520 
521   return Result;
522 }
523 
524 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
525   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
526   return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
527 }
528 
529 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
530   ASSIGN_OR_RETURN(Arg, readInt());
531   return make_unique<CharacteristicsStmt>(*Arg);
532 }
533 
534 RCParser::ParseOptionType RCParser::parseVersionStmt() {
535   ASSIGN_OR_RETURN(Arg, readInt());
536   return make_unique<VersionStmt>(*Arg);
537 }
538 
539 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
540   ASSIGN_OR_RETURN(Arg, readString());
541   return make_unique<CaptionStmt>(*Arg);
542 }
543 
544 RCParser::ParseOptionType RCParser::parseFontStmt() {
545   ASSIGN_OR_RETURN(SizeResult, readInt());
546   RETURN_IF_ERROR(consumeType(Kind::Comma));
547   ASSIGN_OR_RETURN(NameResult, readString());
548   return make_unique<FontStmt>(*SizeResult, *NameResult);
549 }
550 
551 RCParser::ParseOptionType RCParser::parseStyleStmt() {
552   ASSIGN_OR_RETURN(Arg, readInt());
553   return make_unique<StyleStmt>(*Arg);
554 }
555 
556 Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) {
557   return make_error<ParserError>(
558       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
559 }
560 
561 } // namespace rc
562 } // namespace llvm
563