Fix compiler errors
[supertux.git] / src / lisp / lexer.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "lisp/lexer.hpp"
18
19 #include <string.h>
20 #include <sstream>
21 #include <stdexcept>
22 #include <stdio.h>
23
24 namespace lisp {
25
26 Lexer::Lexer(std::istream& newstream) :
27   stream(newstream),
28   eof(false),
29   linenumber(0),
30   bufend(),
31   bufpos(),
32   c(),
33   token_string(),
34   token_length()
35 {
36   // trigger a refill of the buffer
37   bufpos = NULL;
38   bufend = NULL;
39   nextChar();
40 }
41
42 Lexer::~Lexer()
43 {
44 }
45
46 void
47 Lexer::nextChar()
48 {
49   if(bufpos >= bufend || (bufpos == NULL && bufend == NULL) /* Initial refill trigger */) {
50     if(eof) {
51       c = EOF;
52       return;
53     }
54     stream.read(buffer, BUFFER_SIZE);
55     size_t bytes_read = stream.gcount();
56
57     bufpos = buffer;
58     bufend = buffer + bytes_read;
59
60     // the following is a hack that appends an additional ' ' at the end of
61     // the file to avoid problems when parsing symbols/elements and a sudden
62     // EOF. This is faster than relying on unget and IMO also nicer.
63     if(bytes_read == 0 || stream.eof()) {
64       eof = true;
65       *bufend = ' ';
66       ++bufend;
67     }
68   }
69   c = *bufpos++;
70   if(c == '\n')
71     ++linenumber;
72 }
73
74 void
75 Lexer::addChar()
76 {
77   if(token_length < MAX_TOKEN_LENGTH)
78     token_string[token_length++] = c;
79   nextChar();
80 }
81
82 Lexer::TokenType
83 Lexer::getNextToken()
84 {
85   static const char* delims = "\"();";
86
87   while(isspace(c)) {
88     nextChar();
89   }
90
91   token_length = 0;
92
93   switch(c) {
94     case ';': // comment
95       while(c != '\n') {
96         nextChar();
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       while(1) {
108         nextChar();
109         switch(c) {
110           case '"':
111             nextChar();
112             goto string_finished;
113           case '\r':
114             continue;
115           case '\n':
116             break;
117           case '\\':
118             nextChar();
119             switch(c) {
120               case 'n':
121                 c = '\n';
122                 break;
123               case 't':
124                 c = '\t';
125                 break;
126             }
127             break;
128           case EOF: {
129             std::stringstream msg;
130             msg << "Parse error in line " << startline << ": "
131                 << "EOF while parsing string.";
132             throw std::runtime_error(msg.str());
133           }
134           default:
135             break;
136         }
137         if(token_length < MAX_TOKEN_LENGTH)
138           token_string[token_length++] = c;
139       }
140       string_finished:
141       token_string[token_length] = 0;
142       return TOKEN_STRING;
143     }
144     case '#': // constant
145       nextChar();
146
147       while(isalnum(c) || c == '_') {
148         addChar();
149       }
150       token_string[token_length] = 0;
151
152       if(strcmp(token_string, "t") == 0)
153         return TOKEN_TRUE;
154       if(strcmp(token_string, "f") == 0)
155         return TOKEN_FALSE;
156
157       // we only handle #t and #f constants at the moment...
158       {
159         std::stringstream msg;
160         msg << "Parse Error in line " << linenumber << ": "
161             << "Unknown constant '" << token_string << "'.";
162         throw std::runtime_error(msg.str());
163       }
164
165     case EOF:
166       return TOKEN_EOF;
167
168     default:
169       if(isdigit(c) || c == '-') {
170         bool have_nondigits = false;
171         bool have_digits = false;
172         int have_floating_point = 0;
173
174         do {
175           if(isdigit(c))
176             have_digits = true;
177           else if(c == '.')
178             ++have_floating_point;
179           else if(isalnum(c) || c == '_')
180             have_nondigits = true;
181
182           addChar();
183         } while(!isspace(c) && !strchr(delims, c));
184
185         token_string[token_length] = 0;
186
187         // no nextChar
188
189         if(have_nondigits || !have_digits || have_floating_point > 1)
190           return TOKEN_SYMBOL;
191         else if(have_floating_point == 1)
192           return TOKEN_REAL;
193         else
194           return TOKEN_INTEGER;
195       } else {
196         do {
197           addChar();
198         } while(!isspace(c) && !strchr(delims, c));
199         token_string[token_length] = 0;
200
201         // no nextChar
202
203         return TOKEN_SYMBOL;
204       }
205   }
206 }
207
208 } // end of namespace lisp
209
210 /* EOF */