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