[AMDGPU] Test codegen'ing True16 additions.
[llvm-project.git] / llvm / tools / llvm-rc / ResourceScriptParser.cpp
blob0037fec4c0247d7df7d79835d4f29c22ab85b487
1 //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===---------------------------------------------------------------------===//
8 //
9 // This implements the parser defined in ResourceScriptParser.h.
11 //===---------------------------------------------------------------------===//
13 #include "ResourceScriptParser.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/Option/ArgList.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/Process.h"
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);
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();
32 namespace llvm {
33 namespace rc {
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();
42 char RCParser::ParserError::ID = 0;
44 RCParser::RCParser(std::vector<RCToken> TokenList)
45 : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
47 bool RCParser::isEof() const { return CurLoc == End; }
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());
55 if (NameToken->equalsLower("LANGUAGE"))
56 return parseLanguageResource();
57 else if (NameToken->equalsLower("STRINGTABLE"))
58 return parseStringTableResource();
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());
64 ParseType Result = std::unique_ptr<RCResource>();
65 (void)!Result;
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("RCDATA"))
84 Result = parseUserDefinedResource(RkRcData);
85 else if (TypeToken->equalsLower("VERSIONINFO"))
86 Result = parseVersionInfoResource();
87 else
88 Result = parseUserDefinedResource(*TypeToken);
90 if (Result)
91 (*Result)->setName(*NameToken);
93 return Result;
96 bool RCParser::isNextTokenKind(Kind TokenKind) const {
97 return !isEof() && look().kind() == TokenKind;
100 const RCToken &RCParser::look() const {
101 assert(!isEof());
102 return *CurLoc;
105 const RCToken &RCParser::read() {
106 assert(!isEof());
107 return *CurLoc++;
110 void RCParser::consume() {
111 assert(!isEof());
112 CurLoc++;
115 // An integer description might consist of a single integer or
116 // an arithmetic expression evaluating to the integer. The expressions
117 // can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning
118 // is the same as in C++ except for 'not' expression.
119 // The operators in the original RC implementation have the following
120 // precedence:
121 // 1) Unary operators (- ~ not),
122 // 2) Binary operators (+ - & |), with no precedence.
124 // 'not' expression is mostly useful for style values. It evaluates to 0,
125 // but value given to the operator is stored separately from integer value.
126 // It's mostly useful for control style expressions and causes bits from
127 // default control style to be excluded from generated style. For binary
128 // operators the mask from the right operand is applied to the left operand
129 // and masks from both operands are combined in operator result.
131 // The following grammar is used to parse the expressions Exp1:
132 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
133 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
134 // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
135 // separated by binary operators.)
137 // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
138 // is read by parseIntExpr2().
140 // The original Microsoft tool handles multiple unary operators incorrectly.
141 // For example, in 16-bit little-endian integers:
142 // 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
143 // 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
144 // Our implementation differs from the original one and handles these
145 // operators correctly:
146 // 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
147 // 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
149 Expected<RCInt> RCParser::readInt() {
150 ASSIGN_OR_RETURN(Value, parseIntExpr1());
151 return (*Value).getValue();
154 Expected<IntWithNotMask> RCParser::parseIntExpr1() {
155 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
156 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
157 IntWithNotMask Result = *FirstResult;
159 while (!isEof() && look().isBinaryOp()) {
160 auto OpToken = read();
161 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
163 switch (OpToken.kind()) {
164 case Kind::Plus:
165 Result += *NextResult;
166 break;
168 case Kind::Minus:
169 Result -= *NextResult;
170 break;
172 case Kind::Pipe:
173 Result |= *NextResult;
174 break;
176 case Kind::Amp:
177 Result &= *NextResult;
178 break;
180 default:
181 llvm_unreachable("Already processed all binary ops.");
185 return Result;
188 Expected<IntWithNotMask> RCParser::parseIntExpr2() {
189 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
190 static const char ErrorMsg[] = "'-', '~', integer or '('";
192 if (isEof())
193 return getExpectedError(ErrorMsg);
195 switch (look().kind()) {
196 case Kind::Minus: {
197 consume();
198 ASSIGN_OR_RETURN(Result, parseIntExpr2());
199 return -(*Result);
202 case Kind::Tilde: {
203 consume();
204 ASSIGN_OR_RETURN(Result, parseIntExpr2());
205 return ~(*Result);
208 case Kind::Int:
209 return RCInt(read());
211 case Kind::LeftParen: {
212 consume();
213 ASSIGN_OR_RETURN(Result, parseIntExpr1());
214 RETURN_IF_ERROR(consumeType(Kind::RightParen));
215 return *Result;
218 case Kind::Identifier: {
219 if (!read().value().equals_insensitive("not"))
220 return getExpectedError(ErrorMsg, true);
221 ASSIGN_OR_RETURN(Result, parseIntExpr2());
222 return IntWithNotMask(0, (*Result).getValue());
225 default:
226 return getExpectedError(ErrorMsg);
230 Expected<StringRef> RCParser::readString() {
231 if (!isNextTokenKind(Kind::String))
232 return getExpectedError("string");
233 return read().value();
236 Expected<StringRef> RCParser::readFilename() {
237 if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
238 return getExpectedError("string");
239 return read().value();
242 Expected<StringRef> RCParser::readIdentifier() {
243 if (!isNextTokenKind(Kind::Identifier))
244 return getExpectedError("identifier");
245 return read().value();
248 Expected<IntOrString> RCParser::readIntOrString() {
249 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
250 return getExpectedError("int or string");
251 return IntOrString(read());
254 Expected<IntOrString> RCParser::readTypeOrName() {
255 // We suggest that the correct resource name or type should be either an
256 // identifier or an integer. The original RC tool is much more liberal.
257 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
258 return getExpectedError("int or identifier");
259 return IntOrString(read());
262 Error RCParser::consumeType(Kind TokenKind) {
263 if (isNextTokenKind(TokenKind)) {
264 consume();
265 return Error::success();
268 switch (TokenKind) {
269 #define TOKEN(TokenName) \
270 case Kind::TokenName: \
271 return getExpectedError(#TokenName);
272 #define SHORT_TOKEN(TokenName, TokenCh) \
273 case Kind::TokenName: \
274 return getExpectedError(#TokenCh);
275 #include "ResourceScriptTokenList.def"
278 llvm_unreachable("All case options exhausted.");
281 bool RCParser::consumeOptionalType(Kind TokenKind) {
282 if (isNextTokenKind(TokenKind)) {
283 consume();
284 return true;
287 return false;
290 Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
291 size_t MaxCount) {
292 assert(MinCount <= MaxCount);
294 SmallVector<RCInt, 8> Result;
296 auto FailureHandler =
297 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
298 if (Result.size() < MinCount)
299 return std::move(Err);
300 consumeError(std::move(Err));
301 return Result;
304 for (size_t i = 0; i < MaxCount; ++i) {
305 // Try to read a comma unless we read the first token.
306 // Sometimes RC tool requires them and sometimes not. We decide to
307 // always require them.
308 if (i >= 1) {
309 if (auto CommaError = consumeType(Kind::Comma))
310 return FailureHandler(std::move(CommaError));
313 if (auto IntResult = readInt())
314 Result.push_back(*IntResult);
315 else
316 return FailureHandler(IntResult.takeError());
319 return std::move(Result);
322 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
323 ArrayRef<uint32_t> FlagValues) {
324 assert(!FlagDesc.empty());
325 assert(FlagDesc.size() == FlagValues.size());
327 uint32_t Result = 0;
328 while (isNextTokenKind(Kind::Comma)) {
329 consume();
330 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
331 bool FoundFlag = false;
333 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
334 if (!FlagResult->equals_insensitive(FlagDesc[FlagId]))
335 continue;
337 Result |= FlagValues[FlagId];
338 FoundFlag = true;
339 break;
342 if (!FoundFlag)
343 return getExpectedError(join(FlagDesc, "/"), true);
346 return Result;
349 uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
350 while (!isEof()) {
351 const RCToken &Token = look();
352 if (Token.kind() != Kind::Identifier)
353 return Flags;
354 const StringRef Ident = Token.value();
355 if (Ident.equals_insensitive("PRELOAD"))
356 Flags |= MfPreload;
357 else if (Ident.equals_insensitive("LOADONCALL"))
358 Flags &= ~MfPreload;
359 else if (Ident.equals_insensitive("FIXED"))
360 Flags &= ~(MfMoveable | MfDiscardable);
361 else if (Ident.equals_insensitive("MOVEABLE"))
362 Flags |= MfMoveable;
363 else if (Ident.equals_insensitive("DISCARDABLE"))
364 Flags |= MfDiscardable | MfMoveable | MfPure;
365 else if (Ident.equals_insensitive("PURE"))
366 Flags |= MfPure;
367 else if (Ident.equals_insensitive("IMPURE"))
368 Flags &= ~(MfPure | MfDiscardable);
369 else if (Ident.equals_insensitive("SHARED"))
370 Flags |= MfPure;
371 else if (Ident.equals_insensitive("NONSHARED"))
372 Flags &= ~(MfPure | MfDiscardable);
373 else
374 return Flags;
375 consume();
377 return Flags;
380 Expected<OptionalStmtList>
381 RCParser::parseOptionalStatements(OptStmtType StmtsType) {
382 OptionalStmtList Result;
384 // The last statement is always followed by the start of the block.
385 while (!isNextTokenKind(Kind::BlockBegin)) {
386 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
387 Result.addStmt(std::move(*SingleParse));
390 return std::move(Result);
393 Expected<std::unique_ptr<OptionalStmt>>
394 RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
395 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
396 if (TypeToken->equals_insensitive("CHARACTERISTICS"))
397 return parseCharacteristicsStmt();
398 if (TypeToken->equals_insensitive("LANGUAGE"))
399 return parseLanguageStmt();
400 if (TypeToken->equals_insensitive("VERSION"))
401 return parseVersionStmt();
403 if (StmtsType != OptStmtType::BasicStmt) {
404 if (TypeToken->equals_insensitive("CAPTION"))
405 return parseCaptionStmt();
406 if (TypeToken->equals_insensitive("CLASS"))
407 return parseClassStmt();
408 if (TypeToken->equals_insensitive("EXSTYLE"))
409 return parseExStyleStmt();
410 if (TypeToken->equals_insensitive("FONT"))
411 return parseFontStmt(StmtsType);
412 if (TypeToken->equals_insensitive("STYLE"))
413 return parseStyleStmt();
416 return getExpectedError("optional statement type, BEGIN or '{'",
417 /* IsAlreadyRead = */ true);
420 RCParser::ParseType RCParser::parseLanguageResource() {
421 // Read LANGUAGE as an optional statement. If it's read correctly, we can
422 // upcast it to RCResource.
423 return parseLanguageStmt();
426 RCParser::ParseType RCParser::parseAcceleratorsResource() {
427 uint16_t MemoryFlags =
428 parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
429 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
430 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
432 auto Accels = std::make_unique<AcceleratorsResource>(
433 std::move(*OptStatements), MemoryFlags);
435 while (!consumeOptionalType(Kind::BlockEnd)) {
436 ASSIGN_OR_RETURN(EventResult, readIntOrString());
437 RETURN_IF_ERROR(consumeType(Kind::Comma));
438 ASSIGN_OR_RETURN(IDResult, readInt());
439 ASSIGN_OR_RETURN(
440 FlagsResult,
441 parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
442 AcceleratorsResource::Accelerator::OptionsFlags));
443 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
446 return std::move(Accels);
449 RCParser::ParseType RCParser::parseCursorResource() {
450 uint16_t MemoryFlags =
451 parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
452 ASSIGN_OR_RETURN(Arg, readFilename());
453 return std::make_unique<CursorResource>(*Arg, MemoryFlags);
456 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
457 uint16_t MemoryFlags =
458 parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
459 // Dialog resources have the following format of the arguments:
460 // DIALOG: x, y, width, height [opt stmts...] {controls...}
461 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
462 // These are very similar, so we parse them together.
463 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
465 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
466 if (IsExtended && consumeOptionalType(Kind::Comma)) {
467 ASSIGN_OR_RETURN(HelpIDResult, readInt());
468 HelpID = *HelpIDResult;
471 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
472 IsExtended ? OptStmtType::DialogExStmt
473 : OptStmtType::DialogStmt));
475 assert(isNextTokenKind(Kind::BlockBegin) &&
476 "parseOptionalStatements, when successful, halts on BlockBegin.");
477 consume();
479 auto Dialog = std::make_unique<DialogResource>(
480 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
481 HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
483 while (!consumeOptionalType(Kind::BlockEnd)) {
484 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
485 Dialog->addControl(std::move(*ControlDefResult));
488 return std::move(Dialog);
491 RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
492 uint16_t MemoryFlags =
493 parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
494 if (isEof())
495 return getExpectedError("filename, '{' or BEGIN");
497 // Check if this is a file resource.
498 switch (look().kind()) {
499 case Kind::String:
500 case Kind::Identifier:
501 return std::make_unique<UserDefinedResource>(Type, read().value(),
502 MemoryFlags);
503 default:
504 break;
507 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
508 std::vector<IntOrString> Data;
510 while (!consumeOptionalType(Kind::BlockEnd)) {
511 ASSIGN_OR_RETURN(Item, readIntOrString());
512 Data.push_back(*Item);
514 // There can be zero or more commas after each token (but not before
515 // the first one).
516 while (consumeOptionalType(Kind::Comma)) {
520 return std::make_unique<UserDefinedResource>(Type, std::move(Data),
521 MemoryFlags);
524 RCParser::ParseType RCParser::parseVersionInfoResource() {
525 uint16_t MemoryFlags =
526 parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
527 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
528 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
529 return std::make_unique<VersionInfoResource>(
530 std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
533 Expected<Control> RCParser::parseControl() {
534 // Each control definition (except CONTROL) follows one of the schemes below
535 // depending on the control class:
536 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
537 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
538 // Note that control ids must be integers.
539 // Text might be either a string or an integer pointing to resource ID.
540 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
541 std::string ClassUpper = ClassResult->upper();
542 auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
543 if (CtlInfo == Control::SupportedCtls.end())
544 return getExpectedError("control type, END or '}'", true);
546 // Read caption if necessary.
547 IntOrString Caption{StringRef()};
548 if (CtlInfo->getValue().HasTitle) {
549 ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
550 RETURN_IF_ERROR(consumeType(Kind::Comma));
551 Caption = *CaptionResult;
554 ASSIGN_OR_RETURN(ID, readInt());
555 RETURN_IF_ERROR(consumeType(Kind::Comma));
557 IntOrString Class;
558 std::optional<IntWithNotMask> Style;
559 if (ClassUpper == "CONTROL") {
560 // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
561 ASSIGN_OR_RETURN(ClassStr, readString());
562 RETURN_IF_ERROR(consumeType(Kind::Comma));
563 Class = *ClassStr;
564 ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());
565 RETURN_IF_ERROR(consumeType(Kind::Comma));
566 Style = *StyleVal;
567 } else {
568 Class = CtlInfo->getValue().CtlClass;
571 // x, y, width, height
572 ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
574 if (ClassUpper != "CONTROL") {
575 if (consumeOptionalType(Kind::Comma)) {
576 ASSIGN_OR_RETURN(Val, parseIntExpr1());
577 Style = *Val;
581 std::optional<uint32_t> ExStyle;
582 if (consumeOptionalType(Kind::Comma)) {
583 ASSIGN_OR_RETURN(Val, readInt());
584 ExStyle = *Val;
586 std::optional<uint32_t> HelpID;
587 if (consumeOptionalType(Kind::Comma)) {
588 ASSIGN_OR_RETURN(Val, readInt());
589 HelpID = *Val;
592 return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
593 (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
596 RCParser::ParseType RCParser::parseBitmapResource() {
597 uint16_t MemoryFlags =
598 parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
599 ASSIGN_OR_RETURN(Arg, readFilename());
600 return std::make_unique<BitmapResource>(*Arg, MemoryFlags);
603 RCParser::ParseType RCParser::parseIconResource() {
604 uint16_t MemoryFlags =
605 parseMemoryFlags(IconResource::getDefaultMemoryFlags());
606 ASSIGN_OR_RETURN(Arg, readFilename());
607 return std::make_unique<IconResource>(*Arg, MemoryFlags);
610 RCParser::ParseType RCParser::parseHTMLResource() {
611 uint16_t MemoryFlags =
612 parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
613 ASSIGN_OR_RETURN(Arg, readFilename());
614 return std::make_unique<HTMLResource>(*Arg, MemoryFlags);
617 RCParser::ParseType RCParser::parseMenuResource() {
618 uint16_t MemoryFlags =
619 parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
620 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
621 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
622 return std::make_unique<MenuResource>(std::move(*OptStatements),
623 std::move(*Items), MemoryFlags);
626 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
627 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
629 MenuDefinitionList List;
631 // Read a set of items. Each item is of one of three kinds:
632 // MENUITEM SEPARATOR
633 // MENUITEM caption:String, result:Int [, menu flags]...
634 // POPUP caption:String [, menu flags]... { items... }
635 while (!consumeOptionalType(Kind::BlockEnd)) {
636 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
638 bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM");
639 bool IsPopup = ItemTypeResult->equals_insensitive("POPUP");
640 if (!IsMenuItem && !IsPopup)
641 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
643 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
644 // Now, expecting SEPARATOR.
645 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
646 if (SeparatorResult->equals_insensitive("SEPARATOR")) {
647 List.addDefinition(std::make_unique<MenuSeparator>());
648 continue;
651 return getExpectedError("SEPARATOR or string", true);
654 // Not a separator. Read the caption.
655 ASSIGN_OR_RETURN(CaptionResult, readString());
657 // If MENUITEM, expect also a comma and an integer.
658 uint32_t MenuResult = -1;
660 if (IsMenuItem) {
661 RETURN_IF_ERROR(consumeType(Kind::Comma));
662 ASSIGN_OR_RETURN(IntResult, readInt());
663 MenuResult = *IntResult;
666 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
667 MenuDefinition::OptionsFlags));
669 if (IsPopup) {
670 // If POPUP, read submenu items recursively.
671 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
672 List.addDefinition(std::make_unique<PopupItem>(
673 *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
674 continue;
677 assert(IsMenuItem);
678 List.addDefinition(
679 std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
682 return std::move(List);
685 RCParser::ParseType RCParser::parseStringTableResource() {
686 uint16_t MemoryFlags =
687 parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
688 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
689 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
691 auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),
692 MemoryFlags);
694 // Read strings until we reach the end of the block.
695 while (!consumeOptionalType(Kind::BlockEnd)) {
696 // Each definition consists of string's ID (an integer) and a string.
697 // Some examples in documentation suggest that there might be a comma in
698 // between, however we strictly adhere to the single statement definition.
699 ASSIGN_OR_RETURN(IDResult, readInt());
700 consumeOptionalType(Kind::Comma);
702 std::vector<StringRef> Strings;
703 ASSIGN_OR_RETURN(StrResult, readString());
704 Strings.push_back(*StrResult);
705 while (isNextTokenKind(Kind::String))
706 Strings.push_back(read().value());
708 Table->addStrings(*IDResult, std::move(Strings));
711 return std::move(Table);
714 Expected<std::unique_ptr<VersionInfoBlock>>
715 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
716 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
718 auto Contents = std::make_unique<VersionInfoBlock>(BlockName);
720 while (!isNextTokenKind(Kind::BlockEnd)) {
721 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
722 Contents->addStmt(std::move(*Stmt));
725 consume(); // Consume BlockEnd.
727 return std::move(Contents);
730 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
731 // Expect either BLOCK or VALUE, then a name or a key (a string).
732 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
734 if (TypeResult->equals_insensitive("BLOCK")) {
735 ASSIGN_OR_RETURN(NameResult, readString());
736 return parseVersionInfoBlockContents(*NameResult);
739 if (TypeResult->equals_insensitive("VALUE")) {
740 ASSIGN_OR_RETURN(KeyResult, readString());
741 // Read a non-empty list of strings and/or ints, each
742 // possibly preceded by a comma. Unfortunately, the tool behavior depends
743 // on them existing or not, so we need to memorize where we found them.
744 std::vector<IntOrString> Values;
745 BitVector PrecedingCommas;
746 RETURN_IF_ERROR(consumeType(Kind::Comma));
747 while (!isNextTokenKind(Kind::Identifier) &&
748 !isNextTokenKind(Kind::BlockEnd)) {
749 // Try to eat a comma if it's not the first statement.
750 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
751 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
752 Values.push_back(*ValueResult);
753 PrecedingCommas.push_back(HadComma);
755 return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
756 std::move(PrecedingCommas));
759 return getExpectedError("BLOCK or VALUE", true);
762 Expected<VersionInfoResource::VersionInfoFixed>
763 RCParser::parseVersionInfoFixed() {
764 using RetType = VersionInfoResource::VersionInfoFixed;
765 RetType Result;
767 // Read until the beginning of the block.
768 while (!isNextTokenKind(Kind::BlockBegin)) {
769 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
770 auto FixedType = RetType::getFixedType(*TypeResult);
772 if (!RetType::isTypeSupported(FixedType))
773 return getExpectedError("fixed VERSIONINFO statement type", true);
774 if (Result.IsTypePresent[FixedType])
775 return getExpectedError("yet unread fixed VERSIONINFO statement type",
776 true);
778 // VERSION variations take multiple integers.
779 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
780 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(1, NumInts));
781 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
782 while (ArgInts.size() < NumInts)
783 ArgInts.push_back(0);
784 Result.setValue(FixedType, ArgInts);
787 return Result;
790 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
791 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
792 return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
795 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
796 ASSIGN_OR_RETURN(Arg, readInt());
797 return std::make_unique<CharacteristicsStmt>(*Arg);
800 RCParser::ParseOptionType RCParser::parseVersionStmt() {
801 ASSIGN_OR_RETURN(Arg, readInt());
802 return std::make_unique<VersionStmt>(*Arg);
805 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
806 ASSIGN_OR_RETURN(Arg, readString());
807 return std::make_unique<CaptionStmt>(*Arg);
810 RCParser::ParseOptionType RCParser::parseClassStmt() {
811 ASSIGN_OR_RETURN(Arg, readIntOrString());
812 return std::make_unique<ClassStmt>(*Arg);
815 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
816 assert(DialogType != OptStmtType::BasicStmt);
818 ASSIGN_OR_RETURN(SizeResult, readInt());
819 RETURN_IF_ERROR(consumeType(Kind::Comma));
820 ASSIGN_OR_RETURN(NameResult, readString());
822 // Default values for the optional arguments.
823 uint32_t FontWeight = 0;
824 bool FontItalic = false;
825 uint32_t FontCharset = 1;
826 if (DialogType == OptStmtType::DialogExStmt) {
827 if (consumeOptionalType(Kind::Comma)) {
828 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
829 if (Args->size() >= 1)
830 FontWeight = (*Args)[0];
831 if (Args->size() >= 2)
832 FontItalic = (*Args)[1] != 0;
833 if (Args->size() >= 3)
834 FontCharset = (*Args)[2];
837 return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
838 FontItalic, FontCharset);
841 RCParser::ParseOptionType RCParser::parseStyleStmt() {
842 ASSIGN_OR_RETURN(Arg, readInt());
843 return std::make_unique<StyleStmt>(*Arg);
846 RCParser::ParseOptionType RCParser::parseExStyleStmt() {
847 ASSIGN_OR_RETURN(Arg, readInt());
848 return std::make_unique<ExStyleStmt>(*Arg);
851 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
852 return make_error<ParserError>(
853 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
856 } // namespace rc
857 } // namespace llvm