Enable loading levels from .zip files in config dir, e.g. from ~/.supertux2/foo.zip...
[supertux.git] / src / lisp / parser.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <config.h>
21
22 #include <sstream>
23 #include <stdexcept>
24 #include <fstream>
25 #include <cassert>
26 #include <iostream>
27
28 #include "tinygettext/tinygettext.hpp"
29 #include "physfs/physfs_stream.hpp"
30 #include "parser.hpp"
31 #include "lisp.hpp"
32
33 namespace lisp
34 {
35
36 Parser::Parser(bool translate)
37   : lexer(0), dictionary_manager(0), dictionary(0)
38 {
39   if(translate) {
40     dictionary_manager = new TinyGetText::DictionaryManager();
41     dictionary_manager->set_charset("UTF-8");
42   }
43 }
44
45 Parser::~Parser()
46 {
47   delete lexer;
48   delete dictionary_manager;
49 }
50
51 static std::string dirname(std::string filename)
52 {
53   std::string::size_type p = filename.find_last_of('/');
54   if(p == std::string::npos)
55     return "";
56
57   return filename.substr(0, p+1);
58 }
59
60 Lisp*
61 Parser::parse(const std::string& filename)
62 {
63   IFileStreambuf ins(filename);
64   std::istream in(&ins);
65
66   if(!in.good()) {
67     std::stringstream msg;
68     msg << "Parser problem: Couldn't open file '" << filename << "'.";
69     throw std::runtime_error(msg.str());
70   }
71
72   if(dictionary_manager) {
73     dictionary_manager->add_directory(dirname(filename));
74     dictionary = & (dictionary_manager->get_dictionary());
75   }
76
77   return parse(in);
78 }
79
80 Lisp*
81 Parser::parse(std::istream& stream)
82 {
83   delete lexer;
84   lexer = new Lexer(stream);
85
86   token = lexer->getNextToken();
87   Lisp* result = new Lisp(Lisp::TYPE_CONS);
88   result->v.cons.car = read();
89   result->v.cons.cdr = 0;
90
91   delete lexer;
92   lexer = 0;
93
94   return result;
95 }
96
97 Lisp*
98 Parser::read()
99 {
100   Lisp* result;
101   switch(token) {
102     case Lexer::TOKEN_EOF: {
103       std::stringstream msg;
104       msg << "Parse Error at line " << lexer->getLineNumber() << ": "
105         << "Unexpected EOF.";
106       throw std::runtime_error(msg.str());
107     }
108     case Lexer::TOKEN_CLOSE_PAREN: {
109       std::stringstream msg;
110       msg << "Parse Error at line " << lexer->getLineNumber() << ": "
111         << "Unexpected ')'.";
112       throw std::runtime_error(msg.str());
113     }
114     case Lexer::TOKEN_OPEN_PAREN: {
115       result = new Lisp(Lisp::TYPE_CONS);
116
117       token = lexer->getNextToken();
118       if(token == Lexer::TOKEN_CLOSE_PAREN) {
119         result->v.cons.car = 0;
120         result->v.cons.cdr = 0;
121         break;
122       }
123
124       if(token == Lexer::TOKEN_SYMBOL &&
125           strcmp(lexer->getString(), "_") == 0) {
126         // evaluate translation function (_ str) in place here
127         token = lexer->getNextToken();
128         if(token != Lexer::TOKEN_STRING)
129           throw std::runtime_error("Expected string after '(_'");
130
131         result = new Lisp(Lisp::TYPE_STRING);
132         if(dictionary) {
133           std::string translation = dictionary->translate(lexer->getString());
134           result->v.string = new char[translation.size()+1];
135           memcpy(result->v.string, translation.c_str(), translation.size()+1);
136         } else {
137           size_t len = strlen(lexer->getString()) + 1;
138           result->v.string = new char[len];
139           memcpy(result->v.string, lexer->getString(), len);
140         }
141         token = lexer->getNextToken();
142         if(token != Lexer::TOKEN_CLOSE_PAREN)
143           throw std::runtime_error("Expected ')' after '(_ string'");
144         break;
145       }
146
147       Lisp* cur = result;
148       do {
149         cur->v.cons.car = read();
150         if(token == Lexer::TOKEN_CLOSE_PAREN) {
151           cur->v.cons.cdr = 0;
152           break;
153         }
154         cur->v.cons.cdr = new Lisp(Lisp::TYPE_CONS);
155         cur = cur->v.cons.cdr;
156       } while(1);
157
158       break;
159     }
160     case Lexer::TOKEN_SYMBOL: {
161       result = new Lisp(Lisp::TYPE_SYMBOL);
162       size_t len = strlen(lexer->getString()) + 1;
163       result->v.string = new char[len];
164       memcpy(result->v.string, lexer->getString(), len);
165       break;
166     }
167     case Lexer::TOKEN_STRING: {
168       result = new Lisp(Lisp::TYPE_STRING);
169       size_t len = strlen(lexer->getString()) + 1;
170       result->v.string = new char[len];
171       memcpy(result->v.string, lexer->getString(), len);
172       break;
173     }
174     case Lexer::TOKEN_INTEGER:
175       result = new Lisp(Lisp::TYPE_INTEGER);
176       sscanf(lexer->getString(), "%d", &result->v.integer);
177       break;
178     case Lexer::TOKEN_REAL:
179       result = new Lisp(Lisp::TYPE_REAL);
180       sscanf(lexer->getString(), "%f", &result->v.real);
181       break;
182     case Lexer::TOKEN_TRUE:
183       result = new Lisp(Lisp::TYPE_BOOLEAN);
184       result->v.boolean = true;
185       break;
186     case Lexer::TOKEN_FALSE:
187       result = new Lisp(Lisp::TYPE_BOOLEAN);
188       result->v.boolean = false;
189       break;
190
191     default:
192       // this should never happen
193       assert(false);
194   }
195
196   token = lexer->getNextToken();
197   return result;
198 }
199
200 } // end of namespace lisp