blob: 09c88ff879a1dc6f121dbda44c2f8ddaa2f21482 [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();
Martin Storsjo577b9812018-05-07 20:27:37 +000069 else if (TypeToken->equalsLower("BITMAP"))
70 Result = parseBitmapResource();
Marek Sokolowski7f110522017-08-28 22:58:31 +000071 else if (TypeToken->equalsLower("CURSOR"))
Marek Sokolowski72aa9372017-08-28 21:59:54 +000072 Result = parseCursorResource();
Marek Sokolowski4ac54d92017-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 Sokolowski72aa9372017-08-28 21:59:54 +000077 else if (TypeToken->equalsLower("HTML"))
78 Result = parseHTMLResource();
Martin Storsjo827ddb22018-05-07 20:27:15 +000079 else if (TypeToken->equalsLower("ICON"))
80 Result = parseIconResource();
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +000081 else if (TypeToken->equalsLower("MENU"))
82 Result = parseMenuResource();
Martin Storsjo7bc3c582018-05-09 18:20:56 +000083 else if (TypeToken->equalsLower("RCDATA"))
84 Result = parseUserDefinedResource(RkRcData);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +000085 else if (TypeToken->equalsLower("VERSIONINFO"))
86 Result = parseVersionInfoResource();
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000087 else
Marek Sokolowskib5f39a02017-09-29 00:14:18 +000088 Result = parseUserDefinedResource(*TypeToken);
Marek Sokolowski5cd3d5c2017-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 Sokolowski7e89ee72017-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
117// can contain the following tokens: <int> ( ) + - | & ~. Their meaning
118// is the same as in C++.
119// The operators in the original RC implementation have the following
120// precedence:
121// 1) Unary operators (- ~),
122// 2) Binary operators (+ - & |), with no precedence.
123//
124// The following grammar is used to parse the expressions Exp1:
125// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
126// Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
127// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
128// separated by binary operators.)
129//
130// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
131// is read by parseIntExpr2().
132//
133// The original Microsoft tool handles multiple unary operators incorrectly.
134// For example, in 16-bit little-endian integers:
135// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
136// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
137// Our implementation differs from the original one and handles these
138// operators correctly:
139// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
140// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
141
Zachary Turner07bc04f2017-10-06 21:26:06 +0000142Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); }
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000143
Zachary Turner07bc04f2017-10-06 21:26:06 +0000144Expected<RCInt> RCParser::parseIntExpr1() {
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000145 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
146 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
Zachary Turner07bc04f2017-10-06 21:26:06 +0000147 RCInt Result = *FirstResult;
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000148
149 while (!isEof() && look().isBinaryOp()) {
150 auto OpToken = read();
151 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
152
153 switch (OpToken.kind()) {
154 case Kind::Plus:
155 Result += *NextResult;
156 break;
157
158 case Kind::Minus:
159 Result -= *NextResult;
160 break;
161
162 case Kind::Pipe:
163 Result |= *NextResult;
164 break;
165
166 case Kind::Amp:
167 Result &= *NextResult;
168 break;
169
170 default:
171 llvm_unreachable("Already processed all binary ops.");
172 }
173 }
174
175 return Result;
176}
177
Zachary Turner07bc04f2017-10-06 21:26:06 +0000178Expected<RCInt> RCParser::parseIntExpr2() {
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000179 // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
180 static const char ErrorMsg[] = "'-', '~', integer or '('";
181
182 if (isEof())
183 return getExpectedError(ErrorMsg);
184
185 switch (look().kind()) {
186 case Kind::Minus: {
187 consume();
188 ASSIGN_OR_RETURN(Result, parseIntExpr2());
189 return -(*Result);
190 }
191
192 case Kind::Tilde: {
193 consume();
194 ASSIGN_OR_RETURN(Result, parseIntExpr2());
195 return ~(*Result);
196 }
197
198 case Kind::Int:
Zachary Turner07bc04f2017-10-06 21:26:06 +0000199 return RCInt(read());
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000200
201 case Kind::LeftParen: {
202 consume();
203 ASSIGN_OR_RETURN(Result, parseIntExpr1());
204 RETURN_IF_ERROR(consumeType(Kind::RightParen));
205 return *Result;
206 }
207
208 default:
209 return getExpectedError(ErrorMsg);
210 }
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000211}
212
213Expected<StringRef> RCParser::readString() {
214 if (!isNextTokenKind(Kind::String))
215 return getExpectedError("string");
216 return read().value();
217}
218
Martin Storsjo4021cee2018-05-08 08:47:37 +0000219Expected<StringRef> RCParser::readFilename() {
220 if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
221 return getExpectedError("string");
222 return read().value();
223}
224
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000225Expected<StringRef> RCParser::readIdentifier() {
226 if (!isNextTokenKind(Kind::Identifier))
227 return getExpectedError("identifier");
228 return read().value();
229}
230
Marek Sokolowski7f110522017-08-28 22:58:31 +0000231Expected<IntOrString> RCParser::readIntOrString() {
232 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
233 return getExpectedError("int or string");
234 return IntOrString(read());
235}
236
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000237Expected<IntOrString> RCParser::readTypeOrName() {
238 // We suggest that the correct resource name or type should be either an
239 // identifier or an integer. The original RC tool is much more liberal.
240 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
241 return getExpectedError("int or identifier");
Marek Sokolowski7f110522017-08-28 22:58:31 +0000242 return IntOrString(read());
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000243}
244
245Error RCParser::consumeType(Kind TokenKind) {
246 if (isNextTokenKind(TokenKind)) {
247 consume();
248 return Error::success();
249 }
250
251 switch (TokenKind) {
252#define TOKEN(TokenName) \
253 case Kind::TokenName: \
254 return getExpectedError(#TokenName);
255#define SHORT_TOKEN(TokenName, TokenCh) \
256 case Kind::TokenName: \
257 return getExpectedError(#TokenCh);
David Blaikieb961d292017-11-21 00:23:19 +0000258#include "ResourceScriptTokenList.def"
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000259 }
260
261 llvm_unreachable("All case options exhausted.");
262}
263
264bool RCParser::consumeOptionalType(Kind TokenKind) {
265 if (isNextTokenKind(TokenKind)) {
266 consume();
267 return true;
268 }
269
270 return false;
271}
272
Zachary Turner07bc04f2017-10-06 21:26:06 +0000273Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
274 size_t MaxCount) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000275 assert(MinCount <= MaxCount);
276
Zachary Turner07bc04f2017-10-06 21:26:06 +0000277 SmallVector<RCInt, 8> Result;
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000278
279 auto FailureHandler =
Zachary Turner07bc04f2017-10-06 21:26:06 +0000280 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000281 if (Result.size() < MinCount)
282 return std::move(Err);
283 consumeError(std::move(Err));
284 return Result;
285 };
286
287 for (size_t i = 0; i < MaxCount; ++i) {
288 // Try to read a comma unless we read the first token.
289 // Sometimes RC tool requires them and sometimes not. We decide to
290 // always require them.
291 if (i >= 1) {
292 if (auto CommaError = consumeType(Kind::Comma))
293 return FailureHandler(std::move(CommaError));
294 }
295
296 if (auto IntResult = readInt())
297 Result.push_back(*IntResult);
298 else
299 return FailureHandler(IntResult.takeError());
300 }
301
302 return std::move(Result);
303}
304
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000305Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
306 ArrayRef<uint32_t> FlagValues) {
Marek Sokolowski7f110522017-08-28 22:58:31 +0000307 assert(!FlagDesc.empty());
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000308 assert(FlagDesc.size() == FlagValues.size());
Marek Sokolowski7f110522017-08-28 22:58:31 +0000309
310 uint32_t Result = 0;
311 while (isNextTokenKind(Kind::Comma)) {
312 consume();
313 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
314 bool FoundFlag = false;
315
316 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
317 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
318 continue;
319
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000320 Result |= FlagValues[FlagId];
Marek Sokolowski7f110522017-08-28 22:58:31 +0000321 FoundFlag = true;
322 break;
323 }
324
325 if (!FoundFlag)
326 return getExpectedError(join(FlagDesc, "/"), true);
327 }
328
329 return Result;
330}
331
Martin Storsjo11adbac2018-05-15 06:35:29 +0000332uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
333 while (!isEof()) {
334 const RCToken &Token = look();
335 if (Token.kind() != Kind::Identifier)
336 return Flags;
337 const StringRef Ident = Token.value();
338 if (Ident.equals_lower("PRELOAD"))
339 Flags |= MfPreload;
340 else if (Ident.equals_lower("LOADONCALL"))
341 Flags &= ~MfPreload;
342 else if (Ident.equals_lower("FIXED"))
343 Flags &= ~(MfMoveable | MfDiscardable);
344 else if (Ident.equals_lower("MOVEABLE"))
345 Flags |= MfMoveable;
346 else if (Ident.equals_lower("DISCARDABLE"))
347 Flags |= MfDiscardable | MfMoveable | MfPure;
348 else if (Ident.equals_lower("PURE"))
349 Flags |= MfPure;
350 else if (Ident.equals_lower("IMPURE"))
351 Flags &= ~(MfPure | MfDiscardable);
352 else if (Ident.equals_lower("SHARED"))
353 Flags |= MfPure;
354 else if (Ident.equals_lower("NONSHARED"))
355 Flags &= ~(MfPure | MfDiscardable);
356 else
357 return Flags;
358 consume();
359 }
360 return Flags;
361}
362
Zachary Turner420090a2017-10-06 20:51:20 +0000363Expected<OptionalStmtList>
364RCParser::parseOptionalStatements(OptStmtType StmtsType) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000365 OptionalStmtList Result;
366
367 // The last statement is always followed by the start of the block.
368 while (!isNextTokenKind(Kind::BlockBegin)) {
Zachary Turner420090a2017-10-06 20:51:20 +0000369 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000370 Result.addStmt(std::move(*SingleParse));
371 }
372
373 return std::move(Result);
374}
375
376Expected<std::unique_ptr<OptionalStmt>>
Zachary Turner420090a2017-10-06 20:51:20 +0000377RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000378 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
379 if (TypeToken->equals_lower("CHARACTERISTICS"))
380 return parseCharacteristicsStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000381 if (TypeToken->equals_lower("LANGUAGE"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000382 return parseLanguageStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000383 if (TypeToken->equals_lower("VERSION"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000384 return parseVersionStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000385
Zachary Turner420090a2017-10-06 20:51:20 +0000386 if (StmtsType != OptStmtType::BasicStmt) {
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000387 if (TypeToken->equals_lower("CAPTION"))
388 return parseCaptionStmt();
Martin Storsjoe241ce62018-05-15 19:21:28 +0000389 if (TypeToken->equals_lower("CLASS"))
390 return parseClassStmt();
Martin Storsjoa876b5c2018-11-29 12:17:39 +0000391 if (TypeToken->equals_lower("EXSTYLE"))
392 return parseExStyleStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000393 if (TypeToken->equals_lower("FONT"))
Zachary Turner420090a2017-10-06 20:51:20 +0000394 return parseFontStmt(StmtsType);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000395 if (TypeToken->equals_lower("STYLE"))
396 return parseStyleStmt();
397 }
398
399 return getExpectedError("optional statement type, BEGIN or '{'",
400 /* IsAlreadyRead = */ true);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000401}
402
403RCParser::ParseType RCParser::parseLanguageResource() {
404 // Read LANGUAGE as an optional statement. If it's read correctly, we can
405 // upcast it to RCResource.
406 return parseLanguageStmt();
407}
408
Marek Sokolowski7f110522017-08-28 22:58:31 +0000409RCParser::ParseType RCParser::parseAcceleratorsResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000410 uint16_t MemoryFlags =
411 parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
Marek Sokolowski7f110522017-08-28 22:58:31 +0000412 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
413 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
414
Martin Storsjo11adbac2018-05-15 06:35:29 +0000415 auto Accels = llvm::make_unique<AcceleratorsResource>(
416 std::move(*OptStatements), MemoryFlags);
Marek Sokolowski7f110522017-08-28 22:58:31 +0000417
418 while (!consumeOptionalType(Kind::BlockEnd)) {
419 ASSIGN_OR_RETURN(EventResult, readIntOrString());
420 RETURN_IF_ERROR(consumeType(Kind::Comma));
421 ASSIGN_OR_RETURN(IDResult, readInt());
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000422 ASSIGN_OR_RETURN(
423 FlagsResult,
424 parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
425 AcceleratorsResource::Accelerator::OptionsFlags));
Marek Sokolowski7f110522017-08-28 22:58:31 +0000426 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
427 }
428
429 return std::move(Accels);
430}
431
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000432RCParser::ParseType RCParser::parseCursorResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000433 uint16_t MemoryFlags =
434 parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
Martin Storsjo4021cee2018-05-08 08:47:37 +0000435 ASSIGN_OR_RETURN(Arg, readFilename());
Martin Storsjo11adbac2018-05-15 06:35:29 +0000436 return llvm::make_unique<CursorResource>(*Arg, MemoryFlags);
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000437}
438
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000439RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000440 uint16_t MemoryFlags =
441 parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000442 // Dialog resources have the following format of the arguments:
443 // DIALOG: x, y, width, height [opt stmts...] {controls...}
444 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
445 // These are very similar, so we parse them together.
446 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
447
448 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
449 if (IsExtended && consumeOptionalType(Kind::Comma)) {
450 ASSIGN_OR_RETURN(HelpIDResult, readInt());
451 HelpID = *HelpIDResult;
452 }
453
Zachary Turner420090a2017-10-06 20:51:20 +0000454 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
455 IsExtended ? OptStmtType::DialogExStmt
456 : OptStmtType::DialogStmt));
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000457
458 assert(isNextTokenKind(Kind::BlockBegin) &&
459 "parseOptionalStatements, when successful, halts on BlockBegin.");
460 consume();
461
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000462 auto Dialog = llvm::make_unique<DialogResource>(
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000463 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
Martin Storsjo11adbac2018-05-15 06:35:29 +0000464 HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000465
466 while (!consumeOptionalType(Kind::BlockEnd)) {
467 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
468 Dialog->addControl(std::move(*ControlDefResult));
469 }
470
471 return std::move(Dialog);
472}
473
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000474RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000475 uint16_t MemoryFlags =
476 parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000477 if (isEof())
478 return getExpectedError("filename, '{' or BEGIN");
479
480 // Check if this is a file resource.
Martin Storsjo4021cee2018-05-08 08:47:37 +0000481 switch (look().kind()) {
482 case Kind::String:
483 case Kind::Identifier:
Martin Storsjo11adbac2018-05-15 06:35:29 +0000484 return llvm::make_unique<UserDefinedResource>(Type, read().value(),
485 MemoryFlags);
Martin Storsjo4021cee2018-05-08 08:47:37 +0000486 default:
487 break;
488 }
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000489
490 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
491 std::vector<IntOrString> Data;
492
493 // Consume comma before each consecutive token except the first one.
494 bool ConsumeComma = false;
495 while (!consumeOptionalType(Kind::BlockEnd)) {
496 if (ConsumeComma)
497 RETURN_IF_ERROR(consumeType(Kind::Comma));
498 ConsumeComma = true;
499
500 ASSIGN_OR_RETURN(Item, readIntOrString());
501 Data.push_back(*Item);
502 }
503
Martin Storsjo11adbac2018-05-15 06:35:29 +0000504 return llvm::make_unique<UserDefinedResource>(Type, std::move(Data),
505 MemoryFlags);
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000506}
507
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000508RCParser::ParseType RCParser::parseVersionInfoResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000509 uint16_t MemoryFlags =
510 parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000511 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
512 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
Martin Storsjo11adbac2018-05-15 06:35:29 +0000513 return llvm::make_unique<VersionInfoResource>(
514 std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000515}
516
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000517Expected<Control> RCParser::parseControl() {
518 // Each control definition (except CONTROL) follows one of the schemes below
519 // depending on the control class:
520 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
521 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
522 // Note that control ids must be integers.
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000523 // Text might be either a string or an integer pointing to resource ID.
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000524 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
Marek Sokolowski75fa1732017-08-29 20:03:18 +0000525 std::string ClassUpper = ClassResult->upper();
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000526 auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
527 if (CtlInfo == Control::SupportedCtls.end())
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000528 return getExpectedError("control type, END or '}'", true);
529
530 // Read caption if necessary.
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000531 IntOrString Caption{StringRef()};
532 if (CtlInfo->getValue().HasTitle) {
533 ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000534 RETURN_IF_ERROR(consumeType(Kind::Comma));
535 Caption = *CaptionResult;
536 }
537
Martin Storsjo818bd562018-05-08 20:55:58 +0000538 ASSIGN_OR_RETURN(ID, readInt());
539 RETURN_IF_ERROR(consumeType(Kind::Comma));
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000540
Martin Storsjo818bd562018-05-08 20:55:58 +0000541 IntOrString Class;
542 Optional<uint32_t> Style;
543 if (ClassUpper == "CONTROL") {
544 // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
545 ASSIGN_OR_RETURN(ClassStr, readString());
546 RETURN_IF_ERROR(consumeType(Kind::Comma));
547 Class = *ClassStr;
548 ASSIGN_OR_RETURN(StyleVal, readInt());
549 RETURN_IF_ERROR(consumeType(Kind::Comma));
550 Style = *StyleVal;
551 } else {
552 Class = CtlInfo->getValue().CtlClass;
553 }
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000554
Martin Storsjo818bd562018-05-08 20:55:58 +0000555 // x, y, width, height
556 ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
557
558 if (ClassUpper != "CONTROL") {
559 if (consumeOptionalType(Kind::Comma)) {
560 ASSIGN_OR_RETURN(Val, readInt());
561 Style = *Val;
562 }
563 }
564
565 Optional<uint32_t> ExStyle;
566 if (consumeOptionalType(Kind::Comma)) {
567 ASSIGN_OR_RETURN(Val, readInt());
568 ExStyle = *Val;
569 }
570 Optional<uint32_t> HelpID;
571 if (consumeOptionalType(Kind::Comma)) {
572 ASSIGN_OR_RETURN(Val, readInt());
573 HelpID = *Val;
574 }
575
576 return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
577 (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000578}
579
Martin Storsjo577b9812018-05-07 20:27:37 +0000580RCParser::ParseType RCParser::parseBitmapResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000581 uint16_t MemoryFlags =
582 parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
Martin Storsjo4021cee2018-05-08 08:47:37 +0000583 ASSIGN_OR_RETURN(Arg, readFilename());
Martin Storsjo11adbac2018-05-15 06:35:29 +0000584 return llvm::make_unique<BitmapResource>(*Arg, MemoryFlags);
Martin Storsjo577b9812018-05-07 20:27:37 +0000585}
586
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000587RCParser::ParseType RCParser::parseIconResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000588 uint16_t MemoryFlags =
589 parseMemoryFlags(IconResource::getDefaultMemoryFlags());
Martin Storsjo4021cee2018-05-08 08:47:37 +0000590 ASSIGN_OR_RETURN(Arg, readFilename());
Martin Storsjo11adbac2018-05-15 06:35:29 +0000591 return llvm::make_unique<IconResource>(*Arg, MemoryFlags);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000592}
593
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000594RCParser::ParseType RCParser::parseHTMLResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000595 uint16_t MemoryFlags =
596 parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
Martin Storsjo4021cee2018-05-08 08:47:37 +0000597 ASSIGN_OR_RETURN(Arg, readFilename());
Martin Storsjo11adbac2018-05-15 06:35:29 +0000598 return llvm::make_unique<HTMLResource>(*Arg, MemoryFlags);
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000599}
600
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000601RCParser::ParseType RCParser::parseMenuResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000602 uint16_t MemoryFlags =
603 parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000604 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
605 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000606 return llvm::make_unique<MenuResource>(std::move(*OptStatements),
Martin Storsjo11adbac2018-05-15 06:35:29 +0000607 std::move(*Items), MemoryFlags);
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000608}
609
610Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
611 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
612
613 MenuDefinitionList List;
614
615 // Read a set of items. Each item is of one of three kinds:
616 // MENUITEM SEPARATOR
617 // MENUITEM caption:String, result:Int [, menu flags]...
618 // POPUP caption:String [, menu flags]... { items... }
619 while (!consumeOptionalType(Kind::BlockEnd)) {
620 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
621
622 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
623 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
624 if (!IsMenuItem && !IsPopup)
625 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
626
627 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
628 // Now, expecting SEPARATOR.
629 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
630 if (SeparatorResult->equals_lower("SEPARATOR")) {
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000631 List.addDefinition(llvm::make_unique<MenuSeparator>());
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000632 continue;
633 }
634
635 return getExpectedError("SEPARATOR or string", true);
636 }
637
638 // Not a separator. Read the caption.
639 ASSIGN_OR_RETURN(CaptionResult, readString());
640
641 // If MENUITEM, expect also a comma and an integer.
642 uint32_t MenuResult = -1;
643
644 if (IsMenuItem) {
645 RETURN_IF_ERROR(consumeType(Kind::Comma));
646 ASSIGN_OR_RETURN(IntResult, readInt());
647 MenuResult = *IntResult;
648 }
649
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000650 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
651 MenuDefinition::OptionsFlags));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000652
653 if (IsPopup) {
654 // If POPUP, read submenu items recursively.
655 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000656 List.addDefinition(llvm::make_unique<PopupItem>(
657 *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000658 continue;
659 }
660
661 assert(IsMenuItem);
662 List.addDefinition(
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000663 llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000664 }
665
666 return std::move(List);
667}
668
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000669RCParser::ParseType RCParser::parseStringTableResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000670 uint16_t MemoryFlags =
671 parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000672 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
673 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
674
Martin Storsjo11adbac2018-05-15 06:35:29 +0000675 auto Table = llvm::make_unique<StringTableResource>(std::move(*OptStatements),
676 MemoryFlags);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000677
678 // Read strings until we reach the end of the block.
679 while (!consumeOptionalType(Kind::BlockEnd)) {
680 // Each definition consists of string's ID (an integer) and a string.
681 // Some examples in documentation suggest that there might be a comma in
682 // between, however we strictly adhere to the single statement definition.
683 ASSIGN_OR_RETURN(IDResult, readInt());
Martin Storsjo94102762018-05-07 20:27:28 +0000684 consumeOptionalType(Kind::Comma);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000685 ASSIGN_OR_RETURN(StrResult, readString());
686 Table->addString(*IDResult, *StrResult);
687 }
688
689 return std::move(Table);
690}
691
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000692Expected<std::unique_ptr<VersionInfoBlock>>
693RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
694 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
695
Marek Sokolowski99ead702017-09-28 23:12:53 +0000696 auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000697
698 while (!isNextTokenKind(Kind::BlockEnd)) {
699 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
700 Contents->addStmt(std::move(*Stmt));
701 }
702
703 consume(); // Consume BlockEnd.
704
705 return std::move(Contents);
706}
707
708Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
709 // Expect either BLOCK or VALUE, then a name or a key (a string).
710 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
711
712 if (TypeResult->equals_lower("BLOCK")) {
713 ASSIGN_OR_RETURN(NameResult, readString());
714 return parseVersionInfoBlockContents(*NameResult);
715 }
716
717 if (TypeResult->equals_lower("VALUE")) {
718 ASSIGN_OR_RETURN(KeyResult, readString());
Zachary Turner07bc04f2017-10-06 21:26:06 +0000719 // Read a non-empty list of strings and/or ints, each
720 // possibly preceded by a comma. Unfortunately, the tool behavior depends
721 // on them existing or not, so we need to memorize where we found them.
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000722 std::vector<IntOrString> Values;
Zachary Turner07bc04f2017-10-06 21:26:06 +0000723 std::vector<bool> PrecedingCommas;
724 RETURN_IF_ERROR(consumeType(Kind::Comma));
725 while (!isNextTokenKind(Kind::Identifier) &&
726 !isNextTokenKind(Kind::BlockEnd)) {
727 // Try to eat a comma if it's not the first statement.
728 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000729 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
730 Values.push_back(*ValueResult);
Zachary Turner07bc04f2017-10-06 21:26:06 +0000731 PrecedingCommas.push_back(HadComma);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000732 }
Zachary Turner07bc04f2017-10-06 21:26:06 +0000733 return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
734 std::move(PrecedingCommas));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000735 }
736
737 return getExpectedError("BLOCK or VALUE", true);
738}
739
740Expected<VersionInfoResource::VersionInfoFixed>
741RCParser::parseVersionInfoFixed() {
742 using RetType = VersionInfoResource::VersionInfoFixed;
743 RetType Result;
744
745 // Read until the beginning of the block.
746 while (!isNextTokenKind(Kind::BlockBegin)) {
747 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
748 auto FixedType = RetType::getFixedType(*TypeResult);
749
750 if (!RetType::isTypeSupported(FixedType))
751 return getExpectedError("fixed VERSIONINFO statement type", true);
752 if (Result.IsTypePresent[FixedType])
753 return getExpectedError("yet unread fixed VERSIONINFO statement type",
754 true);
755
756 // VERSION variations take multiple integers.
757 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
758 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
Zachary Turner07bc04f2017-10-06 21:26:06 +0000759 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
760 Result.setValue(FixedType, ArgInts);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000761 }
762
763 return Result;
764}
765
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000766RCParser::ParseOptionType RCParser::parseLanguageStmt() {
767 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000768 return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000769}
770
771RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
772 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000773 return llvm::make_unique<CharacteristicsStmt>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000774}
775
776RCParser::ParseOptionType RCParser::parseVersionStmt() {
777 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000778 return llvm::make_unique<VersionStmt>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000779}
780
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000781RCParser::ParseOptionType RCParser::parseCaptionStmt() {
782 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000783 return llvm::make_unique<CaptionStmt>(*Arg);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000784}
785
Martin Storsjoe241ce62018-05-15 19:21:28 +0000786RCParser::ParseOptionType RCParser::parseClassStmt() {
787 ASSIGN_OR_RETURN(Arg, readIntOrString());
788 return llvm::make_unique<ClassStmt>(*Arg);
789}
790
Zachary Turner420090a2017-10-06 20:51:20 +0000791RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
792 assert(DialogType != OptStmtType::BasicStmt);
793
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000794 ASSIGN_OR_RETURN(SizeResult, readInt());
795 RETURN_IF_ERROR(consumeType(Kind::Comma));
796 ASSIGN_OR_RETURN(NameResult, readString());
Zachary Turner420090a2017-10-06 20:51:20 +0000797
798 // Default values for the optional arguments.
799 uint32_t FontWeight = 0;
800 bool FontItalic = false;
801 uint32_t FontCharset = 1;
802 if (DialogType == OptStmtType::DialogExStmt) {
803 if (consumeOptionalType(Kind::Comma)) {
804 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
805 if (Args->size() >= 1)
806 FontWeight = (*Args)[0];
807 if (Args->size() >= 2)
808 FontItalic = (*Args)[1] != 0;
809 if (Args->size() >= 3)
810 FontCharset = (*Args)[2];
811 }
812 }
813 return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
814 FontItalic, FontCharset);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000815}
816
817RCParser::ParseOptionType RCParser::parseStyleStmt() {
818 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000819 return llvm::make_unique<StyleStmt>(*Arg);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000820}
821
Martin Storsjoa876b5c2018-11-29 12:17:39 +0000822RCParser::ParseOptionType RCParser::parseExStyleStmt() {
823 ASSIGN_OR_RETURN(Arg, readInt());
824 return llvm::make_unique<ExStyleStmt>(*Arg);
825}
826
Zachary Turner514b7102017-10-09 18:50:29 +0000827Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000828 return make_error<ParserError>(
829 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
830}
831
832} // namespace rc
833} // namespace llvm