blob: ee7de6b937acdbf16ad735c6987c2925b505b6d7 [file] [log] [blame]
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +00001//===-- 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
16// Take an expression returning llvm::Error and forward the error if it exists.
17#define RETURN_IF_ERROR(Expr) \
18 if (auto Err = (Expr)) \
19 return std::move(Err);
20
21// Take an expression returning llvm::Expected<T> and assign it to Var or
22// forward the error out of the function.
23#define ASSIGN_OR_RETURN(Var, Expr) \
24 auto Var = (Expr); \
25 if (!Var) \
26 return Var.takeError();
27
28namespace llvm {
29namespace rc {
30
31RCParser::ParserError::ParserError(const Twine Expected, const LocIter CurLoc,
32 const LocIter End)
33 : ErrorLoc(CurLoc), FileEnd(End) {
34 CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
35 (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
36}
37
38char RCParser::ParserError::ID = 0;
39
40RCParser::RCParser(const std::vector<RCToken> &TokenList)
41 : Tokens(TokenList), CurLoc(Tokens.begin()), End(Tokens.end()) {}
42
43RCParser::RCParser(std::vector<RCToken> &&TokenList)
44 : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
45
46bool RCParser::isEof() const { return CurLoc == End; }
47
48RCParser::ParseType RCParser::parseSingleResource() {
49 // The first thing we read is usually a resource's name. However, in some
50 // cases (LANGUAGE and STRINGTABLE) the resources don't have their names
51 // and the first token to be read is the type.
52 ASSIGN_OR_RETURN(NameToken, readTypeOrName());
53
54 if (NameToken->equalsLower("LANGUAGE"))
55 return parseLanguageResource();
56 else if (NameToken->equalsLower("STRINGTABLE"))
57 return parseStringTableResource();
58
59 // If it's not an unnamed resource, what we've just read is a name. Now,
60 // read resource type;
61 ASSIGN_OR_RETURN(TypeToken, readTypeOrName());
62
63 ParseType Result = std::unique_ptr<RCResource>();
64 (void)!Result;
65
Marek Sokolowski7f110522017-08-28 22:58:31 +000066 if (TypeToken->equalsLower("ACCELERATORS"))
67 Result = parseAcceleratorsResource();
68 else if (TypeToken->equalsLower("CURSOR"))
Marek Sokolowski72aa9372017-08-28 21:59:54 +000069 Result = parseCursorResource();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +000070 else if (TypeToken->equalsLower("DIALOG"))
71 Result = parseDialogResource(false);
72 else if (TypeToken->equalsLower("DIALOGEX"))
73 Result = parseDialogResource(true);
Marek Sokolowski72aa9372017-08-28 21:59:54 +000074 else if (TypeToken->equalsLower("ICON"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000075 Result = parseIconResource();
Marek Sokolowski72aa9372017-08-28 21:59:54 +000076 else if (TypeToken->equalsLower("HTML"))
77 Result = parseHTMLResource();
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +000078 else if (TypeToken->equalsLower("MENU"))
79 Result = parseMenuResource();
Marek Sokolowskifb74cb12017-09-28 22:41:38 +000080 else if (TypeToken->equalsLower("VERSIONINFO"))
81 Result = parseVersionInfoResource();
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000082 else
83 return getExpectedError("resource type", /* IsAlreadyRead = */ true);
84
85 if (Result)
86 (*Result)->setName(*NameToken);
87
88 return Result;
89}
90
91bool RCParser::isNextTokenKind(Kind TokenKind) const {
92 return !isEof() && look().kind() == TokenKind;
93}
94
95const RCToken &RCParser::look() const {
96 assert(!isEof());
97 return *CurLoc;
98}
99
100const RCToken &RCParser::read() {
101 assert(!isEof());
102 return *CurLoc++;
103}
104
105void RCParser::consume() {
106 assert(!isEof());
107 CurLoc++;
108}
109
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000110// An integer description might consist of a single integer or
111// an arithmetic expression evaluating to the integer. The expressions
112// can contain the following tokens: <int> ( ) + - | & ~. Their meaning
113// is the same as in C++.
114// The operators in the original RC implementation have the following
115// precedence:
116// 1) Unary operators (- ~),
117// 2) Binary operators (+ - & |), with no precedence.
118//
119// The following grammar is used to parse the expressions Exp1:
120// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
121// Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
122// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
123// separated by binary operators.)
124//
125// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
126// is read by parseIntExpr2().
127//
128// The original Microsoft tool handles multiple unary operators incorrectly.
129// For example, in 16-bit little-endian integers:
130// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
131// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
132// Our implementation differs from the original one and handles these
133// operators correctly:
134// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
135// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
136
137Expected<uint32_t> RCParser::readInt() { return parseIntExpr1(); }
138
139Expected<uint32_t> RCParser::parseIntExpr1() {
140 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
141 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
142 uint32_t Result = *FirstResult;
143
144 while (!isEof() && look().isBinaryOp()) {
145 auto OpToken = read();
146 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
147
148 switch (OpToken.kind()) {
149 case Kind::Plus:
150 Result += *NextResult;
151 break;
152
153 case Kind::Minus:
154 Result -= *NextResult;
155 break;
156
157 case Kind::Pipe:
158 Result |= *NextResult;
159 break;
160
161 case Kind::Amp:
162 Result &= *NextResult;
163 break;
164
165 default:
166 llvm_unreachable("Already processed all binary ops.");
167 }
168 }
169
170 return Result;
171}
172
173Expected<uint32_t> RCParser::parseIntExpr2() {
174 // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
175 static const char ErrorMsg[] = "'-', '~', integer or '('";
176
177 if (isEof())
178 return getExpectedError(ErrorMsg);
179
180 switch (look().kind()) {
181 case Kind::Minus: {
182 consume();
183 ASSIGN_OR_RETURN(Result, parseIntExpr2());
184 return -(*Result);
185 }
186
187 case Kind::Tilde: {
188 consume();
189 ASSIGN_OR_RETURN(Result, parseIntExpr2());
190 return ~(*Result);
191 }
192
193 case Kind::Int:
194 return read().intValue();
195
196 case Kind::LeftParen: {
197 consume();
198 ASSIGN_OR_RETURN(Result, parseIntExpr1());
199 RETURN_IF_ERROR(consumeType(Kind::RightParen));
200 return *Result;
201 }
202
203 default:
204 return getExpectedError(ErrorMsg);
205 }
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000206}
207
208Expected<StringRef> RCParser::readString() {
209 if (!isNextTokenKind(Kind::String))
210 return getExpectedError("string");
211 return read().value();
212}
213
214Expected<StringRef> RCParser::readIdentifier() {
215 if (!isNextTokenKind(Kind::Identifier))
216 return getExpectedError("identifier");
217 return read().value();
218}
219
Marek Sokolowski7f110522017-08-28 22:58:31 +0000220Expected<IntOrString> RCParser::readIntOrString() {
221 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
222 return getExpectedError("int or string");
223 return IntOrString(read());
224}
225
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000226Expected<IntOrString> RCParser::readTypeOrName() {
227 // We suggest that the correct resource name or type should be either an
228 // identifier or an integer. The original RC tool is much more liberal.
229 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
230 return getExpectedError("int or identifier");
Marek Sokolowski7f110522017-08-28 22:58:31 +0000231 return IntOrString(read());
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000232}
233
234Error RCParser::consumeType(Kind TokenKind) {
235 if (isNextTokenKind(TokenKind)) {
236 consume();
237 return Error::success();
238 }
239
240 switch (TokenKind) {
241#define TOKEN(TokenName) \
242 case Kind::TokenName: \
243 return getExpectedError(#TokenName);
244#define SHORT_TOKEN(TokenName, TokenCh) \
245 case Kind::TokenName: \
246 return getExpectedError(#TokenCh);
247#include "ResourceScriptTokenList.h"
248#undef SHORT_TOKEN
249#undef TOKEN
250 }
251
252 llvm_unreachable("All case options exhausted.");
253}
254
255bool RCParser::consumeOptionalType(Kind TokenKind) {
256 if (isNextTokenKind(TokenKind)) {
257 consume();
258 return true;
259 }
260
261 return false;
262}
263
264Expected<SmallVector<uint32_t, 8>>
265RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) {
266 assert(MinCount <= MaxCount);
267
268 SmallVector<uint32_t, 8> Result;
269
270 auto FailureHandler =
271 [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 8>> {
272 if (Result.size() < MinCount)
273 return std::move(Err);
274 consumeError(std::move(Err));
275 return Result;
276 };
277
278 for (size_t i = 0; i < MaxCount; ++i) {
279 // Try to read a comma unless we read the first token.
280 // Sometimes RC tool requires them and sometimes not. We decide to
281 // always require them.
282 if (i >= 1) {
283 if (auto CommaError = consumeType(Kind::Comma))
284 return FailureHandler(std::move(CommaError));
285 }
286
287 if (auto IntResult = readInt())
288 Result.push_back(*IntResult);
289 else
290 return FailureHandler(IntResult.takeError());
291 }
292
293 return std::move(Result);
294}
295
Marek Sokolowski7f110522017-08-28 22:58:31 +0000296Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc) {
297 assert(FlagDesc.size() <= 32 && "More than 32 flags won't fit in result.");
298 assert(!FlagDesc.empty());
299
300 uint32_t Result = 0;
301 while (isNextTokenKind(Kind::Comma)) {
302 consume();
303 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
304 bool FoundFlag = false;
305
306 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
307 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
308 continue;
309
310 Result |= (1U << FlagId);
311 FoundFlag = true;
312 break;
313 }
314
315 if (!FoundFlag)
316 return getExpectedError(join(FlagDesc, "/"), true);
317 }
318
319 return Result;
320}
321
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000322// As for now, we ignore the extended set of statements.
323Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) {
324 OptionalStmtList Result;
325
326 // The last statement is always followed by the start of the block.
327 while (!isNextTokenKind(Kind::BlockBegin)) {
328 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(IsExtended));
329 Result.addStmt(std::move(*SingleParse));
330 }
331
332 return std::move(Result);
333}
334
335Expected<std::unique_ptr<OptionalStmt>>
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000336RCParser::parseSingleOptionalStatement(bool IsExtended) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000337 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
338 if (TypeToken->equals_lower("CHARACTERISTICS"))
339 return parseCharacteristicsStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000340 if (TypeToken->equals_lower("LANGUAGE"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000341 return parseLanguageStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000342 if (TypeToken->equals_lower("VERSION"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000343 return parseVersionStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000344
345 if (IsExtended) {
346 if (TypeToken->equals_lower("CAPTION"))
347 return parseCaptionStmt();
348 if (TypeToken->equals_lower("FONT"))
349 return parseFontStmt();
350 if (TypeToken->equals_lower("STYLE"))
351 return parseStyleStmt();
352 }
353
354 return getExpectedError("optional statement type, BEGIN or '{'",
355 /* IsAlreadyRead = */ true);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000356}
357
358RCParser::ParseType RCParser::parseLanguageResource() {
359 // Read LANGUAGE as an optional statement. If it's read correctly, we can
360 // upcast it to RCResource.
361 return parseLanguageStmt();
362}
363
Marek Sokolowski7f110522017-08-28 22:58:31 +0000364RCParser::ParseType RCParser::parseAcceleratorsResource() {
365 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
366 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
367
368 auto Accels = make_unique<AcceleratorsResource>(std::move(*OptStatements));
369
370 while (!consumeOptionalType(Kind::BlockEnd)) {
371 ASSIGN_OR_RETURN(EventResult, readIntOrString());
372 RETURN_IF_ERROR(consumeType(Kind::Comma));
373 ASSIGN_OR_RETURN(IDResult, readInt());
374 ASSIGN_OR_RETURN(FlagsResult,
375 parseFlags(AcceleratorsResource::Accelerator::OptionsStr));
376 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
377 }
378
379 return std::move(Accels);
380}
381
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000382RCParser::ParseType RCParser::parseCursorResource() {
383 ASSIGN_OR_RETURN(Arg, readString());
384 return make_unique<CursorResource>(*Arg);
385}
386
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000387RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
388 // Dialog resources have the following format of the arguments:
389 // DIALOG: x, y, width, height [opt stmts...] {controls...}
390 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
391 // These are very similar, so we parse them together.
392 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
393
394 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
395 if (IsExtended && consumeOptionalType(Kind::Comma)) {
396 ASSIGN_OR_RETURN(HelpIDResult, readInt());
397 HelpID = *HelpIDResult;
398 }
399
400 ASSIGN_OR_RETURN(OptStatements,
401 parseOptionalStatements(/*UseExtendedStmts = */ true));
402
403 assert(isNextTokenKind(Kind::BlockBegin) &&
404 "parseOptionalStatements, when successful, halts on BlockBegin.");
405 consume();
406
407 auto Dialog = make_unique<DialogResource>(
408 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
409 HelpID, std::move(*OptStatements), IsExtended);
410
411 while (!consumeOptionalType(Kind::BlockEnd)) {
412 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
413 Dialog->addControl(std::move(*ControlDefResult));
414 }
415
416 return std::move(Dialog);
417}
418
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000419RCParser::ParseType RCParser::parseVersionInfoResource() {
420 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
421 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
Marek Sokolowski99ead702017-09-28 23:12:53 +0000422 return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult),
423 std::move(*FixedResult));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000424}
425
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000426Expected<Control> RCParser::parseControl() {
427 // Each control definition (except CONTROL) follows one of the schemes below
428 // depending on the control class:
429 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
430 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
431 // Note that control ids must be integers.
432 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
Marek Sokolowski75fa1732017-08-29 20:03:18 +0000433 std::string ClassUpper = ClassResult->upper();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000434 if (Control::SupportedCtls.find(ClassUpper) == Control::SupportedCtls.end())
435 return getExpectedError("control type, END or '}'", true);
436
437 // Read caption if necessary.
438 StringRef Caption;
439 if (Control::CtlsWithTitle.find(ClassUpper) != Control::CtlsWithTitle.end()) {
440 ASSIGN_OR_RETURN(CaptionResult, readString());
441 RETURN_IF_ERROR(consumeType(Kind::Comma));
442 Caption = *CaptionResult;
443 }
444
445 ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8));
446
447 auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> {
448 return Args->size() > Id ? (*Args)[Id] : Optional<uint32_t>();
449 };
450
451 return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2],
452 (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6),
453 TakeOptArg(7));
454}
455
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000456RCParser::ParseType RCParser::parseIconResource() {
457 ASSIGN_OR_RETURN(Arg, readString());
458 return make_unique<IconResource>(*Arg);
459}
460
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000461RCParser::ParseType RCParser::parseHTMLResource() {
462 ASSIGN_OR_RETURN(Arg, readString());
463 return make_unique<HTMLResource>(*Arg);
464}
465
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000466RCParser::ParseType RCParser::parseMenuResource() {
467 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
468 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
469 return make_unique<MenuResource>(std::move(*OptStatements),
470 std::move(*Items));
471}
472
473Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
474 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
475
476 MenuDefinitionList List;
477
478 // Read a set of items. Each item is of one of three kinds:
479 // MENUITEM SEPARATOR
480 // MENUITEM caption:String, result:Int [, menu flags]...
481 // POPUP caption:String [, menu flags]... { items... }
482 while (!consumeOptionalType(Kind::BlockEnd)) {
483 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
484
485 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
486 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
487 if (!IsMenuItem && !IsPopup)
488 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
489
490 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
491 // Now, expecting SEPARATOR.
492 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
493 if (SeparatorResult->equals_lower("SEPARATOR")) {
494 List.addDefinition(make_unique<MenuSeparator>());
495 continue;
496 }
497
498 return getExpectedError("SEPARATOR or string", true);
499 }
500
501 // Not a separator. Read the caption.
502 ASSIGN_OR_RETURN(CaptionResult, readString());
503
504 // If MENUITEM, expect also a comma and an integer.
505 uint32_t MenuResult = -1;
506
507 if (IsMenuItem) {
508 RETURN_IF_ERROR(consumeType(Kind::Comma));
509 ASSIGN_OR_RETURN(IntResult, readInt());
510 MenuResult = *IntResult;
511 }
512
513 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr));
514
515 if (IsPopup) {
516 // If POPUP, read submenu items recursively.
517 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
518 List.addDefinition(make_unique<PopupItem>(*CaptionResult, *FlagsResult,
519 std::move(*SubMenuResult)));
520 continue;
521 }
522
523 assert(IsMenuItem);
524 List.addDefinition(
525 make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
526 }
527
528 return std::move(List);
529}
530
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000531RCParser::ParseType RCParser::parseStringTableResource() {
532 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
533 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
534
535 auto Table = make_unique<StringTableResource>(std::move(*OptStatements));
536
537 // Read strings until we reach the end of the block.
538 while (!consumeOptionalType(Kind::BlockEnd)) {
539 // Each definition consists of string's ID (an integer) and a string.
540 // Some examples in documentation suggest that there might be a comma in
541 // between, however we strictly adhere to the single statement definition.
542 ASSIGN_OR_RETURN(IDResult, readInt());
543 ASSIGN_OR_RETURN(StrResult, readString());
544 Table->addString(*IDResult, *StrResult);
545 }
546
547 return std::move(Table);
548}
549
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000550Expected<std::unique_ptr<VersionInfoBlock>>
551RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
552 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
553
Marek Sokolowski99ead702017-09-28 23:12:53 +0000554 auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000555
556 while (!isNextTokenKind(Kind::BlockEnd)) {
557 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
558 Contents->addStmt(std::move(*Stmt));
559 }
560
561 consume(); // Consume BlockEnd.
562
563 return std::move(Contents);
564}
565
566Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
567 // Expect either BLOCK or VALUE, then a name or a key (a string).
568 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
569
570 if (TypeResult->equals_lower("BLOCK")) {
571 ASSIGN_OR_RETURN(NameResult, readString());
572 return parseVersionInfoBlockContents(*NameResult);
573 }
574
575 if (TypeResult->equals_lower("VALUE")) {
576 ASSIGN_OR_RETURN(KeyResult, readString());
577 // Read a (possibly empty) list of strings and/or ints, each preceded by
578 // a comma.
579 std::vector<IntOrString> Values;
580
581 while (consumeOptionalType(Kind::Comma)) {
582 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
583 Values.push_back(*ValueResult);
584 }
Marek Sokolowski99ead702017-09-28 23:12:53 +0000585 return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000586 }
587
588 return getExpectedError("BLOCK or VALUE", true);
589}
590
591Expected<VersionInfoResource::VersionInfoFixed>
592RCParser::parseVersionInfoFixed() {
593 using RetType = VersionInfoResource::VersionInfoFixed;
594 RetType Result;
595
596 // Read until the beginning of the block.
597 while (!isNextTokenKind(Kind::BlockBegin)) {
598 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
599 auto FixedType = RetType::getFixedType(*TypeResult);
600
601 if (!RetType::isTypeSupported(FixedType))
602 return getExpectedError("fixed VERSIONINFO statement type", true);
603 if (Result.IsTypePresent[FixedType])
604 return getExpectedError("yet unread fixed VERSIONINFO statement type",
605 true);
606
607 // VERSION variations take multiple integers.
608 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
609 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
610 Result.setValue(FixedType, *ArgsResult);
611 }
612
613 return Result;
614}
615
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000616RCParser::ParseOptionType RCParser::parseLanguageStmt() {
617 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
618 return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
619}
620
621RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
622 ASSIGN_OR_RETURN(Arg, readInt());
623 return make_unique<CharacteristicsStmt>(*Arg);
624}
625
626RCParser::ParseOptionType RCParser::parseVersionStmt() {
627 ASSIGN_OR_RETURN(Arg, readInt());
628 return make_unique<VersionStmt>(*Arg);
629}
630
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000631RCParser::ParseOptionType RCParser::parseCaptionStmt() {
632 ASSIGN_OR_RETURN(Arg, readString());
633 return make_unique<CaptionStmt>(*Arg);
634}
635
636RCParser::ParseOptionType RCParser::parseFontStmt() {
637 ASSIGN_OR_RETURN(SizeResult, readInt());
638 RETURN_IF_ERROR(consumeType(Kind::Comma));
639 ASSIGN_OR_RETURN(NameResult, readString());
640 return make_unique<FontStmt>(*SizeResult, *NameResult);
641}
642
643RCParser::ParseOptionType RCParser::parseStyleStmt() {
644 ASSIGN_OR_RETURN(Arg, readInt());
645 return make_unique<StyleStmt>(*Arg);
646}
647
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000648Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) {
649 return make_error<ParserError>(
650 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
651}
652
653} // namespace rc
654} // namespace llvm