restore trunk
[supertux.git] / src / lisp / lexer.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 <iostream>
24
25 #include "lexer.hpp"
26
27 namespace lisp
28 {
29
30 class EOFException
31 {
32 };
33
34 Lexer::Lexer(std::istream& newstream)
35     : stream(newstream), eof(false), linenumber(0)
36 {
37   try {
38     // trigger a refill of the buffer
39     c = 0;
40     bufend = 0;
41     nextChar();
42   } catch(EOFException& ) {
43   }
44 }
45
46 Lexer::~Lexer()
47 {
48 }
49
50 void
51 Lexer::nextChar()
52 {
53   ++c;
54   if(c >= bufend) {
55     if(eof)
56       throw EOFException();
57     stream.read(buffer, BUFFER_SIZE);
58     size_t bytes_read = stream.gcount();
59
60     c = buffer;
61     bufend = buffer + bytes_read;
62
63     // the following is a hack that appends an additional ' ' at the end of
64     // the file to avoid problems when parsing symbols/elements and a sudden
65     // EOF. This is faster than relying on unget and IMO also nicer.
66     if(bytes_read == 0 || stream.eof()) {
67       eof = true;
68       *bufend = ' ';
69       ++bufend;
70     }
71   }
72 }
73
74 Lexer::TokenType
75 Lexer::getNextToken()
76 {
77   static const char* delims = "\"();";
78
79   try {
80     while(isspace(*c)) {
81       if(*c == '\n')
82         ++linenumber;
83       nextChar();
84     };
85
86     token_length = 0;
87
88     switch(*c) {
89       case ';': // comment
90         while(true) {
91           nextChar();
92           if(*c == '\n') {
93             ++linenumber;
94             break;
95           }
96         }
97         return getNextToken(); // and again
98       case '(':
99         nextChar();
100         return TOKEN_OPEN_PAREN;
101       case ')':
102         nextChar();
103         return TOKEN_CLOSE_PAREN;
104       case '"': {  // string
105         int startline = linenumber;
106         try {
107           while(1) {
108             nextChar();
109             if(*c == '"')
110               break;
111             else if (*c == '\r') // XXX this breaks with pure \r EOL
112               continue;
113             else if(*c == '\n')
114               linenumber++;
115             else if(*c == '\\') {
116               nextChar();
117               switch(*c) {
118                 case 'n':
119                   *c = '\n';
120                   break;
121                 case 't':
122                   *c = '\t';
123                   break;
124               }
125             }
126             if(token_length < MAX_TOKEN_LENGTH)
127               token_string[token_length++] = *c;
128           }
129           token_string[token_length] = 0;
130         } catch(EOFException& ) {
131           std::stringstream msg;
132           msg << "Parse error in line " << startline << ": "
133               << "EOF while parsing string.";
134           throw std::runtime_error(msg.str());
135         }
136         nextChar();
137         return TOKEN_STRING;
138       }
139       case '#': // constant
140         try {
141           nextChar();
142
143           while(isalnum(*c) || *c == '_') {
144             if(token_length < MAX_TOKEN_LENGTH)
145               token_string[token_length++] = *c;
146             nextChar();
147           }
148           token_string[token_length] = 0;
149         } catch(EOFException& ) {
150           std::stringstream msg;
151           msg << "Parse Error in line " << linenumber << ": "
152             << "EOF while parsing constant.";
153           throw std::runtime_error(msg.str());
154         }
155
156         if(strcmp(token_string, "t") == 0)
157           return TOKEN_TRUE;
158         if(strcmp(token_string, "f") == 0)
159           return TOKEN_FALSE;
160
161         // we only handle #t and #f constants at the moment...
162
163         {
164           std::stringstream msg;
165           msg << "Parse Error in line " << linenumber << ": "
166             << "Unknown constant '" << token_string << "'.";
167           throw std::runtime_error(msg.str());
168         }
169
170       default:
171         if(isdigit(*c) || *c == '-') {
172           bool have_nondigits = false;
173           bool have_digits = false;
174           int have_floating_point = 0;
175
176           do {
177             if(isdigit(*c))
178               have_digits = true;
179             else if(*c == '.')
180               ++have_floating_point;
181             else if(isalnum(*c) || *c == '_')
182               have_nondigits = true;
183
184             if(token_length < MAX_TOKEN_LENGTH)
185               token_string[token_length++] = *c;
186
187             nextChar();
188           } while(!isspace(*c) && !strchr(delims, *c));
189
190           token_string[token_length] = 0;
191
192           // no nextChar
193
194           if(have_nondigits || !have_digits || have_floating_point > 1)
195             return TOKEN_SYMBOL;
196           else if(have_floating_point == 1)
197             return TOKEN_REAL;
198           else
199             return TOKEN_INTEGER;
200         } else {
201           do {
202             if(token_length < MAX_TOKEN_LENGTH)
203               token_string[token_length++] = *c;
204             nextChar();
205           } while(!isspace(*c) && !strchr(delims, *c));
206           token_string[token_length] = 0;
207
208           // no nextChar
209
210           return TOKEN_SYMBOL;
211         }
212     }
213   } catch(EOFException& ) {
214     return TOKEN_EOF;
215   }
216 }
217
218 } // end of namespace lisp