blob: c66fc4fc2e70c3516f2fea4c2dd1c11448edc171 [file] [log] [blame]
Marek Sokolowski0b33df92017-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 Turnere315d732017-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 Sokolowski0b33df92017-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 Turner5d1b2d32017-10-09 18:50:29 +000035RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
Marek Sokolowski0b33df92017-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 Turner5d1b2d32017-10-09 18:50:29 +000044RCParser::RCParser(std::vector<RCToken> TokenList)
Marek Sokolowski0b33df92017-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 Sokolowski66c13b12017-08-28 22:58:31 +000067 if (TypeToken->equalsLower("ACCELERATORS"))
68 Result = parseAcceleratorsResource();
Martin Storsjo31dc80f2018-05-07 20:27:37 +000069 else if (TypeToken->equalsLower("BITMAP"))
70 Result = parseBitmapResource();
Marek Sokolowski66c13b12017-08-28 22:58:31 +000071 else if (TypeToken->equalsLower("CURSOR"))
Marek Sokolowskif2e55892017-08-28 21:59:54 +000072 Result = parseCursorResource();
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +000073 else if (TypeToken->equalsLower("DIALOG"))
74 Result = parseDialogResource(false);
75 else if (TypeToken->equalsLower("DIALOGEX"))
76 Result = parseDialogResource(true);
Marek Sokolowskif2e55892017-08-28 21:59:54 +000077 else if (TypeToken->equalsLower("HTML"))
78 Result = parseHTMLResource();
Martin Storsjo694780d2018-05-07 20:27:15 +000079 else if (TypeToken->equalsLower("ICON"))
80 Result = parseIconResource();
Marek Sokolowski233d2b82017-08-28 23:46:30 +000081 else if (TypeToken->equalsLower("MENU"))
82 Result = parseMenuResource();
Martin Storsjo0aa38c82018-05-09 18:20:56 +000083 else if (TypeToken->equalsLower("RCDATA"))
84 Result = parseUserDefinedResource(RkRcData);
Marek Sokolowski86b61382017-09-28 22:41:38 +000085 else if (TypeToken->equalsLower("VERSIONINFO"))
86 Result = parseVersionInfoResource();
Marek Sokolowski0b33df92017-08-18 18:24:17 +000087 else
Marek Sokolowski0bce0412017-09-29 00:14:18 +000088 Result = parseUserDefinedResource(*TypeToken);
Marek Sokolowski0b33df92017-08-18 18:24:17 +000089
90 if (Result)
91 (*Result)->setName(*NameToken);
92
93 return Result;
94}
95
96bool RCParser::isNextTokenKind(Kind TokenKind) const {
97 return !isEof() && look().kind() == TokenKind;
98}
99
100const RCToken &RCParser::look() const {
101 assert(!isEof());
102 return *CurLoc;
103}
104
105const RCToken &RCParser::read() {
106 assert(!isEof());
107 return *CurLoc++;
108}
109
110void RCParser::consume() {
111 assert(!isEof());
112 CurLoc++;
113}
114
Marek Sokolowski59066482017-09-28 23:53:25 +0000115// An integer description might consist of a single integer or
116// an arithmetic expression evaluating to the integer. The expressions
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000117// can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning
118// is the same as in C++ except for 'not' expression.
Marek Sokolowski59066482017-09-28 23:53:25 +0000119// The operators in the original RC implementation have the following
120// precedence:
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000121// 1) Unary operators (- ~ not),
Marek Sokolowski59066482017-09-28 23:53:25 +0000122// 2) Binary operators (+ - & |), with no precedence.
123//
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000124// 'not' expression is mostly useful for style values. It evaluates to 0,
125// but value given to the operator is stored separately from integer value.
126// It's mostly useful for control style expressions and causes bits from
127// default control style to be excluded from generated style. For binary
128// operators the mask from the right operand is applied to the left operand
129// and masks from both operands are combined in operator result.
130//
Marek Sokolowski59066482017-09-28 23:53:25 +0000131// The following grammar is used to parse the expressions Exp1:
132// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000133// Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
Marek Sokolowski59066482017-09-28 23:53:25 +0000134// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
135// separated by binary operators.)
136//
137// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
138// is read by parseIntExpr2().
139//
140// The original Microsoft tool handles multiple unary operators incorrectly.
141// For example, in 16-bit little-endian integers:
142// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
143// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
144// Our implementation differs from the original one and handles these
145// operators correctly:
146// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
147// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
148
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000149Expected<RCInt> RCParser::readInt() {
150 ASSIGN_OR_RETURN(Value, parseIntExpr1());
151 return (*Value).getValue();
152}
Marek Sokolowski59066482017-09-28 23:53:25 +0000153
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000154Expected<IntWithNotMask> RCParser::parseIntExpr1() {
Marek Sokolowski59066482017-09-28 23:53:25 +0000155 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
156 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000157 IntWithNotMask Result = *FirstResult;
Marek Sokolowski59066482017-09-28 23:53:25 +0000158
159 while (!isEof() && look().isBinaryOp()) {
160 auto OpToken = read();
161 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
162
163 switch (OpToken.kind()) {
164 case Kind::Plus:
165 Result += *NextResult;
166 break;
167
168 case Kind::Minus:
169 Result -= *NextResult;
170 break;
171
172 case Kind::Pipe:
173 Result |= *NextResult;
174 break;
175
176 case Kind::Amp:
177 Result &= *NextResult;
178 break;
179
180 default:
181 llvm_unreachable("Already processed all binary ops.");
182 }
183 }
184
185 return Result;
186}
187
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000188Expected<IntWithNotMask> RCParser::parseIntExpr2() {
189 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
Marek Sokolowski59066482017-09-28 23:53:25 +0000190 static const char ErrorMsg[] = "'-', '~', integer or '('";
191
192 if (isEof())
193 return getExpectedError(ErrorMsg);
194
195 switch (look().kind()) {
196 case Kind::Minus: {
197 consume();
198 ASSIGN_OR_RETURN(Result, parseIntExpr2());
199 return -(*Result);
200 }
201
202 case Kind::Tilde: {
203 consume();
204 ASSIGN_OR_RETURN(Result, parseIntExpr2());
205 return ~(*Result);
206 }
207
208 case Kind::Int:
Zachary Turner93bb30d2017-10-06 21:26:06 +0000209 return RCInt(read());
Marek Sokolowski59066482017-09-28 23:53:25 +0000210
211 case Kind::LeftParen: {
212 consume();
213 ASSIGN_OR_RETURN(Result, parseIntExpr1());
214 RETURN_IF_ERROR(consumeType(Kind::RightParen));
215 return *Result;
216 }
217
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000218 case Kind::Identifier: {
219 if (!read().value().equals_lower("not"))
220 return getExpectedError(ErrorMsg, true);
221 ASSIGN_OR_RETURN(Result, parseIntExpr2());
222 return IntWithNotMask(0, (*Result).getValue());
223 }
224
Marek Sokolowski59066482017-09-28 23:53:25 +0000225 default:
226 return getExpectedError(ErrorMsg);
227 }
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000228}
229
230Expected<StringRef> RCParser::readString() {
231 if (!isNextTokenKind(Kind::String))
232 return getExpectedError("string");
233 return read().value();
234}
235
Martin Storsjo76e573d2018-05-08 08:47:37 +0000236Expected<StringRef> RCParser::readFilename() {
237 if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
238 return getExpectedError("string");
239 return read().value();
240}
241
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000242Expected<StringRef> RCParser::readIdentifier() {
243 if (!isNextTokenKind(Kind::Identifier))
244 return getExpectedError("identifier");
245 return read().value();
246}
247
Marek Sokolowski66c13b12017-08-28 22:58:31 +0000248Expected<IntOrString> RCParser::readIntOrString() {
249 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
250 return getExpectedError("int or string");
251 return IntOrString(read());
252}
253
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000254Expected<IntOrString> RCParser::readTypeOrName() {
255 // We suggest that the correct resource name or type should be either an
256 // identifier or an integer. The original RC tool is much more liberal.
257 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
258 return getExpectedError("int or identifier");
Marek Sokolowski66c13b12017-08-28 22:58:31 +0000259 return IntOrString(read());
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000260}
261
262Error RCParser::consumeType(Kind TokenKind) {
263 if (isNextTokenKind(TokenKind)) {
264 consume();
265 return Error::success();
266 }
267
268 switch (TokenKind) {
269#define TOKEN(TokenName) \
270 case Kind::TokenName: \
271 return getExpectedError(#TokenName);
272#define SHORT_TOKEN(TokenName, TokenCh) \
273 case Kind::TokenName: \
274 return getExpectedError(#TokenCh);
David Blaikiebb5acf92017-11-21 00:23:19 +0000275#include "ResourceScriptTokenList.def"
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000276 }
277
278 llvm_unreachable("All case options exhausted.");
279}
280
281bool RCParser::consumeOptionalType(Kind TokenKind) {
282 if (isNextTokenKind(TokenKind)) {
283 consume();
284 return true;
285 }
286
287 return false;
288}
289
Zachary Turner93bb30d2017-10-06 21:26:06 +0000290Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
291 size_t MaxCount) {
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000292 assert(MinCount <= MaxCount);
293
Zachary Turner93bb30d2017-10-06 21:26:06 +0000294 SmallVector<RCInt, 8> Result;
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000295
296 auto FailureHandler =
Zachary Turner93bb30d2017-10-06 21:26:06 +0000297 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000298 if (Result.size() < MinCount)
299 return std::move(Err);
300 consumeError(std::move(Err));
301 return Result;
302 };
303
304 for (size_t i = 0; i < MaxCount; ++i) {
305 // Try to read a comma unless we read the first token.
306 // Sometimes RC tool requires them and sometimes not. We decide to
307 // always require them.
308 if (i >= 1) {
309 if (auto CommaError = consumeType(Kind::Comma))
310 return FailureHandler(std::move(CommaError));
311 }
312
313 if (auto IntResult = readInt())
314 Result.push_back(*IntResult);
315 else
316 return FailureHandler(IntResult.takeError());
317 }
318
319 return std::move(Result);
320}
321
Marek Sokolowskiafe86312017-09-29 17:46:32 +0000322Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
323 ArrayRef<uint32_t> FlagValues) {
Marek Sokolowski66c13b12017-08-28 22:58:31 +0000324 assert(!FlagDesc.empty());
Marek Sokolowskiafe86312017-09-29 17:46:32 +0000325 assert(FlagDesc.size() == FlagValues.size());
Marek Sokolowski66c13b12017-08-28 22:58:31 +0000326
327 uint32_t Result = 0;
328 while (isNextTokenKind(Kind::Comma)) {
329 consume();
330 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
331 bool FoundFlag = false;
332
333 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
334 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
335 continue;
336
Marek Sokolowskiafe86312017-09-29 17:46:32 +0000337 Result |= FlagValues[FlagId];
Marek Sokolowski66c13b12017-08-28 22:58:31 +0000338 FoundFlag = true;
339 break;
340 }
341
342 if (!FoundFlag)
343 return getExpectedError(join(FlagDesc, "/"), true);
344 }
345
346 return Result;
347}
348
Martin Storsjo370633f2018-05-15 06:35:29 +0000349uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
350 while (!isEof()) {
351 const RCToken &Token = look();
352 if (Token.kind() != Kind::Identifier)
353 return Flags;
354 const StringRef Ident = Token.value();
355 if (Ident.equals_lower("PRELOAD"))
356 Flags |= MfPreload;
357 else if (Ident.equals_lower("LOADONCALL"))
358 Flags &= ~MfPreload;
359 else if (Ident.equals_lower("FIXED"))
360 Flags &= ~(MfMoveable | MfDiscardable);
361 else if (Ident.equals_lower("MOVEABLE"))
362 Flags |= MfMoveable;
363 else if (Ident.equals_lower("DISCARDABLE"))
364 Flags |= MfDiscardable | MfMoveable | MfPure;
365 else if (Ident.equals_lower("PURE"))
366 Flags |= MfPure;
367 else if (Ident.equals_lower("IMPURE"))
368 Flags &= ~(MfPure | MfDiscardable);
369 else if (Ident.equals_lower("SHARED"))
370 Flags |= MfPure;
371 else if (Ident.equals_lower("NONSHARED"))
372 Flags &= ~(MfPure | MfDiscardable);
373 else
374 return Flags;
375 consume();
376 }
377 return Flags;
378}
379
Zachary Turner44bde8d2017-10-06 20:51:20 +0000380Expected<OptionalStmtList>
381RCParser::parseOptionalStatements(OptStmtType StmtsType) {
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000382 OptionalStmtList Result;
383
384 // The last statement is always followed by the start of the block.
385 while (!isNextTokenKind(Kind::BlockBegin)) {
Zachary Turner44bde8d2017-10-06 20:51:20 +0000386 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000387 Result.addStmt(std::move(*SingleParse));
388 }
389
390 return std::move(Result);
391}
392
393Expected<std::unique_ptr<OptionalStmt>>
Zachary Turner44bde8d2017-10-06 20:51:20 +0000394RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000395 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
396 if (TypeToken->equals_lower("CHARACTERISTICS"))
397 return parseCharacteristicsStmt();
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000398 if (TypeToken->equals_lower("LANGUAGE"))
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000399 return parseLanguageStmt();
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000400 if (TypeToken->equals_lower("VERSION"))
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000401 return parseVersionStmt();
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000402
Zachary Turner44bde8d2017-10-06 20:51:20 +0000403 if (StmtsType != OptStmtType::BasicStmt) {
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000404 if (TypeToken->equals_lower("CAPTION"))
405 return parseCaptionStmt();
Martin Storsjo65de7bd2018-05-15 19:21:28 +0000406 if (TypeToken->equals_lower("CLASS"))
407 return parseClassStmt();
Martin Storsjocec32ed2018-11-29 12:17:39 +0000408 if (TypeToken->equals_lower("EXSTYLE"))
409 return parseExStyleStmt();
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000410 if (TypeToken->equals_lower("FONT"))
Zachary Turner44bde8d2017-10-06 20:51:20 +0000411 return parseFontStmt(StmtsType);
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000412 if (TypeToken->equals_lower("STYLE"))
413 return parseStyleStmt();
414 }
415
416 return getExpectedError("optional statement type, BEGIN or '{'",
417 /* IsAlreadyRead = */ true);
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000418}
419
420RCParser::ParseType RCParser::parseLanguageResource() {
421 // Read LANGUAGE as an optional statement. If it's read correctly, we can
422 // upcast it to RCResource.
423 return parseLanguageStmt();
424}
425
Marek Sokolowski66c13b12017-08-28 22:58:31 +0000426RCParser::ParseType RCParser::parseAcceleratorsResource() {
Martin Storsjo370633f2018-05-15 06:35:29 +0000427 uint16_t MemoryFlags =
428 parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
Marek Sokolowski66c13b12017-08-28 22:58:31 +0000429 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
430 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
431
Martin Storsjo370633f2018-05-15 06:35:29 +0000432 auto Accels = llvm::make_unique<AcceleratorsResource>(
433 std::move(*OptStatements), MemoryFlags);
Marek Sokolowski66c13b12017-08-28 22:58:31 +0000434
435 while (!consumeOptionalType(Kind::BlockEnd)) {
436 ASSIGN_OR_RETURN(EventResult, readIntOrString());
437 RETURN_IF_ERROR(consumeType(Kind::Comma));
438 ASSIGN_OR_RETURN(IDResult, readInt());
Marek Sokolowskiafe86312017-09-29 17:46:32 +0000439 ASSIGN_OR_RETURN(
440 FlagsResult,
441 parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
442 AcceleratorsResource::Accelerator::OptionsFlags));
Marek Sokolowski66c13b12017-08-28 22:58:31 +0000443 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
444 }
445
446 return std::move(Accels);
447}
448
Marek Sokolowskif2e55892017-08-28 21:59:54 +0000449RCParser::ParseType RCParser::parseCursorResource() {
Martin Storsjo370633f2018-05-15 06:35:29 +0000450 uint16_t MemoryFlags =
451 parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
Martin Storsjo76e573d2018-05-08 08:47:37 +0000452 ASSIGN_OR_RETURN(Arg, readFilename());
Martin Storsjo370633f2018-05-15 06:35:29 +0000453 return llvm::make_unique<CursorResource>(*Arg, MemoryFlags);
Marek Sokolowskif2e55892017-08-28 21:59:54 +0000454}
455
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000456RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
Martin Storsjo370633f2018-05-15 06:35:29 +0000457 uint16_t MemoryFlags =
458 parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000459 // Dialog resources have the following format of the arguments:
460 // DIALOG: x, y, width, height [opt stmts...] {controls...}
461 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
462 // These are very similar, so we parse them together.
463 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
464
465 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
466 if (IsExtended && consumeOptionalType(Kind::Comma)) {
467 ASSIGN_OR_RETURN(HelpIDResult, readInt());
468 HelpID = *HelpIDResult;
469 }
470
Zachary Turner44bde8d2017-10-06 20:51:20 +0000471 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
472 IsExtended ? OptStmtType::DialogExStmt
473 : OptStmtType::DialogStmt));
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000474
475 assert(isNextTokenKind(Kind::BlockBegin) &&
476 "parseOptionalStatements, when successful, halts on BlockBegin.");
477 consume();
478
Marek Sokolowski38bee042017-09-29 00:33:57 +0000479 auto Dialog = llvm::make_unique<DialogResource>(
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000480 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
Martin Storsjo370633f2018-05-15 06:35:29 +0000481 HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000482
483 while (!consumeOptionalType(Kind::BlockEnd)) {
484 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
485 Dialog->addControl(std::move(*ControlDefResult));
486 }
487
488 return std::move(Dialog);
489}
490
Marek Sokolowski0bce0412017-09-29 00:14:18 +0000491RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
Martin Storsjo370633f2018-05-15 06:35:29 +0000492 uint16_t MemoryFlags =
493 parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
Marek Sokolowski0bce0412017-09-29 00:14:18 +0000494 if (isEof())
495 return getExpectedError("filename, '{' or BEGIN");
496
497 // Check if this is a file resource.
Martin Storsjo76e573d2018-05-08 08:47:37 +0000498 switch (look().kind()) {
499 case Kind::String:
500 case Kind::Identifier:
Martin Storsjo370633f2018-05-15 06:35:29 +0000501 return llvm::make_unique<UserDefinedResource>(Type, read().value(),
502 MemoryFlags);
Martin Storsjo76e573d2018-05-08 08:47:37 +0000503 default:
504 break;
505 }
Marek Sokolowski0bce0412017-09-29 00:14:18 +0000506
507 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
508 std::vector<IntOrString> Data;
509
510 // Consume comma before each consecutive token except the first one.
511 bool ConsumeComma = false;
512 while (!consumeOptionalType(Kind::BlockEnd)) {
513 if (ConsumeComma)
514 RETURN_IF_ERROR(consumeType(Kind::Comma));
515 ConsumeComma = true;
516
517 ASSIGN_OR_RETURN(Item, readIntOrString());
518 Data.push_back(*Item);
519 }
520
Martin Storsjo370633f2018-05-15 06:35:29 +0000521 return llvm::make_unique<UserDefinedResource>(Type, std::move(Data),
522 MemoryFlags);
Marek Sokolowski0bce0412017-09-29 00:14:18 +0000523}
524
Marek Sokolowski86b61382017-09-28 22:41:38 +0000525RCParser::ParseType RCParser::parseVersionInfoResource() {
Martin Storsjo370633f2018-05-15 06:35:29 +0000526 uint16_t MemoryFlags =
527 parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
Marek Sokolowski86b61382017-09-28 22:41:38 +0000528 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
529 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
Martin Storsjo370633f2018-05-15 06:35:29 +0000530 return llvm::make_unique<VersionInfoResource>(
531 std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
Marek Sokolowski86b61382017-09-28 22:41:38 +0000532}
533
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000534Expected<Control> RCParser::parseControl() {
535 // Each control definition (except CONTROL) follows one of the schemes below
536 // depending on the control class:
537 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
538 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
539 // Note that control ids must be integers.
Marek Sokolowskibbf12f32017-09-30 00:38:52 +0000540 // Text might be either a string or an integer pointing to resource ID.
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000541 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
Marek Sokolowski505883f2017-08-29 20:03:18 +0000542 std::string ClassUpper = ClassResult->upper();
Marek Sokolowskibbf12f32017-09-30 00:38:52 +0000543 auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
544 if (CtlInfo == Control::SupportedCtls.end())
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000545 return getExpectedError("control type, END or '}'", true);
546
547 // Read caption if necessary.
Marek Sokolowskibbf12f32017-09-30 00:38:52 +0000548 IntOrString Caption{StringRef()};
549 if (CtlInfo->getValue().HasTitle) {
550 ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000551 RETURN_IF_ERROR(consumeType(Kind::Comma));
552 Caption = *CaptionResult;
553 }
554
Martin Storsjo793104b2018-05-08 20:55:58 +0000555 ASSIGN_OR_RETURN(ID, readInt());
556 RETURN_IF_ERROR(consumeType(Kind::Comma));
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000557
Martin Storsjo793104b2018-05-08 20:55:58 +0000558 IntOrString Class;
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000559 Optional<IntWithNotMask> Style;
Martin Storsjo793104b2018-05-08 20:55:58 +0000560 if (ClassUpper == "CONTROL") {
561 // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
562 ASSIGN_OR_RETURN(ClassStr, readString());
563 RETURN_IF_ERROR(consumeType(Kind::Comma));
564 Class = *ClassStr;
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000565 ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());
Martin Storsjo793104b2018-05-08 20:55:58 +0000566 RETURN_IF_ERROR(consumeType(Kind::Comma));
567 Style = *StyleVal;
568 } else {
569 Class = CtlInfo->getValue().CtlClass;
570 }
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000571
Martin Storsjo793104b2018-05-08 20:55:58 +0000572 // x, y, width, height
573 ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
574
575 if (ClassUpper != "CONTROL") {
576 if (consumeOptionalType(Kind::Comma)) {
Martin Storsjoe96cf5f2018-12-05 13:22:56 +0000577 ASSIGN_OR_RETURN(Val, parseIntExpr1());
Martin Storsjo793104b2018-05-08 20:55:58 +0000578 Style = *Val;
579 }
580 }
581
582 Optional<uint32_t> ExStyle;
583 if (consumeOptionalType(Kind::Comma)) {
584 ASSIGN_OR_RETURN(Val, readInt());
585 ExStyle = *Val;
586 }
587 Optional<uint32_t> HelpID;
588 if (consumeOptionalType(Kind::Comma)) {
589 ASSIGN_OR_RETURN(Val, readInt());
590 HelpID = *Val;
591 }
592
593 return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
594 (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000595}
596
Martin Storsjo31dc80f2018-05-07 20:27:37 +0000597RCParser::ParseType RCParser::parseBitmapResource() {
Martin Storsjo370633f2018-05-15 06:35:29 +0000598 uint16_t MemoryFlags =
599 parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
Martin Storsjo76e573d2018-05-08 08:47:37 +0000600 ASSIGN_OR_RETURN(Arg, readFilename());
Martin Storsjo370633f2018-05-15 06:35:29 +0000601 return llvm::make_unique<BitmapResource>(*Arg, MemoryFlags);
Martin Storsjo31dc80f2018-05-07 20:27:37 +0000602}
603
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000604RCParser::ParseType RCParser::parseIconResource() {
Martin Storsjo370633f2018-05-15 06:35:29 +0000605 uint16_t MemoryFlags =
606 parseMemoryFlags(IconResource::getDefaultMemoryFlags());
Martin Storsjo76e573d2018-05-08 08:47:37 +0000607 ASSIGN_OR_RETURN(Arg, readFilename());
Martin Storsjo370633f2018-05-15 06:35:29 +0000608 return llvm::make_unique<IconResource>(*Arg, MemoryFlags);
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000609}
610
Marek Sokolowskif2e55892017-08-28 21:59:54 +0000611RCParser::ParseType RCParser::parseHTMLResource() {
Martin Storsjo370633f2018-05-15 06:35:29 +0000612 uint16_t MemoryFlags =
613 parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
Martin Storsjo76e573d2018-05-08 08:47:37 +0000614 ASSIGN_OR_RETURN(Arg, readFilename());
Martin Storsjo370633f2018-05-15 06:35:29 +0000615 return llvm::make_unique<HTMLResource>(*Arg, MemoryFlags);
Marek Sokolowskif2e55892017-08-28 21:59:54 +0000616}
617
Marek Sokolowski233d2b82017-08-28 23:46:30 +0000618RCParser::ParseType RCParser::parseMenuResource() {
Martin Storsjo370633f2018-05-15 06:35:29 +0000619 uint16_t MemoryFlags =
620 parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
Marek Sokolowski233d2b82017-08-28 23:46:30 +0000621 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
622 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
Marek Sokolowski38bee042017-09-29 00:33:57 +0000623 return llvm::make_unique<MenuResource>(std::move(*OptStatements),
Martin Storsjo370633f2018-05-15 06:35:29 +0000624 std::move(*Items), MemoryFlags);
Marek Sokolowski233d2b82017-08-28 23:46:30 +0000625}
626
627Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
628 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
629
630 MenuDefinitionList List;
631
632 // Read a set of items. Each item is of one of three kinds:
633 // MENUITEM SEPARATOR
634 // MENUITEM caption:String, result:Int [, menu flags]...
635 // POPUP caption:String [, menu flags]... { items... }
636 while (!consumeOptionalType(Kind::BlockEnd)) {
637 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
638
639 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
640 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
641 if (!IsMenuItem && !IsPopup)
642 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
643
644 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
645 // Now, expecting SEPARATOR.
646 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
647 if (SeparatorResult->equals_lower("SEPARATOR")) {
Marek Sokolowski38bee042017-09-29 00:33:57 +0000648 List.addDefinition(llvm::make_unique<MenuSeparator>());
Marek Sokolowski233d2b82017-08-28 23:46:30 +0000649 continue;
650 }
651
652 return getExpectedError("SEPARATOR or string", true);
653 }
654
655 // Not a separator. Read the caption.
656 ASSIGN_OR_RETURN(CaptionResult, readString());
657
658 // If MENUITEM, expect also a comma and an integer.
659 uint32_t MenuResult = -1;
660
661 if (IsMenuItem) {
662 RETURN_IF_ERROR(consumeType(Kind::Comma));
663 ASSIGN_OR_RETURN(IntResult, readInt());
664 MenuResult = *IntResult;
665 }
666
Marek Sokolowskiafe86312017-09-29 17:46:32 +0000667 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
668 MenuDefinition::OptionsFlags));
Marek Sokolowski233d2b82017-08-28 23:46:30 +0000669
670 if (IsPopup) {
671 // If POPUP, read submenu items recursively.
672 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
Marek Sokolowski38bee042017-09-29 00:33:57 +0000673 List.addDefinition(llvm::make_unique<PopupItem>(
674 *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
Marek Sokolowski233d2b82017-08-28 23:46:30 +0000675 continue;
676 }
677
678 assert(IsMenuItem);
679 List.addDefinition(
Marek Sokolowski38bee042017-09-29 00:33:57 +0000680 llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
Marek Sokolowski233d2b82017-08-28 23:46:30 +0000681 }
682
683 return std::move(List);
684}
685
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000686RCParser::ParseType RCParser::parseStringTableResource() {
Martin Storsjo370633f2018-05-15 06:35:29 +0000687 uint16_t MemoryFlags =
688 parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000689 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
690 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
691
Martin Storsjo370633f2018-05-15 06:35:29 +0000692 auto Table = llvm::make_unique<StringTableResource>(std::move(*OptStatements),
693 MemoryFlags);
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000694
695 // Read strings until we reach the end of the block.
696 while (!consumeOptionalType(Kind::BlockEnd)) {
697 // Each definition consists of string's ID (an integer) and a string.
698 // Some examples in documentation suggest that there might be a comma in
699 // between, however we strictly adhere to the single statement definition.
700 ASSIGN_OR_RETURN(IDResult, readInt());
Martin Storsjoab499d82018-05-07 20:27:28 +0000701 consumeOptionalType(Kind::Comma);
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000702 ASSIGN_OR_RETURN(StrResult, readString());
703 Table->addString(*IDResult, *StrResult);
704 }
705
706 return std::move(Table);
707}
708
Marek Sokolowski86b61382017-09-28 22:41:38 +0000709Expected<std::unique_ptr<VersionInfoBlock>>
710RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
711 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
712
Marek Sokolowskie3ae5952017-09-28 23:12:53 +0000713 auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
Marek Sokolowski86b61382017-09-28 22:41:38 +0000714
715 while (!isNextTokenKind(Kind::BlockEnd)) {
716 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
717 Contents->addStmt(std::move(*Stmt));
718 }
719
720 consume(); // Consume BlockEnd.
721
722 return std::move(Contents);
723}
724
725Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
726 // Expect either BLOCK or VALUE, then a name or a key (a string).
727 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
728
729 if (TypeResult->equals_lower("BLOCK")) {
730 ASSIGN_OR_RETURN(NameResult, readString());
731 return parseVersionInfoBlockContents(*NameResult);
732 }
733
734 if (TypeResult->equals_lower("VALUE")) {
735 ASSIGN_OR_RETURN(KeyResult, readString());
Zachary Turner93bb30d2017-10-06 21:26:06 +0000736 // Read a non-empty list of strings and/or ints, each
737 // possibly preceded by a comma. Unfortunately, the tool behavior depends
738 // on them existing or not, so we need to memorize where we found them.
Marek Sokolowski86b61382017-09-28 22:41:38 +0000739 std::vector<IntOrString> Values;
Zachary Turner93bb30d2017-10-06 21:26:06 +0000740 std::vector<bool> PrecedingCommas;
741 RETURN_IF_ERROR(consumeType(Kind::Comma));
742 while (!isNextTokenKind(Kind::Identifier) &&
743 !isNextTokenKind(Kind::BlockEnd)) {
744 // Try to eat a comma if it's not the first statement.
745 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
Marek Sokolowski86b61382017-09-28 22:41:38 +0000746 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
747 Values.push_back(*ValueResult);
Zachary Turner93bb30d2017-10-06 21:26:06 +0000748 PrecedingCommas.push_back(HadComma);
Marek Sokolowski86b61382017-09-28 22:41:38 +0000749 }
Zachary Turner93bb30d2017-10-06 21:26:06 +0000750 return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
751 std::move(PrecedingCommas));
Marek Sokolowski86b61382017-09-28 22:41:38 +0000752 }
753
754 return getExpectedError("BLOCK or VALUE", true);
755}
756
757Expected<VersionInfoResource::VersionInfoFixed>
758RCParser::parseVersionInfoFixed() {
759 using RetType = VersionInfoResource::VersionInfoFixed;
760 RetType Result;
761
762 // Read until the beginning of the block.
763 while (!isNextTokenKind(Kind::BlockBegin)) {
764 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
765 auto FixedType = RetType::getFixedType(*TypeResult);
766
767 if (!RetType::isTypeSupported(FixedType))
768 return getExpectedError("fixed VERSIONINFO statement type", true);
769 if (Result.IsTypePresent[FixedType])
770 return getExpectedError("yet unread fixed VERSIONINFO statement type",
771 true);
772
773 // VERSION variations take multiple integers.
774 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
775 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
Zachary Turner93bb30d2017-10-06 21:26:06 +0000776 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
777 Result.setValue(FixedType, ArgInts);
Marek Sokolowski86b61382017-09-28 22:41:38 +0000778 }
779
780 return Result;
781}
782
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000783RCParser::ParseOptionType RCParser::parseLanguageStmt() {
784 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
Marek Sokolowski38bee042017-09-29 00:33:57 +0000785 return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000786}
787
788RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
789 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski38bee042017-09-29 00:33:57 +0000790 return llvm::make_unique<CharacteristicsStmt>(*Arg);
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000791}
792
793RCParser::ParseOptionType RCParser::parseVersionStmt() {
794 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski38bee042017-09-29 00:33:57 +0000795 return llvm::make_unique<VersionStmt>(*Arg);
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000796}
797
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000798RCParser::ParseOptionType RCParser::parseCaptionStmt() {
799 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski38bee042017-09-29 00:33:57 +0000800 return llvm::make_unique<CaptionStmt>(*Arg);
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000801}
802
Martin Storsjo65de7bd2018-05-15 19:21:28 +0000803RCParser::ParseOptionType RCParser::parseClassStmt() {
804 ASSIGN_OR_RETURN(Arg, readIntOrString());
805 return llvm::make_unique<ClassStmt>(*Arg);
806}
807
Zachary Turner44bde8d2017-10-06 20:51:20 +0000808RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
809 assert(DialogType != OptStmtType::BasicStmt);
810
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000811 ASSIGN_OR_RETURN(SizeResult, readInt());
812 RETURN_IF_ERROR(consumeType(Kind::Comma));
813 ASSIGN_OR_RETURN(NameResult, readString());
Zachary Turner44bde8d2017-10-06 20:51:20 +0000814
815 // Default values for the optional arguments.
816 uint32_t FontWeight = 0;
817 bool FontItalic = false;
818 uint32_t FontCharset = 1;
819 if (DialogType == OptStmtType::DialogExStmt) {
820 if (consumeOptionalType(Kind::Comma)) {
821 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
822 if (Args->size() >= 1)
823 FontWeight = (*Args)[0];
824 if (Args->size() >= 2)
825 FontItalic = (*Args)[1] != 0;
826 if (Args->size() >= 3)
827 FontCharset = (*Args)[2];
828 }
829 }
830 return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
831 FontItalic, FontCharset);
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000832}
833
834RCParser::ParseOptionType RCParser::parseStyleStmt() {
835 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski38bee042017-09-29 00:33:57 +0000836 return llvm::make_unique<StyleStmt>(*Arg);
Marek Sokolowski7ca5fcc2017-08-29 16:49:59 +0000837}
838
Martin Storsjocec32ed2018-11-29 12:17:39 +0000839RCParser::ParseOptionType RCParser::parseExStyleStmt() {
840 ASSIGN_OR_RETURN(Arg, readInt());
841 return llvm::make_unique<ExStyleStmt>(*Arg);
842}
843
Zachary Turner5d1b2d32017-10-09 18:50:29 +0000844Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
Marek Sokolowski0b33df92017-08-18 18:24:17 +0000845 return make_error<ParserError>(
846 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
847}
848
849} // namespace rc
850} // namespace llvm