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