blob: d8398b785149fb4d562a73270cbe9e9976dc5e69 [file] [log] [blame]
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +00001//===-- 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"
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +000015#include "llvm/Option/ArgList.h"
16#include "llvm/Support/FileSystem.h"
17#include "llvm/Support/Path.h"
18#include "llvm/Support/Process.h"
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000019
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
32namespace llvm {
33namespace rc {
34
Zachary Turner514b7102017-10-09 18:50:29 +000035RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000036 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
42char RCParser::ParserError::ID = 0;
43
Zachary Turner514b7102017-10-09 18:50:29 +000044RCParser::RCParser(std::vector<RCToken> TokenList)
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000045 : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
46
47bool RCParser::isEof() const { return CurLoc == End; }
48
49RCParser::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
Marek Sokolowski7f110522017-08-28 22:58:31 +000067 if (TypeToken->equalsLower("ACCELERATORS"))
68 Result = parseAcceleratorsResource();
69 else if (TypeToken->equalsLower("CURSOR"))
Marek Sokolowski72aa9372017-08-28 21:59:54 +000070 Result = parseCursorResource();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +000071 else if (TypeToken->equalsLower("DIALOG"))
72 Result = parseDialogResource(false);
73 else if (TypeToken->equalsLower("DIALOGEX"))
74 Result = parseDialogResource(true);
Marek Sokolowski72aa9372017-08-28 21:59:54 +000075 else if (TypeToken->equalsLower("HTML"))
76 Result = parseHTMLResource();
Martin Storsjo827ddb22018-05-07 20:27:15 +000077 else if (TypeToken->equalsLower("ICON"))
78 Result = parseIconResource();
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +000079 else if (TypeToken->equalsLower("MENU"))
80 Result = parseMenuResource();
Marek Sokolowskifb74cb12017-09-28 22:41:38 +000081 else if (TypeToken->equalsLower("VERSIONINFO"))
82 Result = parseVersionInfoResource();
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000083 else
Marek Sokolowskib5f39a02017-09-29 00:14:18 +000084 Result = parseUserDefinedResource(*TypeToken);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000085
86 if (Result)
87 (*Result)->setName(*NameToken);
88
89 return Result;
90}
91
92bool RCParser::isNextTokenKind(Kind TokenKind) const {
93 return !isEof() && look().kind() == TokenKind;
94}
95
96const RCToken &RCParser::look() const {
97 assert(!isEof());
98 return *CurLoc;
99}
100
101const RCToken &RCParser::read() {
102 assert(!isEof());
103 return *CurLoc++;
104}
105
106void RCParser::consume() {
107 assert(!isEof());
108 CurLoc++;
109}
110
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000111// An integer description might consist of a single integer or
112// an arithmetic expression evaluating to the integer. The expressions
113// can contain the following tokens: <int> ( ) + - | & ~. Their meaning
114// is the same as in C++.
115// The operators in the original RC implementation have the following
116// precedence:
117// 1) Unary operators (- ~),
118// 2) Binary operators (+ - & |), with no precedence.
119//
120// The following grammar is used to parse the expressions Exp1:
121// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
122// Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
123// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
124// separated by binary operators.)
125//
126// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
127// is read by parseIntExpr2().
128//
129// The original Microsoft tool handles multiple unary operators incorrectly.
130// For example, in 16-bit little-endian integers:
131// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
132// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
133// Our implementation differs from the original one and handles these
134// operators correctly:
135// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
136// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
137
Zachary Turner07bc04f2017-10-06 21:26:06 +0000138Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); }
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000139
Zachary Turner07bc04f2017-10-06 21:26:06 +0000140Expected<RCInt> RCParser::parseIntExpr1() {
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000141 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
142 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
Zachary Turner07bc04f2017-10-06 21:26:06 +0000143 RCInt Result = *FirstResult;
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000144
145 while (!isEof() && look().isBinaryOp()) {
146 auto OpToken = read();
147 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
148
149 switch (OpToken.kind()) {
150 case Kind::Plus:
151 Result += *NextResult;
152 break;
153
154 case Kind::Minus:
155 Result -= *NextResult;
156 break;
157
158 case Kind::Pipe:
159 Result |= *NextResult;
160 break;
161
162 case Kind::Amp:
163 Result &= *NextResult;
164 break;
165
166 default:
167 llvm_unreachable("Already processed all binary ops.");
168 }
169 }
170
171 return Result;
172}
173
Zachary Turner07bc04f2017-10-06 21:26:06 +0000174Expected<RCInt> RCParser::parseIntExpr2() {
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000175 // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
176 static const char ErrorMsg[] = "'-', '~', integer or '('";
177
178 if (isEof())
179 return getExpectedError(ErrorMsg);
180
181 switch (look().kind()) {
182 case Kind::Minus: {
183 consume();
184 ASSIGN_OR_RETURN(Result, parseIntExpr2());
185 return -(*Result);
186 }
187
188 case Kind::Tilde: {
189 consume();
190 ASSIGN_OR_RETURN(Result, parseIntExpr2());
191 return ~(*Result);
192 }
193
194 case Kind::Int:
Zachary Turner07bc04f2017-10-06 21:26:06 +0000195 return RCInt(read());
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000196
197 case Kind::LeftParen: {
198 consume();
199 ASSIGN_OR_RETURN(Result, parseIntExpr1());
200 RETURN_IF_ERROR(consumeType(Kind::RightParen));
201 return *Result;
202 }
203
204 default:
205 return getExpectedError(ErrorMsg);
206 }
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000207}
208
209Expected<StringRef> RCParser::readString() {
210 if (!isNextTokenKind(Kind::String))
211 return getExpectedError("string");
212 return read().value();
213}
214
215Expected<StringRef> RCParser::readIdentifier() {
216 if (!isNextTokenKind(Kind::Identifier))
217 return getExpectedError("identifier");
218 return read().value();
219}
220
Marek Sokolowski7f110522017-08-28 22:58:31 +0000221Expected<IntOrString> RCParser::readIntOrString() {
222 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
223 return getExpectedError("int or string");
224 return IntOrString(read());
225}
226
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000227Expected<IntOrString> RCParser::readTypeOrName() {
228 // We suggest that the correct resource name or type should be either an
229 // identifier or an integer. The original RC tool is much more liberal.
230 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
231 return getExpectedError("int or identifier");
Marek Sokolowski7f110522017-08-28 22:58:31 +0000232 return IntOrString(read());
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000233}
234
235Error RCParser::consumeType(Kind TokenKind) {
236 if (isNextTokenKind(TokenKind)) {
237 consume();
238 return Error::success();
239 }
240
241 switch (TokenKind) {
242#define TOKEN(TokenName) \
243 case Kind::TokenName: \
244 return getExpectedError(#TokenName);
245#define SHORT_TOKEN(TokenName, TokenCh) \
246 case Kind::TokenName: \
247 return getExpectedError(#TokenCh);
David Blaikieb961d292017-11-21 00:23:19 +0000248#include "ResourceScriptTokenList.def"
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000249 }
250
251 llvm_unreachable("All case options exhausted.");
252}
253
254bool RCParser::consumeOptionalType(Kind TokenKind) {
255 if (isNextTokenKind(TokenKind)) {
256 consume();
257 return true;
258 }
259
260 return false;
261}
262
Zachary Turner07bc04f2017-10-06 21:26:06 +0000263Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
264 size_t MaxCount) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000265 assert(MinCount <= MaxCount);
266
Zachary Turner07bc04f2017-10-06 21:26:06 +0000267 SmallVector<RCInt, 8> Result;
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000268
269 auto FailureHandler =
Zachary Turner07bc04f2017-10-06 21:26:06 +0000270 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000271 if (Result.size() < MinCount)
272 return std::move(Err);
273 consumeError(std::move(Err));
274 return Result;
275 };
276
277 for (size_t i = 0; i < MaxCount; ++i) {
278 // Try to read a comma unless we read the first token.
279 // Sometimes RC tool requires them and sometimes not. We decide to
280 // always require them.
281 if (i >= 1) {
282 if (auto CommaError = consumeType(Kind::Comma))
283 return FailureHandler(std::move(CommaError));
284 }
285
286 if (auto IntResult = readInt())
287 Result.push_back(*IntResult);
288 else
289 return FailureHandler(IntResult.takeError());
290 }
291
292 return std::move(Result);
293}
294
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000295Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
296 ArrayRef<uint32_t> FlagValues) {
Marek Sokolowski7f110522017-08-28 22:58:31 +0000297 assert(!FlagDesc.empty());
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000298 assert(FlagDesc.size() == FlagValues.size());
Marek Sokolowski7f110522017-08-28 22:58:31 +0000299
300 uint32_t Result = 0;
301 while (isNextTokenKind(Kind::Comma)) {
302 consume();
303 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
304 bool FoundFlag = false;
305
306 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
307 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
308 continue;
309
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000310 Result |= FlagValues[FlagId];
Marek Sokolowski7f110522017-08-28 22:58:31 +0000311 FoundFlag = true;
312 break;
313 }
314
315 if (!FoundFlag)
316 return getExpectedError(join(FlagDesc, "/"), true);
317 }
318
319 return Result;
320}
321
Zachary Turner420090a2017-10-06 20:51:20 +0000322Expected<OptionalStmtList>
323RCParser::parseOptionalStatements(OptStmtType StmtsType) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000324 OptionalStmtList Result;
325
326 // The last statement is always followed by the start of the block.
327 while (!isNextTokenKind(Kind::BlockBegin)) {
Zachary Turner420090a2017-10-06 20:51:20 +0000328 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000329 Result.addStmt(std::move(*SingleParse));
330 }
331
332 return std::move(Result);
333}
334
335Expected<std::unique_ptr<OptionalStmt>>
Zachary Turner420090a2017-10-06 20:51:20 +0000336RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000337 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
338 if (TypeToken->equals_lower("CHARACTERISTICS"))
339 return parseCharacteristicsStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000340 if (TypeToken->equals_lower("LANGUAGE"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000341 return parseLanguageStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000342 if (TypeToken->equals_lower("VERSION"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000343 return parseVersionStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000344
Zachary Turner420090a2017-10-06 20:51:20 +0000345 if (StmtsType != OptStmtType::BasicStmt) {
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000346 if (TypeToken->equals_lower("CAPTION"))
347 return parseCaptionStmt();
348 if (TypeToken->equals_lower("FONT"))
Zachary Turner420090a2017-10-06 20:51:20 +0000349 return parseFontStmt(StmtsType);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000350 if (TypeToken->equals_lower("STYLE"))
351 return parseStyleStmt();
352 }
353
354 return getExpectedError("optional statement type, BEGIN or '{'",
355 /* IsAlreadyRead = */ true);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000356}
357
358RCParser::ParseType RCParser::parseLanguageResource() {
359 // Read LANGUAGE as an optional statement. If it's read correctly, we can
360 // upcast it to RCResource.
361 return parseLanguageStmt();
362}
363
Marek Sokolowski7f110522017-08-28 22:58:31 +0000364RCParser::ParseType RCParser::parseAcceleratorsResource() {
365 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
366 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
367
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000368 auto Accels =
369 llvm::make_unique<AcceleratorsResource>(std::move(*OptStatements));
Marek Sokolowski7f110522017-08-28 22:58:31 +0000370
371 while (!consumeOptionalType(Kind::BlockEnd)) {
372 ASSIGN_OR_RETURN(EventResult, readIntOrString());
373 RETURN_IF_ERROR(consumeType(Kind::Comma));
374 ASSIGN_OR_RETURN(IDResult, readInt());
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000375 ASSIGN_OR_RETURN(
376 FlagsResult,
377 parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
378 AcceleratorsResource::Accelerator::OptionsFlags));
Marek Sokolowski7f110522017-08-28 22:58:31 +0000379 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
380 }
381
382 return std::move(Accels);
383}
384
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000385RCParser::ParseType RCParser::parseCursorResource() {
386 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000387 return llvm::make_unique<CursorResource>(*Arg);
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000388}
389
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000390RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
391 // Dialog resources have the following format of the arguments:
392 // DIALOG: x, y, width, height [opt stmts...] {controls...}
393 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
394 // These are very similar, so we parse them together.
395 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
396
397 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
398 if (IsExtended && consumeOptionalType(Kind::Comma)) {
399 ASSIGN_OR_RETURN(HelpIDResult, readInt());
400 HelpID = *HelpIDResult;
401 }
402
Zachary Turner420090a2017-10-06 20:51:20 +0000403 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
404 IsExtended ? OptStmtType::DialogExStmt
405 : OptStmtType::DialogStmt));
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000406
407 assert(isNextTokenKind(Kind::BlockBegin) &&
408 "parseOptionalStatements, when successful, halts on BlockBegin.");
409 consume();
410
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000411 auto Dialog = llvm::make_unique<DialogResource>(
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000412 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
413 HelpID, std::move(*OptStatements), IsExtended);
414
415 while (!consumeOptionalType(Kind::BlockEnd)) {
416 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
417 Dialog->addControl(std::move(*ControlDefResult));
418 }
419
420 return std::move(Dialog);
421}
422
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000423RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
424 if (isEof())
425 return getExpectedError("filename, '{' or BEGIN");
426
427 // Check if this is a file resource.
428 if (look().kind() == Kind::String)
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000429 return llvm::make_unique<UserDefinedResource>(Type, read().value());
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000430
431 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
432 std::vector<IntOrString> Data;
433
434 // Consume comma before each consecutive token except the first one.
435 bool ConsumeComma = false;
436 while (!consumeOptionalType(Kind::BlockEnd)) {
437 if (ConsumeComma)
438 RETURN_IF_ERROR(consumeType(Kind::Comma));
439 ConsumeComma = true;
440
441 ASSIGN_OR_RETURN(Item, readIntOrString());
442 Data.push_back(*Item);
443 }
444
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000445 return llvm::make_unique<UserDefinedResource>(Type, std::move(Data));
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000446}
447
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000448RCParser::ParseType RCParser::parseVersionInfoResource() {
449 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
450 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
Marek Sokolowski99ead702017-09-28 23:12:53 +0000451 return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult),
452 std::move(*FixedResult));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000453}
454
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000455Expected<Control> RCParser::parseControl() {
456 // Each control definition (except CONTROL) follows one of the schemes below
457 // depending on the control class:
458 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
459 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
460 // Note that control ids must be integers.
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000461 // Text might be either a string or an integer pointing to resource ID.
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000462 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
Marek Sokolowski75fa1732017-08-29 20:03:18 +0000463 std::string ClassUpper = ClassResult->upper();
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000464 auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
465 if (CtlInfo == Control::SupportedCtls.end())
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000466 return getExpectedError("control type, END or '}'", true);
467
468 // Read caption if necessary.
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000469 IntOrString Caption{StringRef()};
470 if (CtlInfo->getValue().HasTitle) {
471 ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000472 RETURN_IF_ERROR(consumeType(Kind::Comma));
473 Caption = *CaptionResult;
474 }
475
476 ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8));
477
478 auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> {
Zachary Turner07bc04f2017-10-06 21:26:06 +0000479 return Args->size() > Id ? (uint32_t)(*Args)[Id] : Optional<uint32_t>();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000480 };
481
482 return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2],
483 (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6),
484 TakeOptArg(7));
485}
486
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000487RCParser::ParseType RCParser::parseIconResource() {
488 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000489 return llvm::make_unique<IconResource>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000490}
491
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000492RCParser::ParseType RCParser::parseHTMLResource() {
493 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000494 return llvm::make_unique<HTMLResource>(*Arg);
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000495}
496
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000497RCParser::ParseType RCParser::parseMenuResource() {
498 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
499 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000500 return llvm::make_unique<MenuResource>(std::move(*OptStatements),
501 std::move(*Items));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000502}
503
504Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
505 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
506
507 MenuDefinitionList List;
508
509 // Read a set of items. Each item is of one of three kinds:
510 // MENUITEM SEPARATOR
511 // MENUITEM caption:String, result:Int [, menu flags]...
512 // POPUP caption:String [, menu flags]... { items... }
513 while (!consumeOptionalType(Kind::BlockEnd)) {
514 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
515
516 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
517 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
518 if (!IsMenuItem && !IsPopup)
519 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
520
521 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
522 // Now, expecting SEPARATOR.
523 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
524 if (SeparatorResult->equals_lower("SEPARATOR")) {
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000525 List.addDefinition(llvm::make_unique<MenuSeparator>());
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000526 continue;
527 }
528
529 return getExpectedError("SEPARATOR or string", true);
530 }
531
532 // Not a separator. Read the caption.
533 ASSIGN_OR_RETURN(CaptionResult, readString());
534
535 // If MENUITEM, expect also a comma and an integer.
536 uint32_t MenuResult = -1;
537
538 if (IsMenuItem) {
539 RETURN_IF_ERROR(consumeType(Kind::Comma));
540 ASSIGN_OR_RETURN(IntResult, readInt());
541 MenuResult = *IntResult;
542 }
543
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000544 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
545 MenuDefinition::OptionsFlags));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000546
547 if (IsPopup) {
548 // If POPUP, read submenu items recursively.
549 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000550 List.addDefinition(llvm::make_unique<PopupItem>(
551 *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000552 continue;
553 }
554
555 assert(IsMenuItem);
556 List.addDefinition(
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000557 llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000558 }
559
560 return std::move(List);
561}
562
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000563RCParser::ParseType RCParser::parseStringTableResource() {
564 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
565 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
566
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000567 auto Table =
568 llvm::make_unique<StringTableResource>(std::move(*OptStatements));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000569
570 // Read strings until we reach the end of the block.
571 while (!consumeOptionalType(Kind::BlockEnd)) {
572 // Each definition consists of string's ID (an integer) and a string.
573 // Some examples in documentation suggest that there might be a comma in
574 // between, however we strictly adhere to the single statement definition.
575 ASSIGN_OR_RETURN(IDResult, readInt());
Martin Storsjo94102762018-05-07 20:27:28 +0000576 consumeOptionalType(Kind::Comma);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000577 ASSIGN_OR_RETURN(StrResult, readString());
578 Table->addString(*IDResult, *StrResult);
579 }
580
581 return std::move(Table);
582}
583
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000584Expected<std::unique_ptr<VersionInfoBlock>>
585RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
586 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
587
Marek Sokolowski99ead702017-09-28 23:12:53 +0000588 auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000589
590 while (!isNextTokenKind(Kind::BlockEnd)) {
591 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
592 Contents->addStmt(std::move(*Stmt));
593 }
594
595 consume(); // Consume BlockEnd.
596
597 return std::move(Contents);
598}
599
600Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
601 // Expect either BLOCK or VALUE, then a name or a key (a string).
602 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
603
604 if (TypeResult->equals_lower("BLOCK")) {
605 ASSIGN_OR_RETURN(NameResult, readString());
606 return parseVersionInfoBlockContents(*NameResult);
607 }
608
609 if (TypeResult->equals_lower("VALUE")) {
610 ASSIGN_OR_RETURN(KeyResult, readString());
Zachary Turner07bc04f2017-10-06 21:26:06 +0000611 // Read a non-empty list of strings and/or ints, each
612 // possibly preceded by a comma. Unfortunately, the tool behavior depends
613 // on them existing or not, so we need to memorize where we found them.
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000614 std::vector<IntOrString> Values;
Zachary Turner07bc04f2017-10-06 21:26:06 +0000615 std::vector<bool> PrecedingCommas;
616 RETURN_IF_ERROR(consumeType(Kind::Comma));
617 while (!isNextTokenKind(Kind::Identifier) &&
618 !isNextTokenKind(Kind::BlockEnd)) {
619 // Try to eat a comma if it's not the first statement.
620 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000621 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
622 Values.push_back(*ValueResult);
Zachary Turner07bc04f2017-10-06 21:26:06 +0000623 PrecedingCommas.push_back(HadComma);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000624 }
Zachary Turner07bc04f2017-10-06 21:26:06 +0000625 return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
626 std::move(PrecedingCommas));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000627 }
628
629 return getExpectedError("BLOCK or VALUE", true);
630}
631
632Expected<VersionInfoResource::VersionInfoFixed>
633RCParser::parseVersionInfoFixed() {
634 using RetType = VersionInfoResource::VersionInfoFixed;
635 RetType Result;
636
637 // Read until the beginning of the block.
638 while (!isNextTokenKind(Kind::BlockBegin)) {
639 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
640 auto FixedType = RetType::getFixedType(*TypeResult);
641
642 if (!RetType::isTypeSupported(FixedType))
643 return getExpectedError("fixed VERSIONINFO statement type", true);
644 if (Result.IsTypePresent[FixedType])
645 return getExpectedError("yet unread fixed VERSIONINFO statement type",
646 true);
647
648 // VERSION variations take multiple integers.
649 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
650 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
Zachary Turner07bc04f2017-10-06 21:26:06 +0000651 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
652 Result.setValue(FixedType, ArgInts);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000653 }
654
655 return Result;
656}
657
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000658RCParser::ParseOptionType RCParser::parseLanguageStmt() {
659 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000660 return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000661}
662
663RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
664 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000665 return llvm::make_unique<CharacteristicsStmt>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000666}
667
668RCParser::ParseOptionType RCParser::parseVersionStmt() {
669 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000670 return llvm::make_unique<VersionStmt>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000671}
672
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000673RCParser::ParseOptionType RCParser::parseCaptionStmt() {
674 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000675 return llvm::make_unique<CaptionStmt>(*Arg);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000676}
677
Zachary Turner420090a2017-10-06 20:51:20 +0000678RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
679 assert(DialogType != OptStmtType::BasicStmt);
680
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000681 ASSIGN_OR_RETURN(SizeResult, readInt());
682 RETURN_IF_ERROR(consumeType(Kind::Comma));
683 ASSIGN_OR_RETURN(NameResult, readString());
Zachary Turner420090a2017-10-06 20:51:20 +0000684
685 // Default values for the optional arguments.
686 uint32_t FontWeight = 0;
687 bool FontItalic = false;
688 uint32_t FontCharset = 1;
689 if (DialogType == OptStmtType::DialogExStmt) {
690 if (consumeOptionalType(Kind::Comma)) {
691 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
692 if (Args->size() >= 1)
693 FontWeight = (*Args)[0];
694 if (Args->size() >= 2)
695 FontItalic = (*Args)[1] != 0;
696 if (Args->size() >= 3)
697 FontCharset = (*Args)[2];
698 }
699 }
700 return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
701 FontItalic, FontCharset);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000702}
703
704RCParser::ParseOptionType RCParser::parseStyleStmt() {
705 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000706 return llvm::make_unique<StyleStmt>(*Arg);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000707}
708
Zachary Turner514b7102017-10-09 18:50:29 +0000709Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000710 return make_error<ParserError>(
711 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
712}
713
714} // namespace rc
715} // namespace llvm