]> git.localhorst.tv Git - l2e.git/blob - src/loader/Parser.cpp
52585c4cdd0f11ba40829a7a039456a390674e6f
[l2e.git] / src / loader / Parser.cpp
1 /*
2  * Parser.cpp
3  *
4  *  Created on: Aug 26, 2012
5  *      Author: holy
6  */
7
8 #include "Parser.h"
9
10 #include "utility.h"
11
12 #include <auto_ptr.h>
13 #include <fstream>
14
15 using std::auto_ptr;
16 using std::ifstream;
17 using std::string;
18 using std::vector;
19
20 namespace loader {
21
22 Parser::Parser(const string &file, ParsedSource &product)
23 : file(file)
24 , dirname(Dirname(file))
25 , in(this->file.c_str())
26 , tok(in)
27 , product(product) {
28         if (!in) {
29                 throw Error(file, 0, "unable to read file");
30         }
31 }
32
33 void Parser::Parse() {
34         while (tok.HasMore()) {
35                 ParseStatement();
36         }
37 }
38
39 void Parser::ParseStatement() {
40         Tokenizer::Token t(GetToken());
41         switch (t.type) {
42                 case Tokenizer::Token::KEYWORD_EXPORT:
43                         ParseExportDirective();
44                         break;
45                 case Tokenizer::Token::KEYWORD_INCLUDE:
46                         ParseIncludeDirective();
47                         break;
48                 case Tokenizer::Token::TYPE_NAME:
49                         tok.Putback(t);
50                         {
51                                 Declaration *decl(ProbeDefinition());
52                                 product.AddDeclaration(decl);
53                         }
54                         break;
55                 default:
56                         throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type));
57         }
58 }
59
60 Tokenizer::Token Parser::GetToken() try {
61         return tok.GetNext();
62 } catch (Tokenizer::LexerError &e) {
63         throw Error(file, e.Line(), e.what());
64 }
65
66 void Parser::ParseExportDirective() {
67         Tokenizer::Token t(GetToken());
68         if (t.type != Tokenizer::Token::IDENTIFIER) {
69                 tok.Putback(t);
70                 Declaration *decl(ProbeDefinition());
71                 product.ExportDeclaration(decl);
72         } else {
73                 product.ExportIdentifier(t.str);
74         }
75 }
76
77 void Parser::ParseIncludeDirective() {
78         Tokenizer::Token t(GetToken());
79         AssertTokenType(t.type, Tokenizer::Token::STRING);
80         Parser sub(CatPath(dirname, t.str), product);
81         sub.Parse();
82 }
83
84 Declaration *Parser::ProbeDefinition() {
85         string typeName(ParseTypeName());
86         string identifier(ParseIdentifier());
87
88         if (tok.HasMore()) {
89                 Tokenizer::Token t(GetToken());
90                 tok.Putback(t);
91                 if (BeginOfPropertyList(t)) {
92                         auto_ptr<PropertyList> propertyList(ParsePropertyList());
93                         auto_ptr<Definition> dfn(new Definition(typeName, identifier));
94                         dfn->SetValue(propertyList.release());
95                         product.AddDefinition(dfn.get());
96                         return dfn.release();
97                 } else if (BeginningOfPrimitiveLiteral(t)) {
98                         auto_ptr<Literal> literal(ParseLiteral());
99                         auto_ptr<Definition> dfn(new Definition(typeName, identifier));
100                         dfn->SetValue(literal.release());
101                         product.AddDefinition(dfn.get());
102                         return dfn.release();
103                 }
104         }
105         return new Declaration(typeName, identifier);
106 }
107
108 bool Parser::BeginningOfLiteral(const Tokenizer::Token &t) const {
109         switch (t.type) {
110                 case Tokenizer::Token::CHEVRON_OPEN:
111                 case Tokenizer::Token::COLON:
112                 case Tokenizer::Token::BRACKET_OPEN:
113                 case Tokenizer::Token::PARENTHESIS_OPEN:
114                 case Tokenizer::Token::NUMBER:
115                 case Tokenizer::Token::SCRIPT_BEGIN:
116                 case Tokenizer::Token::STRING:
117                 case Tokenizer::Token::KEYWORD_FALSE:
118                 case Tokenizer::Token::KEYWORD_TRUE:
119                 case Tokenizer::Token::TYPE_NAME:
120                         return true;
121                 default:
122                         return false;
123         }
124 }
125
126 bool Parser::BeginningOfPrimitiveLiteral(const Tokenizer::Token &t) const {
127         switch (t.type) {
128                 case Tokenizer::Token::CHEVRON_OPEN:
129                 case Tokenizer::Token::COLON:
130                 case Tokenizer::Token::BRACKET_OPEN:
131                 case Tokenizer::Token::PARENTHESIS_OPEN:
132                 case Tokenizer::Token::NUMBER:
133                 case Tokenizer::Token::STRING:
134                 case Tokenizer::Token::KEYWORD_FALSE:
135                 case Tokenizer::Token::KEYWORD_TRUE:
136                         return true;
137                 default:
138                         return false;
139         }
140 }
141
142 bool Parser::BeginOfPropertyList(const Tokenizer::Token &t) const {
143         return t.type == Tokenizer::Token::ANGLE_BRACKET_OPEN;
144 }
145
146 bool Parser::BeginningOfScriptLiteral(const Tokenizer::Token &t) const {
147         return t.type == Tokenizer::Token::SCRIPT_BEGIN;
148 }
149
150 Definition *Parser::ParseDefinition() {
151         string typeName(ParseTypeName());
152         string identifier(ParseIdentifier());
153
154         Tokenizer::Token t(GetToken());
155         tok.Putback(t);
156         if (BeginOfPropertyList(t)) {
157                 PropertyList *propertyList(ParsePropertyList());
158                 Definition *dfn(new Definition(typeName, identifier));
159                 dfn->SetValue(propertyList);
160                 return dfn;
161         } else if (BeginningOfLiteral(t)) {
162                 Literal *literal(ParseLiteral());
163                 Definition *dfn(new Definition(typeName, identifier));
164                 dfn->SetValue(literal);
165                 return dfn;
166         } else {
167                 throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected property-list or literal");
168         }
169 }
170
171 string Parser::ParseIdentifier() {
172         Tokenizer::Token t(GetToken());
173         AssertTokenType(t.type, Tokenizer::Token::IDENTIFIER);
174         return t.str;
175 }
176
177 string Parser::ParseTypeName() {
178         Tokenizer::Token t(GetToken());
179         AssertTokenType(t.type, Tokenizer::Token::TYPE_NAME);
180         return t.str;
181 }
182
183 PropertyList *Parser::ParsePropertyList() {
184         Tokenizer::Token t(GetToken());
185         AssertTokenType(t.type, Tokenizer::Token::ANGLE_BRACKET_OPEN);
186
187         auto_ptr<PropertyList> props(new PropertyList);
188
189         while (t.type != Tokenizer::Token::ANGLE_BRACKET_CLOSE) {
190                 Tokenizer::Token name(GetToken());
191                 AssertTokenType(name.type, Tokenizer::Token::IDENTIFIER);
192
193                 t = GetToken();
194                 AssertTokenType(t.type, Tokenizer::Token::COLON);
195
196                 Value *value(ParseValue());
197                 props->SetProperty(name.str, value);
198
199                 t = GetToken();
200                 if (t.type != Tokenizer::Token::ANGLE_BRACKET_CLOSE && t.type != Tokenizer::Token::COMMA) {
201                         throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or }");
202                 }
203         }
204
205         return props.release();
206 }
207
208 Value *Parser::ParseValue() {
209         Tokenizer::Token t(GetToken());
210         if (t.type == Tokenizer::Token::IDENTIFIER) {
211                 return new Value(t.str);
212         } else if (BeginningOfLiteral(t)) {
213                 tok.Putback(t);
214                 Literal *literal(ParseLiteral());
215                 return new Value(literal);
216         } else {
217                 throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected literal or identifier");
218         }
219 }
220
221 Literal *Parser::ParseLiteral() {
222         Tokenizer::Token t(GetToken());
223         if (t.type == Tokenizer::Token::TYPE_NAME) {
224                 PropertyList *props(ParsePropertyList());
225                 return new Literal(t.str, props);
226         } else if (BeginningOfScriptLiteral(t)) {
227                 tok.Putback(t);
228                 return ParseScript();
229         } else if (BeginningOfPrimitiveLiteral(t)) {
230                 switch (t.type) {
231                         case Tokenizer::Token::CHEVRON_OPEN:
232                                 tok.Putback(t);
233                                 return ParseVector();
234                         case Tokenizer::Token::COLON:
235                                 t = GetToken();
236                                 AssertTokenType(t.type, Tokenizer::Token::STRING);
237                                 return new Literal(dirname, t.str);
238                         case Tokenizer::Token::BRACKET_OPEN:
239                                 tok.Putback(t);
240                                 return ParseArray();
241                         case Tokenizer::Token::PARENTHESIS_OPEN:
242                                 tok.Putback(t);
243                                 return ParseColor();
244                         case Tokenizer::Token::NUMBER:
245                                 return new Literal(t.number);
246                         case Tokenizer::Token::STRING:
247                                 return new Literal(t.str);
248                         case Tokenizer::Token::KEYWORD_FALSE:
249                                 return new Literal(false);
250                         case Tokenizer::Token::KEYWORD_TRUE:
251                                 return new Literal(true);
252                         default:
253                                 throw std::logic_error("literal switch reached impossible default branch oO");
254                 }
255         } else {
256                 throw new Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected type-name or primitive");
257         }
258 }
259
260 Literal *Parser::ParseArray() {
261         Tokenizer::Token t(GetToken());
262         AssertTokenType(t.type, Tokenizer::Token::BRACKET_OPEN);
263
264         Tokenizer::Token probe(GetToken());
265
266         if (probe.type == Tokenizer::Token::TYPE_NAME) {
267                 vector<PropertyList *> values;
268                 while (t.type != Tokenizer::Token::BRACKET_CLOSE) {
269                         PropertyList *value(ParsePropertyList());
270                         values.push_back(value);
271
272                         t = GetToken();
273                         if (t.type != Tokenizer::Token::BRACKET_CLOSE && t.type != Tokenizer::Token::COMMA) {
274                                 throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or ]");
275                         }
276                 }
277                 return new Literal(probe.str, values);
278         } else {
279                 tok.Putback(probe);
280
281                 vector<Value *> values;
282                 while (t.type != Tokenizer::Token::BRACKET_CLOSE) {
283                         Value *value(ParseValue());
284                         values.push_back(value);
285
286                         t = GetToken();
287                         if (t.type != Tokenizer::Token::BRACKET_CLOSE && t.type != Tokenizer::Token::COMMA) {
288                                 throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or ]");
289                         }
290                 }
291                 return new Literal(values);
292         }
293 }
294
295 Literal *Parser::ParseColor() {
296         string msg("error parsing color");
297         Tokenizer::Token t(GetToken());
298         AssertTokenType(t.type, Tokenizer::Token::PARENTHESIS_OPEN, msg);
299
300         Tokenizer::Token red(GetToken());
301         AssertTokenType(red.type, Tokenizer::Token::NUMBER, "error parsing red component of color");
302
303         t = GetToken();
304         AssertTokenType(t.type, Tokenizer::Token::COMMA, msg);
305
306         Tokenizer::Token green(GetToken());
307         AssertTokenType(green.type, Tokenizer::Token::NUMBER, "error parsing green component of color");
308
309         t = GetToken();
310         AssertTokenType(t.type, Tokenizer::Token::COMMA, msg);
311
312         Tokenizer::Token blue(GetToken());
313         AssertTokenType(blue.type, Tokenizer::Token::NUMBER, "error parsing blue component of color");
314
315         t = GetToken();
316         if (t.type == Tokenizer::Token::PARENTHESIS_CLOSE) {
317                 return new Literal(red.number, green.number, blue.number);
318         } else if (t.type != Tokenizer::Token::COMMA) {
319                 Tokenizer::Token alpha(GetToken());
320                 AssertTokenType(alpha.type, Tokenizer::Token::NUMBER, "error parsing alpha component of color");
321
322                 t = GetToken();
323                 AssertTokenType(t.type, Tokenizer::Token::PARENTHESIS_CLOSE, msg);
324
325                 return new Literal(red.number, green.number, blue.number, alpha.number);
326         } else {
327                 throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(t.type) + ", expected , or ]");
328         }
329 }
330
331 Literal *Parser::ParseVector() {
332         std::string msg("error parsing vector");
333         Tokenizer::Token t(GetToken());
334         AssertTokenType(t.type, Tokenizer::Token::CHEVRON_OPEN, msg);
335
336         Tokenizer::Token x(GetToken());
337         AssertTokenType(x.type, Tokenizer::Token::NUMBER, "error parsing x component of vector");
338
339         t = GetToken();
340         AssertTokenType(t.type, Tokenizer::Token::COMMA, msg);
341
342         Tokenizer::Token y(GetToken());
343         AssertTokenType(y.type, Tokenizer::Token::NUMBER, "error parsing y component of vector");
344
345         t = GetToken();
346         AssertTokenType(t.type, Tokenizer::Token::CHEVRON_CLOSE, msg);
347
348         return new Literal(x.number, y.number);
349 }
350
351 Literal *Parser::ParseScript() {
352         std::string msg("error parsing script");
353         Tokenizer::Token t(GetToken());
354         AssertTokenType(t.type, Tokenizer::Token::SCRIPT_BEGIN, msg);
355
356         vector<ScriptToken *> script;
357         try {
358                 while (t.type != Tokenizer::Token::SCRIPT_END) {
359                         if (BeginningOfPrimitiveLiteral(t)) {
360                                 tok.Putback(t);
361                                 script.push_back(new ScriptToken(ParseLiteral()));
362                         } else {
363                                 switch (t.type) {
364                                         case Tokenizer::Token::COMMAND: {
365                                                 Tokenizer::Token t2(GetToken());
366                                                 AssertTokenType(t.type, Tokenizer::Token::IDENTIFIER, msg);
367                                                 script.push_back(new ScriptToken(t2.str, ScriptToken::COMMAND));
368                                                 break;
369                                         }
370                                         case Tokenizer::Token::IDENTIFIER: {
371                                                 script.push_back(new ScriptToken(t.str, ScriptToken::IDENTIFIER));
372                                                 break;
373                                         }
374                                         case Tokenizer::Token::REGISTER: {
375                                                 Tokenizer::Token t2(GetToken());
376                                                 AssertTokenType(t.type, Tokenizer::Token::IDENTIFIER, msg);
377                                                 script.push_back(new ScriptToken(t2.str, ScriptToken::REGISTER));
378                                                 break;
379                                         }
380                                         default:
381                                                 throw Error(file, tok.Line(), string("unexpected token in script: ") + TokenTypeToString(t.type));
382                                 }
383                         }
384                         t = GetToken();
385                 }
386         } catch (...) {
387                 for (vector<ScriptToken *>::const_iterator i(script.begin()), end(script.end()); i != end; ++i) {
388                         delete *i;
389                 }
390                 throw;
391         }
392         return new Literal(script);
393 }
394
395
396 void Parser::AssertTokenType(Tokenizer::Token::Type actual, Tokenizer::Token::Type expected) {
397         if (expected != actual) {
398                 throw Error(file, tok.Line(), string("unexpected token ") + TokenTypeToString(actual) + ", expected " + TokenTypeToString(expected));
399         }
400 }
401
402 void Parser::AssertTokenType(Tokenizer::Token::Type actual, Tokenizer::Token::Type expected, const string &msg) {
403         if (expected != actual) {
404                 throw Error(file, tok.Line(), msg + ": unexpected token " + TokenTypeToString(actual) + ", expected " + TokenTypeToString(expected));
405         }
406 }
407
408 }