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