blob: 36b305645fb815fd46c6fdce3029a865b62429d4 [file] [log] [blame]
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +00001//===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +00006//
7//===---------------------------------------------------------------------===//
8//
9// This implements the parser defined in ResourceScriptParser.h.
10//
11//===---------------------------------------------------------------------===//
12
13#include "ResourceScriptParser.h"
Zachary Turnerfa0ca6c2017-10-11 20:12:09 +000014#include "llvm/Option/ArgList.h"
15#include "llvm/Support/FileSystem.h"
16#include "llvm/Support/Path.h"
17#include "llvm/Support/Process.h"
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000018
19// Take an expression returning llvm::Error and forward the error if it exists.
20#define RETURN_IF_ERROR(Expr) \
21 if (auto Err = (Expr)) \
Bill Wendlingc55cf4a2020-02-10 07:06:45 -080022 return std::move(Err);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000023
24// Take an expression returning llvm::Expected<T> and assign it to Var or
25// forward the error out of the function.
26#define ASSIGN_OR_RETURN(Var, Expr) \
27 auto Var = (Expr); \
28 if (!Var) \
29 return Var.takeError();
30
31namespace llvm {
32namespace rc {
33
Zachary Turner514b7102017-10-09 18:50:29 +000034RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000035 const LocIter End)
36 : ErrorLoc(CurLoc), FileEnd(End) {
37 CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
38 (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
39}
40
41char RCParser::ParserError::ID = 0;
42
Zachary Turner514b7102017-10-09 18:50:29 +000043RCParser::RCParser(std::vector<RCToken> TokenList)
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000044 : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
45
46bool RCParser::isEof() const { return CurLoc == End; }
47
48RCParser::ParseType RCParser::parseSingleResource() {
49 // The first thing we read is usually a resource's name. However, in some
50 // cases (LANGUAGE and STRINGTABLE) the resources don't have their names
51 // and the first token to be read is the type.
52 ASSIGN_OR_RETURN(NameToken, readTypeOrName());
53
54 if (NameToken->equalsLower("LANGUAGE"))
55 return parseLanguageResource();
56 else if (NameToken->equalsLower("STRINGTABLE"))
57 return parseStringTableResource();
58
59 // If it's not an unnamed resource, what we've just read is a name. Now,
60 // read resource type;
61 ASSIGN_OR_RETURN(TypeToken, readTypeOrName());
62
63 ParseType Result = std::unique_ptr<RCResource>();
64 (void)!Result;
65
Marek Sokolowski7f110522017-08-28 22:58:31 +000066 if (TypeToken->equalsLower("ACCELERATORS"))
67 Result = parseAcceleratorsResource();
Martin Storsjo577b9812018-05-07 20:27:37 +000068 else if (TypeToken->equalsLower("BITMAP"))
69 Result = parseBitmapResource();
Marek Sokolowski7f110522017-08-28 22:58:31 +000070 else if (TypeToken->equalsLower("CURSOR"))
Marek Sokolowski72aa9372017-08-28 21:59:54 +000071 Result = parseCursorResource();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +000072 else if (TypeToken->equalsLower("DIALOG"))
73 Result = parseDialogResource(false);
74 else if (TypeToken->equalsLower("DIALOGEX"))
75 Result = parseDialogResource(true);
Marek Sokolowski72aa9372017-08-28 21:59:54 +000076 else if (TypeToken->equalsLower("HTML"))
77 Result = parseHTMLResource();
Martin Storsjo827ddb22018-05-07 20:27:15 +000078 else if (TypeToken->equalsLower("ICON"))
79 Result = parseIconResource();
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +000080 else if (TypeToken->equalsLower("MENU"))
81 Result = parseMenuResource();
Martin Storsjo7bc3c582018-05-09 18:20:56 +000082 else if (TypeToken->equalsLower("RCDATA"))
83 Result = parseUserDefinedResource(RkRcData);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +000084 else if (TypeToken->equalsLower("VERSIONINFO"))
85 Result = parseVersionInfoResource();
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000086 else
Marek Sokolowskib5f39a02017-09-29 00:14:18 +000087 Result = parseUserDefinedResource(*TypeToken);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000088
89 if (Result)
90 (*Result)->setName(*NameToken);
91
92 return Result;
93}
94
95bool RCParser::isNextTokenKind(Kind TokenKind) const {
96 return !isEof() && look().kind() == TokenKind;
97}
98
99const RCToken &RCParser::look() const {
100 assert(!isEof());
101 return *CurLoc;
102}
103
104const RCToken &RCParser::read() {
105 assert(!isEof());
106 return *CurLoc++;
107}
108
109void RCParser::consume() {
110 assert(!isEof());
111 CurLoc++;
112}
113
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000114// An integer description might consist of a single integer or
115// an arithmetic expression evaluating to the integer. The expressions
Martin Storsjod0afe722018-12-05 13:22:56 +0000116// can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning
117// is the same as in C++ except for 'not' expression.
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000118// The operators in the original RC implementation have the following
119// precedence:
Martin Storsjod0afe722018-12-05 13:22:56 +0000120// 1) Unary operators (- ~ not),
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000121// 2) Binary operators (+ - & |), with no precedence.
122//
Martin Storsjod0afe722018-12-05 13:22:56 +0000123// 'not' expression is mostly useful for style values. It evaluates to 0,
124// but value given to the operator is stored separately from integer value.
125// It's mostly useful for control style expressions and causes bits from
126// default control style to be excluded from generated style. For binary
127// operators the mask from the right operand is applied to the left operand
128// and masks from both operands are combined in operator result.
129//
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000130// The following grammar is used to parse the expressions Exp1:
131// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
Martin Storsjod0afe722018-12-05 13:22:56 +0000132// Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000133// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
134// separated by binary operators.)
135//
136// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
137// is read by parseIntExpr2().
138//
139// The original Microsoft tool handles multiple unary operators incorrectly.
140// For example, in 16-bit little-endian integers:
141// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
142// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
143// Our implementation differs from the original one and handles these
144// operators correctly:
145// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
146// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
147
Martin Storsjod0afe722018-12-05 13:22:56 +0000148Expected<RCInt> RCParser::readInt() {
149 ASSIGN_OR_RETURN(Value, parseIntExpr1());
150 return (*Value).getValue();
151}
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000152
Martin Storsjod0afe722018-12-05 13:22:56 +0000153Expected<IntWithNotMask> RCParser::parseIntExpr1() {
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000154 // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
155 ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
Martin Storsjod0afe722018-12-05 13:22:56 +0000156 IntWithNotMask Result = *FirstResult;
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000157
158 while (!isEof() && look().isBinaryOp()) {
159 auto OpToken = read();
160 ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
161
162 switch (OpToken.kind()) {
163 case Kind::Plus:
164 Result += *NextResult;
165 break;
166
167 case Kind::Minus:
168 Result -= *NextResult;
169 break;
170
171 case Kind::Pipe:
172 Result |= *NextResult;
173 break;
174
175 case Kind::Amp:
176 Result &= *NextResult;
177 break;
178
179 default:
180 llvm_unreachable("Already processed all binary ops.");
181 }
182 }
183
184 return Result;
185}
186
Martin Storsjod0afe722018-12-05 13:22:56 +0000187Expected<IntWithNotMask> RCParser::parseIntExpr2() {
188 // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000189 static const char ErrorMsg[] = "'-', '~', integer or '('";
190
191 if (isEof())
192 return getExpectedError(ErrorMsg);
193
194 switch (look().kind()) {
195 case Kind::Minus: {
196 consume();
197 ASSIGN_OR_RETURN(Result, parseIntExpr2());
198 return -(*Result);
199 }
200
201 case Kind::Tilde: {
202 consume();
203 ASSIGN_OR_RETURN(Result, parseIntExpr2());
204 return ~(*Result);
205 }
206
207 case Kind::Int:
Zachary Turner07bc04f2017-10-06 21:26:06 +0000208 return RCInt(read());
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000209
210 case Kind::LeftParen: {
211 consume();
212 ASSIGN_OR_RETURN(Result, parseIntExpr1());
213 RETURN_IF_ERROR(consumeType(Kind::RightParen));
214 return *Result;
215 }
216
Martin Storsjod0afe722018-12-05 13:22:56 +0000217 case Kind::Identifier: {
218 if (!read().value().equals_lower("not"))
219 return getExpectedError(ErrorMsg, true);
220 ASSIGN_OR_RETURN(Result, parseIntExpr2());
221 return IntWithNotMask(0, (*Result).getValue());
222 }
223
Marek Sokolowski7e89ee72017-09-28 23:53:25 +0000224 default:
225 return getExpectedError(ErrorMsg);
226 }
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000227}
228
229Expected<StringRef> RCParser::readString() {
230 if (!isNextTokenKind(Kind::String))
231 return getExpectedError("string");
232 return read().value();
233}
234
Martin Storsjo4021cee2018-05-08 08:47:37 +0000235Expected<StringRef> RCParser::readFilename() {
236 if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
237 return getExpectedError("string");
238 return read().value();
239}
240
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000241Expected<StringRef> RCParser::readIdentifier() {
242 if (!isNextTokenKind(Kind::Identifier))
243 return getExpectedError("identifier");
244 return read().value();
245}
246
Marek Sokolowski7f110522017-08-28 22:58:31 +0000247Expected<IntOrString> RCParser::readIntOrString() {
248 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
249 return getExpectedError("int or string");
250 return IntOrString(read());
251}
252
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000253Expected<IntOrString> RCParser::readTypeOrName() {
254 // We suggest that the correct resource name or type should be either an
255 // identifier or an integer. The original RC tool is much more liberal.
256 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
257 return getExpectedError("int or identifier");
Marek Sokolowski7f110522017-08-28 22:58:31 +0000258 return IntOrString(read());
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000259}
260
261Error RCParser::consumeType(Kind TokenKind) {
262 if (isNextTokenKind(TokenKind)) {
263 consume();
264 return Error::success();
265 }
266
267 switch (TokenKind) {
268#define TOKEN(TokenName) \
269 case Kind::TokenName: \
270 return getExpectedError(#TokenName);
271#define SHORT_TOKEN(TokenName, TokenCh) \
272 case Kind::TokenName: \
273 return getExpectedError(#TokenCh);
David Blaikieb961d292017-11-21 00:23:19 +0000274#include "ResourceScriptTokenList.def"
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000275 }
276
277 llvm_unreachable("All case options exhausted.");
278}
279
280bool RCParser::consumeOptionalType(Kind TokenKind) {
281 if (isNextTokenKind(TokenKind)) {
282 consume();
283 return true;
284 }
285
286 return false;
287}
288
Zachary Turner07bc04f2017-10-06 21:26:06 +0000289Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
290 size_t MaxCount) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000291 assert(MinCount <= MaxCount);
292
Zachary Turner07bc04f2017-10-06 21:26:06 +0000293 SmallVector<RCInt, 8> Result;
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000294
295 auto FailureHandler =
Zachary Turner07bc04f2017-10-06 21:26:06 +0000296 [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000297 if (Result.size() < MinCount)
Bill Wendlingc55cf4a2020-02-10 07:06:45 -0800298 return std::move(Err);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000299 consumeError(std::move(Err));
300 return Result;
301 };
302
303 for (size_t i = 0; i < MaxCount; ++i) {
304 // Try to read a comma unless we read the first token.
305 // Sometimes RC tool requires them and sometimes not. We decide to
306 // always require them.
307 if (i >= 1) {
308 if (auto CommaError = consumeType(Kind::Comma))
309 return FailureHandler(std::move(CommaError));
310 }
311
312 if (auto IntResult = readInt())
313 Result.push_back(*IntResult);
314 else
315 return FailureHandler(IntResult.takeError());
316 }
317
Bill Wendlingc55cf4a2020-02-10 07:06:45 -0800318 return std::move(Result);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000319}
320
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000321Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
322 ArrayRef<uint32_t> FlagValues) {
Marek Sokolowski7f110522017-08-28 22:58:31 +0000323 assert(!FlagDesc.empty());
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000324 assert(FlagDesc.size() == FlagValues.size());
Marek Sokolowski7f110522017-08-28 22:58:31 +0000325
326 uint32_t Result = 0;
327 while (isNextTokenKind(Kind::Comma)) {
328 consume();
329 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
330 bool FoundFlag = false;
331
332 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
333 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
334 continue;
335
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000336 Result |= FlagValues[FlagId];
Marek Sokolowski7f110522017-08-28 22:58:31 +0000337 FoundFlag = true;
338 break;
339 }
340
341 if (!FoundFlag)
342 return getExpectedError(join(FlagDesc, "/"), true);
343 }
344
345 return Result;
346}
347
Martin Storsjo11adbac2018-05-15 06:35:29 +0000348uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
349 while (!isEof()) {
350 const RCToken &Token = look();
351 if (Token.kind() != Kind::Identifier)
352 return Flags;
353 const StringRef Ident = Token.value();
354 if (Ident.equals_lower("PRELOAD"))
355 Flags |= MfPreload;
356 else if (Ident.equals_lower("LOADONCALL"))
357 Flags &= ~MfPreload;
358 else if (Ident.equals_lower("FIXED"))
359 Flags &= ~(MfMoveable | MfDiscardable);
360 else if (Ident.equals_lower("MOVEABLE"))
361 Flags |= MfMoveable;
362 else if (Ident.equals_lower("DISCARDABLE"))
363 Flags |= MfDiscardable | MfMoveable | MfPure;
364 else if (Ident.equals_lower("PURE"))
365 Flags |= MfPure;
366 else if (Ident.equals_lower("IMPURE"))
367 Flags &= ~(MfPure | MfDiscardable);
368 else if (Ident.equals_lower("SHARED"))
369 Flags |= MfPure;
370 else if (Ident.equals_lower("NONSHARED"))
371 Flags &= ~(MfPure | MfDiscardable);
372 else
373 return Flags;
374 consume();
375 }
376 return Flags;
377}
378
Zachary Turner420090a2017-10-06 20:51:20 +0000379Expected<OptionalStmtList>
380RCParser::parseOptionalStatements(OptStmtType StmtsType) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000381 OptionalStmtList Result;
382
383 // The last statement is always followed by the start of the block.
384 while (!isNextTokenKind(Kind::BlockBegin)) {
Zachary Turner420090a2017-10-06 20:51:20 +0000385 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000386 Result.addStmt(std::move(*SingleParse));
387 }
388
Bill Wendlingc55cf4a2020-02-10 07:06:45 -0800389 return std::move(Result);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000390}
391
392Expected<std::unique_ptr<OptionalStmt>>
Zachary Turner420090a2017-10-06 20:51:20 +0000393RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000394 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
395 if (TypeToken->equals_lower("CHARACTERISTICS"))
396 return parseCharacteristicsStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000397 if (TypeToken->equals_lower("LANGUAGE"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000398 return parseLanguageStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000399 if (TypeToken->equals_lower("VERSION"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000400 return parseVersionStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000401
Zachary Turner420090a2017-10-06 20:51:20 +0000402 if (StmtsType != OptStmtType::BasicStmt) {
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000403 if (TypeToken->equals_lower("CAPTION"))
404 return parseCaptionStmt();
Martin Storsjoe241ce62018-05-15 19:21:28 +0000405 if (TypeToken->equals_lower("CLASS"))
406 return parseClassStmt();
Martin Storsjoa876b5c2018-11-29 12:17:39 +0000407 if (TypeToken->equals_lower("EXSTYLE"))
408 return parseExStyleStmt();
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000409 if (TypeToken->equals_lower("FONT"))
Zachary Turner420090a2017-10-06 20:51:20 +0000410 return parseFontStmt(StmtsType);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000411 if (TypeToken->equals_lower("STYLE"))
412 return parseStyleStmt();
413 }
414
415 return getExpectedError("optional statement type, BEGIN or '{'",
416 /* IsAlreadyRead = */ true);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000417}
418
419RCParser::ParseType RCParser::parseLanguageResource() {
420 // Read LANGUAGE as an optional statement. If it's read correctly, we can
421 // upcast it to RCResource.
422 return parseLanguageStmt();
423}
424
Marek Sokolowski7f110522017-08-28 22:58:31 +0000425RCParser::ParseType RCParser::parseAcceleratorsResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000426 uint16_t MemoryFlags =
427 parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
Marek Sokolowski7f110522017-08-28 22:58:31 +0000428 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
429 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
430
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000431 auto Accels = std::make_unique<AcceleratorsResource>(
Martin Storsjo11adbac2018-05-15 06:35:29 +0000432 std::move(*OptStatements), MemoryFlags);
Marek Sokolowski7f110522017-08-28 22:58:31 +0000433
434 while (!consumeOptionalType(Kind::BlockEnd)) {
435 ASSIGN_OR_RETURN(EventResult, readIntOrString());
436 RETURN_IF_ERROR(consumeType(Kind::Comma));
437 ASSIGN_OR_RETURN(IDResult, readInt());
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000438 ASSIGN_OR_RETURN(
439 FlagsResult,
440 parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
441 AcceleratorsResource::Accelerator::OptionsFlags));
Marek Sokolowski7f110522017-08-28 22:58:31 +0000442 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
443 }
444
Bill Wendlingc55cf4a2020-02-10 07:06:45 -0800445 return std::move(Accels);
Marek Sokolowski7f110522017-08-28 22:58:31 +0000446}
447
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000448RCParser::ParseType RCParser::parseCursorResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000449 uint16_t MemoryFlags =
450 parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
Martin Storsjo4021cee2018-05-08 08:47:37 +0000451 ASSIGN_OR_RETURN(Arg, readFilename());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000452 return std::make_unique<CursorResource>(*Arg, MemoryFlags);
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000453}
454
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000455RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000456 uint16_t MemoryFlags =
457 parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000458 // Dialog resources have the following format of the arguments:
459 // DIALOG: x, y, width, height [opt stmts...] {controls...}
460 // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
461 // These are very similar, so we parse them together.
462 ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
463
464 uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
465 if (IsExtended && consumeOptionalType(Kind::Comma)) {
466 ASSIGN_OR_RETURN(HelpIDResult, readInt());
467 HelpID = *HelpIDResult;
468 }
469
Zachary Turner420090a2017-10-06 20:51:20 +0000470 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
471 IsExtended ? OptStmtType::DialogExStmt
472 : OptStmtType::DialogStmt));
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000473
474 assert(isNextTokenKind(Kind::BlockBegin) &&
475 "parseOptionalStatements, when successful, halts on BlockBegin.");
476 consume();
477
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000478 auto Dialog = std::make_unique<DialogResource>(
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000479 (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
Martin Storsjo11adbac2018-05-15 06:35:29 +0000480 HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000481
482 while (!consumeOptionalType(Kind::BlockEnd)) {
483 ASSIGN_OR_RETURN(ControlDefResult, parseControl());
484 Dialog->addControl(std::move(*ControlDefResult));
485 }
486
Bill Wendlingc55cf4a2020-02-10 07:06:45 -0800487 return std::move(Dialog);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000488}
489
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000490RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000491 uint16_t MemoryFlags =
492 parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000493 if (isEof())
494 return getExpectedError("filename, '{' or BEGIN");
495
496 // Check if this is a file resource.
Martin Storsjo4021cee2018-05-08 08:47:37 +0000497 switch (look().kind()) {
498 case Kind::String:
499 case Kind::Identifier:
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000500 return std::make_unique<UserDefinedResource>(Type, read().value(),
Martin Storsjo11adbac2018-05-15 06:35:29 +0000501 MemoryFlags);
Martin Storsjo4021cee2018-05-08 08:47:37 +0000502 default:
503 break;
504 }
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000505
506 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
507 std::vector<IntOrString> Data;
508
509 // Consume comma before each consecutive token except the first one.
510 bool ConsumeComma = false;
511 while (!consumeOptionalType(Kind::BlockEnd)) {
512 if (ConsumeComma)
513 RETURN_IF_ERROR(consumeType(Kind::Comma));
514 ConsumeComma = true;
515
516 ASSIGN_OR_RETURN(Item, readIntOrString());
517 Data.push_back(*Item);
518 }
519
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000520 return std::make_unique<UserDefinedResource>(Type, std::move(Data),
Martin Storsjo11adbac2018-05-15 06:35:29 +0000521 MemoryFlags);
Marek Sokolowskib5f39a02017-09-29 00:14:18 +0000522}
523
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000524RCParser::ParseType RCParser::parseVersionInfoResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000525 uint16_t MemoryFlags =
526 parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000527 ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
528 ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000529 return std::make_unique<VersionInfoResource>(
Martin Storsjo11adbac2018-05-15 06:35:29 +0000530 std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000531}
532
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000533Expected<Control> RCParser::parseControl() {
534 // Each control definition (except CONTROL) follows one of the schemes below
535 // depending on the control class:
536 // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
537 // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
538 // Note that control ids must be integers.
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000539 // Text might be either a string or an integer pointing to resource ID.
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000540 ASSIGN_OR_RETURN(ClassResult, readIdentifier());
Marek Sokolowski75fa1732017-08-29 20:03:18 +0000541 std::string ClassUpper = ClassResult->upper();
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000542 auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
543 if (CtlInfo == Control::SupportedCtls.end())
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000544 return getExpectedError("control type, END or '}'", true);
545
546 // Read caption if necessary.
Marek Sokolowski7f7745c2017-09-30 00:38:52 +0000547 IntOrString Caption{StringRef()};
548 if (CtlInfo->getValue().HasTitle) {
549 ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000550 RETURN_IF_ERROR(consumeType(Kind::Comma));
551 Caption = *CaptionResult;
552 }
553
Martin Storsjo818bd562018-05-08 20:55:58 +0000554 ASSIGN_OR_RETURN(ID, readInt());
555 RETURN_IF_ERROR(consumeType(Kind::Comma));
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000556
Martin Storsjo818bd562018-05-08 20:55:58 +0000557 IntOrString Class;
Martin Storsjod0afe722018-12-05 13:22:56 +0000558 Optional<IntWithNotMask> Style;
Martin Storsjo818bd562018-05-08 20:55:58 +0000559 if (ClassUpper == "CONTROL") {
560 // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
561 ASSIGN_OR_RETURN(ClassStr, readString());
562 RETURN_IF_ERROR(consumeType(Kind::Comma));
563 Class = *ClassStr;
Martin Storsjod0afe722018-12-05 13:22:56 +0000564 ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());
Martin Storsjo818bd562018-05-08 20:55:58 +0000565 RETURN_IF_ERROR(consumeType(Kind::Comma));
566 Style = *StyleVal;
567 } else {
568 Class = CtlInfo->getValue().CtlClass;
569 }
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000570
Martin Storsjo818bd562018-05-08 20:55:58 +0000571 // x, y, width, height
572 ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
573
574 if (ClassUpper != "CONTROL") {
575 if (consumeOptionalType(Kind::Comma)) {
Martin Storsjod0afe722018-12-05 13:22:56 +0000576 ASSIGN_OR_RETURN(Val, parseIntExpr1());
Martin Storsjo818bd562018-05-08 20:55:58 +0000577 Style = *Val;
578 }
579 }
580
581 Optional<uint32_t> ExStyle;
582 if (consumeOptionalType(Kind::Comma)) {
583 ASSIGN_OR_RETURN(Val, readInt());
584 ExStyle = *Val;
585 }
586 Optional<uint32_t> HelpID;
587 if (consumeOptionalType(Kind::Comma)) {
588 ASSIGN_OR_RETURN(Val, readInt());
589 HelpID = *Val;
590 }
591
592 return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
593 (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000594}
595
Martin Storsjo577b9812018-05-07 20:27:37 +0000596RCParser::ParseType RCParser::parseBitmapResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000597 uint16_t MemoryFlags =
598 parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
Martin Storsjo4021cee2018-05-08 08:47:37 +0000599 ASSIGN_OR_RETURN(Arg, readFilename());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000600 return std::make_unique<BitmapResource>(*Arg, MemoryFlags);
Martin Storsjo577b9812018-05-07 20:27:37 +0000601}
602
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000603RCParser::ParseType RCParser::parseIconResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000604 uint16_t MemoryFlags =
605 parseMemoryFlags(IconResource::getDefaultMemoryFlags());
Martin Storsjo4021cee2018-05-08 08:47:37 +0000606 ASSIGN_OR_RETURN(Arg, readFilename());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000607 return std::make_unique<IconResource>(*Arg, MemoryFlags);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000608}
609
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000610RCParser::ParseType RCParser::parseHTMLResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000611 uint16_t MemoryFlags =
612 parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
Martin Storsjo4021cee2018-05-08 08:47:37 +0000613 ASSIGN_OR_RETURN(Arg, readFilename());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000614 return std::make_unique<HTMLResource>(*Arg, MemoryFlags);
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000615}
616
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000617RCParser::ParseType RCParser::parseMenuResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000618 uint16_t MemoryFlags =
619 parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000620 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
621 ASSIGN_OR_RETURN(Items, parseMenuItemsList());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000622 return std::make_unique<MenuResource>(std::move(*OptStatements),
Martin Storsjo11adbac2018-05-15 06:35:29 +0000623 std::move(*Items), MemoryFlags);
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000624}
625
626Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
627 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
628
629 MenuDefinitionList List;
630
631 // Read a set of items. Each item is of one of three kinds:
632 // MENUITEM SEPARATOR
633 // MENUITEM caption:String, result:Int [, menu flags]...
634 // POPUP caption:String [, menu flags]... { items... }
635 while (!consumeOptionalType(Kind::BlockEnd)) {
636 ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
637
638 bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM");
639 bool IsPopup = ItemTypeResult->equals_lower("POPUP");
640 if (!IsMenuItem && !IsPopup)
641 return getExpectedError("MENUITEM, POPUP, END or '}'", true);
642
643 if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
644 // Now, expecting SEPARATOR.
645 ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
646 if (SeparatorResult->equals_lower("SEPARATOR")) {
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000647 List.addDefinition(std::make_unique<MenuSeparator>());
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000648 continue;
649 }
650
651 return getExpectedError("SEPARATOR or string", true);
652 }
653
654 // Not a separator. Read the caption.
655 ASSIGN_OR_RETURN(CaptionResult, readString());
656
657 // If MENUITEM, expect also a comma and an integer.
658 uint32_t MenuResult = -1;
659
660 if (IsMenuItem) {
661 RETURN_IF_ERROR(consumeType(Kind::Comma));
662 ASSIGN_OR_RETURN(IntResult, readInt());
663 MenuResult = *IntResult;
664 }
665
Marek Sokolowskic75a0872017-09-29 17:46:32 +0000666 ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
667 MenuDefinition::OptionsFlags));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000668
669 if (IsPopup) {
670 // If POPUP, read submenu items recursively.
671 ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000672 List.addDefinition(std::make_unique<PopupItem>(
Marek Sokolowski4a765da2017-09-29 00:33:57 +0000673 *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000674 continue;
675 }
676
677 assert(IsMenuItem);
678 List.addDefinition(
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000679 std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000680 }
681
Bill Wendlingc55cf4a2020-02-10 07:06:45 -0800682 return std::move(List);
Marek Sokolowski99ecb0e2017-08-28 23:46:30 +0000683}
684
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000685RCParser::ParseType RCParser::parseStringTableResource() {
Martin Storsjo11adbac2018-05-15 06:35:29 +0000686 uint16_t MemoryFlags =
687 parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000688 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
689 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
690
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000691 auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),
Martin Storsjo11adbac2018-05-15 06:35:29 +0000692 MemoryFlags);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000693
694 // Read strings until we reach the end of the block.
695 while (!consumeOptionalType(Kind::BlockEnd)) {
696 // Each definition consists of string's ID (an integer) and a string.
697 // Some examples in documentation suggest that there might be a comma in
698 // between, however we strictly adhere to the single statement definition.
699 ASSIGN_OR_RETURN(IDResult, readInt());
Martin Storsjo94102762018-05-07 20:27:28 +0000700 consumeOptionalType(Kind::Comma);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000701 ASSIGN_OR_RETURN(StrResult, readString());
702 Table->addString(*IDResult, *StrResult);
703 }
704
Bill Wendlingc55cf4a2020-02-10 07:06:45 -0800705 return std::move(Table);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000706}
707
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000708Expected<std::unique_ptr<VersionInfoBlock>>
709RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
710 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
711
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000712 auto Contents = std::make_unique<VersionInfoBlock>(BlockName);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000713
714 while (!isNextTokenKind(Kind::BlockEnd)) {
715 ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
716 Contents->addStmt(std::move(*Stmt));
717 }
718
719 consume(); // Consume BlockEnd.
720
Bill Wendlingc55cf4a2020-02-10 07:06:45 -0800721 return std::move(Contents);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000722}
723
724Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
725 // Expect either BLOCK or VALUE, then a name or a key (a string).
726 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
727
728 if (TypeResult->equals_lower("BLOCK")) {
729 ASSIGN_OR_RETURN(NameResult, readString());
730 return parseVersionInfoBlockContents(*NameResult);
731 }
732
733 if (TypeResult->equals_lower("VALUE")) {
734 ASSIGN_OR_RETURN(KeyResult, readString());
Zachary Turner07bc04f2017-10-06 21:26:06 +0000735 // Read a non-empty list of strings and/or ints, each
736 // possibly preceded by a comma. Unfortunately, the tool behavior depends
737 // on them existing or not, so we need to memorize where we found them.
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000738 std::vector<IntOrString> Values;
Zachary Turner07bc04f2017-10-06 21:26:06 +0000739 std::vector<bool> PrecedingCommas;
740 RETURN_IF_ERROR(consumeType(Kind::Comma));
741 while (!isNextTokenKind(Kind::Identifier) &&
742 !isNextTokenKind(Kind::BlockEnd)) {
743 // Try to eat a comma if it's not the first statement.
744 bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000745 ASSIGN_OR_RETURN(ValueResult, readIntOrString());
746 Values.push_back(*ValueResult);
Zachary Turner07bc04f2017-10-06 21:26:06 +0000747 PrecedingCommas.push_back(HadComma);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000748 }
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000749 return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
Zachary Turner07bc04f2017-10-06 21:26:06 +0000750 std::move(PrecedingCommas));
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000751 }
752
753 return getExpectedError("BLOCK or VALUE", true);
754}
755
756Expected<VersionInfoResource::VersionInfoFixed>
757RCParser::parseVersionInfoFixed() {
758 using RetType = VersionInfoResource::VersionInfoFixed;
759 RetType Result;
760
761 // Read until the beginning of the block.
762 while (!isNextTokenKind(Kind::BlockBegin)) {
763 ASSIGN_OR_RETURN(TypeResult, readIdentifier());
764 auto FixedType = RetType::getFixedType(*TypeResult);
765
766 if (!RetType::isTypeSupported(FixedType))
767 return getExpectedError("fixed VERSIONINFO statement type", true);
768 if (Result.IsTypePresent[FixedType])
769 return getExpectedError("yet unread fixed VERSIONINFO statement type",
770 true);
771
772 // VERSION variations take multiple integers.
773 size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
774 ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
Zachary Turner07bc04f2017-10-06 21:26:06 +0000775 SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
776 Result.setValue(FixedType, ArgInts);
Marek Sokolowskifb74cb12017-09-28 22:41:38 +0000777 }
778
779 return Result;
780}
781
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000782RCParser::ParseOptionType RCParser::parseLanguageStmt() {
783 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000784 return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000785}
786
787RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
788 ASSIGN_OR_RETURN(Arg, readInt());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000789 return std::make_unique<CharacteristicsStmt>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000790}
791
792RCParser::ParseOptionType RCParser::parseVersionStmt() {
793 ASSIGN_OR_RETURN(Arg, readInt());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000794 return std::make_unique<VersionStmt>(*Arg);
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000795}
796
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000797RCParser::ParseOptionType RCParser::parseCaptionStmt() {
798 ASSIGN_OR_RETURN(Arg, readString());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000799 return std::make_unique<CaptionStmt>(*Arg);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000800}
801
Martin Storsjoe241ce62018-05-15 19:21:28 +0000802RCParser::ParseOptionType RCParser::parseClassStmt() {
803 ASSIGN_OR_RETURN(Arg, readIntOrString());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000804 return std::make_unique<ClassStmt>(*Arg);
Martin Storsjoe241ce62018-05-15 19:21:28 +0000805}
806
Zachary Turner420090a2017-10-06 20:51:20 +0000807RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
808 assert(DialogType != OptStmtType::BasicStmt);
809
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000810 ASSIGN_OR_RETURN(SizeResult, readInt());
811 RETURN_IF_ERROR(consumeType(Kind::Comma));
812 ASSIGN_OR_RETURN(NameResult, readString());
Zachary Turner420090a2017-10-06 20:51:20 +0000813
814 // Default values for the optional arguments.
815 uint32_t FontWeight = 0;
816 bool FontItalic = false;
817 uint32_t FontCharset = 1;
818 if (DialogType == OptStmtType::DialogExStmt) {
819 if (consumeOptionalType(Kind::Comma)) {
820 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
821 if (Args->size() >= 1)
822 FontWeight = (*Args)[0];
823 if (Args->size() >= 2)
824 FontItalic = (*Args)[1] != 0;
825 if (Args->size() >= 3)
826 FontCharset = (*Args)[2];
827 }
828 }
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000829 return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
Zachary Turner420090a2017-10-06 20:51:20 +0000830 FontItalic, FontCharset);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000831}
832
833RCParser::ParseOptionType RCParser::parseStyleStmt() {
834 ASSIGN_OR_RETURN(Arg, readInt());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000835 return std::make_unique<StyleStmt>(*Arg);
Marek Sokolowski4ac54d92017-08-29 16:49:59 +0000836}
837
Martin Storsjoa876b5c2018-11-29 12:17:39 +0000838RCParser::ParseOptionType RCParser::parseExStyleStmt() {
839 ASSIGN_OR_RETURN(Arg, readInt());
Jonas Devlieghere0eaee542019-08-15 15:54:37 +0000840 return std::make_unique<ExStyleStmt>(*Arg);
Martin Storsjoa876b5c2018-11-29 12:17:39 +0000841}
842
Zachary Turner514b7102017-10-09 18:50:29 +0000843Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000844 return make_error<ParserError>(
845 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
846}
847
848} // namespace rc
849} // namespace llvm