xref: /openbsd-src/gnu/llvm/llvm/tools/llvm-rc/ResourceScriptParser.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
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_insensitive("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_insensitive(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_insensitive("PRELOAD"))
355       Flags |= MfPreload;
356     else if (Ident.equals_insensitive("LOADONCALL"))
357       Flags &= ~MfPreload;
358     else if (Ident.equals_insensitive("FIXED"))
359       Flags &= ~(MfMoveable | MfDiscardable);
360     else if (Ident.equals_insensitive("MOVEABLE"))
361       Flags |= MfMoveable;
362     else if (Ident.equals_insensitive("DISCARDABLE"))
363       Flags |= MfDiscardable | MfMoveable | MfPure;
364     else if (Ident.equals_insensitive("PURE"))
365       Flags |= MfPure;
366     else if (Ident.equals_insensitive("IMPURE"))
367       Flags &= ~(MfPure | MfDiscardable);
368     else if (Ident.equals_insensitive("SHARED"))
369       Flags |= MfPure;
370     else if (Ident.equals_insensitive("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_insensitive("CHARACTERISTICS"))
396     return parseCharacteristicsStmt();
397   if (TypeToken->equals_insensitive("LANGUAGE"))
398     return parseLanguageStmt();
399   if (TypeToken->equals_insensitive("VERSION"))
400     return parseVersionStmt();
401 
402   if (StmtsType != OptStmtType::BasicStmt) {
403     if (TypeToken->equals_insensitive("CAPTION"))
404       return parseCaptionStmt();
405     if (TypeToken->equals_insensitive("CLASS"))
406       return parseClassStmt();
407     if (TypeToken->equals_insensitive("EXSTYLE"))
408       return parseExStyleStmt();
409     if (TypeToken->equals_insensitive("FONT"))
410       return parseFontStmt(StmtsType);
411     if (TypeToken->equals_insensitive("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   while (!consumeOptionalType(Kind::BlockEnd)) {
510     ASSIGN_OR_RETURN(Item, readIntOrString());
511     Data.push_back(*Item);
512 
513     // There can be zero or more commas after each token (but not before
514     // the first one).
515     while (consumeOptionalType(Kind::Comma)) {
516     }
517   }
518 
519   return std::make_unique<UserDefinedResource>(Type, std::move(Data),
520                                                 MemoryFlags);
521 }
522 
parseVersionInfoResource()523 RCParser::ParseType RCParser::parseVersionInfoResource() {
524   uint16_t MemoryFlags =
525       parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
526   ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
527   ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
528   return std::make_unique<VersionInfoResource>(
529       std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
530 }
531 
parseControl()532 Expected<Control> RCParser::parseControl() {
533   // Each control definition (except CONTROL) follows one of the schemes below
534   // depending on the control class:
535   //  [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
536   //  [class]       id, x, y, width, height [, style] [, exstyle] [, helpID]
537   // Note that control ids must be integers.
538   // Text might be either a string or an integer pointing to resource ID.
539   ASSIGN_OR_RETURN(ClassResult, readIdentifier());
540   std::string ClassUpper = ClassResult->upper();
541   auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
542   if (CtlInfo == Control::SupportedCtls.end())
543     return getExpectedError("control type, END or '}'", true);
544 
545   // Read caption if necessary.
546   IntOrString Caption{StringRef()};
547   if (CtlInfo->getValue().HasTitle) {
548     ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
549     RETURN_IF_ERROR(consumeType(Kind::Comma));
550     Caption = *CaptionResult;
551   }
552 
553   ASSIGN_OR_RETURN(ID, readInt());
554   RETURN_IF_ERROR(consumeType(Kind::Comma));
555 
556   IntOrString Class;
557   std::optional<IntWithNotMask> Style;
558   if (ClassUpper == "CONTROL") {
559     // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
560     ASSIGN_OR_RETURN(ClassStr, readString());
561     RETURN_IF_ERROR(consumeType(Kind::Comma));
562     Class = *ClassStr;
563     ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());
564     RETURN_IF_ERROR(consumeType(Kind::Comma));
565     Style = *StyleVal;
566   } else {
567     Class = CtlInfo->getValue().CtlClass;
568   }
569 
570   // x, y, width, height
571   ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
572 
573   if (ClassUpper != "CONTROL") {
574     if (consumeOptionalType(Kind::Comma)) {
575       ASSIGN_OR_RETURN(Val, parseIntExpr1());
576       Style = *Val;
577     }
578   }
579 
580   std::optional<uint32_t> ExStyle;
581   if (consumeOptionalType(Kind::Comma)) {
582     ASSIGN_OR_RETURN(Val, readInt());
583     ExStyle = *Val;
584   }
585   std::optional<uint32_t> HelpID;
586   if (consumeOptionalType(Kind::Comma)) {
587     ASSIGN_OR_RETURN(Val, readInt());
588     HelpID = *Val;
589   }
590 
591   return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
592                  (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
593 }
594 
parseBitmapResource()595 RCParser::ParseType RCParser::parseBitmapResource() {
596   uint16_t MemoryFlags =
597       parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
598   ASSIGN_OR_RETURN(Arg, readFilename());
599   return std::make_unique<BitmapResource>(*Arg, MemoryFlags);
600 }
601 
parseIconResource()602 RCParser::ParseType RCParser::parseIconResource() {
603   uint16_t MemoryFlags =
604       parseMemoryFlags(IconResource::getDefaultMemoryFlags());
605   ASSIGN_OR_RETURN(Arg, readFilename());
606   return std::make_unique<IconResource>(*Arg, MemoryFlags);
607 }
608 
parseHTMLResource()609 RCParser::ParseType RCParser::parseHTMLResource() {
610   uint16_t MemoryFlags =
611       parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
612   ASSIGN_OR_RETURN(Arg, readFilename());
613   return std::make_unique<HTMLResource>(*Arg, MemoryFlags);
614 }
615 
parseMenuResource()616 RCParser::ParseType RCParser::parseMenuResource() {
617   uint16_t MemoryFlags =
618       parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
619   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
620   ASSIGN_OR_RETURN(Items, parseMenuItemsList());
621   return std::make_unique<MenuResource>(std::move(*OptStatements),
622                                          std::move(*Items), MemoryFlags);
623 }
624 
parseMenuItemsList()625 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
626   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
627 
628   MenuDefinitionList List;
629 
630   // Read a set of items. Each item is of one of three kinds:
631   //   MENUITEM SEPARATOR
632   //   MENUITEM caption:String, result:Int [, menu flags]...
633   //   POPUP caption:String [, menu flags]... { items... }
634   while (!consumeOptionalType(Kind::BlockEnd)) {
635     ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
636 
637     bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM");
638     bool IsPopup = ItemTypeResult->equals_insensitive("POPUP");
639     if (!IsMenuItem && !IsPopup)
640       return getExpectedError("MENUITEM, POPUP, END or '}'", true);
641 
642     if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
643       // Now, expecting SEPARATOR.
644       ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
645       if (SeparatorResult->equals_insensitive("SEPARATOR")) {
646         List.addDefinition(std::make_unique<MenuSeparator>());
647         continue;
648       }
649 
650       return getExpectedError("SEPARATOR or string", true);
651     }
652 
653     // Not a separator. Read the caption.
654     ASSIGN_OR_RETURN(CaptionResult, readString());
655 
656     // If MENUITEM, expect also a comma and an integer.
657     uint32_t MenuResult = -1;
658 
659     if (IsMenuItem) {
660       RETURN_IF_ERROR(consumeType(Kind::Comma));
661       ASSIGN_OR_RETURN(IntResult, readInt());
662       MenuResult = *IntResult;
663     }
664 
665     ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
666                                              MenuDefinition::OptionsFlags));
667 
668     if (IsPopup) {
669       // If POPUP, read submenu items recursively.
670       ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
671       List.addDefinition(std::make_unique<PopupItem>(
672           *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
673       continue;
674     }
675 
676     assert(IsMenuItem);
677     List.addDefinition(
678         std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
679   }
680 
681   return std::move(List);
682 }
683 
parseStringTableResource()684 RCParser::ParseType RCParser::parseStringTableResource() {
685   uint16_t MemoryFlags =
686       parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
687   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
688   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
689 
690   auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),
691                                                       MemoryFlags);
692 
693   // Read strings until we reach the end of the block.
694   while (!consumeOptionalType(Kind::BlockEnd)) {
695     // Each definition consists of string's ID (an integer) and a string.
696     // Some examples in documentation suggest that there might be a comma in
697     // between, however we strictly adhere to the single statement definition.
698     ASSIGN_OR_RETURN(IDResult, readInt());
699     consumeOptionalType(Kind::Comma);
700 
701     std::vector<StringRef> Strings;
702     ASSIGN_OR_RETURN(StrResult, readString());
703     Strings.push_back(*StrResult);
704     while (isNextTokenKind(Kind::String))
705       Strings.push_back(read().value());
706 
707     Table->addStrings(*IDResult, std::move(Strings));
708   }
709 
710   return std::move(Table);
711 }
712 
713 Expected<std::unique_ptr<VersionInfoBlock>>
parseVersionInfoBlockContents(StringRef BlockName)714 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
715   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
716 
717   auto Contents = std::make_unique<VersionInfoBlock>(BlockName);
718 
719   while (!isNextTokenKind(Kind::BlockEnd)) {
720     ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
721     Contents->addStmt(std::move(*Stmt));
722   }
723 
724   consume(); // Consume BlockEnd.
725 
726   return std::move(Contents);
727 }
728 
parseVersionInfoStmt()729 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
730   // Expect either BLOCK or VALUE, then a name or a key (a string).
731   ASSIGN_OR_RETURN(TypeResult, readIdentifier());
732 
733   if (TypeResult->equals_insensitive("BLOCK")) {
734     ASSIGN_OR_RETURN(NameResult, readString());
735     return parseVersionInfoBlockContents(*NameResult);
736   }
737 
738   if (TypeResult->equals_insensitive("VALUE")) {
739     ASSIGN_OR_RETURN(KeyResult, readString());
740     // Read a non-empty list of strings and/or ints, each
741     // possibly preceded by a comma. Unfortunately, the tool behavior depends
742     // on them existing or not, so we need to memorize where we found them.
743     std::vector<IntOrString> Values;
744     BitVector PrecedingCommas;
745     RETURN_IF_ERROR(consumeType(Kind::Comma));
746     while (!isNextTokenKind(Kind::Identifier) &&
747            !isNextTokenKind(Kind::BlockEnd)) {
748       // Try to eat a comma if it's not the first statement.
749       bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
750       ASSIGN_OR_RETURN(ValueResult, readIntOrString());
751       Values.push_back(*ValueResult);
752       PrecedingCommas.push_back(HadComma);
753     }
754     return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
755                                                std::move(PrecedingCommas));
756   }
757 
758   return getExpectedError("BLOCK or VALUE", true);
759 }
760 
761 Expected<VersionInfoResource::VersionInfoFixed>
parseVersionInfoFixed()762 RCParser::parseVersionInfoFixed() {
763   using RetType = VersionInfoResource::VersionInfoFixed;
764   RetType Result;
765 
766   // Read until the beginning of the block.
767   while (!isNextTokenKind(Kind::BlockBegin)) {
768     ASSIGN_OR_RETURN(TypeResult, readIdentifier());
769     auto FixedType = RetType::getFixedType(*TypeResult);
770 
771     if (!RetType::isTypeSupported(FixedType))
772       return getExpectedError("fixed VERSIONINFO statement type", true);
773     if (Result.IsTypePresent[FixedType])
774       return getExpectedError("yet unread fixed VERSIONINFO statement type",
775                               true);
776 
777     // VERSION variations take multiple integers.
778     size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
779     ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(1, NumInts));
780     SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
781     while (ArgInts.size() < NumInts)
782       ArgInts.push_back(0);
783     Result.setValue(FixedType, ArgInts);
784   }
785 
786   return Result;
787 }
788 
parseLanguageStmt()789 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
790   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
791   return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
792 }
793 
parseCharacteristicsStmt()794 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
795   ASSIGN_OR_RETURN(Arg, readInt());
796   return std::make_unique<CharacteristicsStmt>(*Arg);
797 }
798 
parseVersionStmt()799 RCParser::ParseOptionType RCParser::parseVersionStmt() {
800   ASSIGN_OR_RETURN(Arg, readInt());
801   return std::make_unique<VersionStmt>(*Arg);
802 }
803 
parseCaptionStmt()804 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
805   ASSIGN_OR_RETURN(Arg, readString());
806   return std::make_unique<CaptionStmt>(*Arg);
807 }
808 
parseClassStmt()809 RCParser::ParseOptionType RCParser::parseClassStmt() {
810   ASSIGN_OR_RETURN(Arg, readIntOrString());
811   return std::make_unique<ClassStmt>(*Arg);
812 }
813 
parseFontStmt(OptStmtType DialogType)814 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
815   assert(DialogType != OptStmtType::BasicStmt);
816 
817   ASSIGN_OR_RETURN(SizeResult, readInt());
818   RETURN_IF_ERROR(consumeType(Kind::Comma));
819   ASSIGN_OR_RETURN(NameResult, readString());
820 
821   // Default values for the optional arguments.
822   uint32_t FontWeight = 0;
823   bool FontItalic = false;
824   uint32_t FontCharset = 1;
825   if (DialogType == OptStmtType::DialogExStmt) {
826     if (consumeOptionalType(Kind::Comma)) {
827       ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
828       if (Args->size() >= 1)
829         FontWeight = (*Args)[0];
830       if (Args->size() >= 2)
831         FontItalic = (*Args)[1] != 0;
832       if (Args->size() >= 3)
833         FontCharset = (*Args)[2];
834     }
835   }
836   return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
837                                      FontItalic, FontCharset);
838 }
839 
parseStyleStmt()840 RCParser::ParseOptionType RCParser::parseStyleStmt() {
841   ASSIGN_OR_RETURN(Arg, readInt());
842   return std::make_unique<StyleStmt>(*Arg);
843 }
844 
parseExStyleStmt()845 RCParser::ParseOptionType RCParser::parseExStyleStmt() {
846   ASSIGN_OR_RETURN(Arg, readInt());
847   return std::make_unique<ExStyleStmt>(*Arg);
848 }
849 
getExpectedError(const Twine & Message,bool IsAlreadyRead)850 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
851   return make_error<ParserError>(
852       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
853 }
854 
855 } // namespace rc
856 } // namespace llvm
857