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