fix cr/lfs and remove trailing whitespaces...
[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   IFileStream in(filename);
64   if(!in.good()) {
65     std::stringstream msg;
66     msg << "Parser problem: Couldn't open file '" << filename << "'.";
67     throw std::runtime_error(msg.str());
68   }
69
70   if(dictionary_manager) {
71     dictionary_manager->add_directory(dirname(filename));
72     dictionary = & (dictionary_manager->get_dictionary());
73   }
74
75   return parse(in);
76 }
77
78 Lisp*
79 Parser::parse(std::istream& stream)
80 {
81   delete lexer;
82   lexer = new Lexer(stream);
83
84   token = lexer->getNextToken();
85   Lisp* result = new Lisp(Lisp::TYPE_CONS);
86   result->v.cons.car = read();
87   result->v.cons.cdr = 0;
88
89   delete lexer;
90   lexer = 0;
91
92   return result;
93 }
94
95 Lisp*
96 Parser::read()
97 {
98   Lisp* result;
99   switch(token) {
100     case Lexer::TOKEN_EOF: {
101       std::stringstream msg;
102       msg << "Parse Error at line " << lexer->getLineNumber() << ": "
103         << "Unexpected EOF.";
104       throw std::runtime_error(msg.str());
105     }
106     case Lexer::TOKEN_CLOSE_PAREN: {
107       std::stringstream msg;
108       msg << "Parse Error at line " << lexer->getLineNumber() << ": "
109         << "Unexpected ')'.";
110       throw std::runtime_error(msg.str());
111     }
112     case Lexer::TOKEN_OPEN_PAREN: {
113       result = new Lisp(Lisp::TYPE_CONS);
114
115       token = lexer->getNextToken();
116       if(token == Lexer::TOKEN_CLOSE_PAREN) {
117         result->v.cons.car = 0;
118         result->v.cons.cdr = 0;
119         break;
120       }
121
122       if(token == Lexer::TOKEN_SYMBOL &&
123           strcmp(lexer->getString(), "_") == 0) {
124         // evaluate translation function (_ str) in place here
125         token = lexer->getNextToken();
126         if(token != Lexer::TOKEN_STRING)
127           throw std::runtime_error("Expected string after '(_'");
128
129         result = new Lisp(Lisp::TYPE_STRING);
130         if(dictionary) {
131           std::string translation = dictionary->translate(lexer->getString());
132           result->v.string = new char[translation.size()+1];
133           memcpy(result->v.string, translation.c_str(), translation.size()+1);
134         } else {
135           size_t len = strlen(lexer->getString()) + 1;
136           result->v.string = new char[len];
137           memcpy(result->v.string, lexer->getString(), len);
138         }
139         token = lexer->getNextToken();
140         if(token != Lexer::TOKEN_CLOSE_PAREN)
141           throw std::runtime_error("Expected ')' after '(_ string'");
142         break;
143       }
144
145       Lisp* cur = result;
146       do {
147         cur->v.cons.car = read();
148         if(token == Lexer::TOKEN_CLOSE_PAREN) {
149           cur->v.cons.cdr = 0;
150           break;
151         }
152         cur->v.cons.cdr = new Lisp(Lisp::TYPE_CONS);
153         cur = cur->v.cons.cdr;
154       } while(1);
155
156       break;
157     }
158     case Lexer::TOKEN_SYMBOL: {
159       result = new Lisp(Lisp::TYPE_SYMBOL);
160       size_t len = strlen(lexer->getString()) + 1;
161       result->v.string = new char[len];
162       memcpy(result->v.string, lexer->getString(), len);
163       break;
164     }
165     case Lexer::TOKEN_STRING: {
166       result = new Lisp(Lisp::TYPE_STRING);
167       size_t len = strlen(lexer->getString()) + 1;
168       result->v.string = new char[len];
169       memcpy(result->v.string, lexer->getString(), len);
170       break;
171     }
172     case Lexer::TOKEN_INTEGER:
173       result = new Lisp(Lisp::TYPE_INTEGER);
174       sscanf(lexer->getString(), "%d", &result->v.integer);
175       break;
176     case Lexer::TOKEN_REAL:
177       result = new Lisp(Lisp::TYPE_REAL);
178       sscanf(lexer->getString(), "%f", &result->v.real);
179       break;
180     case Lexer::TOKEN_TRUE:
181       result = new Lisp(Lisp::TYPE_BOOLEAN);
182       result->v.boolean = true;
183       break;
184     case Lexer::TOKEN_FALSE:
185       result = new Lisp(Lisp::TYPE_BOOLEAN);
186       result->v.boolean = false;
187       break;
188
189     default:
190       // this should never happen
191       assert(false);
192   }
193
194   token = lexer->getNextToken();
195   return result;
196 }
197
198 } // end of namespace lisp