blob: 8d08396e615fa98691b27cbd30bc95bbb4d5cbbe [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
31RCParser::ParserError::ParserError(const Twine Expected, const LocIter CurLoc,
32 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
40RCParser::RCParser(const std::vector<RCToken> &TokenList)
41 : Tokens(TokenList), CurLoc(Tokens.begin()), End(Tokens.end()) {}
42
43RCParser::RCParser(std::vector<RCToken> &&TokenList)
44 : 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();
68 else if (TypeToken->equalsLower("CURSOR"))
Marek Sokolowski72aa9372017-08-28 21:59:54 +000069 Result = parseCursorResource();
70 else if (TypeToken->equalsLower("ICON"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000071 Result = parseIconResource();
Marek Sokolowski72aa9372017-08-28 21:59:54 +000072 else if (TypeToken->equalsLower("HTML"))
73 Result = parseHTMLResource();
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000074 else
75 return getExpectedError("resource type", /* IsAlreadyRead = */ true);
76
77 if (Result)
78 (*Result)->setName(*NameToken);
79
80 return Result;
81}
82
83bool RCParser::isNextTokenKind(Kind TokenKind) const {
84 return !isEof() && look().kind() == TokenKind;
85}
86
87const RCToken &RCParser::look() const {
88 assert(!isEof());
89 return *CurLoc;
90}
91
92const RCToken &RCParser::read() {
93 assert(!isEof());
94 return *CurLoc++;
95}
96
97void RCParser::consume() {
98 assert(!isEof());
99 CurLoc++;
100}
101
102Expected<uint32_t> RCParser::readInt() {
103 if (!isNextTokenKind(Kind::Int))
104 return getExpectedError("integer");
105 return read().intValue();
106}
107
108Expected<StringRef> RCParser::readString() {
109 if (!isNextTokenKind(Kind::String))
110 return getExpectedError("string");
111 return read().value();
112}
113
114Expected<StringRef> RCParser::readIdentifier() {
115 if (!isNextTokenKind(Kind::Identifier))
116 return getExpectedError("identifier");
117 return read().value();
118}
119
Marek Sokolowski7f110522017-08-28 22:58:31 +0000120Expected<IntOrString> RCParser::readIntOrString() {
121 if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
122 return getExpectedError("int or string");
123 return IntOrString(read());
124}
125
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000126Expected<IntOrString> RCParser::readTypeOrName() {
127 // We suggest that the correct resource name or type should be either an
128 // identifier or an integer. The original RC tool is much more liberal.
129 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
130 return getExpectedError("int or identifier");
Marek Sokolowski7f110522017-08-28 22:58:31 +0000131 return IntOrString(read());
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000132}
133
134Error RCParser::consumeType(Kind TokenKind) {
135 if (isNextTokenKind(TokenKind)) {
136 consume();
137 return Error::success();
138 }
139
140 switch (TokenKind) {
141#define TOKEN(TokenName) \
142 case Kind::TokenName: \
143 return getExpectedError(#TokenName);
144#define SHORT_TOKEN(TokenName, TokenCh) \
145 case Kind::TokenName: \
146 return getExpectedError(#TokenCh);
147#include "ResourceScriptTokenList.h"
148#undef SHORT_TOKEN
149#undef TOKEN
150 }
151
152 llvm_unreachable("All case options exhausted.");
153}
154
155bool RCParser::consumeOptionalType(Kind TokenKind) {
156 if (isNextTokenKind(TokenKind)) {
157 consume();
158 return true;
159 }
160
161 return false;
162}
163
164Expected<SmallVector<uint32_t, 8>>
165RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) {
166 assert(MinCount <= MaxCount);
167
168 SmallVector<uint32_t, 8> Result;
169
170 auto FailureHandler =
171 [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 8>> {
172 if (Result.size() < MinCount)
173 return std::move(Err);
174 consumeError(std::move(Err));
175 return Result;
176 };
177
178 for (size_t i = 0; i < MaxCount; ++i) {
179 // Try to read a comma unless we read the first token.
180 // Sometimes RC tool requires them and sometimes not. We decide to
181 // always require them.
182 if (i >= 1) {
183 if (auto CommaError = consumeType(Kind::Comma))
184 return FailureHandler(std::move(CommaError));
185 }
186
187 if (auto IntResult = readInt())
188 Result.push_back(*IntResult);
189 else
190 return FailureHandler(IntResult.takeError());
191 }
192
193 return std::move(Result);
194}
195
Marek Sokolowski7f110522017-08-28 22:58:31 +0000196Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc) {
197 assert(FlagDesc.size() <= 32 && "More than 32 flags won't fit in result.");
198 assert(!FlagDesc.empty());
199
200 uint32_t Result = 0;
201 while (isNextTokenKind(Kind::Comma)) {
202 consume();
203 ASSIGN_OR_RETURN(FlagResult, readIdentifier());
204 bool FoundFlag = false;
205
206 for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
207 if (!FlagResult->equals_lower(FlagDesc[FlagId]))
208 continue;
209
210 Result |= (1U << FlagId);
211 FoundFlag = true;
212 break;
213 }
214
215 if (!FoundFlag)
216 return getExpectedError(join(FlagDesc, "/"), true);
217 }
218
219 return Result;
220}
221
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000222// As for now, we ignore the extended set of statements.
223Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) {
224 OptionalStmtList Result;
225
226 // The last statement is always followed by the start of the block.
227 while (!isNextTokenKind(Kind::BlockBegin)) {
228 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(IsExtended));
229 Result.addStmt(std::move(*SingleParse));
230 }
231
232 return std::move(Result);
233}
234
235Expected<std::unique_ptr<OptionalStmt>>
236RCParser::parseSingleOptionalStatement(bool) {
237 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
238 if (TypeToken->equals_lower("CHARACTERISTICS"))
239 return parseCharacteristicsStmt();
240 else if (TypeToken->equals_lower("LANGUAGE"))
241 return parseLanguageStmt();
242 else if (TypeToken->equals_lower("VERSION"))
243 return parseVersionStmt();
244 else
245 return getExpectedError("optional statement type, BEGIN or '{'",
246 /* IsAlreadyRead = */ true);
247}
248
249RCParser::ParseType RCParser::parseLanguageResource() {
250 // Read LANGUAGE as an optional statement. If it's read correctly, we can
251 // upcast it to RCResource.
252 return parseLanguageStmt();
253}
254
Marek Sokolowski7f110522017-08-28 22:58:31 +0000255RCParser::ParseType RCParser::parseAcceleratorsResource() {
256 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
257 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
258
259 auto Accels = make_unique<AcceleratorsResource>(std::move(*OptStatements));
260
261 while (!consumeOptionalType(Kind::BlockEnd)) {
262 ASSIGN_OR_RETURN(EventResult, readIntOrString());
263 RETURN_IF_ERROR(consumeType(Kind::Comma));
264 ASSIGN_OR_RETURN(IDResult, readInt());
265 ASSIGN_OR_RETURN(FlagsResult,
266 parseFlags(AcceleratorsResource::Accelerator::OptionsStr));
267 Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
268 }
269
270 return std::move(Accels);
271}
272
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000273RCParser::ParseType RCParser::parseCursorResource() {
274 ASSIGN_OR_RETURN(Arg, readString());
275 return make_unique<CursorResource>(*Arg);
276}
277
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000278RCParser::ParseType RCParser::parseIconResource() {
279 ASSIGN_OR_RETURN(Arg, readString());
280 return make_unique<IconResource>(*Arg);
281}
282
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000283RCParser::ParseType RCParser::parseHTMLResource() {
284 ASSIGN_OR_RETURN(Arg, readString());
285 return make_unique<HTMLResource>(*Arg);
286}
287
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000288RCParser::ParseType RCParser::parseStringTableResource() {
289 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
290 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
291
292 auto Table = make_unique<StringTableResource>(std::move(*OptStatements));
293
294 // Read strings until we reach the end of the block.
295 while (!consumeOptionalType(Kind::BlockEnd)) {
296 // Each definition consists of string's ID (an integer) and a string.
297 // Some examples in documentation suggest that there might be a comma in
298 // between, however we strictly adhere to the single statement definition.
299 ASSIGN_OR_RETURN(IDResult, readInt());
300 ASSIGN_OR_RETURN(StrResult, readString());
301 Table->addString(*IDResult, *StrResult);
302 }
303
304 return std::move(Table);
305}
306
307RCParser::ParseOptionType RCParser::parseLanguageStmt() {
308 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
309 return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
310}
311
312RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
313 ASSIGN_OR_RETURN(Arg, readInt());
314 return make_unique<CharacteristicsStmt>(*Arg);
315}
316
317RCParser::ParseOptionType RCParser::parseVersionStmt() {
318 ASSIGN_OR_RETURN(Arg, readInt());
319 return make_unique<VersionStmt>(*Arg);
320}
321
322Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) {
323 return make_error<ParserError>(
324 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
325}
326
327} // namespace rc
328} // namespace llvm