1 //===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===---------------------------------------------------------------------===// 9 // 10 // This implements the parser defined in ResourceScriptParser.h. 11 // 12 //===---------------------------------------------------------------------===// 13 14 #include "ResourceScriptParser.h" 15 16 // Take an expression returning llvm::Error and forward the error if it exists. 17 #define RETURN_IF_ERROR(Expr) \ 18 if (auto Err = (Expr)) \ 19 return std::move(Err); 20 21 // Take an expression returning llvm::Expected<T> and assign it to Var or 22 // forward the error out of the function. 23 #define ASSIGN_OR_RETURN(Var, Expr) \ 24 auto Var = (Expr); \ 25 if (!Var) \ 26 return Var.takeError(); 27 28 namespace llvm { 29 namespace rc { 30 31 RCParser::ParserError::ParserError(const Twine Expected, const LocIter CurLoc, 32 const LocIter End) 33 : ErrorLoc(CurLoc), FileEnd(End) { 34 CurMessage = "Error parsing file: expected " + Expected.str() + ", got " + 35 (CurLoc == End ? "<EOF>" : CurLoc->value()).str(); 36 } 37 38 char RCParser::ParserError::ID = 0; 39 40 RCParser::RCParser(const std::vector<RCToken> &TokenList) 41 : Tokens(TokenList), CurLoc(Tokens.begin()), End(Tokens.end()) {} 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("CURSOR")) 69 Result = parseCursorResource(); 70 else if (TypeToken->equalsLower("DIALOG")) 71 Result = parseDialogResource(false); 72 else if (TypeToken->equalsLower("DIALOGEX")) 73 Result = parseDialogResource(true); 74 else if (TypeToken->equalsLower("ICON")) 75 Result = parseIconResource(); 76 else if (TypeToken->equalsLower("HTML")) 77 Result = parseHTMLResource(); 78 else if (TypeToken->equalsLower("MENU")) 79 Result = parseMenuResource(); 80 else if (TypeToken->equalsLower("VERSIONINFO")) 81 Result = parseVersionInfoResource(); 82 else 83 return getExpectedError("resource type", /* IsAlreadyRead = */ true); 84 85 if (Result) 86 (*Result)->setName(*NameToken); 87 88 return Result; 89 } 90 91 bool RCParser::isNextTokenKind(Kind TokenKind) const { 92 return !isEof() && look().kind() == TokenKind; 93 } 94 95 const RCToken &RCParser::look() const { 96 assert(!isEof()); 97 return *CurLoc; 98 } 99 100 const RCToken &RCParser::read() { 101 assert(!isEof()); 102 return *CurLoc++; 103 } 104 105 void RCParser::consume() { 106 assert(!isEof()); 107 CurLoc++; 108 } 109 110 Expected<uint32_t> RCParser::readInt() { 111 if (!isNextTokenKind(Kind::Int)) 112 return getExpectedError("integer"); 113 return read().intValue(); 114 } 115 116 Expected<StringRef> RCParser::readString() { 117 if (!isNextTokenKind(Kind::String)) 118 return getExpectedError("string"); 119 return read().value(); 120 } 121 122 Expected<StringRef> RCParser::readIdentifier() { 123 if (!isNextTokenKind(Kind::Identifier)) 124 return getExpectedError("identifier"); 125 return read().value(); 126 } 127 128 Expected<IntOrString> RCParser::readIntOrString() { 129 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String)) 130 return getExpectedError("int or string"); 131 return IntOrString(read()); 132 } 133 134 Expected<IntOrString> RCParser::readTypeOrName() { 135 // We suggest that the correct resource name or type should be either an 136 // identifier or an integer. The original RC tool is much more liberal. 137 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int)) 138 return getExpectedError("int or identifier"); 139 return IntOrString(read()); 140 } 141 142 Error RCParser::consumeType(Kind TokenKind) { 143 if (isNextTokenKind(TokenKind)) { 144 consume(); 145 return Error::success(); 146 } 147 148 switch (TokenKind) { 149 #define TOKEN(TokenName) \ 150 case Kind::TokenName: \ 151 return getExpectedError(#TokenName); 152 #define SHORT_TOKEN(TokenName, TokenCh) \ 153 case Kind::TokenName: \ 154 return getExpectedError(#TokenCh); 155 #include "ResourceScriptTokenList.h" 156 #undef SHORT_TOKEN 157 #undef TOKEN 158 } 159 160 llvm_unreachable("All case options exhausted."); 161 } 162 163 bool RCParser::consumeOptionalType(Kind TokenKind) { 164 if (isNextTokenKind(TokenKind)) { 165 consume(); 166 return true; 167 } 168 169 return false; 170 } 171 172 Expected<SmallVector<uint32_t, 8>> 173 RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) { 174 assert(MinCount <= MaxCount); 175 176 SmallVector<uint32_t, 8> Result; 177 178 auto FailureHandler = 179 [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 8>> { 180 if (Result.size() < MinCount) 181 return std::move(Err); 182 consumeError(std::move(Err)); 183 return Result; 184 }; 185 186 for (size_t i = 0; i < MaxCount; ++i) { 187 // Try to read a comma unless we read the first token. 188 // Sometimes RC tool requires them and sometimes not. We decide to 189 // always require them. 190 if (i >= 1) { 191 if (auto CommaError = consumeType(Kind::Comma)) 192 return FailureHandler(std::move(CommaError)); 193 } 194 195 if (auto IntResult = readInt()) 196 Result.push_back(*IntResult); 197 else 198 return FailureHandler(IntResult.takeError()); 199 } 200 201 return std::move(Result); 202 } 203 204 Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc) { 205 assert(FlagDesc.size() <= 32 && "More than 32 flags won't fit in result."); 206 assert(!FlagDesc.empty()); 207 208 uint32_t Result = 0; 209 while (isNextTokenKind(Kind::Comma)) { 210 consume(); 211 ASSIGN_OR_RETURN(FlagResult, readIdentifier()); 212 bool FoundFlag = false; 213 214 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { 215 if (!FlagResult->equals_lower(FlagDesc[FlagId])) 216 continue; 217 218 Result |= (1U << FlagId); 219 FoundFlag = true; 220 break; 221 } 222 223 if (!FoundFlag) 224 return getExpectedError(join(FlagDesc, "/"), true); 225 } 226 227 return Result; 228 } 229 230 // As for now, we ignore the extended set of statements. 231 Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) { 232 OptionalStmtList Result; 233 234 // The last statement is always followed by the start of the block. 235 while (!isNextTokenKind(Kind::BlockBegin)) { 236 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(IsExtended)); 237 Result.addStmt(std::move(*SingleParse)); 238 } 239 240 return std::move(Result); 241 } 242 243 Expected<std::unique_ptr<OptionalStmt>> 244 RCParser::parseSingleOptionalStatement(bool IsExtended) { 245 ASSIGN_OR_RETURN(TypeToken, readIdentifier()); 246 if (TypeToken->equals_lower("CHARACTERISTICS")) 247 return parseCharacteristicsStmt(); 248 if (TypeToken->equals_lower("LANGUAGE")) 249 return parseLanguageStmt(); 250 if (TypeToken->equals_lower("VERSION")) 251 return parseVersionStmt(); 252 253 if (IsExtended) { 254 if (TypeToken->equals_lower("CAPTION")) 255 return parseCaptionStmt(); 256 if (TypeToken->equals_lower("FONT")) 257 return parseFontStmt(); 258 if (TypeToken->equals_lower("STYLE")) 259 return parseStyleStmt(); 260 } 261 262 return getExpectedError("optional statement type, BEGIN or '{'", 263 /* IsAlreadyRead = */ true); 264 } 265 266 RCParser::ParseType RCParser::parseLanguageResource() { 267 // Read LANGUAGE as an optional statement. If it's read correctly, we can 268 // upcast it to RCResource. 269 return parseLanguageStmt(); 270 } 271 272 RCParser::ParseType RCParser::parseAcceleratorsResource() { 273 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 274 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 275 276 auto Accels = make_unique<AcceleratorsResource>(std::move(*OptStatements)); 277 278 while (!consumeOptionalType(Kind::BlockEnd)) { 279 ASSIGN_OR_RETURN(EventResult, readIntOrString()); 280 RETURN_IF_ERROR(consumeType(Kind::Comma)); 281 ASSIGN_OR_RETURN(IDResult, readInt()); 282 ASSIGN_OR_RETURN(FlagsResult, 283 parseFlags(AcceleratorsResource::Accelerator::OptionsStr)); 284 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult); 285 } 286 287 return std::move(Accels); 288 } 289 290 RCParser::ParseType RCParser::parseCursorResource() { 291 ASSIGN_OR_RETURN(Arg, readString()); 292 return make_unique<CursorResource>(*Arg); 293 } 294 295 RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { 296 // Dialog resources have the following format of the arguments: 297 // DIALOG: x, y, width, height [opt stmts...] {controls...} 298 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...} 299 // These are very similar, so we parse them together. 300 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4)); 301 302 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0. 303 if (IsExtended && consumeOptionalType(Kind::Comma)) { 304 ASSIGN_OR_RETURN(HelpIDResult, readInt()); 305 HelpID = *HelpIDResult; 306 } 307 308 ASSIGN_OR_RETURN(OptStatements, 309 parseOptionalStatements(/*UseExtendedStmts = */ true)); 310 311 assert(isNextTokenKind(Kind::BlockBegin) && 312 "parseOptionalStatements, when successful, halts on BlockBegin."); 313 consume(); 314 315 auto Dialog = make_unique<DialogResource>( 316 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], 317 HelpID, std::move(*OptStatements), IsExtended); 318 319 while (!consumeOptionalType(Kind::BlockEnd)) { 320 ASSIGN_OR_RETURN(ControlDefResult, parseControl()); 321 Dialog->addControl(std::move(*ControlDefResult)); 322 } 323 324 return std::move(Dialog); 325 } 326 327 RCParser::ParseType RCParser::parseVersionInfoResource() { 328 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed()); 329 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef())); 330 return make_unique<VersionInfoResource>(std::move(**BlockResult), 331 std::move(*FixedResult)); 332 } 333 334 Expected<Control> RCParser::parseControl() { 335 // Each control definition (except CONTROL) follows one of the schemes below 336 // depending on the control class: 337 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID] 338 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID] 339 // Note that control ids must be integers. 340 ASSIGN_OR_RETURN(ClassResult, readIdentifier()); 341 std::string ClassUpper = ClassResult->upper(); 342 if (Control::SupportedCtls.find(ClassUpper) == Control::SupportedCtls.end()) 343 return getExpectedError("control type, END or '}'", true); 344 345 // Read caption if necessary. 346 StringRef Caption; 347 if (Control::CtlsWithTitle.find(ClassUpper) != Control::CtlsWithTitle.end()) { 348 ASSIGN_OR_RETURN(CaptionResult, readString()); 349 RETURN_IF_ERROR(consumeType(Kind::Comma)); 350 Caption = *CaptionResult; 351 } 352 353 ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8)); 354 355 auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> { 356 return Args->size() > Id ? (*Args)[Id] : Optional<uint32_t>(); 357 }; 358 359 return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2], 360 (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6), 361 TakeOptArg(7)); 362 } 363 364 RCParser::ParseType RCParser::parseIconResource() { 365 ASSIGN_OR_RETURN(Arg, readString()); 366 return make_unique<IconResource>(*Arg); 367 } 368 369 RCParser::ParseType RCParser::parseHTMLResource() { 370 ASSIGN_OR_RETURN(Arg, readString()); 371 return make_unique<HTMLResource>(*Arg); 372 } 373 374 RCParser::ParseType RCParser::parseMenuResource() { 375 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 376 ASSIGN_OR_RETURN(Items, parseMenuItemsList()); 377 return make_unique<MenuResource>(std::move(*OptStatements), 378 std::move(*Items)); 379 } 380 381 Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { 382 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 383 384 MenuDefinitionList List; 385 386 // Read a set of items. Each item is of one of three kinds: 387 // MENUITEM SEPARATOR 388 // MENUITEM caption:String, result:Int [, menu flags]... 389 // POPUP caption:String [, menu flags]... { items... } 390 while (!consumeOptionalType(Kind::BlockEnd)) { 391 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier()); 392 393 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM"); 394 bool IsPopup = ItemTypeResult->equals_lower("POPUP"); 395 if (!IsMenuItem && !IsPopup) 396 return getExpectedError("MENUITEM, POPUP, END or '}'", true); 397 398 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) { 399 // Now, expecting SEPARATOR. 400 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier()); 401 if (SeparatorResult->equals_lower("SEPARATOR")) { 402 List.addDefinition(make_unique<MenuSeparator>()); 403 continue; 404 } 405 406 return getExpectedError("SEPARATOR or string", true); 407 } 408 409 // Not a separator. Read the caption. 410 ASSIGN_OR_RETURN(CaptionResult, readString()); 411 412 // If MENUITEM, expect also a comma and an integer. 413 uint32_t MenuResult = -1; 414 415 if (IsMenuItem) { 416 RETURN_IF_ERROR(consumeType(Kind::Comma)); 417 ASSIGN_OR_RETURN(IntResult, readInt()); 418 MenuResult = *IntResult; 419 } 420 421 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr)); 422 423 if (IsPopup) { 424 // If POPUP, read submenu items recursively. 425 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList()); 426 List.addDefinition(make_unique<PopupItem>(*CaptionResult, *FlagsResult, 427 std::move(*SubMenuResult))); 428 continue; 429 } 430 431 assert(IsMenuItem); 432 List.addDefinition( 433 make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult)); 434 } 435 436 return std::move(List); 437 } 438 439 RCParser::ParseType RCParser::parseStringTableResource() { 440 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); 441 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 442 443 auto Table = make_unique<StringTableResource>(std::move(*OptStatements)); 444 445 // Read strings until we reach the end of the block. 446 while (!consumeOptionalType(Kind::BlockEnd)) { 447 // Each definition consists of string's ID (an integer) and a string. 448 // Some examples in documentation suggest that there might be a comma in 449 // between, however we strictly adhere to the single statement definition. 450 ASSIGN_OR_RETURN(IDResult, readInt()); 451 ASSIGN_OR_RETURN(StrResult, readString()); 452 Table->addString(*IDResult, *StrResult); 453 } 454 455 return std::move(Table); 456 } 457 458 Expected<std::unique_ptr<VersionInfoBlock>> 459 RCParser::parseVersionInfoBlockContents(StringRef BlockName) { 460 RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); 461 462 auto Contents = make_unique<VersionInfoBlock>(BlockName); 463 464 while (!isNextTokenKind(Kind::BlockEnd)) { 465 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt()); 466 Contents->addStmt(std::move(*Stmt)); 467 } 468 469 consume(); // Consume BlockEnd. 470 471 return std::move(Contents); 472 } 473 474 Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() { 475 // Expect either BLOCK or VALUE, then a name or a key (a string). 476 ASSIGN_OR_RETURN(TypeResult, readIdentifier()); 477 478 if (TypeResult->equals_lower("BLOCK")) { 479 ASSIGN_OR_RETURN(NameResult, readString()); 480 return parseVersionInfoBlockContents(*NameResult); 481 } 482 483 if (TypeResult->equals_lower("VALUE")) { 484 ASSIGN_OR_RETURN(KeyResult, readString()); 485 // Read a (possibly empty) list of strings and/or ints, each preceded by 486 // a comma. 487 std::vector<IntOrString> Values; 488 489 while (consumeOptionalType(Kind::Comma)) { 490 ASSIGN_OR_RETURN(ValueResult, readIntOrString()); 491 Values.push_back(*ValueResult); 492 } 493 return make_unique<VersionInfoValue>(*KeyResult, std::move(Values)); 494 } 495 496 return getExpectedError("BLOCK or VALUE", true); 497 } 498 499 Expected<VersionInfoResource::VersionInfoFixed> 500 RCParser::parseVersionInfoFixed() { 501 using RetType = VersionInfoResource::VersionInfoFixed; 502 RetType Result; 503 504 // Read until the beginning of the block. 505 while (!isNextTokenKind(Kind::BlockBegin)) { 506 ASSIGN_OR_RETURN(TypeResult, readIdentifier()); 507 auto FixedType = RetType::getFixedType(*TypeResult); 508 509 if (!RetType::isTypeSupported(FixedType)) 510 return getExpectedError("fixed VERSIONINFO statement type", true); 511 if (Result.IsTypePresent[FixedType]) 512 return getExpectedError("yet unread fixed VERSIONINFO statement type", 513 true); 514 515 // VERSION variations take multiple integers. 516 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1; 517 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts)); 518 Result.setValue(FixedType, *ArgsResult); 519 } 520 521 return Result; 522 } 523 524 RCParser::ParseOptionType RCParser::parseLanguageStmt() { 525 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2)); 526 return make_unique<LanguageResource>((*Args)[0], (*Args)[1]); 527 } 528 529 RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() { 530 ASSIGN_OR_RETURN(Arg, readInt()); 531 return make_unique<CharacteristicsStmt>(*Arg); 532 } 533 534 RCParser::ParseOptionType RCParser::parseVersionStmt() { 535 ASSIGN_OR_RETURN(Arg, readInt()); 536 return make_unique<VersionStmt>(*Arg); 537 } 538 539 RCParser::ParseOptionType RCParser::parseCaptionStmt() { 540 ASSIGN_OR_RETURN(Arg, readString()); 541 return make_unique<CaptionStmt>(*Arg); 542 } 543 544 RCParser::ParseOptionType RCParser::parseFontStmt() { 545 ASSIGN_OR_RETURN(SizeResult, readInt()); 546 RETURN_IF_ERROR(consumeType(Kind::Comma)); 547 ASSIGN_OR_RETURN(NameResult, readString()); 548 return make_unique<FontStmt>(*SizeResult, *NameResult); 549 } 550 551 RCParser::ParseOptionType RCParser::parseStyleStmt() { 552 ASSIGN_OR_RETURN(Arg, readInt()); 553 return make_unique<StyleStmt>(*Arg); 554 } 555 556 Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) { 557 return make_error<ParserError>( 558 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); 559 } 560 561 } // namespace rc 562 } // namespace llvm 563