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/Option/ArgList.h" 15 #include "llvm/Support/FileSystem.h" 16 #include "llvm/Support/Path.h" 17 #include "llvm/Support/Process.h" 18 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); 23 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(); 30 31 namespace llvm { 32 namespace rc { 33 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(); 39 } 40 41 char RCParser::ParserError::ID = 0; 42 43 RCParser::RCParser(std::vector<RCToken> TokenList) 44 : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {} 45 46 bool RCParser::isEof() const { return CurLoc == End; } 47 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()); 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 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); 88 89 if (Result) 90 (*Result)->setName(*NameToken); 91 92 return Result; 93 } 94 95 bool RCParser::isNextTokenKind(Kind TokenKind) const { 96 return !isEof() && look().kind() == TokenKind; 97 } 98 99 const RCToken &RCParser::look() const { 100 assert(!isEof()); 101 return *CurLoc; 102 } 103 104 const RCToken &RCParser::read() { 105 assert(!isEof()); 106 return *CurLoc++; 107 } 108 109 void RCParser::consume() { 110 assert(!isEof()); 111 CurLoc++; 112 } 113 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. 122 // 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. 129 // 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.) 135 // 136 // Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2 137 // is read by parseIntExpr2(). 138 // 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. 147 148 Expected<RCInt> RCParser::readInt() { 149 ASSIGN_OR_RETURN(Value, parseIntExpr1()); 150 return (*Value).getValue(); 151 } 152 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; 157 158 while (!isEof() && look().isBinaryOp()) { 159 auto OpToken = read(); 160 ASSIGN_OR_RETURN(NextResult, parseIntExpr2()); 161 162 switch (OpToken.kind()) { 163 case Kind::Plus: 164 Result += *NextResult; 165 break; 166 167 case Kind::Minus: 168 Result -= *NextResult; 169 break; 170 171 case Kind::Pipe: 172 Result |= *NextResult; 173 break; 174 175 case Kind::Amp: 176 Result &= *NextResult; 177 break; 178 179 default: 180 llvm_unreachable("Already processed all binary ops."); 181 } 182 } 183 184 return Result; 185 } 186 187 Expected<IntWithNotMask> RCParser::parseIntExpr2() { 188 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). 189 static const char ErrorMsg[] = "'-', '~', integer or '('"; 190 191 if (isEof()) 192 return getExpectedError(ErrorMsg); 193 194 switch (look().kind()) { 195 case Kind::Minus: { 196 consume(); 197 ASSIGN_OR_RETURN(Result, parseIntExpr2()); 198 return -(*Result); 199 } 200 201 case Kind::Tilde: { 202 consume(); 203 ASSIGN_OR_RETURN(Result, parseIntExpr2()); 204 return ~(*Result); 205 } 206 207 case Kind::Int: 208 return RCInt(read()); 209 210 case Kind::LeftParen: { 211 consume(); 212 ASSIGN_OR_RETURN(Result, parseIntExpr1()); 213 RETURN_IF_ERROR(consumeType(Kind::RightParen)); 214 return *Result; 215 } 216 217 case Kind::Identifier: { 218 if (!read().value().equals_lower("not")) 219 return getExpectedError(ErrorMsg, true); 220 ASSIGN_OR_RETURN(Result, parseIntExpr2()); 221 return IntWithNotMask(0, (*Result).getValue()); 222 } 223 224 default: 225 return getExpectedError(ErrorMsg); 226 } 227 } 228 229 Expected<StringRef> RCParser::readString() { 230 if (!isNextTokenKind(Kind::String)) 231 return getExpectedError("string"); 232 return read().value(); 233 } 234 235 Expected<StringRef> RCParser::readFilename() { 236 if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier)) 237 return getExpectedError("string"); 238 return read().value(); 239 } 240 241 Expected<StringRef> RCParser::readIdentifier() { 242 if (!isNextTokenKind(Kind::Identifier)) 243 return getExpectedError("identifier"); 244 return read().value(); 245 } 246 247 Expected<IntOrString> RCParser::readIntOrString() { 248 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String)) 249 return getExpectedError("int or string"); 250 return IntOrString(read()); 251 } 252 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()); 259 } 260 261 Error RCParser::consumeType(Kind TokenKind) { 262 if (isNextTokenKind(TokenKind)) { 263 consume(); 264 return Error::success(); 265 } 266 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" 275 } 276 277 llvm_unreachable("All case options exhausted."); 278 } 279 280 bool RCParser::consumeOptionalType(Kind TokenKind) { 281 if (isNextTokenKind(TokenKind)) { 282 consume(); 283 return true; 284 } 285 286 return false; 287 } 288 289 Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount, 290 size_t MaxCount) { 291 assert(MinCount <= MaxCount); 292 293 SmallVector<RCInt, 8> Result; 294 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; 301 }; 302 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)); 310 } 311 312 if (auto IntResult = readInt()) 313 Result.push_back(*IntResult); 314 else 315 return FailureHandler(IntResult.takeError()); 316 } 317 318 return std::move(Result); 319 } 320 321 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc, 322 ArrayRef<uint32_t> FlagValues) { 323 assert(!FlagDesc.empty()); 324 assert(FlagDesc.size() == FlagValues.size()); 325 326 uint32_t Result = 0; 327 while (isNextTokenKind(Kind::Comma)) { 328 consume(); 329 ASSIGN_OR_RETURN(FlagResult, readIdentifier()); 330 bool FoundFlag = false; 331 332 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { 333 if (!FlagResult->equals_lower(FlagDesc[FlagId])) 334 continue; 335 336 Result |= FlagValues[FlagId]; 337 FoundFlag = true; 338 break; 339 } 340 341 if (!FoundFlag) 342 return getExpectedError(join(FlagDesc, "/"), true); 343 } 344 345 return Result; 346 } 347 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_lower("PRELOAD")) 355 Flags |= MfPreload; 356 else if (Ident.equals_lower("LOADONCALL")) 357 Flags &= ~MfPreload; 358 else if (Ident.equals_lower("FIXED")) 359 Flags &= ~(MfMoveable | MfDiscardable); 360 else if (Ident.equals_lower("MOVEABLE")) 361 Flags |= MfMoveable; 362 else if (Ident.equals_lower("DISCARDABLE")) 363 Flags |= MfDiscardable | MfMoveable | MfPure; 364 else if (Ident.equals_lower("PURE")) 365 Flags |= MfPure; 366 else if (Ident.equals_lower("IMPURE")) 367 Flags &= ~(MfPure | MfDiscardable); 368 else if (Ident.equals_lower("SHARED")) 369 Flags |= MfPure; 370 else if (Ident.equals_lower("NONSHARED")) 371 Flags &= ~(MfPure | MfDiscardable); 372 else 373 return Flags; 374 consume(); 375 } 376 return Flags; 377 } 378 379 Expected<OptionalStmtList> 380 RCParser::parseOptionalStatements(OptStmtType StmtsType) { 381 OptionalStmtList Result; 382 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)); 387 } 388 389 return std::move(Result); 390 } 391 392 Expected<std::unique_ptr<OptionalStmt>> 393 RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) { 394 ASSIGN_OR_RETURN(TypeToken, readIdentifier()); 395 if (TypeToken->equals_lower("CHARACTERISTICS")) 396 return parseCharacteristicsStmt(); 397 if (TypeToken->equals_lower("LANGUAGE")) 398 return parseLanguageStmt(); 399 if (TypeToken->equals_lower("VERSION")) 400 return parseVersionStmt(); 401 402 if (StmtsType != OptStmtType::BasicStmt) { 403 if (TypeToken->equals_lower("CAPTION")) 404 return parseCaptionStmt(); 405 if (TypeToken->equals_lower("CLASS")) 406 return parseClassStmt(); 407 if (TypeToken->equals_lower("EXSTYLE")) 408 return parseExStyleStmt(); 409 if (TypeToken->equals_lower("FONT")) 410 return parseFontStmt(StmtsType); 411 if (TypeToken->equals_lower("STYLE")) 412 return parseStyleStmt(); 413 } 414 415 return getExpectedError("optional statement type, BEGIN or '{'", 416 /* IsAlreadyRead = */ true); 417 } 418 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(); 423 } 424 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)); 430 431 auto Accels = std::make_unique<AcceleratorsResource>( 432 std::move(*OptStatements), MemoryFlags); 433 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); 443 } 444 445 return std::move(Accels); 446 } 447 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); 453 } 454 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)); 463 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; 468 } 469 470 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements( 471 IsExtended ? OptStmtType::DialogExStmt 472 : OptStmtType::DialogStmt)); 473 474 assert(isNextTokenKind(Kind::BlockBegin) && 475 "parseOptionalStatements, when successful, halts on BlockBegin."); 476 consume(); 477 478 auto Dialog = std::make_unique<DialogResource>( 479 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], 480 HelpID, std::move(*OptStatements), IsExtended, MemoryFlags); 481 482 while (!consumeOptionalType(Kind::BlockEnd)) { 483 ASSIGN_OR_RETURN(ControlDefResult, parseControl()); 484 Dialog->addControl(std::move(*ControlDefResult)); 485 } 486 487 return std::move(Dialog); 488 } 489 490 RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) { 491 uint16_t MemoryFlags = 492 parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags()); 493 if (isEof()) 494 return getExpectedError("filename, '{' or BEGIN"); 495 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; 504 } 505 506 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 507 std::vector<IntOrString> Data; 508 509 // Consume comma before each consecutive token except the first one. 510 bool ConsumeComma = false; 511 while (!consumeOptionalType(Kind::BlockEnd)) { 512 if (ConsumeComma) 513 RETURN_IF_ERROR(consumeType(Kind::Comma)); 514 ConsumeComma = true; 515 516 ASSIGN_OR_RETURN(Item, readIntOrString()); 517 Data.push_back(*Item); 518 } 519 520 return std::make_unique<UserDefinedResource>(Type, std::move(Data), 521 MemoryFlags); 522 } 523 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); 531 } 532 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); 545 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; 552 } 553 554 ASSIGN_OR_RETURN(ID, readInt()); 555 RETURN_IF_ERROR(consumeType(Kind::Comma)); 556 557 IntOrString Class; 558 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; 569 } 570 571 // x, y, width, height 572 ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4)); 573 574 if (ClassUpper != "CONTROL") { 575 if (consumeOptionalType(Kind::Comma)) { 576 ASSIGN_OR_RETURN(Val, parseIntExpr1()); 577 Style = *Val; 578 } 579 } 580 581 Optional<uint32_t> ExStyle; 582 if (consumeOptionalType(Kind::Comma)) { 583 ASSIGN_OR_RETURN(Val, readInt()); 584 ExStyle = *Val; 585 } 586 Optional<uint32_t> HelpID; 587 if (consumeOptionalType(Kind::Comma)) { 588 ASSIGN_OR_RETURN(Val, readInt()); 589 HelpID = *Val; 590 } 591 592 return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1], 593 (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class); 594 } 595 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); 601 } 602 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); 608 } 609 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); 615 } 616 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); 624 } 625 626 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { 627 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 628 629 MenuDefinitionList List; 630 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()); 637 638 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM"); 639 bool IsPopup = ItemTypeResult->equals_lower("POPUP"); 640 if (!IsMenuItem && !IsPopup) 641 return getExpectedError("MENUITEM, POPUP, END or '}'", true); 642 643 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) { 644 // Now, expecting SEPARATOR. 645 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier()); 646 if (SeparatorResult->equals_lower("SEPARATOR")) { 647 List.addDefinition(std::make_unique<MenuSeparator>()); 648 continue; 649 } 650 651 return getExpectedError("SEPARATOR or string", true); 652 } 653 654 // Not a separator. Read the caption. 655 ASSIGN_OR_RETURN(CaptionResult, readString()); 656 657 // If MENUITEM, expect also a comma and an integer. 658 uint32_t MenuResult = -1; 659 660 if (IsMenuItem) { 661 RETURN_IF_ERROR(consumeType(Kind::Comma)); 662 ASSIGN_OR_RETURN(IntResult, readInt()); 663 MenuResult = *IntResult; 664 } 665 666 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr, 667 MenuDefinition::OptionsFlags)); 668 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; 675 } 676 677 assert(IsMenuItem); 678 List.addDefinition( 679 std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult)); 680 } 681 682 return std::move(List); 683 } 684 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)); 690 691 auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements), 692 MemoryFlags); 693 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); 701 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()); 707 708 Table->addStrings(*IDResult, std::move(Strings)); 709 } 710 711 return std::move(Table); 712 } 713 714 Expected<std::unique_ptr<VersionInfoBlock>> 715 RCParser::parseVersionInfoBlockContents(StringRef BlockName) { 716 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 717 718 auto Contents = std::make_unique<VersionInfoBlock>(BlockName); 719 720 while (!isNextTokenKind(Kind::BlockEnd)) { 721 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt()); 722 Contents->addStmt(std::move(*Stmt)); 723 } 724 725 consume(); // Consume BlockEnd. 726 727 return std::move(Contents); 728 } 729 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()); 733 734 if (TypeResult->equals_lower("BLOCK")) { 735 ASSIGN_OR_RETURN(NameResult, readString()); 736 return parseVersionInfoBlockContents(*NameResult); 737 } 738 739 if (TypeResult->equals_lower("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 std::vector<bool> 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); 754 } 755 return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values), 756 std::move(PrecedingCommas)); 757 } 758 759 return getExpectedError("BLOCK or VALUE", true); 760 } 761 762 Expected<VersionInfoResource::VersionInfoFixed> 763 RCParser::parseVersionInfoFixed() { 764 using RetType = VersionInfoResource::VersionInfoFixed; 765 RetType Result; 766 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); 771 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); 777 778 // VERSION variations take multiple integers. 779 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1; 780 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts)); 781 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end()); 782 Result.setValue(FixedType, ArgInts); 783 } 784 785 return Result; 786 } 787 788 RCParser::ParseOptionType RCParser::parseLanguageStmt() { 789 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2)); 790 return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]); 791 } 792 793 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() { 794 ASSIGN_OR_RETURN(Arg, readInt()); 795 return std::make_unique<CharacteristicsStmt>(*Arg); 796 } 797 798 RCParser::ParseOptionType RCParser::parseVersionStmt() { 799 ASSIGN_OR_RETURN(Arg, readInt()); 800 return std::make_unique<VersionStmt>(*Arg); 801 } 802 803 RCParser::ParseOptionType RCParser::parseCaptionStmt() { 804 ASSIGN_OR_RETURN(Arg, readString()); 805 return std::make_unique<CaptionStmt>(*Arg); 806 } 807 808 RCParser::ParseOptionType RCParser::parseClassStmt() { 809 ASSIGN_OR_RETURN(Arg, readIntOrString()); 810 return std::make_unique<ClassStmt>(*Arg); 811 } 812 813 RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) { 814 assert(DialogType != OptStmtType::BasicStmt); 815 816 ASSIGN_OR_RETURN(SizeResult, readInt()); 817 RETURN_IF_ERROR(consumeType(Kind::Comma)); 818 ASSIGN_OR_RETURN(NameResult, readString()); 819 820 // Default values for the optional arguments. 821 uint32_t FontWeight = 0; 822 bool FontItalic = false; 823 uint32_t FontCharset = 1; 824 if (DialogType == OptStmtType::DialogExStmt) { 825 if (consumeOptionalType(Kind::Comma)) { 826 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3)); 827 if (Args->size() >= 1) 828 FontWeight = (*Args)[0]; 829 if (Args->size() >= 2) 830 FontItalic = (*Args)[1] != 0; 831 if (Args->size() >= 3) 832 FontCharset = (*Args)[2]; 833 } 834 } 835 return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight, 836 FontItalic, FontCharset); 837 } 838 839 RCParser::ParseOptionType RCParser::parseStyleStmt() { 840 ASSIGN_OR_RETURN(Arg, readInt()); 841 return std::make_unique<StyleStmt>(*Arg); 842 } 843 844 RCParser::ParseOptionType RCParser::parseExStyleStmt() { 845 ASSIGN_OR_RETURN(Arg, readInt()); 846 return std::make_unique<ExStyleStmt>(*Arg); 847 } 848 849 Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) { 850 return make_error<ParserError>( 851 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); 852 } 853 854 } // namespace rc 855 } // namespace llvm 856