OSDN Git Service

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