xref: /netbsd-src/external/apache2/llvm/dist/llvm/tools/llvm-rc/ResourceScriptParser.cpp (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===---------------------------------------------------------------------===//
8 //
9 // This implements the parser defined in ResourceScriptParser.h.
10 //
11 //===---------------------------------------------------------------------===//
12 
13 #include "ResourceScriptParser.h"
14 #include "llvm/Option/ArgList.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/Process.h"
18 
19 // Take an expression returning llvm::Error and forward the error if it exists.
20 #define RETURN_IF_ERROR(Expr)                                                  \
21   if (auto Err = (Expr))                                                       \
22     return std::move(Err);
23 
24 // Take an expression returning llvm::Expected<T> and assign it to Var or
25 // forward the error out of the function.
26 #define ASSIGN_OR_RETURN(Var, Expr)                                            \
27   auto Var = (Expr);                                                           \
28   if (!Var)                                                                    \
29     return Var.takeError();
30 
31 namespace llvm {
32 namespace rc {
33 
ParserError(const Twine & Expected,const LocIter CurLoc,const LocIter End)34 RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
35                                    const LocIter End)
36     : ErrorLoc(CurLoc), FileEnd(End) {
37   CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
38                (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
39 }
40 
41 char RCParser::ParserError::ID = 0;
42 
RCParser(std::vector<RCToken> TokenList)43 RCParser::RCParser(std::vector<RCToken> TokenList)
44     : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
45 
isEof() const46 bool RCParser::isEof() const { return CurLoc == End; }
47 
parseSingleResource()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("BITMAP"))
69     Result = parseBitmapResource();
70   else if (TypeToken->equalsLower("CURSOR"))
71     Result = parseCursorResource();
72   else if (TypeToken->equalsLower("DIALOG"))
73     Result = parseDialogResource(false);
74   else if (TypeToken->equalsLower("DIALOGEX"))
75     Result = parseDialogResource(true);
76   else if (TypeToken->equalsLower("HTML"))
77     Result = parseHTMLResource();
78   else if (TypeToken->equalsLower("ICON"))
79     Result = parseIconResource();
80   else if (TypeToken->equalsLower("MENU"))
81     Result = parseMenuResource();
82   else if (TypeToken->equalsLower("RCDATA"))
83     Result = parseUserDefinedResource(RkRcData);
84   else if (TypeToken->equalsLower("VERSIONINFO"))
85     Result = parseVersionInfoResource();
86   else
87     Result = parseUserDefinedResource(*TypeToken);
88 
89   if (Result)
90     (*Result)->setName(*NameToken);
91 
92   return Result;
93 }
94 
isNextTokenKind(Kind TokenKind) const95 bool RCParser::isNextTokenKind(Kind TokenKind) const {
96   return !isEof() && look().kind() == TokenKind;
97 }
98 
look() const99 const RCToken &RCParser::look() const {
100   assert(!isEof());
101   return *CurLoc;
102 }
103 
read()104 const RCToken &RCParser::read() {
105   assert(!isEof());
106   return *CurLoc++;
107 }
108 
consume()109 void RCParser::consume() {
110   assert(!isEof());
111   CurLoc++;
112 }
113 
114 // An integer description might consist of a single integer or
115 // an arithmetic expression evaluating to the integer. The expressions
116 // can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning
117 // is the same as in C++ except for 'not' expression.
118 // The operators in the original RC implementation have the following
119 // precedence:
120 //   1) Unary operators (- ~ not),
121 //   2) Binary operators (+ - & |), with no precedence.
122 //
123 // 'not' expression is mostly useful for style values. It evaluates to 0,
124 // but value given to the operator is stored separately from integer value.
125 // It's mostly useful for control style expressions and causes bits from
126 // default control style to be excluded from generated style. For binary
127 // operators the mask from the right operand is applied to the left operand
128 // and masks from both operands are combined in operator result.
129 //
130 // The following grammar is used to parse the expressions Exp1:
131 //   Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
132 //   Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
133 // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
134 // separated by binary operators.)
135 //
136 // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
137 // is read by parseIntExpr2().
138 //
139 // The original Microsoft tool handles multiple unary operators incorrectly.
140 // For example, in 16-bit little-endian integers:
141 //    1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
142 //    1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
143 // Our implementation differs from the original one and handles these
144 // operators correctly:
145 //    1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
146 //    1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
147 
readInt()148 Expected<RCInt> RCParser::readInt() {
149   ASSIGN_OR_RETURN(Value, parseIntExpr1());
150   return (*Value).getValue();
151 }
152 
parseIntExpr1()153 Expected<IntWithNotMask> RCParser::parseIntExpr1() {
154   // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
155   ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
156   IntWithNotMask Result = *FirstResult;
157 
158   while (!isEof() && look().isBinaryOp()) {
159     auto OpToken = read();
160     ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
161 
162     switch (OpToken.kind()) {
163     case Kind::Plus:
164       Result += *NextResult;
165       break;
166 
167     case Kind::Minus:
168       Result -= *NextResult;
169       break;
170 
171     case Kind::Pipe:
172       Result |= *NextResult;
173       break;
174 
175     case Kind::Amp:
176       Result &= *NextResult;
177       break;
178 
179     default:
180       llvm_unreachable("Already processed all binary ops.");
181     }
182   }
183 
184   return Result;
185 }
186 
parseIntExpr2()187 Expected<IntWithNotMask> RCParser::parseIntExpr2() {
188   // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
189   static const char ErrorMsg[] = "'-', '~', integer or '('";
190 
191   if (isEof())
192     return getExpectedError(ErrorMsg);
193 
194   switch (look().kind()) {
195   case Kind::Minus: {
196     consume();
197     ASSIGN_OR_RETURN(Result, parseIntExpr2());
198     return -(*Result);
199   }
200 
201   case Kind::Tilde: {
202     consume();
203     ASSIGN_OR_RETURN(Result, parseIntExpr2());
204     return ~(*Result);
205   }
206 
207   case Kind::Int:
208     return RCInt(read());
209 
210   case Kind::LeftParen: {
211     consume();
212     ASSIGN_OR_RETURN(Result, parseIntExpr1());
213     RETURN_IF_ERROR(consumeType(Kind::RightParen));
214     return *Result;
215   }
216 
217   case Kind::Identifier: {
218     if (!read().value().equals_lower("not"))
219       return getExpectedError(ErrorMsg, true);
220     ASSIGN_OR_RETURN(Result, parseIntExpr2());
221     return IntWithNotMask(0, (*Result).getValue());
222   }
223 
224   default:
225     return getExpectedError(ErrorMsg);
226   }
227 }
228 
readString()229 Expected<StringRef> RCParser::readString() {
230   if (!isNextTokenKind(Kind::String))
231     return getExpectedError("string");
232   return read().value();
233 }
234 
readFilename()235 Expected<StringRef> RCParser::readFilename() {
236   if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
237     return getExpectedError("string");
238   return read().value();
239 }
240 
readIdentifier()241 Expected<StringRef> RCParser::readIdentifier() {
242   if (!isNextTokenKind(Kind::Identifier))
243     return getExpectedError("identifier");
244   return read().value();
245 }
246 
readIntOrString()247 Expected<IntOrString> RCParser::readIntOrString() {
248   if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
249     return getExpectedError("int or string");
250   return IntOrString(read());
251 }
252 
readTypeOrName()253 Expected<IntOrString> RCParser::readTypeOrName() {
254   // We suggest that the correct resource name or type should be either an
255   // identifier or an integer. The original RC tool is much more liberal.
256   if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
257     return getExpectedError("int or identifier");
258   return IntOrString(read());
259 }
260 
consumeType(Kind TokenKind)261 Error RCParser::consumeType(Kind TokenKind) {
262   if (isNextTokenKind(TokenKind)) {
263     consume();
264     return Error::success();
265   }
266 
267   switch (TokenKind) {
268 #define TOKEN(TokenName)                                                       \
269   case Kind::TokenName:                                                        \
270     return getExpectedError(#TokenName);
271 #define SHORT_TOKEN(TokenName, TokenCh)                                        \
272   case Kind::TokenName:                                                        \
273     return getExpectedError(#TokenCh);
274 #include "ResourceScriptTokenList.def"
275   }
276 
277   llvm_unreachable("All case options exhausted.");
278 }
279 
consumeOptionalType(Kind TokenKind)280 bool RCParser::consumeOptionalType(Kind TokenKind) {
281   if (isNextTokenKind(TokenKind)) {
282     consume();
283     return true;
284   }
285 
286   return false;
287 }
288 
readIntsWithCommas(size_t MinCount,size_t MaxCount)289 Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
290                                                              size_t MaxCount) {
291   assert(MinCount <= MaxCount);
292 
293   SmallVector<RCInt, 8> Result;
294 
295   auto FailureHandler =
296       [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
297     if (Result.size() < MinCount)
298       return std::move(Err);
299     consumeError(std::move(Err));
300     return Result;
301   };
302 
303   for (size_t i = 0; i < MaxCount; ++i) {
304     // Try to read a comma unless we read the first token.
305     // Sometimes RC tool requires them and sometimes not. We decide to
306     // always require them.
307     if (i >= 1) {
308       if (auto CommaError = consumeType(Kind::Comma))
309         return FailureHandler(std::move(CommaError));
310     }
311 
312     if (auto IntResult = readInt())
313       Result.push_back(*IntResult);
314     else
315       return FailureHandler(IntResult.takeError());
316   }
317 
318   return std::move(Result);
319 }
320 
parseFlags(ArrayRef<StringRef> FlagDesc,ArrayRef<uint32_t> FlagValues)321 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
322                                         ArrayRef<uint32_t> FlagValues) {
323   assert(!FlagDesc.empty());
324   assert(FlagDesc.size() == FlagValues.size());
325 
326   uint32_t Result = 0;
327   while (isNextTokenKind(Kind::Comma)) {
328     consume();
329     ASSIGN_OR_RETURN(FlagResult, readIdentifier());
330     bool FoundFlag = false;
331 
332     for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
333       if (!FlagResult->equals_lower(FlagDesc[FlagId]))
334         continue;
335 
336       Result |= FlagValues[FlagId];
337       FoundFlag = true;
338       break;
339     }
340 
341     if (!FoundFlag)
342       return getExpectedError(join(FlagDesc, "/"), true);
343   }
344 
345   return Result;
346 }
347 
parseMemoryFlags(uint16_t Flags)348 uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
349   while (!isEof()) {
350     const RCToken &Token = look();
351     if (Token.kind() != Kind::Identifier)
352       return Flags;
353     const StringRef Ident = Token.value();
354     if (Ident.equals_lower("PRELOAD"))
355       Flags |= MfPreload;
356     else if (Ident.equals_lower("LOADONCALL"))
357       Flags &= ~MfPreload;
358     else if (Ident.equals_lower("FIXED"))
359       Flags &= ~(MfMoveable | MfDiscardable);
360     else if (Ident.equals_lower("MOVEABLE"))
361       Flags |= MfMoveable;
362     else if (Ident.equals_lower("DISCARDABLE"))
363       Flags |= MfDiscardable | MfMoveable | MfPure;
364     else if (Ident.equals_lower("PURE"))
365       Flags |= MfPure;
366     else if (Ident.equals_lower("IMPURE"))
367       Flags &= ~(MfPure | MfDiscardable);
368     else if (Ident.equals_lower("SHARED"))
369       Flags |= MfPure;
370     else if (Ident.equals_lower("NONSHARED"))
371       Flags &= ~(MfPure | MfDiscardable);
372     else
373       return Flags;
374     consume();
375   }
376   return Flags;
377 }
378 
379 Expected<OptionalStmtList>
parseOptionalStatements(OptStmtType StmtsType)380 RCParser::parseOptionalStatements(OptStmtType StmtsType) {
381   OptionalStmtList Result;
382 
383   // The last statement is always followed by the start of the block.
384   while (!isNextTokenKind(Kind::BlockBegin)) {
385     ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
386     Result.addStmt(std::move(*SingleParse));
387   }
388 
389   return std::move(Result);
390 }
391 
392 Expected<std::unique_ptr<OptionalStmt>>
parseSingleOptionalStatement(OptStmtType StmtsType)393 RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
394   ASSIGN_OR_RETURN(TypeToken, readIdentifier());
395   if (TypeToken->equals_lower("CHARACTERISTICS"))
396     return parseCharacteristicsStmt();
397   if (TypeToken->equals_lower("LANGUAGE"))
398     return parseLanguageStmt();
399   if (TypeToken->equals_lower("VERSION"))
400     return parseVersionStmt();
401 
402   if (StmtsType != OptStmtType::BasicStmt) {
403     if (TypeToken->equals_lower("CAPTION"))
404       return parseCaptionStmt();
405     if (TypeToken->equals_lower("CLASS"))
406       return parseClassStmt();
407     if (TypeToken->equals_lower("EXSTYLE"))
408       return parseExStyleStmt();
409     if (TypeToken->equals_lower("FONT"))
410       return parseFontStmt(StmtsType);
411     if (TypeToken->equals_lower("STYLE"))
412       return parseStyleStmt();
413   }
414 
415   return getExpectedError("optional statement type, BEGIN or '{'",
416                           /* IsAlreadyRead = */ true);
417 }
418 
parseLanguageResource()419 RCParser::ParseType RCParser::parseLanguageResource() {
420   // Read LANGUAGE as an optional statement. If it's read correctly, we can
421   // upcast it to RCResource.
422   return parseLanguageStmt();
423 }
424 
parseAcceleratorsResource()425 RCParser::ParseType RCParser::parseAcceleratorsResource() {
426   uint16_t MemoryFlags =
427       parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
428   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
429   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
430 
431   auto Accels = std::make_unique<AcceleratorsResource>(
432       std::move(*OptStatements), MemoryFlags);
433 
434   while (!consumeOptionalType(Kind::BlockEnd)) {
435     ASSIGN_OR_RETURN(EventResult, readIntOrString());
436     RETURN_IF_ERROR(consumeType(Kind::Comma));
437     ASSIGN_OR_RETURN(IDResult, readInt());
438     ASSIGN_OR_RETURN(
439         FlagsResult,
440         parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
441                    AcceleratorsResource::Accelerator::OptionsFlags));
442     Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
443   }
444 
445   return std::move(Accels);
446 }
447 
parseCursorResource()448 RCParser::ParseType RCParser::parseCursorResource() {
449   uint16_t MemoryFlags =
450       parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
451   ASSIGN_OR_RETURN(Arg, readFilename());
452   return std::make_unique<CursorResource>(*Arg, MemoryFlags);
453 }
454 
parseDialogResource(bool IsExtended)455 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
456   uint16_t MemoryFlags =
457       parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
458   // Dialog resources have the following format of the arguments:
459   //  DIALOG:   x, y, width, height [opt stmts...] {controls...}
460   //  DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
461   // These are very similar, so we parse them together.
462   ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
463 
464   uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
465   if (IsExtended && consumeOptionalType(Kind::Comma)) {
466     ASSIGN_OR_RETURN(HelpIDResult, readInt());
467     HelpID = *HelpIDResult;
468   }
469 
470   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
471                                       IsExtended ? OptStmtType::DialogExStmt
472                                                  : OptStmtType::DialogStmt));
473 
474   assert(isNextTokenKind(Kind::BlockBegin) &&
475          "parseOptionalStatements, when successful, halts on BlockBegin.");
476   consume();
477 
478   auto Dialog = std::make_unique<DialogResource>(
479       (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
480       HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
481 
482   while (!consumeOptionalType(Kind::BlockEnd)) {
483     ASSIGN_OR_RETURN(ControlDefResult, parseControl());
484     Dialog->addControl(std::move(*ControlDefResult));
485   }
486 
487   return std::move(Dialog);
488 }
489 
parseUserDefinedResource(IntOrString Type)490 RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
491   uint16_t MemoryFlags =
492       parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
493   if (isEof())
494     return getExpectedError("filename, '{' or BEGIN");
495 
496   // Check if this is a file resource.
497   switch (look().kind()) {
498   case Kind::String:
499   case Kind::Identifier:
500     return std::make_unique<UserDefinedResource>(Type, read().value(),
501                                                   MemoryFlags);
502   default:
503     break;
504   }
505 
506   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
507   std::vector<IntOrString> Data;
508 
509   // Consume comma before each consecutive token except the first one.
510   bool ConsumeComma = false;
511   while (!consumeOptionalType(Kind::BlockEnd)) {
512     if (ConsumeComma)
513       RETURN_IF_ERROR(consumeType(Kind::Comma));
514     ConsumeComma = true;
515 
516     ASSIGN_OR_RETURN(Item, readIntOrString());
517     Data.push_back(*Item);
518   }
519 
520   return std::make_unique<UserDefinedResource>(Type, std::move(Data),
521                                                 MemoryFlags);
522 }
523 
parseVersionInfoResource()524 RCParser::ParseType RCParser::parseVersionInfoResource() {
525   uint16_t MemoryFlags =
526       parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
527   ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
528   ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
529   return std::make_unique<VersionInfoResource>(
530       std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
531 }
532 
parseControl()533 Expected<Control> RCParser::parseControl() {
534   // Each control definition (except CONTROL) follows one of the schemes below
535   // depending on the control class:
536   //  [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
537   //  [class]       id, x, y, width, height [, style] [, exstyle] [, helpID]
538   // Note that control ids must be integers.
539   // Text might be either a string or an integer pointing to resource ID.
540   ASSIGN_OR_RETURN(ClassResult, readIdentifier());
541   std::string ClassUpper = ClassResult->upper();
542   auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
543   if (CtlInfo == Control::SupportedCtls.end())
544     return getExpectedError("control type, END or '}'", true);
545 
546   // Read caption if necessary.
547   IntOrString Caption{StringRef()};
548   if (CtlInfo->getValue().HasTitle) {
549     ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
550     RETURN_IF_ERROR(consumeType(Kind::Comma));
551     Caption = *CaptionResult;
552   }
553 
554   ASSIGN_OR_RETURN(ID, readInt());
555   RETURN_IF_ERROR(consumeType(Kind::Comma));
556 
557   IntOrString Class;
558   Optional<IntWithNotMask> Style;
559   if (ClassUpper == "CONTROL") {
560     // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
561     ASSIGN_OR_RETURN(ClassStr, readString());
562     RETURN_IF_ERROR(consumeType(Kind::Comma));
563     Class = *ClassStr;
564     ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());
565     RETURN_IF_ERROR(consumeType(Kind::Comma));
566     Style = *StyleVal;
567   } else {
568     Class = CtlInfo->getValue().CtlClass;
569   }
570 
571   // x, y, width, height
572   ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
573 
574   if (ClassUpper != "CONTROL") {
575     if (consumeOptionalType(Kind::Comma)) {
576       ASSIGN_OR_RETURN(Val, parseIntExpr1());
577       Style = *Val;
578     }
579   }
580 
581   Optional<uint32_t> ExStyle;
582   if (consumeOptionalType(Kind::Comma)) {
583     ASSIGN_OR_RETURN(Val, readInt());
584     ExStyle = *Val;
585   }
586   Optional<uint32_t> HelpID;
587   if (consumeOptionalType(Kind::Comma)) {
588     ASSIGN_OR_RETURN(Val, readInt());
589     HelpID = *Val;
590   }
591 
592   return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
593                  (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
594 }
595 
parseBitmapResource()596 RCParser::ParseType RCParser::parseBitmapResource() {
597   uint16_t MemoryFlags =
598       parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
599   ASSIGN_OR_RETURN(Arg, readFilename());
600   return std::make_unique<BitmapResource>(*Arg, MemoryFlags);
601 }
602 
parseIconResource()603 RCParser::ParseType RCParser::parseIconResource() {
604   uint16_t MemoryFlags =
605       parseMemoryFlags(IconResource::getDefaultMemoryFlags());
606   ASSIGN_OR_RETURN(Arg, readFilename());
607   return std::make_unique<IconResource>(*Arg, MemoryFlags);
608 }
609 
parseHTMLResource()610 RCParser::ParseType RCParser::parseHTMLResource() {
611   uint16_t MemoryFlags =
612       parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
613   ASSIGN_OR_RETURN(Arg, readFilename());
614   return std::make_unique<HTMLResource>(*Arg, MemoryFlags);
615 }
616 
parseMenuResource()617 RCParser::ParseType RCParser::parseMenuResource() {
618   uint16_t MemoryFlags =
619       parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
620   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
621   ASSIGN_OR_RETURN(Items, parseMenuItemsList());
622   return std::make_unique<MenuResource>(std::move(*OptStatements),
623                                          std::move(*Items), MemoryFlags);
624 }
625 
parseMenuItemsList()626 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
627   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
628 
629   MenuDefinitionList List;
630 
631   // Read a set of items. Each item is of one of three kinds:
632   //   MENUITEM SEPARATOR
633   //   MENUITEM caption:String, result:Int [, menu flags]...
634   //   POPUP caption:String [, menu flags]... { items... }
635   while (!consumeOptionalType(Kind::BlockEnd)) {
636     ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
637 
638     bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
639     bool IsPopup = ItemTypeResult->equals_lower("POPUP");
640     if (!IsMenuItem && !IsPopup)
641       return getExpectedError("MENUITEM, POPUP, END or '}'", true);
642 
643     if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
644       // Now, expecting SEPARATOR.
645       ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
646       if (SeparatorResult->equals_lower("SEPARATOR")) {
647         List.addDefinition(std::make_unique<MenuSeparator>());
648         continue;
649       }
650 
651       return getExpectedError("SEPARATOR or string", true);
652     }
653 
654     // Not a separator. Read the caption.
655     ASSIGN_OR_RETURN(CaptionResult, readString());
656 
657     // If MENUITEM, expect also a comma and an integer.
658     uint32_t MenuResult = -1;
659 
660     if (IsMenuItem) {
661       RETURN_IF_ERROR(consumeType(Kind::Comma));
662       ASSIGN_OR_RETURN(IntResult, readInt());
663       MenuResult = *IntResult;
664     }
665 
666     ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
667                                              MenuDefinition::OptionsFlags));
668 
669     if (IsPopup) {
670       // If POPUP, read submenu items recursively.
671       ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
672       List.addDefinition(std::make_unique<PopupItem>(
673           *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
674       continue;
675     }
676 
677     assert(IsMenuItem);
678     List.addDefinition(
679         std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
680   }
681 
682   return std::move(List);
683 }
684 
parseStringTableResource()685 RCParser::ParseType RCParser::parseStringTableResource() {
686   uint16_t MemoryFlags =
687       parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
688   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
689   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
690 
691   auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),
692                                                       MemoryFlags);
693 
694   // Read strings until we reach the end of the block.
695   while (!consumeOptionalType(Kind::BlockEnd)) {
696     // Each definition consists of string's ID (an integer) and a string.
697     // Some examples in documentation suggest that there might be a comma in
698     // between, however we strictly adhere to the single statement definition.
699     ASSIGN_OR_RETURN(IDResult, readInt());
700     consumeOptionalType(Kind::Comma);
701 
702     std::vector<StringRef> Strings;
703     ASSIGN_OR_RETURN(StrResult, readString());
704     Strings.push_back(*StrResult);
705     while (isNextTokenKind(Kind::String))
706       Strings.push_back(read().value());
707 
708     Table->addStrings(*IDResult, std::move(Strings));
709   }
710 
711   return std::move(Table);
712 }
713 
714 Expected<std::unique_ptr<VersionInfoBlock>>
parseVersionInfoBlockContents(StringRef BlockName)715 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
716   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
717 
718   auto Contents = std::make_unique<VersionInfoBlock>(BlockName);
719 
720   while (!isNextTokenKind(Kind::BlockEnd)) {
721     ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
722     Contents->addStmt(std::move(*Stmt));
723   }
724 
725   consume(); // Consume BlockEnd.
726 
727   return std::move(Contents);
728 }
729 
parseVersionInfoStmt()730 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
731   // Expect either BLOCK or VALUE, then a name or a key (a string).
732   ASSIGN_OR_RETURN(TypeResult, readIdentifier());
733 
734   if (TypeResult->equals_lower("BLOCK")) {
735     ASSIGN_OR_RETURN(NameResult, readString());
736     return parseVersionInfoBlockContents(*NameResult);
737   }
738 
739   if (TypeResult->equals_lower("VALUE")) {
740     ASSIGN_OR_RETURN(KeyResult, readString());
741     // Read a non-empty list of strings and/or ints, each
742     // possibly preceded by a comma. Unfortunately, the tool behavior depends
743     // on them existing or not, so we need to memorize where we found them.
744     std::vector<IntOrString> Values;
745     std::vector<bool> PrecedingCommas;
746     RETURN_IF_ERROR(consumeType(Kind::Comma));
747     while (!isNextTokenKind(Kind::Identifier) &&
748            !isNextTokenKind(Kind::BlockEnd)) {
749       // Try to eat a comma if it's not the first statement.
750       bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
751       ASSIGN_OR_RETURN(ValueResult, readIntOrString());
752       Values.push_back(*ValueResult);
753       PrecedingCommas.push_back(HadComma);
754     }
755     return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
756                                                std::move(PrecedingCommas));
757   }
758 
759   return getExpectedError("BLOCK or VALUE", true);
760 }
761 
762 Expected<VersionInfoResource::VersionInfoFixed>
parseVersionInfoFixed()763 RCParser::parseVersionInfoFixed() {
764   using RetType = VersionInfoResource::VersionInfoFixed;
765   RetType Result;
766 
767   // Read until the beginning of the block.
768   while (!isNextTokenKind(Kind::BlockBegin)) {
769     ASSIGN_OR_RETURN(TypeResult, readIdentifier());
770     auto FixedType = RetType::getFixedType(*TypeResult);
771 
772     if (!RetType::isTypeSupported(FixedType))
773       return getExpectedError("fixed VERSIONINFO statement type", true);
774     if (Result.IsTypePresent[FixedType])
775       return getExpectedError("yet unread fixed VERSIONINFO statement type",
776                               true);
777 
778     // VERSION variations take multiple integers.
779     size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
780     ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(1, NumInts));
781     SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
782     while (ArgInts.size() < NumInts)
783       ArgInts.push_back(0);
784     Result.setValue(FixedType, ArgInts);
785   }
786 
787   return Result;
788 }
789 
parseLanguageStmt()790 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
791   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
792   return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
793 }
794 
parseCharacteristicsStmt()795 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
796   ASSIGN_OR_RETURN(Arg, readInt());
797   return std::make_unique<CharacteristicsStmt>(*Arg);
798 }
799 
parseVersionStmt()800 RCParser::ParseOptionType RCParser::parseVersionStmt() {
801   ASSIGN_OR_RETURN(Arg, readInt());
802   return std::make_unique<VersionStmt>(*Arg);
803 }
804 
parseCaptionStmt()805 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
806   ASSIGN_OR_RETURN(Arg, readString());
807   return std::make_unique<CaptionStmt>(*Arg);
808 }
809 
parseClassStmt()810 RCParser::ParseOptionType RCParser::parseClassStmt() {
811   ASSIGN_OR_RETURN(Arg, readIntOrString());
812   return std::make_unique<ClassStmt>(*Arg);
813 }
814 
parseFontStmt(OptStmtType DialogType)815 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
816   assert(DialogType != OptStmtType::BasicStmt);
817 
818   ASSIGN_OR_RETURN(SizeResult, readInt());
819   RETURN_IF_ERROR(consumeType(Kind::Comma));
820   ASSIGN_OR_RETURN(NameResult, readString());
821 
822   // Default values for the optional arguments.
823   uint32_t FontWeight = 0;
824   bool FontItalic = false;
825   uint32_t FontCharset = 1;
826   if (DialogType == OptStmtType::DialogExStmt) {
827     if (consumeOptionalType(Kind::Comma)) {
828       ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
829       if (Args->size() >= 1)
830         FontWeight = (*Args)[0];
831       if (Args->size() >= 2)
832         FontItalic = (*Args)[1] != 0;
833       if (Args->size() >= 3)
834         FontCharset = (*Args)[2];
835     }
836   }
837   return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
838                                      FontItalic, FontCharset);
839 }
840 
parseStyleStmt()841 RCParser::ParseOptionType RCParser::parseStyleStmt() {
842   ASSIGN_OR_RETURN(Arg, readInt());
843   return std::make_unique<StyleStmt>(*Arg);
844 }
845 
parseExStyleStmt()846 RCParser::ParseOptionType RCParser::parseExStyleStmt() {
847   ASSIGN_OR_RETURN(Arg, readInt());
848   return std::make_unique<ExStyleStmt>(*Arg);
849 }
850 
getExpectedError(const Twine & Message,bool IsAlreadyRead)851 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
852   return make_error<ParserError>(
853       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
854 }
855 
856 } // namespace rc
857 } // namespace llvm
858