[sanitizer] Improve FreeBSD ASLR detection
[llvm-project.git] / llvm / tools / llvm-rc / ResourceScriptParser.cpp
blob7cb4d02e3c58edcb0a81c5deda1f0dc498bcd1e3
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/Option/ArgList.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/Process.h"
19 // Take an expression returning llvm::Error and forward the error if it exists.
20 #define RETURN_IF_ERROR(Expr) \
21 if (auto Err = (Expr)) \
22 return std::move(Err);
24 // Take an expression returning llvm::Expected<T> and assign it to Var or
25 // forward the error out of the function.
26 #define ASSIGN_OR_RETURN(Var, Expr) \
27 auto Var = (Expr); \
28 if (!Var) \
29 return Var.takeError();
31 namespace llvm {
32 namespace rc {
34 RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
35 const LocIter End)
36 : ErrorLoc(CurLoc), FileEnd(End) {
37 CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
38 (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
41 char RCParser::ParserError::ID = 0;
43 RCParser::RCParser(std::vector<RCToken> TokenList)
44 : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
46 bool RCParser::isEof() const { return CurLoc == End; }
48 RCParser::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());
54 if (NameToken->equalsLower("LANGUAGE"))
55 return parseLanguageResource();
56 else if (NameToken->equalsLower("STRINGTABLE"))
57 return parseStringTableResource();
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());
63 ParseType Result = std::unique_ptr<RCResource>();
64 (void)!Result;
66 if (TypeToken->equalsLower("ACCELERATORS"))
67 Result = parseAcceleratorsResource();
68 else if (TypeToken->equalsLower("BITMAP"))
69 Result = parseBitmapResource();
70 else if (TypeToken->equalsLower("CURSOR"))
71 Result = parseCursorResource();
72 else if (TypeToken->equalsLower("DIALOG"))
73 Result = parseDialogResource(false);
74 else if (TypeToken->equalsLower("DIALOGEX"))
75 Result = parseDialogResource(true);
76 else if (TypeToken->equalsLower("HTML"))
77 Result = parseHTMLResource();
78 else if (TypeToken->equalsLower("ICON"))
79 Result = parseIconResource();
80 else if (TypeToken->equalsLower("MENU"))
81 Result = parseMenuResource();
82 else if (TypeToken->equalsLower("RCDATA"))
83 Result = parseUserDefinedResource(RkRcData);
84 else if (TypeToken->equalsLower("VERSIONINFO"))
85 Result = parseVersionInfoResource();
86 else
87 Result = parseUserDefinedResource(*TypeToken);
89 if (Result)
90 (*Result)->setName(*NameToken);
92 return Result;
95 bool RCParser::isNextTokenKind(Kind TokenKind) const {
96 return !isEof() && look().kind() == TokenKind;
99 const RCToken &RCParser::look() const {
100 assert(!isEof());
101 return *CurLoc;
104 const RCToken &RCParser::read() {
105 assert(!isEof());
106 return *CurLoc++;
109 void RCParser::consume() {
110 assert(!isEof());
111 CurLoc++;
114 // An integer description might consist of a single integer or
115 // an arithmetic expression evaluating to the integer. The expressions
116 // can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning
117 // is the same as in C++ except for 'not' expression.
118 // The operators in the original RC implementation have the following
119 // precedence:
120 // 1) Unary operators (- ~ not),
121 // 2) Binary operators (+ - & |), with no precedence.
123 // 'not' expression is mostly useful for style values. It evaluates to 0,
124 // but value given to the operator is stored separately from integer value.
125 // It's mostly useful for control style expressions and causes bits from
126 // default control style to be excluded from generated style. For binary
127 // operators the mask from the right operand is applied to the left operand
128 // and masks from both operands are combined in operator result.
130 // The following grammar is used to parse the expressions Exp1:
131 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
132 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
133 // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
134 // separated by binary operators.)
136 // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
137 // is read by parseIntExpr2().
139 // The original Microsoft tool handles multiple unary operators incorrectly.
140 // For example, in 16-bit little-endian integers:
141 // 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
142 // 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
143 // Our implementation differs from the original one and handles these
144 // operators correctly:
145 // 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
146 // 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
148 Expected<RCInt> RCParser::readInt() {
149 ASSIGN_OR_RETURN(Value, parseIntExpr1());
150 return (*Value).getValue();
153 Expected<IntWithNotMask> RCParser::parseIntExpr1() {
154 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
155 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
156 IntWithNotMask Result = *FirstResult;
158 while (!isEof() && look().isBinaryOp()) {
159 auto OpToken = read();
160 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
162 switch (OpToken.kind()) {
163 case Kind::Plus:
164 Result += *NextResult;
165 break;
167 case Kind::Minus:
168 Result -= *NextResult;
169 break;
171 case Kind::Pipe:
172 Result |= *NextResult;
173 break;
175 case Kind::Amp:
176 Result &= *NextResult;
177 break;
179 default:
180 llvm_unreachable("Already processed all binary ops.");
184 return Result;
187 Expected<IntWithNotMask> RCParser::parseIntExpr2() {
188 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
189 static const char ErrorMsg[] = "'-', '~', integer or '('";
191 if (isEof())
192 return getExpectedError(ErrorMsg);
194 switch (look().kind()) {
195 case Kind::Minus: {
196 consume();
197 ASSIGN_OR_RETURN(Result, parseIntExpr2());
198 return -(*Result);
201 case Kind::Tilde: {
202 consume();
203 ASSIGN_OR_RETURN(Result, parseIntExpr2());
204 return ~(*Result);
207 case Kind::Int:
208 return RCInt(read());
210 case Kind::LeftParen: {
211 consume();
212 ASSIGN_OR_RETURN(Result, parseIntExpr1());
213 RETURN_IF_ERROR(consumeType(Kind::RightParen));
214 return *Result;
217 case Kind::Identifier: {
218 if (!read().value().equals_insensitive("not"))
219 return getExpectedError(ErrorMsg, true);
220 ASSIGN_OR_RETURN(Result, parseIntExpr2());
221 return IntWithNotMask(0, (*Result).getValue());
224 default:
225 return getExpectedError(ErrorMsg);
229 Expected<StringRef> RCParser::readString() {
230 if (!isNextTokenKind(Kind::String))
231 return getExpectedError("string");
232 return read().value();
235 Expected<StringRef> RCParser::readFilename() {
236 if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
237 return getExpectedError("string");
238 return read().value();
241 Expected<StringRef> RCParser::readIdentifier() {
242 if (!isNextTokenKind(Kind::Identifier))
243 return getExpectedError("identifier");
244 return read().value();
247 Expected<IntOrString> RCParser::readIntOrString() {
248 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
249 return getExpectedError("int or string");
250 return IntOrString(read());
253 Expected<IntOrString> RCParser::readTypeOrName() {
254 // We suggest that the correct resource name or type should be either an
255 // identifier or an integer. The original RC tool is much more liberal.
256 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
257 return getExpectedError("int or identifier");
258 return IntOrString(read());
261 Error RCParser::consumeType(Kind TokenKind) {
262 if (isNextTokenKind(TokenKind)) {
263 consume();
264 return Error::success();
267 switch (TokenKind) {
268 #define TOKEN(TokenName) \
269 case Kind::TokenName: \
270 return getExpectedError(#TokenName);
271 #define SHORT_TOKEN(TokenName, TokenCh) \
272 case Kind::TokenName: \
273 return getExpectedError(#TokenCh);
274 #include "ResourceScriptTokenList.def"
277 llvm_unreachable("All case options exhausted.");
280 bool RCParser::consumeOptionalType(Kind TokenKind) {
281 if (isNextTokenKind(TokenKind)) {
282 consume();
283 return true;
286 return false;
289 Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
290 size_t MaxCount) {
291 assert(MinCount <= MaxCount);
293 SmallVector<RCInt, 8> Result;
295 auto FailureHandler =
296 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
297 if (Result.size() < MinCount)
298 return std::move(Err);
299 consumeError(std::move(Err));
300 return Result;
303 for (size_t i = 0; i < MaxCount; ++i) {
304 // Try to read a comma unless we read the first token.
305 // Sometimes RC tool requires them and sometimes not. We decide to
306 // always require them.
307 if (i >= 1) {
308 if (auto CommaError = consumeType(Kind::Comma))
309 return FailureHandler(std::move(CommaError));
312 if (auto IntResult = readInt())
313 Result.push_back(*IntResult);
314 else
315 return FailureHandler(IntResult.takeError());
318 return std::move(Result);
321 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
322 ArrayRef<uint32_t> FlagValues) {
323 assert(!FlagDesc.empty());
324 assert(FlagDesc.size() == FlagValues.size());
326 uint32_t Result = 0;
327 while (isNextTokenKind(Kind::Comma)) {
328 consume();
329 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
330 bool FoundFlag = false;
332 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
333 if (!FlagResult->equals_insensitive(FlagDesc[FlagId]))
334 continue;
336 Result |= FlagValues[FlagId];
337 FoundFlag = true;
338 break;
341 if (!FoundFlag)
342 return getExpectedError(join(FlagDesc, "/"), true);
345 return Result;
348 uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
349 while (!isEof()) {
350 const RCToken &Token = look();
351 if (Token.kind() != Kind::Identifier)
352 return Flags;
353 const StringRef Ident = Token.value();
354 if (Ident.equals_insensitive("PRELOAD"))
355 Flags |= MfPreload;
356 else if (Ident.equals_insensitive("LOADONCALL"))
357 Flags &= ~MfPreload;
358 else if (Ident.equals_insensitive("FIXED"))
359 Flags &= ~(MfMoveable | MfDiscardable);
360 else if (Ident.equals_insensitive("MOVEABLE"))
361 Flags |= MfMoveable;
362 else if (Ident.equals_insensitive("DISCARDABLE"))
363 Flags |= MfDiscardable | MfMoveable | MfPure;
364 else if (Ident.equals_insensitive("PURE"))
365 Flags |= MfPure;
366 else if (Ident.equals_insensitive("IMPURE"))
367 Flags &= ~(MfPure | MfDiscardable);
368 else if (Ident.equals_insensitive("SHARED"))
369 Flags |= MfPure;
370 else if (Ident.equals_insensitive("NONSHARED"))
371 Flags &= ~(MfPure | MfDiscardable);
372 else
373 return Flags;
374 consume();
376 return Flags;
379 Expected<OptionalStmtList>
380 RCParser::parseOptionalStatements(OptStmtType StmtsType) {
381 OptionalStmtList Result;
383 // The last statement is always followed by the start of the block.
384 while (!isNextTokenKind(Kind::BlockBegin)) {
385 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
386 Result.addStmt(std::move(*SingleParse));
389 return std::move(Result);
392 Expected<std::unique_ptr<OptionalStmt>>
393 RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
394 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
395 if (TypeToken->equals_insensitive("CHARACTERISTICS"))
396 return parseCharacteristicsStmt();
397 if (TypeToken->equals_insensitive("LANGUAGE"))
398 return parseLanguageStmt();
399 if (TypeToken->equals_insensitive("VERSION"))
400 return parseVersionStmt();
402 if (StmtsType != OptStmtType::BasicStmt) {
403 if (TypeToken->equals_insensitive("CAPTION"))
404 return parseCaptionStmt();
405 if (TypeToken->equals_insensitive("CLASS"))
406 return parseClassStmt();
407 if (TypeToken->equals_insensitive("EXSTYLE"))
408 return parseExStyleStmt();
409 if (TypeToken->equals_insensitive("FONT"))
410 return parseFontStmt(StmtsType);
411 if (TypeToken->equals_insensitive("STYLE"))
412 return parseStyleStmt();
415 return getExpectedError("optional statement type, BEGIN or '{'",
416 /* IsAlreadyRead = */ true);
419 RCParser::ParseType RCParser::parseLanguageResource() {
420 // Read LANGUAGE as an optional statement. If it's read correctly, we can
421 // upcast it to RCResource.
422 return parseLanguageStmt();
425 RCParser::ParseType RCParser::parseAcceleratorsResource() {
426 uint16_t MemoryFlags =
427 parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
428 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
429 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
431 auto Accels = std::make_unique<AcceleratorsResource>(
432 std::move(*OptStatements), MemoryFlags);
434 while (!consumeOptionalType(Kind::BlockEnd)) {
435 ASSIGN_OR_RETURN(EventResult, readIntOrString());
436 RETURN_IF_ERROR(consumeType(Kind::Comma));
437 ASSIGN_OR_RETURN(IDResult, readInt());
438 ASSIGN_OR_RETURN(
439 FlagsResult,
440 parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
441 AcceleratorsResource::Accelerator::OptionsFlags));
442 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
445 return std::move(Accels);
448 RCParser::ParseType RCParser::parseCursorResource() {
449 uint16_t MemoryFlags =
450 parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
451 ASSIGN_OR_RETURN(Arg, readFilename());
452 return std::make_unique<CursorResource>(*Arg, MemoryFlags);
455 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
456 uint16_t MemoryFlags =
457 parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
458 // Dialog resources have the following format of the arguments:
459 // DIALOG: x, y, width, height [opt stmts...] {controls...}
460 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
461 // These are very similar, so we parse them together.
462 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
464 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
465 if (IsExtended && consumeOptionalType(Kind::Comma)) {
466 ASSIGN_OR_RETURN(HelpIDResult, readInt());
467 HelpID = *HelpIDResult;
470 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
471 IsExtended ? OptStmtType::DialogExStmt
472 : OptStmtType::DialogStmt));
474 assert(isNextTokenKind(Kind::BlockBegin) &&
475 "parseOptionalStatements, when successful, halts on BlockBegin.");
476 consume();
478 auto Dialog = std::make_unique<DialogResource>(
479 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
480 HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
482 while (!consumeOptionalType(Kind::BlockEnd)) {
483 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
484 Dialog->addControl(std::move(*ControlDefResult));
487 return std::move(Dialog);
490 RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
491 uint16_t MemoryFlags =
492 parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
493 if (isEof())
494 return getExpectedError("filename, '{' or BEGIN");
496 // Check if this is a file resource.
497 switch (look().kind()) {
498 case Kind::String:
499 case Kind::Identifier:
500 return std::make_unique<UserDefinedResource>(Type, read().value(),
501 MemoryFlags);
502 default:
503 break;
506 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
507 std::vector<IntOrString> Data;
509 while (!consumeOptionalType(Kind::BlockEnd)) {
510 ASSIGN_OR_RETURN(Item, readIntOrString());
511 Data.push_back(*Item);
513 // There can be zero or more commas after each token (but not before
514 // the first one).
515 while (consumeOptionalType(Kind::Comma)) {
519 return std::make_unique<UserDefinedResource>(Type, std::move(Data),
520 MemoryFlags);
523 RCParser::ParseType RCParser::parseVersionInfoResource() {
524 uint16_t MemoryFlags =
525 parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
526 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
527 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
528 return std::make_unique<VersionInfoResource>(
529 std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
532 Expected<Control> RCParser::parseControl() {
533 // Each control definition (except CONTROL) follows one of the schemes below
534 // depending on the control class:
535 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
536 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
537 // Note that control ids must be integers.
538 // Text might be either a string or an integer pointing to resource ID.
539 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
540 std::string ClassUpper = ClassResult->upper();
541 auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
542 if (CtlInfo == Control::SupportedCtls.end())
543 return getExpectedError("control type, END or '}'", true);
545 // Read caption if necessary.
546 IntOrString Caption{StringRef()};
547 if (CtlInfo->getValue().HasTitle) {
548 ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
549 RETURN_IF_ERROR(consumeType(Kind::Comma));
550 Caption = *CaptionResult;
553 ASSIGN_OR_RETURN(ID, readInt());
554 RETURN_IF_ERROR(consumeType(Kind::Comma));
556 IntOrString Class;
557 Optional<IntWithNotMask> Style;
558 if (ClassUpper == "CONTROL") {
559 // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
560 ASSIGN_OR_RETURN(ClassStr, readString());
561 RETURN_IF_ERROR(consumeType(Kind::Comma));
562 Class = *ClassStr;
563 ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());
564 RETURN_IF_ERROR(consumeType(Kind::Comma));
565 Style = *StyleVal;
566 } else {
567 Class = CtlInfo->getValue().CtlClass;
570 // x, y, width, height
571 ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
573 if (ClassUpper != "CONTROL") {
574 if (consumeOptionalType(Kind::Comma)) {
575 ASSIGN_OR_RETURN(Val, parseIntExpr1());
576 Style = *Val;
580 Optional<uint32_t> ExStyle;
581 if (consumeOptionalType(Kind::Comma)) {
582 ASSIGN_OR_RETURN(Val, readInt());
583 ExStyle = *Val;
585 Optional<uint32_t> HelpID;
586 if (consumeOptionalType(Kind::Comma)) {
587 ASSIGN_OR_RETURN(Val, readInt());
588 HelpID = *Val;
591 return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
592 (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
595 RCParser::ParseType RCParser::parseBitmapResource() {
596 uint16_t MemoryFlags =
597 parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
598 ASSIGN_OR_RETURN(Arg, readFilename());
599 return std::make_unique<BitmapResource>(*Arg, MemoryFlags);
602 RCParser::ParseType RCParser::parseIconResource() {
603 uint16_t MemoryFlags =
604 parseMemoryFlags(IconResource::getDefaultMemoryFlags());
605 ASSIGN_OR_RETURN(Arg, readFilename());
606 return std::make_unique<IconResource>(*Arg, MemoryFlags);
609 RCParser::ParseType RCParser::parseHTMLResource() {
610 uint16_t MemoryFlags =
611 parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
612 ASSIGN_OR_RETURN(Arg, readFilename());
613 return std::make_unique<HTMLResource>(*Arg, MemoryFlags);
616 RCParser::ParseType RCParser::parseMenuResource() {
617 uint16_t MemoryFlags =
618 parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
619 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
620 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
621 return std::make_unique<MenuResource>(std::move(*OptStatements),
622 std::move(*Items), MemoryFlags);
625 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
626 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
628 MenuDefinitionList List;
630 // Read a set of items. Each item is of one of three kinds:
631 // MENUITEM SEPARATOR
632 // MENUITEM caption:String, result:Int [, menu flags]...
633 // POPUP caption:String [, menu flags]... { items... }
634 while (!consumeOptionalType(Kind::BlockEnd)) {
635 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
637 bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM");
638 bool IsPopup = ItemTypeResult->equals_insensitive("POPUP");
639 if (!IsMenuItem && !IsPopup)
640 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
642 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
643 // Now, expecting SEPARATOR.
644 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
645 if (SeparatorResult->equals_insensitive("SEPARATOR")) {
646 List.addDefinition(std::make_unique<MenuSeparator>());
647 continue;
650 return getExpectedError("SEPARATOR or string", true);
653 // Not a separator. Read the caption.
654 ASSIGN_OR_RETURN(CaptionResult, readString());
656 // If MENUITEM, expect also a comma and an integer.
657 uint32_t MenuResult = -1;
659 if (IsMenuItem) {
660 RETURN_IF_ERROR(consumeType(Kind::Comma));
661 ASSIGN_OR_RETURN(IntResult, readInt());
662 MenuResult = *IntResult;
665 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
666 MenuDefinition::OptionsFlags));
668 if (IsPopup) {
669 // If POPUP, read submenu items recursively.
670 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
671 List.addDefinition(std::make_unique<PopupItem>(
672 *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
673 continue;
676 assert(IsMenuItem);
677 List.addDefinition(
678 std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
681 return std::move(List);
684 RCParser::ParseType RCParser::parseStringTableResource() {
685 uint16_t MemoryFlags =
686 parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
687 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
688 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
690 auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),
691 MemoryFlags);
693 // Read strings until we reach the end of the block.
694 while (!consumeOptionalType(Kind::BlockEnd)) {
695 // Each definition consists of string's ID (an integer) and a string.
696 // Some examples in documentation suggest that there might be a comma in
697 // between, however we strictly adhere to the single statement definition.
698 ASSIGN_OR_RETURN(IDResult, readInt());
699 consumeOptionalType(Kind::Comma);
701 std::vector<StringRef> Strings;
702 ASSIGN_OR_RETURN(StrResult, readString());
703 Strings.push_back(*StrResult);
704 while (isNextTokenKind(Kind::String))
705 Strings.push_back(read().value());
707 Table->addStrings(*IDResult, std::move(Strings));
710 return std::move(Table);
713 Expected<std::unique_ptr<VersionInfoBlock>>
714 RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
715 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
717 auto Contents = std::make_unique<VersionInfoBlock>(BlockName);
719 while (!isNextTokenKind(Kind::BlockEnd)) {
720 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
721 Contents->addStmt(std::move(*Stmt));
724 consume(); // Consume BlockEnd.
726 return std::move(Contents);
729 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
730 // Expect either BLOCK or VALUE, then a name or a key (a string).
731 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
733 if (TypeResult->equals_insensitive("BLOCK")) {
734 ASSIGN_OR_RETURN(NameResult, readString());
735 return parseVersionInfoBlockContents(*NameResult);
738 if (TypeResult->equals_insensitive("VALUE")) {
739 ASSIGN_OR_RETURN(KeyResult, readString());
740 // Read a non-empty list of strings and/or ints, each
741 // possibly preceded by a comma. Unfortunately, the tool behavior depends
742 // on them existing or not, so we need to memorize where we found them.
743 std::vector<IntOrString> Values;
744 BitVector PrecedingCommas;
745 RETURN_IF_ERROR(consumeType(Kind::Comma));
746 while (!isNextTokenKind(Kind::Identifier) &&
747 !isNextTokenKind(Kind::BlockEnd)) {
748 // Try to eat a comma if it's not the first statement.
749 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
750 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
751 Values.push_back(*ValueResult);
752 PrecedingCommas.push_back(HadComma);
754 return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
755 std::move(PrecedingCommas));
758 return getExpectedError("BLOCK or VALUE", true);
761 Expected<VersionInfoResource::VersionInfoFixed>
762 RCParser::parseVersionInfoFixed() {
763 using RetType = VersionInfoResource::VersionInfoFixed;
764 RetType Result;
766 // Read until the beginning of the block.
767 while (!isNextTokenKind(Kind::BlockBegin)) {
768 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
769 auto FixedType = RetType::getFixedType(*TypeResult);
771 if (!RetType::isTypeSupported(FixedType))
772 return getExpectedError("fixed VERSIONINFO statement type", true);
773 if (Result.IsTypePresent[FixedType])
774 return getExpectedError("yet unread fixed VERSIONINFO statement type",
775 true);
777 // VERSION variations take multiple integers.
778 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
779 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(1, NumInts));
780 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
781 while (ArgInts.size() < NumInts)
782 ArgInts.push_back(0);
783 Result.setValue(FixedType, ArgInts);
786 return Result;
789 RCParser::ParseOptionType RCParser::parseLanguageStmt() {
790 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
791 return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
794 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
795 ASSIGN_OR_RETURN(Arg, readInt());
796 return std::make_unique<CharacteristicsStmt>(*Arg);
799 RCParser::ParseOptionType RCParser::parseVersionStmt() {
800 ASSIGN_OR_RETURN(Arg, readInt());
801 return std::make_unique<VersionStmt>(*Arg);
804 RCParser::ParseOptionType RCParser::parseCaptionStmt() {
805 ASSIGN_OR_RETURN(Arg, readString());
806 return std::make_unique<CaptionStmt>(*Arg);
809 RCParser::ParseOptionType RCParser::parseClassStmt() {
810 ASSIGN_OR_RETURN(Arg, readIntOrString());
811 return std::make_unique<ClassStmt>(*Arg);
814 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
815 assert(DialogType != OptStmtType::BasicStmt);
817 ASSIGN_OR_RETURN(SizeResult, readInt());
818 RETURN_IF_ERROR(consumeType(Kind::Comma));
819 ASSIGN_OR_RETURN(NameResult, readString());
821 // Default values for the optional arguments.
822 uint32_t FontWeight = 0;
823 bool FontItalic = false;
824 uint32_t FontCharset = 1;
825 if (DialogType == OptStmtType::DialogExStmt) {
826 if (consumeOptionalType(Kind::Comma)) {
827 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
828 if (Args->size() >= 1)
829 FontWeight = (*Args)[0];
830 if (Args->size() >= 2)
831 FontItalic = (*Args)[1] != 0;
832 if (Args->size() >= 3)
833 FontCharset = (*Args)[2];
836 return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
837 FontItalic, FontCharset);
840 RCParser::ParseOptionType RCParser::parseStyleStmt() {
841 ASSIGN_OR_RETURN(Arg, readInt());
842 return std::make_unique<StyleStmt>(*Arg);
845 RCParser::ParseOptionType RCParser::parseExStyleStmt() {
846 ASSIGN_OR_RETURN(Arg, readInt());
847 return std::make_unique<ExStyleStmt>(*Arg);
850 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
851 return make_error<ParserError>(
852 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
855 } // namespace rc
856 } // namespace llvm