blob: 211d9e7e954433f44dfc633ed9753ca6fb2b13aa [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
Marek Sokolowskib5f39a02017-09-29 00:14:18 +000083 Result = parseUserDefinedResource(*TypeToken);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000084
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 Sokolowskib5f39a02017-09-29 00:14:18 +0000419RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
420 if (isEof())
421 return getExpectedError("filename, '{' or BEGIN");
422
423 // Check if this is a file resource.
424 if (look().kind() == Kind::String)
425 return make_unique<UserDefinedResource>(Type, read().value());
426
427 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
428 std::vector<IntOrString> Data;
429
430 // Consume comma before each consecutive token except the first one.
431 bool ConsumeComma = false;
432 while (!consumeOptionalType(Kind::BlockEnd)) {
433 if (ConsumeComma)
434 RETURN_IF_ERROR(consumeType(Kind::Comma));
435 ConsumeComma = true;
436
437 ASSIGN_OR_RETURN(Item, readIntOrString());
438 Data.push_back(*Item);
439 }
440
441 return make_unique<UserDefinedResource>(Type, std::move(Data));
442}
443
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000444RCParser::ParseType RCParser::parseVersionInfoResource() {
445 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
446 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
Marek Sokolowski99ead702017-09-28 23:12:53 +0000447 return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult),
448 std::move(*FixedResult));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000449}
450
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000451Expected<Control> RCParser::parseControl() {
452 // Each control definition (except CONTROL) follows one of the schemes below
453 // depending on the control class:
454 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
455 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
456 // Note that control ids must be integers.
457 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
Marek Sokolowski75fa1732017-08-29 20:03:18 +0000458 std::string ClassUpper = ClassResult->upper();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000459 if (Control::SupportedCtls.find(ClassUpper) == Control::SupportedCtls.end())
460 return getExpectedError("control type, END or '}'", true);
461
462 // Read caption if necessary.
463 StringRef Caption;
464 if (Control::CtlsWithTitle.find(ClassUpper) != Control::CtlsWithTitle.end()) {
465 ASSIGN_OR_RETURN(CaptionResult, readString());
466 RETURN_IF_ERROR(consumeType(Kind::Comma));
467 Caption = *CaptionResult;
468 }
469
470 ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8));
471
472 auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> {
473 return Args->size() > Id ? (*Args)[Id] : Optional<uint32_t>();
474 };
475
476 return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2],
477 (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6),
478 TakeOptArg(7));
479}
480
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000481RCParser::ParseType RCParser::parseIconResource() {
482 ASSIGN_OR_RETURN(Arg, readString());
483 return make_unique<IconResource>(*Arg);
484}
485
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000486RCParser::ParseType RCParser::parseHTMLResource() {
487 ASSIGN_OR_RETURN(Arg, readString());
488 return make_unique<HTMLResource>(*Arg);
489}
490
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000491RCParser::ParseType RCParser::parseMenuResource() {
492 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
493 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
494 return make_unique<MenuResource>(std::move(*OptStatements),
495 std::move(*Items));
496}
497
498Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
499 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
500
501 MenuDefinitionList List;
502
503 // Read a set of items. Each item is of one of three kinds:
504 // MENUITEM SEPARATOR
505 // MENUITEM caption:String, result:Int [, menu flags]...
506 // POPUP caption:String [, menu flags]... { items... }
507 while (!consumeOptionalType(Kind::BlockEnd)) {
508 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
509
510 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
511 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
512 if (!IsMenuItem && !IsPopup)
513 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
514
515 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
516 // Now, expecting SEPARATOR.
517 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
518 if (SeparatorResult->equals_lower("SEPARATOR")) {
519 List.addDefinition(make_unique<MenuSeparator>());
520 continue;
521 }
522
523 return getExpectedError("SEPARATOR or string", true);
524 }
525
526 // Not a separator. Read the caption.
527 ASSIGN_OR_RETURN(CaptionResult, readString());
528
529 // If MENUITEM, expect also a comma and an integer.
530 uint32_t MenuResult = -1;
531
532 if (IsMenuItem) {
533 RETURN_IF_ERROR(consumeType(Kind::Comma));
534 ASSIGN_OR_RETURN(IntResult, readInt());
535 MenuResult = *IntResult;
536 }
537
538 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr));
539
540 if (IsPopup) {
541 // If POPUP, read submenu items recursively.
542 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
543 List.addDefinition(make_unique<PopupItem>(*CaptionResult, *FlagsResult,
544 std::move(*SubMenuResult)));
545 continue;
546 }
547
548 assert(IsMenuItem);
549 List.addDefinition(
550 make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
551 }
552
553 return std::move(List);
554}
555
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000556RCParser::ParseType RCParser::parseStringTableResource() {
557 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
558 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
559
560 auto Table = make_unique<StringTableResource>(std::move(*OptStatements));
561
562 // Read strings until we reach the end of the block.
563 while (!consumeOptionalType(Kind::BlockEnd)) {
564 // Each definition consists of string's ID (an integer) and a string.
565 // Some examples in documentation suggest that there might be a comma in
566 // between, however we strictly adhere to the single statement definition.
567 ASSIGN_OR_RETURN(IDResult, readInt());
568 ASSIGN_OR_RETURN(StrResult, readString());
569 Table->addString(*IDResult, *StrResult);
570 }
571
572 return std::move(Table);
573}
574
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000575Expected<std::unique_ptr<VersionInfoBlock>>
576RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
577 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
578
Marek Sokolowski99ead702017-09-28 23:12:53 +0000579 auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000580
581 while (!isNextTokenKind(Kind::BlockEnd)) {
582 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
583 Contents->addStmt(std::move(*Stmt));
584 }
585
586 consume(); // Consume BlockEnd.
587
588 return std::move(Contents);
589}
590
591Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
592 // Expect either BLOCK or VALUE, then a name or a key (a string).
593 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
594
595 if (TypeResult->equals_lower("BLOCK")) {
596 ASSIGN_OR_RETURN(NameResult, readString());
597 return parseVersionInfoBlockContents(*NameResult);
598 }
599
600 if (TypeResult->equals_lower("VALUE")) {
601 ASSIGN_OR_RETURN(KeyResult, readString());
602 // Read a (possibly empty) list of strings and/or ints, each preceded by
603 // a comma.
604 std::vector<IntOrString> Values;
605
606 while (consumeOptionalType(Kind::Comma)) {
607 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
608 Values.push_back(*ValueResult);
609 }
Marek Sokolowski99ead702017-09-28 23:12:53 +0000610 return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000611 }
612
613 return getExpectedError("BLOCK or VALUE", true);
614}
615
616Expected<VersionInfoResource::VersionInfoFixed>
617RCParser::parseVersionInfoFixed() {
618 using RetType = VersionInfoResource::VersionInfoFixed;
619 RetType Result;
620
621 // Read until the beginning of the block.
622 while (!isNextTokenKind(Kind::BlockBegin)) {
623 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
624 auto FixedType = RetType::getFixedType(*TypeResult);
625
626 if (!RetType::isTypeSupported(FixedType))
627 return getExpectedError("fixed VERSIONINFO statement type", true);
628 if (Result.IsTypePresent[FixedType])
629 return getExpectedError("yet unread fixed VERSIONINFO statement type",
630 true);
631
632 // VERSION variations take multiple integers.
633 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
634 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
635 Result.setValue(FixedType, *ArgsResult);
636 }
637
638 return Result;
639}
640
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000641RCParser::ParseOptionType RCParser::parseLanguageStmt() {
642 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
643 return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
644}
645
646RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
647 ASSIGN_OR_RETURN(Arg, readInt());
648 return make_unique<CharacteristicsStmt>(*Arg);
649}
650
651RCParser::ParseOptionType RCParser::parseVersionStmt() {
652 ASSIGN_OR_RETURN(Arg, readInt());
653 return make_unique<VersionStmt>(*Arg);
654}
655
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000656RCParser::ParseOptionType RCParser::parseCaptionStmt() {
657 ASSIGN_OR_RETURN(Arg, readString());
658 return make_unique<CaptionStmt>(*Arg);
659}
660
661RCParser::ParseOptionType RCParser::parseFontStmt() {
662 ASSIGN_OR_RETURN(SizeResult, readInt());
663 RETURN_IF_ERROR(consumeType(Kind::Comma));
664 ASSIGN_OR_RETURN(NameResult, readString());
665 return make_unique<FontStmt>(*SizeResult, *NameResult);
666}
667
668RCParser::ParseOptionType RCParser::parseStyleStmt() {
669 ASSIGN_OR_RETURN(Arg, readInt());
670 return make_unique<StyleStmt>(*Arg);
671}
672
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000673Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) {
674 return make_error<ParserError>(
675 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
676}
677
678} // namespace rc
679} // namespace llvm