// $Id$ // // TuxKart - a fun racing game with go-kart // Copyright (C) 2004 Matthias Braun // code in this file based on lispreader from Mark Probst // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include #include "lexer.hpp" #include "translation.hpp" #if defined(WIN32) && !defined(__CYGWIN__) # define snprintf _snprintf #endif namespace lisp { class EOFException {} ; Lexer::Lexer(std::istream& newstream) : m_stream(newstream), m_is_eof(false) { try { // trigger a refill of the m_buffer m_c = 0; m_buffer_end = m_c + 1; nextChar(); } catch(EOFException& e) {(void)e; // avoid 'unreferenced local variable' warning } } //----------------------------------------------------------------------------- Lexer::~Lexer() {} //----------------------------------------------------------------------------- void Lexer::nextChar() { ++m_c; if(m_c >= m_buffer_end) { if(m_is_eof) throw EOFException(); m_stream.read(m_buffer, LEXER_BUFFER_SIZE); std::streamsize n = m_stream.gcount(); m_c = m_buffer; m_buffer_end = m_buffer + n; // the following is a hack that appends an additional ' ' at the end of // the file to avoid problems when parsing symbols/elements and a sudden // EOF. This is faster than relying on unget and IMO also nicer. if(n < LEXER_BUFFER_SIZE || n == 0) { *m_buffer_end = ' '; ++m_buffer_end; m_is_eof = true; } } } //----------------------------------------------------------------------------- Lexer::TokenType Lexer::getNextToken() { static const char* delims = "\"();"; try { while(isspace(*m_c)) { nextChar(); if(*m_c == '\n') ++m_line_number; }; m_token_length = 0; switch(*m_c) { case ';': // comment while(true) { nextChar(); if(*m_c == '\n') { ++m_line_number; break; } } return getNextToken(); // and again case '(': nextChar(); return TOKEN_OPEN_PAREN; case ')': nextChar(); return TOKEN_CLOSE_PAREN; case '"': // string { const int STARTLINE = m_line_number; try { while(1) { nextChar(); if(*m_c == '"') break; if(*m_c == '\\') { nextChar(); switch(*m_c) { case 'n': *m_c = '\n'; break; case 't': *m_c = '\t'; break; } } if(m_token_length < MAX_TOKEN_LENGTH) m_token_string[m_token_length++] = *m_c; } m_token_string[m_token_length] = 0; } catch(EOFException& ) { char msg[MAX_ERROR_MESSAGE_LENGTH]; snprintf(msg, sizeof(msg), "Parse error in line %d: EOF while parsing string.", STARTLINE); throw std::runtime_error(msg); } nextChar(); return TOKEN_STRING; } case '#': // constant try { nextChar(); while(isalnum(*m_c) || *m_c == '_') { if(m_token_length < MAX_TOKEN_LENGTH) m_token_string[m_token_length++] = *m_c; nextChar(); } m_token_string[m_token_length] = 0; } catch(EOFException& ) { char msg[MAX_ERROR_MESSAGE_LENGTH]; snprintf(msg, sizeof(msg), "Parse Error in line %d: EOF while parsing constant.", m_line_number); throw std::runtime_error(msg); } if(strcmp(m_token_string, "t") == 0) return TOKEN_TRUE; if(strcmp(m_token_string, "f") == 0) return TOKEN_FALSE; // this would be the place to add more sophisticated handling of // constants { char msg[MAX_ERROR_MESSAGE_LENGTH]; snprintf(msg, sizeof(msg), "Parse Error in line %d: Unknown constant '%s'.", m_line_number, m_token_string); throw std::runtime_error(msg); } default: if(isdigit(*m_c) || *m_c == '-') { bool have_nondigits = false; bool have_digits = false; int have_floating_point = 0; do { if(isdigit(*m_c)) have_digits = true; else if(*m_c == '.') ++have_floating_point; else if(isalnum(*m_c) || *m_c == '_') have_nondigits = true; if(m_token_length < MAX_TOKEN_LENGTH) m_token_string[m_token_length++] = *m_c; nextChar(); } while(!isspace(*m_c) && !strchr(delims, *m_c)); m_token_string[m_token_length] = 0; // no nextChar if(have_nondigits || !have_digits || have_floating_point > 1) return TOKEN_SYMBOL; else if(have_floating_point == 1) return TOKEN_REAL; else return TOKEN_INTEGER; } else { do { if(m_token_length < MAX_TOKEN_LENGTH) m_token_string[m_token_length++] = *m_c; nextChar(); } while(!isspace(*m_c) && !strchr(delims, *m_c)); m_token_string[m_token_length] = 0; // no nextChar return TOKEN_SYMBOL; } } } catch(EOFException& ) { return TOKEN_EOF; } } } // end of namespace lisp