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