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