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