blob: 4acae313558163a07f52c3b086c7da64ae9a59cb [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"
15
16// Take an expression returning llvm::Error and forward the error if it exists.
17#define RETURN_IF_ERROR(Expr) \
18 if (auto Err = (Expr)) \
19 return std::move(Err);
20
21// Take an expression returning llvm::Expected<T> and assign it to Var or
22// forward the error out of the function.
23#define ASSIGN_OR_RETURN(Var, Expr) \
24 auto Var = (Expr); \
25 if (!Var) \
26 return Var.takeError();
27
28namespace llvm {
29namespace rc {
30
Zachary Turner514b7102017-10-09 18:50:29 +000031RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000032 const LocIter End)
33 : ErrorLoc(CurLoc), FileEnd(End) {
34 CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
35 (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
36}
37
38char RCParser::ParserError::ID = 0;
39
Zachary Turner514b7102017-10-09 18:50:29 +000040RCParser::RCParser(std::vector<RCToken> TokenList)
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000041 : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
42
43bool RCParser::isEof() const { return CurLoc == End; }
44
45RCParser::ParseType RCParser::parseSingleResource() {
46 // The first thing we read is usually a resource's name. However, in some
47 // cases (LANGUAGE and STRINGTABLE) the resources don't have their names
48 // and the first token to be read is the type.
49 ASSIGN_OR_RETURN(NameToken, readTypeOrName());
50
51 if (NameToken->equalsLower("LANGUAGE"))
52 return parseLanguageResource();
53 else if (NameToken->equalsLower("STRINGTABLE"))
54 return parseStringTableResource();
55
56 // If it's not an unnamed resource, what we've just read is a name. Now,
57 // read resource type;
58 ASSIGN_OR_RETURN(TypeToken, readTypeOrName());
59
60 ParseType Result = std::unique_ptr<RCResource>();
61 (void)!Result;
62
Marek Sokolowski7f110522017-08-28 22:58:31 +000063 if (TypeToken->equalsLower("ACCELERATORS"))
64 Result = parseAcceleratorsResource();
65 else if (TypeToken->equalsLower("CURSOR"))
Marek Sokolowski72aa9372017-08-28 21:59:54 +000066 Result = parseCursorResource();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +000067 else if (TypeToken->equalsLower("DIALOG"))
68 Result = parseDialogResource(false);
69 else if (TypeToken->equalsLower("DIALOGEX"))
70 Result = parseDialogResource(true);
Marek Sokolowski72aa9372017-08-28 21:59:54 +000071 else if (TypeToken->equalsLower("ICON"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000072 Result = parseIconResource();
Marek Sokolowski72aa9372017-08-28 21:59:54 +000073 else if (TypeToken->equalsLower("HTML"))
74 Result = parseHTMLResource();
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +000075 else if (TypeToken->equalsLower("MENU"))
76 Result = parseMenuResource();
Marek Sokolowskifb74cb12017-09-28 22:41:38 +000077 else if (TypeToken->equalsLower("VERSIONINFO"))
78 Result = parseVersionInfoResource();
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000079 else
Marek Sokolowskib5f39a02017-09-29 00:14:18 +000080 Result = parseUserDefinedResource(*TypeToken);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000081
82 if (Result)
83 (*Result)->setName(*NameToken);
84
85 return Result;
86}
87
88bool RCParser::isNextTokenKind(Kind TokenKind) const {
89 return !isEof() && look().kind() == TokenKind;
90}
91
92const RCToken &RCParser::look() const {
93 assert(!isEof());
94 return *CurLoc;
95}
96
97const RCToken &RCParser::read() {
98 assert(!isEof());
99 return *CurLoc++;
100}
101
102void RCParser::consume() {
103 assert(!isEof());
104 CurLoc++;
105}
106
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000107// An integer description might consist of a single integer or
108// an arithmetic expression evaluating to the integer. The expressions
109// can contain the following tokens: <int> ( ) + - | & ~. Their meaning
110// is the same as in C++.
111// The operators in the original RC implementation have the following
112// precedence:
113// 1) Unary operators (- ~),
114// 2) Binary operators (+ - & |), with no precedence.
115//
116// The following grammar is used to parse the expressions Exp1:
117// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
118// Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
119// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
120// separated by binary operators.)
121//
122// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
123// is read by parseIntExpr2().
124//
125// The original Microsoft tool handles multiple unary operators incorrectly.
126// For example, in 16-bit little-endian integers:
127// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
128// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
129// Our implementation differs from the original one and handles these
130// operators correctly:
131// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
132// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
133
Zachary Turner07bc04f2017-10-06 21:26:06 +0000134Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); }
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000135
Zachary Turner07bc04f2017-10-06 21:26:06 +0000136Expected<RCInt> RCParser::parseIntExpr1() {
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000137 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
138 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
Zachary Turner07bc04f2017-10-06 21:26:06 +0000139 RCInt Result = *FirstResult;
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000140
141 while (!isEof() && look().isBinaryOp()) {
142 auto OpToken = read();
143 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
144
145 switch (OpToken.kind()) {
146 case Kind::Plus:
147 Result += *NextResult;
148 break;
149
150 case Kind::Minus:
151 Result -= *NextResult;
152 break;
153
154 case Kind::Pipe:
155 Result |= *NextResult;
156 break;
157
158 case Kind::Amp:
159 Result &= *NextResult;
160 break;
161
162 default:
163 llvm_unreachable("Already processed all binary ops.");
164 }
165 }
166
167 return Result;
168}
169
Zachary Turner07bc04f2017-10-06 21:26:06 +0000170Expected<RCInt> RCParser::parseIntExpr2() {
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000171 // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
172 static const char ErrorMsg[] = "'-', '~', integer or '('";
173
174 if (isEof())
175 return getExpectedError(ErrorMsg);
176
177 switch (look().kind()) {
178 case Kind::Minus: {
179 consume();
180 ASSIGN_OR_RETURN(Result, parseIntExpr2());
181 return -(*Result);
182 }
183
184 case Kind::Tilde: {
185 consume();
186 ASSIGN_OR_RETURN(Result, parseIntExpr2());
187 return ~(*Result);
188 }
189
190 case Kind::Int:
Zachary Turner07bc04f2017-10-06 21:26:06 +0000191 return RCInt(read());
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000192
193 case Kind::LeftParen: {
194 consume();
195 ASSIGN_OR_RETURN(Result, parseIntExpr1());
196 RETURN_IF_ERROR(consumeType(Kind::RightParen));
197 return *Result;
198 }
199
200 default:
201 return getExpectedError(ErrorMsg);
202 }
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000203}
204
205Expected<StringRef> RCParser::readString() {
206 if (!isNextTokenKind(Kind::String))
207 return getExpectedError("string");
208 return read().value();
209}
210
211Expected<StringRef> RCParser::readIdentifier() {
212 if (!isNextTokenKind(Kind::Identifier))
213 return getExpectedError("identifier");
214 return read().value();
215}
216
Marek Sokolowski7f110522017-08-28 22:58:31 +0000217Expected<IntOrString> RCParser::readIntOrString() {
218 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
219 return getExpectedError("int or string");
220 return IntOrString(read());
221}
222
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000223Expected<IntOrString> RCParser::readTypeOrName() {
224 // We suggest that the correct resource name or type should be either an
225 // identifier or an integer. The original RC tool is much more liberal.
226 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
227 return getExpectedError("int or identifier");
Marek Sokolowski7f110522017-08-28 22:58:31 +0000228 return IntOrString(read());
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000229}
230
231Error RCParser::consumeType(Kind TokenKind) {
232 if (isNextTokenKind(TokenKind)) {
233 consume();
234 return Error::success();
235 }
236
237 switch (TokenKind) {
238#define TOKEN(TokenName) \
239 case Kind::TokenName: \
240 return getExpectedError(#TokenName);
241#define SHORT_TOKEN(TokenName, TokenCh) \
242 case Kind::TokenName: \
243 return getExpectedError(#TokenCh);
244#include "ResourceScriptTokenList.h"
245#undef SHORT_TOKEN
246#undef TOKEN
247 }
248
249 llvm_unreachable("All case options exhausted.");
250}
251
252bool RCParser::consumeOptionalType(Kind TokenKind) {
253 if (isNextTokenKind(TokenKind)) {
254 consume();
255 return true;
256 }
257
258 return false;
259}
260
Zachary Turner07bc04f2017-10-06 21:26:06 +0000261Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
262 size_t MaxCount) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000263 assert(MinCount <= MaxCount);
264
Zachary Turner07bc04f2017-10-06 21:26:06 +0000265 SmallVector<RCInt, 8> Result;
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000266
267 auto FailureHandler =
Zachary Turner07bc04f2017-10-06 21:26:06 +0000268 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000269 if (Result.size() < MinCount)
270 return std::move(Err);
271 consumeError(std::move(Err));
272 return Result;
273 };
274
275 for (size_t i = 0; i < MaxCount; ++i) {
276 // Try to read a comma unless we read the first token.
277 // Sometimes RC tool requires them and sometimes not. We decide to
278 // always require them.
279 if (i >= 1) {
280 if (auto CommaError = consumeType(Kind::Comma))
281 return FailureHandler(std::move(CommaError));
282 }
283
284 if (auto IntResult = readInt())
285 Result.push_back(*IntResult);
286 else
287 return FailureHandler(IntResult.takeError());
288 }
289
290 return std::move(Result);
291}
292
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000293Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
294 ArrayRef<uint32_t> FlagValues) {
Marek Sokolowski7f110522017-08-28 22:58:31 +0000295 assert(!FlagDesc.empty());
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000296 assert(FlagDesc.size() == FlagValues.size());
Marek Sokolowski7f110522017-08-28 22:58:31 +0000297
298 uint32_t Result = 0;
299 while (isNextTokenKind(Kind::Comma)) {
300 consume();
301 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
302 bool FoundFlag = false;
303
304 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
305 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
306 continue;
307
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000308 Result |= FlagValues[FlagId];
Marek Sokolowski7f110522017-08-28 22:58:31 +0000309 FoundFlag = true;
310 break;
311 }
312
313 if (!FoundFlag)
314 return getExpectedError(join(FlagDesc, "/"), true);
315 }
316
317 return Result;
318}
319
Zachary Turner420090a2017-10-06 20:51:20 +0000320Expected<OptionalStmtList>
321RCParser::parseOptionalStatements(OptStmtType StmtsType) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000322 OptionalStmtList Result;
323
324 // The last statement is always followed by the start of the block.
325 while (!isNextTokenKind(Kind::BlockBegin)) {
Zachary Turner420090a2017-10-06 20:51:20 +0000326 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000327 Result.addStmt(std::move(*SingleParse));
328 }
329
330 return std::move(Result);
331}
332
333Expected<std::unique_ptr<OptionalStmt>>
Zachary Turner420090a2017-10-06 20:51:20 +0000334RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000335 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
336 if (TypeToken->equals_lower("CHARACTERISTICS"))
337 return parseCharacteristicsStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000338 if (TypeToken->equals_lower("LANGUAGE"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000339 return parseLanguageStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000340 if (TypeToken->equals_lower("VERSION"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000341 return parseVersionStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000342
Zachary Turner420090a2017-10-06 20:51:20 +0000343 if (StmtsType != OptStmtType::BasicStmt) {
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000344 if (TypeToken->equals_lower("CAPTION"))
345 return parseCaptionStmt();
346 if (TypeToken->equals_lower("FONT"))
Zachary Turner420090a2017-10-06 20:51:20 +0000347 return parseFontStmt(StmtsType);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000348 if (TypeToken->equals_lower("STYLE"))
349 return parseStyleStmt();
350 }
351
352 return getExpectedError("optional statement type, BEGIN or '{'",
353 /* IsAlreadyRead = */ true);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000354}
355
356RCParser::ParseType RCParser::parseLanguageResource() {
357 // Read LANGUAGE as an optional statement. If it's read correctly, we can
358 // upcast it to RCResource.
359 return parseLanguageStmt();
360}
361
Marek Sokolowski7f110522017-08-28 22:58:31 +0000362RCParser::ParseType RCParser::parseAcceleratorsResource() {
363 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
364 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
365
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000366 auto Accels =
367 llvm::make_unique<AcceleratorsResource>(std::move(*OptStatements));
Marek Sokolowski7f110522017-08-28 22:58:31 +0000368
369 while (!consumeOptionalType(Kind::BlockEnd)) {
370 ASSIGN_OR_RETURN(EventResult, readIntOrString());
371 RETURN_IF_ERROR(consumeType(Kind::Comma));
372 ASSIGN_OR_RETURN(IDResult, readInt());
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000373 ASSIGN_OR_RETURN(
374 FlagsResult,
375 parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
376 AcceleratorsResource::Accelerator::OptionsFlags));
Marek Sokolowski7f110522017-08-28 22:58:31 +0000377 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
378 }
379
380 return std::move(Accels);
381}
382
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000383RCParser::ParseType RCParser::parseCursorResource() {
384 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000385 return llvm::make_unique<CursorResource>(*Arg);
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000386}
387
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000388RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
389 // Dialog resources have the following format of the arguments:
390 // DIALOG: x, y, width, height [opt stmts...] {controls...}
391 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
392 // These are very similar, so we parse them together.
393 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
394
395 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
396 if (IsExtended && consumeOptionalType(Kind::Comma)) {
397 ASSIGN_OR_RETURN(HelpIDResult, readInt());
398 HelpID = *HelpIDResult;
399 }
400
Zachary Turner420090a2017-10-06 20:51:20 +0000401 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
402 IsExtended ? OptStmtType::DialogExStmt
403 : OptStmtType::DialogStmt));
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000404
405 assert(isNextTokenKind(Kind::BlockBegin) &&
406 "parseOptionalStatements, when successful, halts on BlockBegin.");
407 consume();
408
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000409 auto Dialog = llvm::make_unique<DialogResource>(
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000410 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
411 HelpID, std::move(*OptStatements), IsExtended);
412
413 while (!consumeOptionalType(Kind::BlockEnd)) {
414 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
415 Dialog->addControl(std::move(*ControlDefResult));
416 }
417
418 return std::move(Dialog);
419}
420
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000421RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
422 if (isEof())
423 return getExpectedError("filename, '{' or BEGIN");
424
425 // Check if this is a file resource.
426 if (look().kind() == Kind::String)
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000427 return llvm::make_unique<UserDefinedResource>(Type, read().value());
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000428
429 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
430 std::vector<IntOrString> Data;
431
432 // Consume comma before each consecutive token except the first one.
433 bool ConsumeComma = false;
434 while (!consumeOptionalType(Kind::BlockEnd)) {
435 if (ConsumeComma)
436 RETURN_IF_ERROR(consumeType(Kind::Comma));
437 ConsumeComma = true;
438
439 ASSIGN_OR_RETURN(Item, readIntOrString());
440 Data.push_back(*Item);
441 }
442
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000443 return llvm::make_unique<UserDefinedResource>(Type, std::move(Data));
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000444}
445
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000446RCParser::ParseType RCParser::parseVersionInfoResource() {
447 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
448 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
Marek Sokolowski99ead702017-09-28 23:12:53 +0000449 return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult),
450 std::move(*FixedResult));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000451}
452
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000453Expected<Control> RCParser::parseControl() {
454 // Each control definition (except CONTROL) follows one of the schemes below
455 // depending on the control class:
456 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
457 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
458 // Note that control ids must be integers.
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000459 // Text might be either a string or an integer pointing to resource ID.
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000460 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
Marek Sokolowski75fa1732017-08-29 20:03:18 +0000461 std::string ClassUpper = ClassResult->upper();
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000462 auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
463 if (CtlInfo == Control::SupportedCtls.end())
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000464 return getExpectedError("control type, END or '}'", true);
465
466 // Read caption if necessary.
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000467 IntOrString Caption{StringRef()};
468 if (CtlInfo->getValue().HasTitle) {
469 ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000470 RETURN_IF_ERROR(consumeType(Kind::Comma));
471 Caption = *CaptionResult;
472 }
473
474 ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8));
475
476 auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> {
Zachary Turner07bc04f2017-10-06 21:26:06 +0000477 return Args->size() > Id ? (uint32_t)(*Args)[Id] : Optional<uint32_t>();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000478 };
479
480 return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2],
481 (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6),
482 TakeOptArg(7));
483}
484
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000485RCParser::ParseType RCParser::parseIconResource() {
486 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000487 return llvm::make_unique<IconResource>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000488}
489
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000490RCParser::ParseType RCParser::parseHTMLResource() {
491 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000492 return llvm::make_unique<HTMLResource>(*Arg);
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000493}
494
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000495RCParser::ParseType RCParser::parseMenuResource() {
496 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
497 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000498 return llvm::make_unique<MenuResource>(std::move(*OptStatements),
499 std::move(*Items));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000500}
501
502Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
503 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
504
505 MenuDefinitionList List;
506
507 // Read a set of items. Each item is of one of three kinds:
508 // MENUITEM SEPARATOR
509 // MENUITEM caption:String, result:Int [, menu flags]...
510 // POPUP caption:String [, menu flags]... { items... }
511 while (!consumeOptionalType(Kind::BlockEnd)) {
512 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
513
514 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
515 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
516 if (!IsMenuItem && !IsPopup)
517 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
518
519 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
520 // Now, expecting SEPARATOR.
521 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
522 if (SeparatorResult->equals_lower("SEPARATOR")) {
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000523 List.addDefinition(llvm::make_unique<MenuSeparator>());
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000524 continue;
525 }
526
527 return getExpectedError("SEPARATOR or string", true);
528 }
529
530 // Not a separator. Read the caption.
531 ASSIGN_OR_RETURN(CaptionResult, readString());
532
533 // If MENUITEM, expect also a comma and an integer.
534 uint32_t MenuResult = -1;
535
536 if (IsMenuItem) {
537 RETURN_IF_ERROR(consumeType(Kind::Comma));
538 ASSIGN_OR_RETURN(IntResult, readInt());
539 MenuResult = *IntResult;
540 }
541
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000542 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
543 MenuDefinition::OptionsFlags));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000544
545 if (IsPopup) {
546 // If POPUP, read submenu items recursively.
547 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000548 List.addDefinition(llvm::make_unique<PopupItem>(
549 *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000550 continue;
551 }
552
553 assert(IsMenuItem);
554 List.addDefinition(
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000555 llvm::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000556 }
557
558 return std::move(List);
559}
560
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000561RCParser::ParseType RCParser::parseStringTableResource() {
562 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
563 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
564
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000565 auto Table =
566 llvm::make_unique<StringTableResource>(std::move(*OptStatements));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000567
568 // Read strings until we reach the end of the block.
569 while (!consumeOptionalType(Kind::BlockEnd)) {
570 // Each definition consists of string's ID (an integer) and a string.
571 // Some examples in documentation suggest that there might be a comma in
572 // between, however we strictly adhere to the single statement definition.
573 ASSIGN_OR_RETURN(IDResult, readInt());
574 ASSIGN_OR_RETURN(StrResult, readString());
575 Table->addString(*IDResult, *StrResult);
576 }
577
578 return std::move(Table);
579}
580
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000581Expected<std::unique_ptr<VersionInfoBlock>>
582RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
583 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
584
Marek Sokolowski99ead702017-09-28 23:12:53 +0000585 auto Contents = llvm::make_unique<VersionInfoBlock>(BlockName);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000586
587 while (!isNextTokenKind(Kind::BlockEnd)) {
588 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
589 Contents->addStmt(std::move(*Stmt));
590 }
591
592 consume(); // Consume BlockEnd.
593
594 return std::move(Contents);
595}
596
597Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
598 // Expect either BLOCK or VALUE, then a name or a key (a string).
599 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
600
601 if (TypeResult->equals_lower("BLOCK")) {
602 ASSIGN_OR_RETURN(NameResult, readString());
603 return parseVersionInfoBlockContents(*NameResult);
604 }
605
606 if (TypeResult->equals_lower("VALUE")) {
607 ASSIGN_OR_RETURN(KeyResult, readString());
Zachary Turner07bc04f2017-10-06 21:26:06 +0000608 // Read a non-empty list of strings and/or ints, each
609 // possibly preceded by a comma. Unfortunately, the tool behavior depends
610 // on them existing or not, so we need to memorize where we found them.
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000611 std::vector<IntOrString> Values;
Zachary Turner07bc04f2017-10-06 21:26:06 +0000612 std::vector<bool> PrecedingCommas;
613 RETURN_IF_ERROR(consumeType(Kind::Comma));
614 while (!isNextTokenKind(Kind::Identifier) &&
615 !isNextTokenKind(Kind::BlockEnd)) {
616 // Try to eat a comma if it's not the first statement.
617 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000618 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
619 Values.push_back(*ValueResult);
Zachary Turner07bc04f2017-10-06 21:26:06 +0000620 PrecedingCommas.push_back(HadComma);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000621 }
Zachary Turner07bc04f2017-10-06 21:26:06 +0000622 return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
623 std::move(PrecedingCommas));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000624 }
625
626 return getExpectedError("BLOCK or VALUE", true);
627}
628
629Expected<VersionInfoResource::VersionInfoFixed>
630RCParser::parseVersionInfoFixed() {
631 using RetType = VersionInfoResource::VersionInfoFixed;
632 RetType Result;
633
634 // Read until the beginning of the block.
635 while (!isNextTokenKind(Kind::BlockBegin)) {
636 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
637 auto FixedType = RetType::getFixedType(*TypeResult);
638
639 if (!RetType::isTypeSupported(FixedType))
640 return getExpectedError("fixed VERSIONINFO statement type", true);
641 if (Result.IsTypePresent[FixedType])
642 return getExpectedError("yet unread fixed VERSIONINFO statement type",
643 true);
644
645 // VERSION variations take multiple integers.
646 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
647 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
Zachary Turner07bc04f2017-10-06 21:26:06 +0000648 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
649 Result.setValue(FixedType, ArgInts);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000650 }
651
652 return Result;
653}
654
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000655RCParser::ParseOptionType RCParser::parseLanguageStmt() {
656 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000657 return llvm::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000658}
659
660RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
661 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000662 return llvm::make_unique<CharacteristicsStmt>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000663}
664
665RCParser::ParseOptionType RCParser::parseVersionStmt() {
666 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000667 return llvm::make_unique<VersionStmt>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000668}
669
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000670RCParser::ParseOptionType RCParser::parseCaptionStmt() {
671 ASSIGN_OR_RETURN(Arg, readString());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000672 return llvm::make_unique<CaptionStmt>(*Arg);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000673}
674
Zachary Turner420090a2017-10-06 20:51:20 +0000675RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
676 assert(DialogType != OptStmtType::BasicStmt);
677
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000678 ASSIGN_OR_RETURN(SizeResult, readInt());
679 RETURN_IF_ERROR(consumeType(Kind::Comma));
680 ASSIGN_OR_RETURN(NameResult, readString());
Zachary Turner420090a2017-10-06 20:51:20 +0000681
682 // Default values for the optional arguments.
683 uint32_t FontWeight = 0;
684 bool FontItalic = false;
685 uint32_t FontCharset = 1;
686 if (DialogType == OptStmtType::DialogExStmt) {
687 if (consumeOptionalType(Kind::Comma)) {
688 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
689 if (Args->size() >= 1)
690 FontWeight = (*Args)[0];
691 if (Args->size() >= 2)
692 FontItalic = (*Args)[1] != 0;
693 if (Args->size() >= 3)
694 FontCharset = (*Args)[2];
695 }
696 }
697 return llvm::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
698 FontItalic, FontCharset);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000699}
700
701RCParser::ParseOptionType RCParser::parseStyleStmt() {
702 ASSIGN_OR_RETURN(Arg, readInt());
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000703 return llvm::make_unique<StyleStmt>(*Arg);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000704}
705
Zachary Turner514b7102017-10-09 18:50:29 +0000706Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000707 return make_error<ParserError>(
708 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
709}
710
711} // namespace rc
712} // namespace llvm