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. 10 // 11 //===---------------------------------------------------------------------===// 12 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" 19 20 // Take an expression returning llvm::Error and forward the error if it exists. 21 #define RETURN_IF_ERROR(Expr) \ 22 if (auto Err = (Expr)) \ 23 return std::move(Err); 24 25 // Take an expression returning llvm::Expected<T> and assign it to Var or 26 // forward the error out of the function. 27 #define ASSIGN_OR_RETURN(Var, Expr) \ 28 auto Var = (Expr); \ 29 if (!Var) \ 30 return Var.takeError(); 31 32 namespace llvm { 33 namespace rc { 34 35 RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc, 36 const LocIter End) 37 : ErrorLoc(CurLoc), FileEnd(End) { 38 CurMessage = "Error parsing file: expected " + Expected.str() + ", got " + 39 (CurLoc == End ? "<EOF>" : CurLoc->value()).str(); 40 } 41 42 char RCParser::ParserError::ID = 0; 43 44 RCParser::RCParser(std::vector<RCToken> TokenList) 45 : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {} 46 47 bool RCParser::isEof() const { return CurLoc == End; } 48 49 RCParser::ParseType RCParser::parseSingleResource() { 50 // The first thing we read is usually a resource's name. However, in some 51 // cases (LANGUAGE and STRINGTABLE) the resources don't have their names 52 // and the first token to be read is the type. 53 ASSIGN_OR_RETURN(NameToken, readTypeOrName()); 54 55 if (NameToken->equalsLower("LANGUAGE")) 56 return parseLanguageResource(); 57 else if (NameToken->equalsLower("STRINGTABLE")) 58 return parseStringTableResource(); 59 60 // If it's not an unnamed resource, what we've just read is a name. Now, 61 // read resource type; 62 ASSIGN_OR_RETURN(TypeToken, readTypeOrName()); 63 64 ParseType Result = std::unique_ptr<RCResource>(); 65 (void)!Result; 66 67 if (TypeToken->equalsLower("ACCELERATORS")) 68 Result = parseAcceleratorsResource(); 69 else if (TypeToken->equalsLower("BITMAP")) 70 Result = parseBitmapResource(); 71 else if (TypeToken->equalsLower("CURSOR")) 72 Result = parseCursorResource(); 73 else if (TypeToken->equalsLower("DIALOG")) 74 Result = parseDialogResource(false); 75 else if (TypeToken->equalsLower("DIALOGEX")) 76 Result = parseDialogResource(true); 77 else if (TypeToken->equalsLower("HTML")) 78 Result = parseHTMLResource(); 79 else if (TypeToken->equalsLower("ICON")) 80 Result = parseIconResource(); 81 else if (TypeToken->equalsLower("MENU")) 82 Result = parseMenuResource(); 83 else if (TypeToken->equalsLower("MENUEX")) 84 Result = parseMenuExResource(); 85 else if (TypeToken->equalsLower("RCDATA")) 86 Result = parseUserDefinedResource(RkRcData); 87 else if (TypeToken->equalsLower("VERSIONINFO")) 88 Result = parseVersionInfoResource(); 89 else 90 Result = parseUserDefinedResource(*TypeToken); 91 92 if (Result) 93 (*Result)->setName(*NameToken); 94 95 return Result; 96 } 97 98 bool RCParser::isNextTokenKind(Kind TokenKind) const { 99 return !isEof() && look().kind() == TokenKind; 100 } 101 102 const RCToken &RCParser::look() const { 103 assert(!isEof()); 104 return *CurLoc; 105 } 106 107 const RCToken &RCParser::read() { 108 assert(!isEof()); 109 return *CurLoc++; 110 } 111 112 void RCParser::consume() { 113 assert(!isEof()); 114 CurLoc++; 115 } 116 117 // An integer description might consist of a single integer or 118 // an arithmetic expression evaluating to the integer. The expressions 119 // can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning 120 // is the same as in C++ except for 'not' expression. 121 // The operators in the original RC implementation have the following 122 // precedence: 123 // 1) Unary operators (- ~ not), 124 // 2) Binary operators (+ - & |), with no precedence. 125 // 126 // 'not' expression is mostly useful for style values. It evaluates to 0, 127 // but value given to the operator is stored separately from integer value. 128 // It's mostly useful for control style expressions and causes bits from 129 // default control style to be excluded from generated style. For binary 130 // operators the mask from the right operand is applied to the left operand 131 // and masks from both operands are combined in operator result. 132 // 133 // The following grammar is used to parse the expressions Exp1: 134 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2 135 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). 136 // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions, 137 // separated by binary operators.) 138 // 139 // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2 140 // is read by parseIntExpr2(). 141 // 142 // The original Microsoft tool handles multiple unary operators incorrectly. 143 // For example, in 16-bit little-endian integers: 144 // 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00; 145 // 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff. 146 // Our implementation differs from the original one and handles these 147 // operators correctly: 148 // 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff; 149 // 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff. 150 151 Expected<RCInt> RCParser::readInt() { 152 ASSIGN_OR_RETURN(Value, parseIntExpr1()); 153 return (*Value).getValue(); 154 } 155 156 Expected<IntWithNotMask> RCParser::parseIntExpr1() { 157 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2. 158 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2()); 159 IntWithNotMask Result = *FirstResult; 160 161 while (!isEof() && look().isBinaryOp()) { 162 auto OpToken = read(); 163 ASSIGN_OR_RETURN(NextResult, parseIntExpr2()); 164 165 switch (OpToken.kind()) { 166 case Kind::Plus: 167 Result += *NextResult; 168 break; 169 170 case Kind::Minus: 171 Result -= *NextResult; 172 break; 173 174 case Kind::Pipe: 175 Result |= *NextResult; 176 break; 177 178 case Kind::Amp: 179 Result &= *NextResult; 180 break; 181 182 default: 183 llvm_unreachable("Already processed all binary ops."); 184 } 185 } 186 187 return Result; 188 } 189 190 Expected<IntWithNotMask> RCParser::parseIntExpr2() { 191 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). 192 static const char ErrorMsg[] = "'-', '~', integer or '('"; 193 194 if (isEof()) 195 return getExpectedError(ErrorMsg); 196 197 switch (look().kind()) { 198 case Kind::Minus: { 199 consume(); 200 ASSIGN_OR_RETURN(Result, parseIntExpr2()); 201 return -(*Result); 202 } 203 204 case Kind::Tilde: { 205 consume(); 206 ASSIGN_OR_RETURN(Result, parseIntExpr2()); 207 return ~(*Result); 208 } 209 210 case Kind::Int: 211 return RCInt(read()); 212 213 case Kind::LeftParen: { 214 consume(); 215 ASSIGN_OR_RETURN(Result, parseIntExpr1()); 216 RETURN_IF_ERROR(consumeType(Kind::RightParen)); 217 return *Result; 218 } 219 220 case Kind::Identifier: { 221 if (!read().value().equals_insensitive("not")) 222 return getExpectedError(ErrorMsg, true); 223 ASSIGN_OR_RETURN(Result, parseIntExpr2()); 224 return IntWithNotMask(0, (*Result).getValue()); 225 } 226 227 default: 228 return getExpectedError(ErrorMsg); 229 } 230 } 231 232 Expected<StringRef> RCParser::readString() { 233 if (!isNextTokenKind(Kind::String)) 234 return getExpectedError("string"); 235 return read().value(); 236 } 237 238 Expected<StringRef> RCParser::readFilename() { 239 if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier)) 240 return getExpectedError("string"); 241 const RCToken &Token = read(); 242 StringRef Str = Token.value(); 243 if (Token.kind() != Kind::String) 244 return Str; 245 while (isNextTokenKind(Kind::String)) { 246 const RCToken &NextToken = read(); 247 StringRef Next = NextToken.value(); 248 bool IsWide = Str.consume_front_insensitive("L"); 249 Next.consume_front_insensitive("L"); 250 bool StrUnquoted = Str.consume_front("\"") && Str.consume_back("\""); 251 bool NextUnquoted = Next.consume_front("\"") && Next.consume_back("\""); 252 assert(StrUnquoted && NextUnquoted); 253 (void)StrUnquoted; 254 (void)NextUnquoted; 255 256 Str = Saver.save(Twine(IsWide ? "L" : "") + "\"" + Str + Next + "\""); 257 } 258 return Str; 259 } 260 261 Expected<StringRef> RCParser::readIdentifier() { 262 if (!isNextTokenKind(Kind::Identifier)) 263 return getExpectedError("identifier"); 264 return read().value(); 265 } 266 267 Expected<IntOrString> RCParser::readIntOrString() { 268 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String)) 269 return getExpectedError("int or string"); 270 return IntOrString(read()); 271 } 272 273 Expected<IntOrString> RCParser::readTypeOrName() { 274 // We suggest that the correct resource name or type should be either an 275 // identifier or an integer. The original RC tool is much more liberal. 276 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int)) 277 return getExpectedError("int or identifier"); 278 return IntOrString(read()); 279 } 280 281 Error RCParser::consumeType(Kind TokenKind) { 282 if (isNextTokenKind(TokenKind)) { 283 consume(); 284 return Error::success(); 285 } 286 287 switch (TokenKind) { 288 #define TOKEN(TokenName) \ 289 case Kind::TokenName: \ 290 return getExpectedError(#TokenName); 291 #define SHORT_TOKEN(TokenName, TokenCh) \ 292 case Kind::TokenName: \ 293 return getExpectedError(#TokenCh); 294 #include "ResourceScriptTokenList.def" 295 } 296 297 llvm_unreachable("All case options exhausted."); 298 } 299 300 bool RCParser::consumeOptionalType(Kind TokenKind) { 301 if (isNextTokenKind(TokenKind)) { 302 consume(); 303 return true; 304 } 305 306 return false; 307 } 308 309 Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount, 310 size_t MaxCount) { 311 assert(MinCount <= MaxCount); 312 313 SmallVector<RCInt, 8> Result; 314 315 auto FailureHandler = 316 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> { 317 if (Result.size() < MinCount) 318 return std::move(Err); 319 consumeError(std::move(Err)); 320 return Result; 321 }; 322 323 for (size_t i = 0; i < MaxCount; ++i) { 324 // Try to read a comma unless we read the first token. 325 // Sometimes RC tool requires them and sometimes not. We decide to 326 // always require them. 327 if (i >= 1) { 328 if (auto CommaError = consumeType(Kind::Comma)) 329 return FailureHandler(std::move(CommaError)); 330 } 331 332 if (auto IntResult = readInt()) 333 Result.push_back(*IntResult); 334 else 335 return FailureHandler(IntResult.takeError()); 336 } 337 338 return std::move(Result); 339 } 340 341 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc, 342 ArrayRef<uint32_t> FlagValues) { 343 assert(!FlagDesc.empty()); 344 assert(FlagDesc.size() == FlagValues.size()); 345 346 uint32_t Result = 0; 347 while (isNextTokenKind(Kind::Comma)) { 348 consume(); 349 ASSIGN_OR_RETURN(FlagResult, readIdentifier()); 350 bool FoundFlag = false; 351 352 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { 353 if (!FlagResult->equals_insensitive(FlagDesc[FlagId])) 354 continue; 355 356 Result |= FlagValues[FlagId]; 357 FoundFlag = true; 358 break; 359 } 360 361 if (!FoundFlag) 362 return getExpectedError(join(FlagDesc, "/"), true); 363 } 364 365 return Result; 366 } 367 368 uint16_t RCParser::parseMemoryFlags(uint16_t Flags) { 369 while (!isEof()) { 370 const RCToken &Token = look(); 371 if (Token.kind() != Kind::Identifier) 372 return Flags; 373 const StringRef Ident = Token.value(); 374 if (Ident.equals_insensitive("PRELOAD")) 375 Flags |= MfPreload; 376 else if (Ident.equals_insensitive("LOADONCALL")) 377 Flags &= ~MfPreload; 378 else if (Ident.equals_insensitive("FIXED")) 379 Flags &= ~(MfMoveable | MfDiscardable); 380 else if (Ident.equals_insensitive("MOVEABLE")) 381 Flags |= MfMoveable; 382 else if (Ident.equals_insensitive("DISCARDABLE")) 383 Flags |= MfDiscardable | MfMoveable | MfPure; 384 else if (Ident.equals_insensitive("PURE")) 385 Flags |= MfPure; 386 else if (Ident.equals_insensitive("IMPURE")) 387 Flags &= ~(MfPure | MfDiscardable); 388 else if (Ident.equals_insensitive("SHARED")) 389 Flags |= MfPure; 390 else if (Ident.equals_insensitive("NONSHARED")) 391 Flags &= ~(MfPure | MfDiscardable); 392 else 393 return Flags; 394 consume(); 395 } 396 return Flags; 397 } 398 399 Expected<OptionalStmtList> 400 RCParser::parseOptionalStatements(OptStmtType StmtsType) { 401 OptionalStmtList Result; 402 403 // The last statement is always followed by the start of the block. 404 while (!isNextTokenKind(Kind::BlockBegin)) { 405 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType)); 406 Result.addStmt(std::move(*SingleParse)); 407 } 408 409 return std::move(Result); 410 } 411 412 Expected<std::unique_ptr<OptionalStmt>> 413 RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) { 414 ASSIGN_OR_RETURN(TypeToken, readIdentifier()); 415 if (TypeToken->equals_insensitive("CHARACTERISTICS")) 416 return parseCharacteristicsStmt(); 417 if (TypeToken->equals_insensitive("LANGUAGE")) 418 return parseLanguageStmt(); 419 if (TypeToken->equals_insensitive("VERSION")) 420 return parseVersionStmt(); 421 422 if (StmtsType != OptStmtType::BasicStmt) { 423 if (TypeToken->equals_insensitive("CAPTION")) 424 return parseCaptionStmt(); 425 if (TypeToken->equals_insensitive("CLASS")) 426 return parseClassStmt(); 427 if (TypeToken->equals_insensitive("EXSTYLE")) 428 return parseExStyleStmt(); 429 if (TypeToken->equals_insensitive("FONT")) 430 return parseFontStmt(StmtsType); 431 if (TypeToken->equals_insensitive("STYLE")) 432 return parseStyleStmt(); 433 } 434 435 return getExpectedError("optional statement type, BEGIN or '{'", 436 /* IsAlreadyRead = */ true); 437 } 438 439 RCParser::ParseType RCParser::parseLanguageResource() { 440 // Read LANGUAGE as an optional statement. If it's read correctly, we can 441 // upcast it to RCResource. 442 return parseLanguageStmt(); 443 } 444 445 RCParser::ParseType RCParser::parseAcceleratorsResource() { 446 uint16_t MemoryFlags = 447 parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags()); 448 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 449 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 450 451 auto Accels = std::make_unique<AcceleratorsResource>( 452 std::move(*OptStatements), MemoryFlags); 453 454 while (!consumeOptionalType(Kind::BlockEnd)) { 455 ASSIGN_OR_RETURN(EventResult, readIntOrString()); 456 RETURN_IF_ERROR(consumeType(Kind::Comma)); 457 ASSIGN_OR_RETURN(IDResult, readInt()); 458 ASSIGN_OR_RETURN( 459 FlagsResult, 460 parseFlags(AcceleratorsResource::Accelerator::OptionsStr, 461 AcceleratorsResource::Accelerator::OptionsFlags)); 462 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult); 463 } 464 465 return std::move(Accels); 466 } 467 468 RCParser::ParseType RCParser::parseCursorResource() { 469 uint16_t MemoryFlags = 470 parseMemoryFlags(CursorResource::getDefaultMemoryFlags()); 471 ASSIGN_OR_RETURN(Arg, readFilename()); 472 return std::make_unique<CursorResource>(*Arg, MemoryFlags); 473 } 474 475 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { 476 uint16_t MemoryFlags = 477 parseMemoryFlags(DialogResource::getDefaultMemoryFlags()); 478 // Dialog resources have the following format of the arguments: 479 // DIALOG: x, y, width, height [opt stmts...] {controls...} 480 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...} 481 // These are very similar, so we parse them together. 482 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4)); 483 484 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0. 485 if (IsExtended && consumeOptionalType(Kind::Comma)) { 486 ASSIGN_OR_RETURN(HelpIDResult, readInt()); 487 HelpID = *HelpIDResult; 488 } 489 490 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements( 491 IsExtended ? OptStmtType::DialogExStmt 492 : OptStmtType::DialogStmt)); 493 494 assert(isNextTokenKind(Kind::BlockBegin) && 495 "parseOptionalStatements, when successful, halts on BlockBegin."); 496 consume(); 497 498 auto Dialog = std::make_unique<DialogResource>( 499 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], 500 HelpID, std::move(*OptStatements), IsExtended, MemoryFlags); 501 502 while (!consumeOptionalType(Kind::BlockEnd)) { 503 ASSIGN_OR_RETURN(ControlDefResult, parseControl()); 504 Dialog->addControl(std::move(*ControlDefResult)); 505 } 506 507 return std::move(Dialog); 508 } 509 510 RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) { 511 uint16_t MemoryFlags = 512 parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags()); 513 if (isEof()) 514 return getExpectedError("filename, '{' or BEGIN"); 515 516 // Check if this is a file resource. 517 switch (look().kind()) { 518 case Kind::String: 519 case Kind::Identifier: { 520 ASSIGN_OR_RETURN(Filename, readFilename()); 521 return std::make_unique<UserDefinedResource>(Type, *Filename, MemoryFlags); 522 } 523 default: 524 break; 525 } 526 527 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 528 std::vector<IntOrString> Data; 529 530 while (!consumeOptionalType(Kind::BlockEnd)) { 531 ASSIGN_OR_RETURN(Item, readIntOrString()); 532 Data.push_back(*Item); 533 534 // There can be zero or more commas after each token (but not before 535 // the first one). 536 while (consumeOptionalType(Kind::Comma)) { 537 } 538 } 539 540 return std::make_unique<UserDefinedResource>(Type, std::move(Data), 541 MemoryFlags); 542 } 543 544 RCParser::ParseType RCParser::parseVersionInfoResource() { 545 uint16_t MemoryFlags = 546 parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags()); 547 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed()); 548 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef())); 549 return std::make_unique<VersionInfoResource>( 550 std::move(**BlockResult), std::move(*FixedResult), MemoryFlags); 551 } 552 553 Expected<Control> RCParser::parseControl() { 554 // Each control definition (except CONTROL) follows one of the schemes below 555 // depending on the control class: 556 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID] 557 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID] 558 // Note that control ids must be integers. 559 // Text might be either a string or an integer pointing to resource ID. 560 ASSIGN_OR_RETURN(ClassResult, readIdentifier()); 561 std::string ClassUpper = ClassResult->upper(); 562 auto CtlInfo = Control::SupportedCtls.find(ClassUpper); 563 if (CtlInfo == Control::SupportedCtls.end()) 564 return getExpectedError("control type, END or '}'", true); 565 566 // Read caption if necessary. 567 IntOrString Caption{StringRef()}; 568 if (CtlInfo->getValue().HasTitle) { 569 ASSIGN_OR_RETURN(CaptionResult, readIntOrString()); 570 RETURN_IF_ERROR(consumeType(Kind::Comma)); 571 Caption = *CaptionResult; 572 } 573 574 ASSIGN_OR_RETURN(ID, readInt()); 575 RETURN_IF_ERROR(consumeType(Kind::Comma)); 576 577 IntOrString Class; 578 std::optional<IntWithNotMask> Style; 579 if (ClassUpper == "CONTROL") { 580 // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, 581 // helpID] 582 ASSIGN_OR_RETURN(ClassStr, readString()); 583 RETURN_IF_ERROR(consumeType(Kind::Comma)); 584 Class = *ClassStr; 585 ASSIGN_OR_RETURN(StyleVal, parseIntExpr1()); 586 RETURN_IF_ERROR(consumeType(Kind::Comma)); 587 Style = *StyleVal; 588 } else { 589 Class = CtlInfo->getValue().CtlClass; 590 } 591 592 // x, y, width, height 593 ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4)); 594 595 if (ClassUpper != "CONTROL") { 596 if (consumeOptionalType(Kind::Comma)) { 597 ASSIGN_OR_RETURN(Val, parseIntExpr1()); 598 Style = *Val; 599 } 600 } 601 602 std::optional<uint32_t> ExStyle; 603 if (consumeOptionalType(Kind::Comma)) { 604 ASSIGN_OR_RETURN(Val, readInt()); 605 ExStyle = *Val; 606 } 607 std::optional<uint32_t> HelpID; 608 if (consumeOptionalType(Kind::Comma)) { 609 ASSIGN_OR_RETURN(Val, readInt()); 610 HelpID = *Val; 611 } 612 613 return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1], (*Args)[2], 614 (*Args)[3], Style, ExStyle, HelpID, Class); 615 } 616 617 RCParser::ParseType RCParser::parseBitmapResource() { 618 uint16_t MemoryFlags = 619 parseMemoryFlags(BitmapResource::getDefaultMemoryFlags()); 620 ASSIGN_OR_RETURN(Arg, readFilename()); 621 return std::make_unique<BitmapResource>(*Arg, MemoryFlags); 622 } 623 624 RCParser::ParseType RCParser::parseIconResource() { 625 uint16_t MemoryFlags = 626 parseMemoryFlags(IconResource::getDefaultMemoryFlags()); 627 ASSIGN_OR_RETURN(Arg, readFilename()); 628 return std::make_unique<IconResource>(*Arg, MemoryFlags); 629 } 630 631 RCParser::ParseType RCParser::parseHTMLResource() { 632 uint16_t MemoryFlags = 633 parseMemoryFlags(HTMLResource::getDefaultMemoryFlags()); 634 ASSIGN_OR_RETURN(Arg, readFilename()); 635 return std::make_unique<HTMLResource>(*Arg, MemoryFlags); 636 } 637 638 RCParser::ParseType RCParser::parseMenuResource() { 639 uint16_t MemoryFlags = 640 parseMemoryFlags(MenuResource::getDefaultMemoryFlags()); 641 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 642 ASSIGN_OR_RETURN(Items, parseMenuItemsList()); 643 return std::make_unique<MenuResource>(std::move(*OptStatements), 644 std::move(*Items), MemoryFlags); 645 } 646 647 RCParser::ParseType RCParser::parseMenuExResource() { 648 uint16_t MemoryFlags = 649 parseMemoryFlags(MenuExResource::getDefaultMemoryFlags()); 650 ASSIGN_OR_RETURN(Items, parseMenuExItemsList()); 651 return std::make_unique<MenuExResource>(std::move(*Items), MemoryFlags); 652 } 653 654 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { 655 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 656 657 MenuDefinitionList List; 658 659 // Read a set of items. Each item is of one of three kinds: 660 // MENUITEM SEPARATOR 661 // MENUITEM caption:String, result:Int [, menu flags]... 662 // POPUP caption:String [, menu flags]... { items... } 663 while (!consumeOptionalType(Kind::BlockEnd)) { 664 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier()); 665 666 bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM"); 667 bool IsPopup = ItemTypeResult->equals_insensitive("POPUP"); 668 if (!IsMenuItem && !IsPopup) 669 return getExpectedError("MENUITEM, POPUP, END or '}'", true); 670 671 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) { 672 // Now, expecting SEPARATOR. 673 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier()); 674 if (SeparatorResult->equals_insensitive("SEPARATOR")) { 675 List.addDefinition(std::make_unique<MenuSeparator>()); 676 continue; 677 } 678 679 return getExpectedError("SEPARATOR or string", true); 680 } 681 682 // Not a separator. Read the caption. 683 ASSIGN_OR_RETURN(CaptionResult, readString()); 684 685 // If MENUITEM, expect also a comma and an integer. 686 uint32_t MenuResult = -1; 687 688 if (IsMenuItem) { 689 RETURN_IF_ERROR(consumeType(Kind::Comma)); 690 ASSIGN_OR_RETURN(IntResult, readInt()); 691 MenuResult = *IntResult; 692 } 693 694 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr, 695 MenuDefinition::OptionsFlags)); 696 697 if (IsPopup) { 698 // If POPUP, read submenu items recursively. 699 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList()); 700 List.addDefinition(std::make_unique<PopupItem>( 701 *CaptionResult, *FlagsResult, std::move(*SubMenuResult))); 702 continue; 703 } 704 705 assert(IsMenuItem); 706 List.addDefinition( 707 std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult)); 708 } 709 710 return std::move(List); 711 } 712 713 Expected<MenuDefinitionList> RCParser::parseMenuExItemsList() { 714 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 715 716 MenuDefinitionList List; 717 718 // Read a set of items. Each item is of one of two kinds: 719 // MENUITEM caption:String [,[id][, [type][, state]]]] 720 // POPUP caption:String [,[id][, [type][, [state][, helpID]]]] { popupBody } 721 while (!consumeOptionalType(Kind::BlockEnd)) { 722 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier()); 723 724 bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM"); 725 bool IsPopup = ItemTypeResult->equals_insensitive("POPUP"); 726 if (!IsMenuItem && !IsPopup) 727 return getExpectedError("MENUITEM, POPUP, END or '}'", true); 728 729 // Not a separator. Read the caption. 730 ASSIGN_OR_RETURN(CaptionResult, readString()); 731 732 // If MENUITEM, expect [,[id][, [type][, state]]]] 733 if (IsMenuItem) { 734 uint32_t MenuId = 0; 735 uint32_t MenuType = 0; 736 uint32_t MenuState = 0; 737 738 if (consumeOptionalType(Kind::Comma)) { 739 auto IntId = readInt(); 740 if (IntId) { 741 MenuId = *IntId; 742 } 743 if (consumeOptionalType(Kind::Comma)) { 744 auto IntType = readInt(); 745 if (IntType) { 746 MenuType = *IntType; 747 } 748 if (consumeOptionalType(Kind::Comma)) { 749 auto IntState = readInt(); 750 if (IntState) { 751 MenuState = *IntState; 752 } 753 } 754 } 755 } 756 List.addDefinition(std::make_unique<MenuExItem>(*CaptionResult, MenuId, 757 MenuType, MenuState)); 758 continue; 759 } 760 761 assert(IsPopup); 762 763 uint32_t PopupId = 0; 764 uint32_t PopupType = 0; 765 uint32_t PopupState = 0; 766 uint32_t PopupHelpID = 0; 767 768 if (consumeOptionalType(Kind::Comma)) { 769 auto IntId = readInt(); 770 if (IntId) { 771 PopupId = *IntId; 772 } 773 if (consumeOptionalType(Kind::Comma)) { 774 auto IntType = readInt(); 775 if (IntType) { 776 PopupType = *IntType; 777 } 778 if (consumeOptionalType(Kind::Comma)) { 779 auto IntState = readInt(); 780 if (IntState) { 781 PopupState = *IntState; 782 } 783 if (consumeOptionalType(Kind::Comma)) { 784 auto IntHelpID = readInt(); 785 if (IntHelpID) { 786 PopupHelpID = *IntHelpID; 787 } 788 } 789 } 790 } 791 } 792 // If POPUP, read submenu items recursively. 793 ASSIGN_OR_RETURN(SubMenuResult, parseMenuExItemsList()); 794 List.addDefinition(std::make_unique<PopupExItem>( 795 *CaptionResult, PopupId, PopupType, PopupState, PopupHelpID, 796 std::move(*SubMenuResult))); 797 } 798 799 return std::move(List); 800 } 801 802 RCParser::ParseType RCParser::parseStringTableResource() { 803 uint16_t MemoryFlags = 804 parseMemoryFlags(StringTableResource::getDefaultMemoryFlags()); 805 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 806 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 807 808 auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements), 809 MemoryFlags); 810 811 // Read strings until we reach the end of the block. 812 while (!consumeOptionalType(Kind::BlockEnd)) { 813 // Each definition consists of string's ID (an integer) and a string. 814 // Some examples in documentation suggest that there might be a comma in 815 // between, however we strictly adhere to the single statement definition. 816 ASSIGN_OR_RETURN(IDResult, readInt()); 817 consumeOptionalType(Kind::Comma); 818 819 std::vector<StringRef> Strings; 820 ASSIGN_OR_RETURN(StrResult, readString()); 821 Strings.push_back(*StrResult); 822 while (isNextTokenKind(Kind::String)) 823 Strings.push_back(read().value()); 824 825 Table->addStrings(*IDResult, std::move(Strings)); 826 } 827 828 return std::move(Table); 829 } 830 831 Expected<std::unique_ptr<VersionInfoBlock>> 832 RCParser::parseVersionInfoBlockContents(StringRef BlockName) { 833 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 834 835 auto Contents = std::make_unique<VersionInfoBlock>(BlockName); 836 837 while (!isNextTokenKind(Kind::BlockEnd)) { 838 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt()); 839 Contents->addStmt(std::move(*Stmt)); 840 } 841 842 consume(); // Consume BlockEnd. 843 844 return std::move(Contents); 845 } 846 847 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() { 848 // Expect either BLOCK or VALUE, then a name or a key (a string). 849 ASSIGN_OR_RETURN(TypeResult, readIdentifier()); 850 851 if (TypeResult->equals_insensitive("BLOCK")) { 852 ASSIGN_OR_RETURN(NameResult, readString()); 853 return parseVersionInfoBlockContents(*NameResult); 854 } 855 856 if (TypeResult->equals_insensitive("VALUE")) { 857 ASSIGN_OR_RETURN(KeyResult, readString()); 858 // Read a non-empty list of strings and/or ints, each 859 // possibly preceded by a comma. Unfortunately, the tool behavior depends 860 // on them existing or not, so we need to memorize where we found them. 861 std::vector<IntOrString> Values; 862 BitVector PrecedingCommas; 863 RETURN_IF_ERROR(consumeType(Kind::Comma)); 864 while (!isNextTokenKind(Kind::Identifier) && 865 !isNextTokenKind(Kind::BlockEnd)) { 866 // Try to eat a comma if it's not the first statement. 867 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma); 868 ASSIGN_OR_RETURN(ValueResult, readIntOrString()); 869 Values.push_back(*ValueResult); 870 PrecedingCommas.push_back(HadComma); 871 } 872 return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values), 873 std::move(PrecedingCommas)); 874 } 875 876 return getExpectedError("BLOCK or VALUE", true); 877 } 878 879 Expected<VersionInfoResource::VersionInfoFixed> 880 RCParser::parseVersionInfoFixed() { 881 using RetType = VersionInfoResource::VersionInfoFixed; 882 RetType Result; 883 884 // Read until the beginning of the block. 885 while (!isNextTokenKind(Kind::BlockBegin)) { 886 ASSIGN_OR_RETURN(TypeResult, readIdentifier()); 887 auto FixedType = RetType::getFixedType(*TypeResult); 888 889 if (!RetType::isTypeSupported(FixedType)) 890 return getExpectedError("fixed VERSIONINFO statement type", true); 891 if (Result.IsTypePresent[FixedType]) 892 return getExpectedError("yet unread fixed VERSIONINFO statement type", 893 true); 894 895 // VERSION variations take multiple integers. 896 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1; 897 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(1, NumInts)); 898 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end()); 899 while (ArgInts.size() < NumInts) 900 ArgInts.push_back(0); 901 Result.setValue(FixedType, ArgInts); 902 } 903 904 return Result; 905 } 906 907 RCParser::ParseOptionType RCParser::parseLanguageStmt() { 908 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2)); 909 return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]); 910 } 911 912 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() { 913 ASSIGN_OR_RETURN(Arg, readInt()); 914 return std::make_unique<CharacteristicsStmt>(*Arg); 915 } 916 917 RCParser::ParseOptionType RCParser::parseVersionStmt() { 918 ASSIGN_OR_RETURN(Arg, readInt()); 919 return std::make_unique<VersionStmt>(*Arg); 920 } 921 922 RCParser::ParseOptionType RCParser::parseCaptionStmt() { 923 ASSIGN_OR_RETURN(Arg, readString()); 924 return std::make_unique<CaptionStmt>(*Arg); 925 } 926 927 RCParser::ParseOptionType RCParser::parseClassStmt() { 928 ASSIGN_OR_RETURN(Arg, readIntOrString()); 929 return std::make_unique<ClassStmt>(*Arg); 930 } 931 932 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) { 933 assert(DialogType != OptStmtType::BasicStmt); 934 935 ASSIGN_OR_RETURN(SizeResult, readInt()); 936 RETURN_IF_ERROR(consumeType(Kind::Comma)); 937 ASSIGN_OR_RETURN(NameResult, readString()); 938 939 // Default values for the optional arguments. 940 uint32_t FontWeight = 0; 941 bool FontItalic = false; 942 uint32_t FontCharset = 1; 943 if (DialogType == OptStmtType::DialogExStmt) { 944 if (consumeOptionalType(Kind::Comma)) { 945 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3)); 946 if (Args->size() >= 1) 947 FontWeight = (*Args)[0]; 948 if (Args->size() >= 2) 949 FontItalic = (*Args)[1] != 0; 950 if (Args->size() >= 3) 951 FontCharset = (*Args)[2]; 952 } 953 } 954 return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight, 955 FontItalic, FontCharset); 956 } 957 958 RCParser::ParseOptionType RCParser::parseStyleStmt() { 959 ASSIGN_OR_RETURN(Arg, readInt()); 960 return std::make_unique<StyleStmt>(*Arg); 961 } 962 963 RCParser::ParseOptionType RCParser::parseExStyleStmt() { 964 ASSIGN_OR_RETURN(Arg, readInt()); 965 return std::make_unique<ExStyleStmt>(*Arg); 966 } 967 968 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) { 969 return make_error<ParserError>( 970 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); 971 } 972 973 } // namespace rc 974 } // namespace llvm 975