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