blob: 7123df9029ad03f5de8ad06333c73deec21594aa [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();
Marek Sokolowskifb74cb12017-09-28 22:41:38 +000083 else if (TypeToken->equalsLower("VERSIONINFO"))
84 Result = parseVersionInfoResource();
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000085 else
Marek Sokolowskib5f39a02017-09-29 00:14:18 +000086 Result = parseUserDefinedResource(*TypeToken);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000087
88 if (Result)
89 (*Result)->setName(*NameToken);
90
91 return Result;
92}
93
94bool RCParser::isNextTokenKind(Kind TokenKind) const {
95 return !isEof() && look().kind() == TokenKind;
96}
97
98const RCToken &RCParser::look() const {
99 assert(!isEof());
100 return *CurLoc;
101}
102
103const RCToken &RCParser::read() {
104 assert(!isEof());
105 return *CurLoc++;
106}
107
108void RCParser::consume() {
109 assert(!isEof());
110 CurLoc++;
111}
112
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000113// An integer description might consist of a single integer or
114// an arithmetic expression evaluating to the integer. The expressions
115// can contain the following tokens: <int> ( ) + - | & ~. Their meaning
116// is the same as in C++.
117// The operators in the original RC implementation have the following
118// precedence:
119// 1) Unary operators (- ~),
120// 2) Binary operators (+ - & |), with no precedence.
121//
122// The following grammar is used to parse the expressions Exp1:
123// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
124// Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
125// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
126// separated by binary operators.)
127//
128// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
129// is read by parseIntExpr2().
130//
131// The original Microsoft tool handles multiple unary operators incorrectly.
132// For example, in 16-bit little-endian integers:
133// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
134// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
135// Our implementation differs from the original one and handles these
136// operators correctly:
137// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
138// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
139
Zachary Turner07bc04f2017-10-06 21:26:06 +0000140Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); }
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000141
Zachary Turner07bc04f2017-10-06 21:26:06 +0000142Expected<RCInt> RCParser::parseIntExpr1() {
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000143 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
144 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
Zachary Turner07bc04f2017-10-06 21:26:06 +0000145 RCInt Result = *FirstResult;
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000146
147 while (!isEof() && look().isBinaryOp()) {
148 auto OpToken = read();
149 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
150
151 switch (OpToken.kind()) {
152 case Kind::Plus:
153 Result += *NextResult;
154 break;
155
156 case Kind::Minus:
157 Result -= *NextResult;
158 break;
159
160 case Kind::Pipe:
161 Result |= *NextResult;
162 break;
163
164 case Kind::Amp:
165 Result &= *NextResult;
166 break;
167
168 default:
169 llvm_unreachable("Already processed all binary ops.");
170 }
171 }
172
173 return Result;
174}
175
Zachary Turner07bc04f2017-10-06 21:26:06 +0000176Expected<RCInt> RCParser::parseIntExpr2() {
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000177 // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
178 static const char ErrorMsg[] = "'-', '~', integer or '('";
179
180 if (isEof())
181 return getExpectedError(ErrorMsg);
182
183 switch (look().kind()) {
184 case Kind::Minus: {
185 consume();
186 ASSIGN_OR_RETURN(Result, parseIntExpr2());
187 return -(*Result);
188 }
189
190 case Kind::Tilde: {
191 consume();
192 ASSIGN_OR_RETURN(Result, parseIntExpr2());
193 return ~(*Result);
194 }
195
196 case Kind::Int:
Zachary Turner07bc04f2017-10-06 21:26:06 +0000197 return RCInt(read());
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000198
199 case Kind::LeftParen: {
200 consume();
201 ASSIGN_OR_RETURN(Result, parseIntExpr1());
202 RETURN_IF_ERROR(consumeType(Kind::RightParen));
203 return *Result;
204 }
205
206 default:
207 return getExpectedError(ErrorMsg);
208 }
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000209}
210
211Expected<StringRef> RCParser::readString() {
212 if (!isNextTokenKind(Kind::String))
213 return getExpectedError("string");
214 return read().value();
215}
216
Martin Storsjo4021cee2018-05-08 08:47:37 +0000217Expected<StringRef> RCParser::readFilename() {
218 if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
219 return getExpectedError("string");
220 return read().value();
221}
222
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000223Expected<StringRef> RCParser::readIdentifier() {
224 if (!isNextTokenKind(Kind::Identifier))
225 return getExpectedError("identifier");
226 return read().value();
227}
228
Marek Sokolowski7f110522017-08-28 22:58:31 +0000229Expected<IntOrString> RCParser::readIntOrString() {
230 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
231 return getExpectedError("int or string");
232 return IntOrString(read());
233}
234
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000235Expected<IntOrString> RCParser::readTypeOrName() {
236 // We suggest that the correct resource name or type should be either an
237 // identifier or an integer. The original RC tool is much more liberal.
238 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
239 return getExpectedError("int or identifier");
Marek Sokolowski7f110522017-08-28 22:58:31 +0000240 return IntOrString(read());
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000241}
242
243Error RCParser::consumeType(Kind TokenKind) {
244 if (isNextTokenKind(TokenKind)) {
245 consume();
246 return Error::success();
247 }
248
249 switch (TokenKind) {
250#define TOKEN(TokenName) \
251 case Kind::TokenName: \
252 return getExpectedError(#TokenName);
253#define SHORT_TOKEN(TokenName, TokenCh) \
254 case Kind::TokenName: \
255 return getExpectedError(#TokenCh);
David Blaikieb961d292017-11-21 00:23:19 +0000256#include "ResourceScriptTokenList.def"
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000257 }
258
259 llvm_unreachable("All case options exhausted.");
260}
261
262bool RCParser::consumeOptionalType(Kind TokenKind) {
263 if (isNextTokenKind(TokenKind)) {
264 consume();
265 return true;
266 }
267
268 return false;
269}
270
Zachary Turner07bc04f2017-10-06 21:26:06 +0000271Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
272 size_t MaxCount) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000273 assert(MinCount <= MaxCount);
274
Zachary Turner07bc04f2017-10-06 21:26:06 +0000275 SmallVector<RCInt, 8> Result;
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000276
277 auto FailureHandler =
Zachary Turner07bc04f2017-10-06 21:26:06 +0000278 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000279 if (Result.size() < MinCount)
280 return std::move(Err);
281 consumeError(std::move(Err));
282 return Result;
283 };
284
285 for (size_t i = 0; i < MaxCount; ++i) {
286 // Try to read a comma unless we read the first token.
287 // Sometimes RC tool requires them and sometimes not. We decide to
288 // always require them.
289 if (i >= 1) {
290 if (auto CommaError = consumeType(Kind::Comma))
291 return FailureHandler(std::move(CommaError));
292 }
293
294 if (auto IntResult = readInt())
295 Result.push_back(*IntResult);
296 else
297 return FailureHandler(IntResult.takeError());
298 }
299
300 return std::move(Result);
301}
302
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000303Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
304 ArrayRef<uint32_t> FlagValues) {
Marek Sokolowski7f110522017-08-28 22:58:31 +0000305 assert(!FlagDesc.empty());
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000306 assert(FlagDesc.size() == FlagValues.size());
Marek Sokolowski7f110522017-08-28 22:58:31 +0000307
308 uint32_t Result = 0;
309 while (isNextTokenKind(Kind::Comma)) {
310 consume();
311 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
312 bool FoundFlag = false;
313
314 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
315 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
316 continue;
317
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000318 Result |= FlagValues[FlagId];
Marek Sokolowski7f110522017-08-28 22:58:31 +0000319 FoundFlag = true;
320 break;
321 }
322
323 if (!FoundFlag)
324 return getExpectedError(join(FlagDesc, "/"), true);
325 }
326
327 return Result;
328}
329
Zachary Turner420090a2017-10-06 20:51:20 +0000330Expected<OptionalStmtList>
331RCParser::parseOptionalStatements(OptStmtType StmtsType) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000332 OptionalStmtList Result;
333
334 // The last statement is always followed by the start of the block.
335 while (!isNextTokenKind(Kind::BlockBegin)) {
Zachary Turner420090a2017-10-06 20:51:20 +0000336 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000337 Result.addStmt(std::move(*SingleParse));
338 }
339
340 return std::move(Result);
341}
342
343Expected<std::unique_ptr<OptionalStmt>>
Zachary Turner420090a2017-10-06 20:51:20 +0000344RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000345 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
346 if (TypeToken->equals_lower("CHARACTERISTICS"))
347 return parseCharacteristicsStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000348 if (TypeToken->equals_lower("LANGUAGE"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000349 return parseLanguageStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000350 if (TypeToken->equals_lower("VERSION"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000351 return parseVersionStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000352
Zachary Turner420090a2017-10-06 20:51:20 +0000353 if (StmtsType != OptStmtType::BasicStmt) {
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000354 if (TypeToken->equals_lower("CAPTION"))
355 return parseCaptionStmt();
356 if (TypeToken->equals_lower("FONT"))
Zachary Turner420090a2017-10-06 20:51:20 +0000357 return parseFontStmt(StmtsType);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000358 if (TypeToken->equals_lower("STYLE"))
359 return parseStyleStmt();
360 }
361
362 return getExpectedError("optional statement type, BEGIN or '{'",
363 /* IsAlreadyRead = */ true);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000364}
365
366RCParser::ParseType RCParser::parseLanguageResource() {
367 // Read LANGUAGE as an optional statement. If it's read correctly, we can
368 // upcast it to RCResource.
369 return parseLanguageStmt();
370}
371
Marek Sokolowski7f110522017-08-28 22:58:31 +0000372RCParser::ParseType RCParser::parseAcceleratorsResource() {
373 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
374 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
375
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000376 auto Accels =
377 llvm::make_unique<AcceleratorsResource>(std::move(*OptStatements));
Marek Sokolowski7f110522017-08-28 22:58:31 +0000378
379 while (!consumeOptionalType(Kind::BlockEnd)) {
380 ASSIGN_OR_RETURN(EventResult, readIntOrString());
381 RETURN_IF_ERROR(consumeType(Kind::Comma));
382 ASSIGN_OR_RETURN(IDResult, readInt());
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000383 ASSIGN_OR_RETURN(
384 FlagsResult,
385 parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
386 AcceleratorsResource::Accelerator::OptionsFlags));
Marek Sokolowski7f110522017-08-28 22:58:31 +0000387 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
388 }
389
390 return std::move(Accels);
391}
392
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000393RCParser::ParseType RCParser::parseCursorResource() {
Martin Storsjo4021cee2018-05-08 08:47:37 +0000394 ASSIGN_OR_RETURN(Arg, readFilename());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000395 return llvm::make_unique<CursorResource>(*Arg);
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000396}
397
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000398RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
399 // Dialog resources have the following format of the arguments:
400 // DIALOG: x, y, width, height [opt stmts...] {controls...}
401 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
402 // These are very similar, so we parse them together.
403 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
404
405 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
406 if (IsExtended && consumeOptionalType(Kind::Comma)) {
407 ASSIGN_OR_RETURN(HelpIDResult, readInt());
408 HelpID = *HelpIDResult;
409 }
410
Zachary Turner420090a2017-10-06 20:51:20 +0000411 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
412 IsExtended ? OptStmtType::DialogExStmt
413 : OptStmtType::DialogStmt));
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000414
415 assert(isNextTokenKind(Kind::BlockBegin) &&
416 "parseOptionalStatements, when successful, halts on BlockBegin.");
417 consume();
418
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000419 auto Dialog = llvm::make_unique<DialogResource>(
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000420 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
421 HelpID, std::move(*OptStatements), IsExtended);
422
423 while (!consumeOptionalType(Kind::BlockEnd)) {
424 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
425 Dialog->addControl(std::move(*ControlDefResult));
426 }
427
428 return std::move(Dialog);
429}
430
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000431RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
432 if (isEof())
433 return getExpectedError("filename, '{' or BEGIN");
434
435 // Check if this is a file resource.
Martin Storsjo4021cee2018-05-08 08:47:37 +0000436 switch (look().kind()) {
437 case Kind::String:
438 case Kind::Identifier:
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000439 return llvm::make_unique<UserDefinedResource>(Type, read().value());
Martin Storsjo4021cee2018-05-08 08:47:37 +0000440 default:
441 break;
442 }
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000443
444 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
445 std::vector<IntOrString> Data;
446
447 // Consume comma before each consecutive token except the first one.
448 bool ConsumeComma = false;
449 while (!consumeOptionalType(Kind::BlockEnd)) {
450 if (ConsumeComma)
451 RETURN_IF_ERROR(consumeType(Kind::Comma));
452 ConsumeComma = true;
453
454 ASSIGN_OR_RETURN(Item, readIntOrString());
455 Data.push_back(*Item);
456 }
457
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000458 return llvm::make_unique<UserDefinedResource>(Type, std::move(Data));
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000459}
460
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000461RCParser::ParseType RCParser::parseVersionInfoResource() {
462 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
463 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
Marek Sokolowski99ead702017-09-28 23:12:53 +0000464 return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult),
465 std::move(*FixedResult));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000466}
467
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000468Expected<Control> RCParser::parseControl() {
469 // Each control definition (except CONTROL) follows one of the schemes below
470 // depending on the control class:
471 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
472 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
473 // Note that control ids must be integers.
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000474 // Text might be either a string or an integer pointing to resource ID.
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000475 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
Marek Sokolowski75fa1732017-08-29 20:03:18 +0000476 std::string ClassUpper = ClassResult->upper();
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000477 auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
478 if (CtlInfo == Control::SupportedCtls.end())
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000479 return getExpectedError("control type, END or '}'", true);
480
481 // Read caption if necessary.
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000482 IntOrString Caption{StringRef()};
483 if (CtlInfo->getValue().HasTitle) {
484 ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000485 RETURN_IF_ERROR(consumeType(Kind::Comma));
486 Caption = *CaptionResult;
487 }
488
489 ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8));
490
491 auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> {
Zachary Turner07bc04f2017-10-06 21:26:06 +0000492 return Args->size() > Id ? (uint32_t)(*Args)[Id] : Optional<uint32_t>();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000493 };
494
495 return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2],
496 (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6),
497 TakeOptArg(7));
498}
499
Martin Storsjo577b9812018-05-07 20:27:37 +0000500RCParser::ParseType RCParser::parseBitmapResource() {
Martin Storsjo4021cee2018-05-08 08:47:37 +0000501 ASSIGN_OR_RETURN(Arg, readFilename());
Martin Storsjo577b9812018-05-07 20:27:37 +0000502 return llvm::make_unique<BitmapResource>(*Arg);
503}
504
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000505RCParser::ParseType RCParser::parseIconResource() {
Martin Storsjo4021cee2018-05-08 08:47:37 +0000506 ASSIGN_OR_RETURN(Arg, readFilename());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000507 return llvm::make_unique<IconResource>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000508}
509
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000510RCParser::ParseType RCParser::parseHTMLResource() {
Martin Storsjo4021cee2018-05-08 08:47:37 +0000511 ASSIGN_OR_RETURN(Arg, readFilename());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000512 return llvm::make_unique<HTMLResource>(*Arg);
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000513}
514
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000515RCParser::ParseType RCParser::parseMenuResource() {
516 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
517 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000518 return llvm::make_unique<MenuResource>(std::move(*OptStatements),
519 std::move(*Items));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000520}
521
522Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
523 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
524
525 MenuDefinitionList List;
526
527 // Read a set of items. Each item is of one of three kinds:
528 // MENUITEM SEPARATOR
529 // MENUITEM caption:String, result:Int [, menu flags]...
530 // POPUP caption:String [, menu flags]... { items... }
531 while (!consumeOptionalType(Kind::BlockEnd)) {
532 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
533
534 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
535 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
536 if (!IsMenuItem && !IsPopup)
537 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
538
539 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
540 // Now, expecting SEPARATOR.
541 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
542 if (SeparatorResult->equals_lower("SEPARATOR")) {
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000543 List.addDefinition(llvm::make_unique<MenuSeparator>());
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000544 continue;
545 }
546
547 return getExpectedError("SEPARATOR or string", true);
548 }
549
550 // Not a separator. Read the caption.
551 ASSIGN_OR_RETURN(CaptionResult, readString());
552
553 // If MENUITEM, expect also a comma and an integer.
554 uint32_t MenuResult = -1;
555
556 if (IsMenuItem) {
557 RETURN_IF_ERROR(consumeType(Kind::Comma));
558 ASSIGN_OR_RETURN(IntResult, readInt());
559 MenuResult = *IntResult;
560 }
561
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000562 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
563 MenuDefinition::OptionsFlags));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000564
565 if (IsPopup) {
566 // If POPUP, read submenu items recursively.
567 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000568 List.addDefinition(llvm::make_unique<PopupItem>(
569 *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000570 continue;
571 }
572
573 assert(IsMenuItem);
574 List.addDefinition(
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000575 llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000576 }
577
578 return std::move(List);
579}
580
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000581RCParser::ParseType RCParser::parseStringTableResource() {
582 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
583 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
584
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000585 auto Table =
586 llvm::make_unique<StringTableResource>(std::move(*OptStatements));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000587
588 // Read strings until we reach the end of the block.
589 while (!consumeOptionalType(Kind::BlockEnd)) {
590 // Each definition consists of string's ID (an integer) and a string.
591 // Some examples in documentation suggest that there might be a comma in
592 // between, however we strictly adhere to the single statement definition.
593 ASSIGN_OR_RETURN(IDResult, readInt());
Martin Storsjo94102762018-05-07 20:27:28 +0000594 consumeOptionalType(Kind::Comma);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000595 ASSIGN_OR_RETURN(StrResult, readString());
596 Table->addString(*IDResult, *StrResult);
597 }
598
599 return std::move(Table);
600}
601
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000602Expected<std::unique_ptr<VersionInfoBlock>>
603RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
604 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
605
Marek Sokolowski99ead702017-09-28 23:12:53 +0000606 auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000607
608 while (!isNextTokenKind(Kind::BlockEnd)) {
609 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
610 Contents->addStmt(std::move(*Stmt));
611 }
612
613 consume(); // Consume BlockEnd.
614
615 return std::move(Contents);
616}
617
618Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
619 // Expect either BLOCK or VALUE, then a name or a key (a string).
620 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
621
622 if (TypeResult->equals_lower("BLOCK")) {
623 ASSIGN_OR_RETURN(NameResult, readString());
624 return parseVersionInfoBlockContents(*NameResult);
625 }
626
627 if (TypeResult->equals_lower("VALUE")) {
628 ASSIGN_OR_RETURN(KeyResult, readString());
Zachary Turner07bc04f2017-10-06 21:26:06 +0000629 // Read a non-empty list of strings and/or ints, each
630 // possibly preceded by a comma. Unfortunately, the tool behavior depends
631 // on them existing or not, so we need to memorize where we found them.
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000632 std::vector<IntOrString> Values;
Zachary Turner07bc04f2017-10-06 21:26:06 +0000633 std::vector<bool> PrecedingCommas;
634 RETURN_IF_ERROR(consumeType(Kind::Comma));
635 while (!isNextTokenKind(Kind::Identifier) &&
636 !isNextTokenKind(Kind::BlockEnd)) {
637 // Try to eat a comma if it's not the first statement.
638 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000639 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
640 Values.push_back(*ValueResult);
Zachary Turner07bc04f2017-10-06 21:26:06 +0000641 PrecedingCommas.push_back(HadComma);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000642 }
Zachary Turner07bc04f2017-10-06 21:26:06 +0000643 return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
644 std::move(PrecedingCommas));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000645 }
646
647 return getExpectedError("BLOCK or VALUE", true);
648}
649
650Expected<VersionInfoResource::VersionInfoFixed>
651RCParser::parseVersionInfoFixed() {
652 using RetType = VersionInfoResource::VersionInfoFixed;
653 RetType Result;
654
655 // Read until the beginning of the block.
656 while (!isNextTokenKind(Kind::BlockBegin)) {
657 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
658 auto FixedType = RetType::getFixedType(*TypeResult);
659
660 if (!RetType::isTypeSupported(FixedType))
661 return getExpectedError("fixed VERSIONINFO statement type", true);
662 if (Result.IsTypePresent[FixedType])
663 return getExpectedError("yet unread fixed VERSIONINFO statement type",
664 true);
665
666 // VERSION variations take multiple integers.
667 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
668 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
Zachary Turner07bc04f2017-10-06 21:26:06 +0000669 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
670 Result.setValue(FixedType, ArgInts);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000671 }
672
673 return Result;
674}
675
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000676RCParser::ParseOptionType RCParser::parseLanguageStmt() {
677 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000678 return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000679}
680
681RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
682 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000683 return llvm::make_unique<CharacteristicsStmt>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000684}
685
686RCParser::ParseOptionType RCParser::parseVersionStmt() {
687 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000688 return llvm::make_unique<VersionStmt>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000689}
690
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000691RCParser::ParseOptionType RCParser::parseCaptionStmt() {
692 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000693 return llvm::make_unique<CaptionStmt>(*Arg);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000694}
695
Zachary Turner420090a2017-10-06 20:51:20 +0000696RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
697 assert(DialogType != OptStmtType::BasicStmt);
698
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000699 ASSIGN_OR_RETURN(SizeResult, readInt());
700 RETURN_IF_ERROR(consumeType(Kind::Comma));
701 ASSIGN_OR_RETURN(NameResult, readString());
Zachary Turner420090a2017-10-06 20:51:20 +0000702
703 // Default values for the optional arguments.
704 uint32_t FontWeight = 0;
705 bool FontItalic = false;
706 uint32_t FontCharset = 1;
707 if (DialogType == OptStmtType::DialogExStmt) {
708 if (consumeOptionalType(Kind::Comma)) {
709 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
710 if (Args->size() >= 1)
711 FontWeight = (*Args)[0];
712 if (Args->size() >= 2)
713 FontItalic = (*Args)[1] != 0;
714 if (Args->size() >= 3)
715 FontCharset = (*Args)[2];
716 }
717 }
718 return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
719 FontItalic, FontCharset);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000720}
721
722RCParser::ParseOptionType RCParser::parseStyleStmt() {
723 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000724 return llvm::make_unique<StyleStmt>(*Arg);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000725}
726
Zachary Turner514b7102017-10-09 18:50:29 +0000727Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000728 return make_error<ParserError>(
729 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
730}
731
732} // namespace rc
733} // namespace llvm