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