blob: e4c0f2a7f7ca640169c3cebf25e659d34bfaab7d [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 Sokolowski72aa9372017-08-28 21:59:54 +000066 if (TypeToken->equalsLower("CURSOR"))
67 Result = parseCursorResource();
68 else if (TypeToken->equalsLower("ICON"))
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000069 Result = parseIconResource();
Marek Sokolowski72aa9372017-08-28 21:59:54 +000070 else if (TypeToken->equalsLower("HTML"))
71 Result = parseHTMLResource();
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +000072 else
73 return getExpectedError("resource type", /* IsAlreadyRead = */ true);
74
75 if (Result)
76 (*Result)->setName(*NameToken);
77
78 return Result;
79}
80
81bool RCParser::isNextTokenKind(Kind TokenKind) const {
82 return !isEof() && look().kind() == TokenKind;
83}
84
85const RCToken &RCParser::look() const {
86 assert(!isEof());
87 return *CurLoc;
88}
89
90const RCToken &RCParser::read() {
91 assert(!isEof());
92 return *CurLoc++;
93}
94
95void RCParser::consume() {
96 assert(!isEof());
97 CurLoc++;
98}
99
100Expected<uint32_t> RCParser::readInt() {
101 if (!isNextTokenKind(Kind::Int))
102 return getExpectedError("integer");
103 return read().intValue();
104}
105
106Expected<StringRef> RCParser::readString() {
107 if (!isNextTokenKind(Kind::String))
108 return getExpectedError("string");
109 return read().value();
110}
111
112Expected<StringRef> RCParser::readIdentifier() {
113 if (!isNextTokenKind(Kind::Identifier))
114 return getExpectedError("identifier");
115 return read().value();
116}
117
118Expected<IntOrString> RCParser::readTypeOrName() {
119 // We suggest that the correct resource name or type should be either an
120 // identifier or an integer. The original RC tool is much more liberal.
121 if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
122 return getExpectedError("int or identifier");
123
124 const RCToken &Tok = read();
125 if (Tok.kind() == Kind::Int)
126 return IntOrString(Tok.intValue());
127 else
128 return IntOrString(Tok.value());
129}
130
131Error RCParser::consumeType(Kind TokenKind) {
132 if (isNextTokenKind(TokenKind)) {
133 consume();
134 return Error::success();
135 }
136
137 switch (TokenKind) {
138#define TOKEN(TokenName) \
139 case Kind::TokenName: \
140 return getExpectedError(#TokenName);
141#define SHORT_TOKEN(TokenName, TokenCh) \
142 case Kind::TokenName: \
143 return getExpectedError(#TokenCh);
144#include "ResourceScriptTokenList.h"
145#undef SHORT_TOKEN
146#undef TOKEN
147 }
148
149 llvm_unreachable("All case options exhausted.");
150}
151
152bool RCParser::consumeOptionalType(Kind TokenKind) {
153 if (isNextTokenKind(TokenKind)) {
154 consume();
155 return true;
156 }
157
158 return false;
159}
160
161Expected<SmallVector<uint32_t, 8>>
162RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) {
163 assert(MinCount <= MaxCount);
164
165 SmallVector<uint32_t, 8> Result;
166
167 auto FailureHandler =
168 [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 8>> {
169 if (Result.size() < MinCount)
170 return std::move(Err);
171 consumeError(std::move(Err));
172 return Result;
173 };
174
175 for (size_t i = 0; i < MaxCount; ++i) {
176 // Try to read a comma unless we read the first token.
177 // Sometimes RC tool requires them and sometimes not. We decide to
178 // always require them.
179 if (i >= 1) {
180 if (auto CommaError = consumeType(Kind::Comma))
181 return FailureHandler(std::move(CommaError));
182 }
183
184 if (auto IntResult = readInt())
185 Result.push_back(*IntResult);
186 else
187 return FailureHandler(IntResult.takeError());
188 }
189
190 return std::move(Result);
191}
192
193// As for now, we ignore the extended set of statements.
194Expected<OptionalStmtList> RCParser::parseOptionalStatements(bool IsExtended) {
195 OptionalStmtList Result;
196
197 // The last statement is always followed by the start of the block.
198 while (!isNextTokenKind(Kind::BlockBegin)) {
199 ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(IsExtended));
200 Result.addStmt(std::move(*SingleParse));
201 }
202
203 return std::move(Result);
204}
205
206Expected<std::unique_ptr<OptionalStmt>>
207RCParser::parseSingleOptionalStatement(bool) {
208 ASSIGN_OR_RETURN(TypeToken, readIdentifier());
209 if (TypeToken->equals_lower("CHARACTERISTICS"))
210 return parseCharacteristicsStmt();
211 else if (TypeToken->equals_lower("LANGUAGE"))
212 return parseLanguageStmt();
213 else if (TypeToken->equals_lower("VERSION"))
214 return parseVersionStmt();
215 else
216 return getExpectedError("optional statement type, BEGIN or '{'",
217 /* IsAlreadyRead = */ true);
218}
219
220RCParser::ParseType RCParser::parseLanguageResource() {
221 // Read LANGUAGE as an optional statement. If it's read correctly, we can
222 // upcast it to RCResource.
223 return parseLanguageStmt();
224}
225
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000226RCParser::ParseType RCParser::parseCursorResource() {
227 ASSIGN_OR_RETURN(Arg, readString());
228 return make_unique<CursorResource>(*Arg);
229}
230
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000231RCParser::ParseType RCParser::parseIconResource() {
232 ASSIGN_OR_RETURN(Arg, readString());
233 return make_unique<IconResource>(*Arg);
234}
235
Marek Sokolowski72aa9372017-08-28 21:59:54 +0000236RCParser::ParseType RCParser::parseHTMLResource() {
237 ASSIGN_OR_RETURN(Arg, readString());
238 return make_unique<HTMLResource>(*Arg);
239}
240
Marek Sokolowski5cd3d5c2017-08-18 18:24:17 +0000241RCParser::ParseType RCParser::parseStringTableResource() {
242 ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
243 RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
244
245 auto Table = make_unique<StringTableResource>(std::move(*OptStatements));
246
247 // Read strings until we reach the end of the block.
248 while (!consumeOptionalType(Kind::BlockEnd)) {
249 // Each definition consists of string's ID (an integer) and a string.
250 // Some examples in documentation suggest that there might be a comma in
251 // between, however we strictly adhere to the single statement definition.
252 ASSIGN_OR_RETURN(IDResult, readInt());
253 ASSIGN_OR_RETURN(StrResult, readString());
254 Table->addString(*IDResult, *StrResult);
255 }
256
257 return std::move(Table);
258}
259
260RCParser::ParseOptionType RCParser::parseLanguageStmt() {
261 ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
262 return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
263}
264
265RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
266 ASSIGN_OR_RETURN(Arg, readInt());
267 return make_unique<CharacteristicsStmt>(*Arg);
268}
269
270RCParser::ParseOptionType RCParser::parseVersionStmt() {
271 ASSIGN_OR_RETURN(Arg, readInt());
272 return make_unique<VersionStmt>(*Arg);
273}
274
275Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) {
276 return make_error<ParserError>(
277 Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
278}
279
280} // namespace rc
281} // namespace llvm