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