Initial integration, lots of broken stuff
[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 #include <config.h>
20
21 #include <sstream>
22 #include <stdexcept>
23 #include <fstream>
24 #include <cassert>
25 #include <iostream>
26
27 #include "tinygettext/tinygettext.hpp"
28 #include "physfs/physfs_stream.hpp"
29 #include "parser.hpp"
30 #include "lisp.hpp"
31 #include "obstack/obstackpp.hpp"
32
33 #include "gameconfig.hpp"
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     if (config && (config->locale != "")) dictionary_manager->set_language(config->locale);
45   }
46
47   obstack_init(&obst);
48 }
49
50 Parser::~Parser()
51 {
52   obstack_free(&obst, NULL);
53   delete lexer;
54   delete dictionary_manager;
55 }
56
57 static std::string dirname(const std::string& filename)
58 {
59   std::string::size_type p = filename.find_last_of('/');
60   if(p == std::string::npos)
61     return "";
62
63   return filename.substr(0, p+1);
64 }
65
66 const Lisp*
67 Parser::parse(const std::string& filename)
68 {
69   Unison::VFS::istream in(filename);
70   //IFileStreambuf ins(filename);
71   //std::istream in(&ins);
72
73   if(!in.good()) {
74     std::stringstream msg;
75     msg << "Parser problem: Couldn't open file '" << filename << "'.";
76     throw std::runtime_error(msg.str());
77   }
78
79   if(dictionary_manager) {
80     dictionary_manager->add_directory(dirname(filename));
81     dictionary = & (dictionary_manager->get_dictionary());
82   }
83
84   return parse(in, filename);
85 }
86
87 const Lisp*
88 Parser::parse(std::istream& stream, const std::string& sourcename)
89 {
90   delete lexer;
91   lexer = new Lexer(stream);
92
93   this->filename = sourcename;
94   token = lexer->getNextToken();
95
96   Lisp* result = new(obst) Lisp(Lisp::TYPE_CONS);
97   result->v.cons.car = read();
98   result->v.cons.cdr = 0;
99
100   delete lexer;
101   lexer = 0;
102
103   return result;
104 }
105
106 void
107 Parser::parse_error(const char* msg) const
108 {
109   std::stringstream emsg;
110   emsg << "Parse Error at '" << filename << "' line " << lexer->getLineNumber()
111           << ": " << msg;
112   throw std::runtime_error(emsg.str());
113 }
114
115 const Lisp*
116 Parser::read()
117 {
118   Lisp* result;
119   switch(token) {
120     case Lexer::TOKEN_EOF: {
121           parse_error("Unexpected EOF.");
122     }
123     case Lexer::TOKEN_CLOSE_PAREN: {
124       parse_error("Unexpected ')'.");
125     }
126     case Lexer::TOKEN_OPEN_PAREN: {
127       result = new(obst) Lisp(Lisp::TYPE_CONS);
128
129       token = lexer->getNextToken();
130       if(token == Lexer::TOKEN_CLOSE_PAREN) {
131         result->v.cons.car = 0;
132         result->v.cons.cdr = 0;
133         break;
134       }
135
136       if(token == Lexer::TOKEN_SYMBOL &&
137           strcmp(lexer->getString(), "_") == 0) {
138         // evaluate translation function (_ str) in place here
139         token = lexer->getNextToken();
140         if(token != Lexer::TOKEN_STRING)
141                   parse_error("Expected string after '(_'");
142
143         result = new(obst) Lisp(Lisp::TYPE_STRING);
144         if(dictionary) {
145           std::string translation = dictionary->translate(lexer->getString());
146           result->v.string = new(obst) char[translation.size()+1];
147           memcpy(result->v.string, translation.c_str(), translation.size()+1);
148         } else {
149           size_t len = strlen(lexer->getString()) + 1;
150           result->v.string = new(obst) char[len];
151           memcpy(result->v.string, lexer->getString(), len);
152         }
153         token = lexer->getNextToken();
154         if(token != Lexer::TOKEN_CLOSE_PAREN)
155                   parse_error("Expected ')' after '(_ string'");
156         break;
157       }
158
159       Lisp* cur = result;
160       do {
161         cur->v.cons.car = read();
162         if(token == Lexer::TOKEN_CLOSE_PAREN) {
163           cur->v.cons.cdr = 0;
164           break;
165         }
166         Lisp *newcur = new(obst) Lisp(Lisp::TYPE_CONS);
167         cur->v.cons.cdr = newcur;
168         cur = newcur;
169       } while(1);
170
171       break;
172     }
173     case Lexer::TOKEN_SYMBOL: {
174       result = new(obst) Lisp(Lisp::TYPE_SYMBOL);
175       size_t len = strlen(lexer->getString()) + 1;
176       result->v.string = new(obst) char[len];
177       memcpy(result->v.string, lexer->getString(), len);
178       break;
179     }
180     case Lexer::TOKEN_STRING: {
181       result = new(obst) Lisp(Lisp::TYPE_STRING);
182       size_t len = strlen(lexer->getString()) + 1;
183       result->v.string = new(obst) char[len];
184       memcpy(result->v.string, lexer->getString(), len);
185       break;
186     }
187     case Lexer::TOKEN_INTEGER:
188       result = new(obst) Lisp(Lisp::TYPE_INTEGER);
189       sscanf(lexer->getString(), "%d", &result->v.integer);
190       break;
191     case Lexer::TOKEN_REAL:
192       result = new(obst) Lisp(Lisp::TYPE_REAL);
193       sscanf(lexer->getString(), "%f", &result->v.real);
194       break;
195     case Lexer::TOKEN_TRUE:
196       result = new(obst) Lisp(Lisp::TYPE_BOOLEAN);
197       result->v.boolean = true;
198       break;
199     case Lexer::TOKEN_FALSE:
200       result = new(obst) Lisp(Lisp::TYPE_BOOLEAN);
201       result->v.boolean = false;
202       break;
203
204     default:
205       // this should never happen
206       assert(false);
207   }
208
209   token = lexer->getNextToken();
210   return result;
211 }
212
213 } // end of namespace lisp