OSDN Git Service

[llvm-rc] Add support for all missing dialog controls
[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::readFilename() {
218   if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
219     return getExpectedError("string");
220   return read().value();
221 }
222
223 Expected<StringRef> RCParser::readIdentifier() {
224   if (!isNextTokenKind(Kind::Identifier))
225     return getExpectedError("identifier");
226   return read().value();
227 }
228
229 Expected<IntOrString> RCParser::readIntOrString() {
230   if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
231     return getExpectedError("int or string");
232   return IntOrString(read());
233 }
234
235 Expected<IntOrString> RCParser::readTypeOrName() {
236   // We suggest that the correct resource name or type should be either an
237   // identifier or an integer. The original RC tool is much more liberal.
238   if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
239     return getExpectedError("int or identifier");
240   return IntOrString(read());
241 }
242
243 Error RCParser::consumeType(Kind TokenKind) {
244   if (isNextTokenKind(TokenKind)) {
245     consume();
246     return Error::success();
247   }
248
249   switch (TokenKind) {
250 #define TOKEN(TokenName)                                                       \
251   case Kind::TokenName:                                                        \
252     return getExpectedError(#TokenName);
253 #define SHORT_TOKEN(TokenName, TokenCh)                                        \
254   case Kind::TokenName:                                                        \
255     return getExpectedError(#TokenCh);
256 #include "ResourceScriptTokenList.def"
257   }
258
259   llvm_unreachable("All case options exhausted.");
260 }
261
262 bool RCParser::consumeOptionalType(Kind TokenKind) {
263   if (isNextTokenKind(TokenKind)) {
264     consume();
265     return true;
266   }
267
268   return false;
269 }
270
271 Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
272                                                              size_t MaxCount) {
273   assert(MinCount <= MaxCount);
274
275   SmallVector<RCInt, 8> Result;
276
277   auto FailureHandler =
278       [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
279     if (Result.size() < MinCount)
280       return std::move(Err);
281     consumeError(std::move(Err));
282     return Result;
283   };
284
285   for (size_t i = 0; i < MaxCount; ++i) {
286     // Try to read a comma unless we read the first token.
287     // Sometimes RC tool requires them and sometimes not. We decide to
288     // always require them.
289     if (i >= 1) {
290       if (auto CommaError = consumeType(Kind::Comma))
291         return FailureHandler(std::move(CommaError));
292     }
293
294     if (auto IntResult = readInt())
295       Result.push_back(*IntResult);
296     else
297       return FailureHandler(IntResult.takeError());
298   }
299
300   return std::move(Result);
301 }
302
303 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
304                                         ArrayRef<uint32_t> FlagValues) {
305   assert(!FlagDesc.empty());
306   assert(FlagDesc.size() == FlagValues.size());
307
308   uint32_t Result = 0;
309   while (isNextTokenKind(Kind::Comma)) {
310     consume();
311     ASSIGN_OR_RETURN(FlagResult, readIdentifier());
312     bool FoundFlag = false;
313
314     for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
315       if (!FlagResult->equals_lower(FlagDesc[FlagId]))
316         continue;
317
318       Result |= FlagValues[FlagId];
319       FoundFlag = true;
320       break;
321     }
322
323     if (!FoundFlag)
324       return getExpectedError(join(FlagDesc, "/"), true);
325   }
326
327   return Result;
328 }
329
330 Expected<OptionalStmtList>
331 RCParser::parseOptionalStatements(OptStmtType StmtsType) {
332   OptionalStmtList Result;
333
334   // The last statement is always followed by the start of the block.
335   while (!isNextTokenKind(Kind::BlockBegin)) {
336     ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
337     Result.addStmt(std::move(*SingleParse));
338   }
339
340   return std::move(Result);
341 }
342
343 Expected<std::unique_ptr<OptionalStmt>>
344 RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
345   ASSIGN_OR_RETURN(TypeToken, readIdentifier());
346   if (TypeToken->equals_lower("CHARACTERISTICS"))
347     return parseCharacteristicsStmt();
348   if (TypeToken->equals_lower("LANGUAGE"))
349     return parseLanguageStmt();
350   if (TypeToken->equals_lower("VERSION"))
351     return parseVersionStmt();
352
353   if (StmtsType != OptStmtType::BasicStmt) {
354     if (TypeToken->equals_lower("CAPTION"))
355       return parseCaptionStmt();
356     if (TypeToken->equals_lower("FONT"))
357       return parseFontStmt(StmtsType);
358     if (TypeToken->equals_lower("STYLE"))
359       return parseStyleStmt();
360   }
361
362   return getExpectedError("optional statement type, BEGIN or '{'",
363                           /* IsAlreadyRead = */ true);
364 }
365
366 RCParser::ParseType RCParser::parseLanguageResource() {
367   // Read LANGUAGE as an optional statement. If it's read correctly, we can
368   // upcast it to RCResource.
369   return parseLanguageStmt();
370 }
371
372 RCParser::ParseType RCParser::parseAcceleratorsResource() {
373   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
374   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
375
376   auto Accels =
377       llvm::make_unique<AcceleratorsResource>(std::move(*OptStatements));
378
379   while (!consumeOptionalType(Kind::BlockEnd)) {
380     ASSIGN_OR_RETURN(EventResult, readIntOrString());
381     RETURN_IF_ERROR(consumeType(Kind::Comma));
382     ASSIGN_OR_RETURN(IDResult, readInt());
383     ASSIGN_OR_RETURN(
384         FlagsResult,
385         parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
386                    AcceleratorsResource::Accelerator::OptionsFlags));
387     Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
388   }
389
390   return std::move(Accels);
391 }
392
393 RCParser::ParseType RCParser::parseCursorResource() {
394   ASSIGN_OR_RETURN(Arg, readFilename());
395   return llvm::make_unique<CursorResource>(*Arg);
396 }
397
398 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
399   // Dialog resources have the following format of the arguments:
400   //  DIALOG:   x, y, width, height [opt stmts...] {controls...}
401   //  DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
402   // These are very similar, so we parse them together.
403   ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
404
405   uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
406   if (IsExtended && consumeOptionalType(Kind::Comma)) {
407     ASSIGN_OR_RETURN(HelpIDResult, readInt());
408     HelpID = *HelpIDResult;
409   }
410
411   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
412                                       IsExtended ? OptStmtType::DialogExStmt
413                                                  : OptStmtType::DialogStmt));
414
415   assert(isNextTokenKind(Kind::BlockBegin) &&
416          "parseOptionalStatements, when successful, halts on BlockBegin.");
417   consume();
418
419   auto Dialog = llvm::make_unique<DialogResource>(
420       (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
421       HelpID, std::move(*OptStatements), IsExtended);
422
423   while (!consumeOptionalType(Kind::BlockEnd)) {
424     ASSIGN_OR_RETURN(ControlDefResult, parseControl());
425     Dialog->addControl(std::move(*ControlDefResult));
426   }
427
428   return std::move(Dialog);
429 }
430
431 RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
432   if (isEof())
433     return getExpectedError("filename, '{' or BEGIN");
434
435   // Check if this is a file resource.
436   switch (look().kind()) {
437   case Kind::String:
438   case Kind::Identifier:
439     return llvm::make_unique<UserDefinedResource>(Type, read().value());
440   default:
441     break;
442   }
443
444   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
445   std::vector<IntOrString> Data;
446
447   // Consume comma before each consecutive token except the first one.
448   bool ConsumeComma = false;
449   while (!consumeOptionalType(Kind::BlockEnd)) {
450     if (ConsumeComma)
451       RETURN_IF_ERROR(consumeType(Kind::Comma));
452     ConsumeComma = true;
453
454     ASSIGN_OR_RETURN(Item, readIntOrString());
455     Data.push_back(*Item);
456   }
457
458   return llvm::make_unique<UserDefinedResource>(Type, std::move(Data));
459 }
460
461 RCParser::ParseType RCParser::parseVersionInfoResource() {
462   ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
463   ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
464   return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult),
465                                                 std::move(*FixedResult));
466 }
467
468 Expected<Control> RCParser::parseControl() {
469   // Each control definition (except CONTROL) follows one of the schemes below
470   // depending on the control class:
471   //  [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
472   //  [class]       id, x, y, width, height [, style] [, exstyle] [, helpID]
473   // Note that control ids must be integers.
474   // Text might be either a string or an integer pointing to resource ID.
475   ASSIGN_OR_RETURN(ClassResult, readIdentifier());
476   std::string ClassUpper = ClassResult->upper();
477   auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
478   if (CtlInfo == Control::SupportedCtls.end())
479     return getExpectedError("control type, END or '}'", true);
480
481   // Read caption if necessary.
482   IntOrString Caption{StringRef()};
483   if (CtlInfo->getValue().HasTitle) {
484     ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
485     RETURN_IF_ERROR(consumeType(Kind::Comma));
486     Caption = *CaptionResult;
487   }
488
489   ASSIGN_OR_RETURN(ID, readInt());
490   RETURN_IF_ERROR(consumeType(Kind::Comma));
491
492   IntOrString Class;
493   Optional<uint32_t> Style;
494   if (ClassUpper == "CONTROL") {
495     // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
496     ASSIGN_OR_RETURN(ClassStr, readString());
497     RETURN_IF_ERROR(consumeType(Kind::Comma));
498     Class = *ClassStr;
499     ASSIGN_OR_RETURN(StyleVal, readInt());
500     RETURN_IF_ERROR(consumeType(Kind::Comma));
501     Style = *StyleVal;
502   } else {
503     Class = CtlInfo->getValue().CtlClass;
504   }
505
506   // x, y, width, height
507   ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
508
509   if (ClassUpper != "CONTROL") {
510     if (consumeOptionalType(Kind::Comma)) {
511       ASSIGN_OR_RETURN(Val, readInt());
512       Style = *Val;
513     }
514   }
515
516   Optional<uint32_t> ExStyle;
517   if (consumeOptionalType(Kind::Comma)) {
518     ASSIGN_OR_RETURN(Val, readInt());
519     ExStyle = *Val;
520   }
521   Optional<uint32_t> HelpID;
522   if (consumeOptionalType(Kind::Comma)) {
523     ASSIGN_OR_RETURN(Val, readInt());
524     HelpID = *Val;
525   }
526
527   return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
528                  (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
529 }
530
531 RCParser::ParseType RCParser::parseBitmapResource() {
532   ASSIGN_OR_RETURN(Arg, readFilename());
533   return llvm::make_unique<BitmapResource>(*Arg);
534 }
535
536 RCParser::ParseType RCParser::parseIconResource() {
537   ASSIGN_OR_RETURN(Arg, readFilename());
538   return llvm::make_unique<IconResource>(*Arg);
539 }
540
541 RCParser::ParseType RCParser::parseHTMLResource() {
542   ASSIGN_OR_RETURN(Arg, readFilename());
543   return llvm::make_unique<HTMLResource>(*Arg);
544 }
545
546 RCParser::ParseType RCParser::parseMenuResource() {
547   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
548   ASSIGN_OR_RETURN(Items, parseMenuItemsList());
549   return llvm::make_unique<MenuResource>(std::move(*OptStatements),
550                                          std::move(*Items));
551 }
552
553 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
554   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
555
556   MenuDefinitionList List;
557
558   // Read a set of items. Each item is of one of three kinds:
559   //   MENUITEM SEPARATOR
560   //   MENUITEM caption:String, result:Int [, menu flags]...
561   //   POPUP caption:String [, menu flags]... { items... }
562   while (!consumeOptionalType(Kind::BlockEnd)) {
563     ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
564
565     bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
566     bool IsPopup = ItemTypeResult->equals_lower("POPUP");
567     if (!IsMenuItem && !IsPopup)
568       return getExpectedError("MENUITEM, POPUP, END or '}'", true);
569
570     if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
571       // Now, expecting SEPARATOR.
572       ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
573       if (SeparatorResult->equals_lower("SEPARATOR")) {
574         List.addDefinition(llvm::make_unique<MenuSeparator>());
575         continue;
576       }
577
578       return getExpectedError("SEPARATOR or string", true);
579     }
580
581     // Not a separator. Read the caption.
582     ASSIGN_OR_RETURN(CaptionResult, readString());
583
584     // If MENUITEM, expect also a comma and an integer.
585     uint32_t MenuResult = -1;
586
587     if (IsMenuItem) {
588       RETURN_IF_ERROR(consumeType(Kind::Comma));
589       ASSIGN_OR_RETURN(IntResult, readInt());
590       MenuResult = *IntResult;
591     }
592
593     ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
594                                              MenuDefinition::OptionsFlags));
595
596     if (IsPopup) {
597       // If POPUP, read submenu items recursively.
598       ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
599       List.addDefinition(llvm::make_unique<PopupItem>(
600           *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
601       continue;
602     }
603
604     assert(IsMenuItem);
605     List.addDefinition(
606         llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
607   }
608
609   return std::move(List);
610 }
611
612 RCParser::ParseType RCParser::parseStringTableResource() {
613   ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
614   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
615
616   auto Table =
617       llvm::make_unique<StringTableResource>(std::move(*OptStatements));
618
619   // Read strings until we reach the end of the block.
620   while (!consumeOptionalType(Kind::BlockEnd)) {
621     // Each definition consists of string's ID (an integer) and a string.
622     // Some examples in documentation suggest that there might be a comma in
623     // between, however we strictly adhere to the single statement definition.
624     ASSIGN_OR_RETURN(IDResult, readInt());
625     consumeOptionalType(Kind::Comma);
626     ASSIGN_OR_RETURN(StrResult, readString());
627     Table->addString(*IDResult, *StrResult);
628   }
629
630   return std::move(Table);
631 }
632
633 Expected<std::unique_ptr<VersionInfoBlock>>
634 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
635   RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
636
637   auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
638
639   while (!isNextTokenKind(Kind::BlockEnd)) {
640     ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
641     Contents->addStmt(std::move(*Stmt));
642   }
643
644   consume(); // Consume BlockEnd.
645
646   return std::move(Contents);
647 }
648
649 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
650   // Expect either BLOCK or VALUE, then a name or a key (a string).
651   ASSIGN_OR_RETURN(TypeResult, readIdentifier());
652
653   if (TypeResult->equals_lower("BLOCK")) {
654     ASSIGN_OR_RETURN(NameResult, readString());
655     return parseVersionInfoBlockContents(*NameResult);
656   }
657
658   if (TypeResult->equals_lower("VALUE")) {
659     ASSIGN_OR_RETURN(KeyResult, readString());
660     // Read a non-empty list of strings and/or ints, each
661     // possibly preceded by a comma. Unfortunately, the tool behavior depends
662     // on them existing or not, so we need to memorize where we found them.
663     std::vector<IntOrString> Values;
664     std::vector<bool> PrecedingCommas;
665     RETURN_IF_ERROR(consumeType(Kind::Comma));
666     while (!isNextTokenKind(Kind::Identifier) &&
667            !isNextTokenKind(Kind::BlockEnd)) {
668       // Try to eat a comma if it's not the first statement.
669       bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
670       ASSIGN_OR_RETURN(ValueResult, readIntOrString());
671       Values.push_back(*ValueResult);
672       PrecedingCommas.push_back(HadComma);
673     }
674     return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
675                                                std::move(PrecedingCommas));
676   }
677
678   return getExpectedError("BLOCK or VALUE", true);
679 }
680
681 Expected<VersionInfoResource::VersionInfoFixed>
682 RCParser::parseVersionInfoFixed() {
683   using RetType = VersionInfoResource::VersionInfoFixed;
684   RetType Result;
685
686   // Read until the beginning of the block.
687   while (!isNextTokenKind(Kind::BlockBegin)) {
688     ASSIGN_OR_RETURN(TypeResult, readIdentifier());
689     auto FixedType = RetType::getFixedType(*TypeResult);
690
691     if (!RetType::isTypeSupported(FixedType))
692       return getExpectedError("fixed VERSIONINFO statement type", true);
693     if (Result.IsTypePresent[FixedType])
694       return getExpectedError("yet unread fixed VERSIONINFO statement type",
695                               true);
696
697     // VERSION variations take multiple integers.
698     size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
699     ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
700     SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
701     Result.setValue(FixedType, ArgInts);
702   }
703
704   return Result;
705 }
706
707 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
708   ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
709   return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
710 }
711
712 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
713   ASSIGN_OR_RETURN(Arg, readInt());
714   return llvm::make_unique<CharacteristicsStmt>(*Arg);
715 }
716
717 RCParser::ParseOptionType RCParser::parseVersionStmt() {
718   ASSIGN_OR_RETURN(Arg, readInt());
719   return llvm::make_unique<VersionStmt>(*Arg);
720 }
721
722 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
723   ASSIGN_OR_RETURN(Arg, readString());
724   return llvm::make_unique<CaptionStmt>(*Arg);
725 }
726
727 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
728   assert(DialogType != OptStmtType::BasicStmt);
729
730   ASSIGN_OR_RETURN(SizeResult, readInt());
731   RETURN_IF_ERROR(consumeType(Kind::Comma));
732   ASSIGN_OR_RETURN(NameResult, readString());
733
734   // Default values for the optional arguments.
735   uint32_t FontWeight = 0;
736   bool FontItalic = false;
737   uint32_t FontCharset = 1;
738   if (DialogType == OptStmtType::DialogExStmt) {
739     if (consumeOptionalType(Kind::Comma)) {
740       ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
741       if (Args->size() >= 1)
742         FontWeight = (*Args)[0];
743       if (Args->size() >= 2)
744         FontItalic = (*Args)[1] != 0;
745       if (Args->size() >= 3)
746         FontCharset = (*Args)[2];
747     }
748   }
749   return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
750                                      FontItalic, FontCharset);
751 }
752
753 RCParser::ParseOptionType RCParser::parseStyleStmt() {
754   ASSIGN_OR_RETURN(Arg, readInt());
755   return llvm::make_unique<StyleStmt>(*Arg);
756 }
757
758 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
759   return make_error<ParserError>(
760       Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
761 }
762
763 } // namespace rc
764 } // namespace llvm