[Super-tux-commit] supertux/lib/lisp lexer.cpp,NONE,1.1 lexer.h,NONE,1.1 lisp.cpp,NONE,1.1 lisp.h,NO
Brought to you by:
wkendrick
Update of /cvsroot/super-tux/supertux/lib/lisp In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13781/lib/lisp Added Files: lexer.cpp lexer.h lisp.cpp lisp.h list_iterator.cpp list_iterator.h parser.cpp parser.h writer.cpp writer.h Log Message: move over rewritten lispreader from tuxkart (with additional fixes), generalized TileManager and Tile classes and use them for the worldmap too --- NEW FILE: lisp.h --- // $Id: lisp.h,v 1.1 2004/11/28 14:57:45 matzebraun Exp $ // // TuxKart - a fun racing game with go-kart // Copyright (C) 2004 Matthias Braun <ma...@br...> // 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. #ifndef __LISPREADER_H__ #define __LISPREADER_H__ #include <string> #include <vector> namespace lisp { class Lisp { public: ~Lisp(); enum LispType { TYPE_CONS, TYPE_SYMBOL, TYPE_INTEGER, TYPE_STRING, TYPE_REAL, TYPE_BOOLEAN }; LispType get_type() const { return type; } Lisp* get_car() const { return v.cons.car; } Lisp* get_cdr() const { return v.cons.cdr; } bool get(std::string& val) const { if(type != TYPE_STRING && type != TYPE_SYMBOL) return false; val = v.string; return true; } bool get(unsigned int& val) const { if(type != TYPE_INTEGER) return false; val = v.integer; return true; } bool get(int& val) const { if(type != TYPE_INTEGER) return false; val = v.integer; return true; } bool get(float& val) const { if(type != TYPE_REAL) { if(type == TYPE_INTEGER) { val = v.integer; return true; } return false; } val = v.real; return true; } bool get(bool& val) const { if(type != TYPE_BOOLEAN) return false; val = v.boolean; return true; } /* conveniance functions which traverse the list until a child with a * specified name is found. The value part is then interpreted in a specific * way. The functions return true, if a child was found and could be * interpreted correctly, otherwise false is returned and the variable value * is not changed. * (Please note that searching the lisp structure is O(n) so these functions * are no good idea for performance critical areas) */ template<class T> bool get(const char* name, T& val) const { const Lisp* lisp = get_lisp(name); if(!lisp) return false; if(lisp->get_type() != TYPE_CONS) return false; lisp = lisp->get_car(); if(!lisp) return false; return lisp->get(val); } template<class T> bool get_vector(const char* name, std::vector<T>& vec) const { vec.clear(); const Lisp* child = get_lisp(name); if(!child) return false; for( ; child != 0; child = child->get_cdr()) { T val; if(!child->get_car()) continue; if(child->get_car()->get(val)) { vec.push_back(val); } } return true; } Lisp* get_lisp(const char* name) const; Lisp* get_lisp(const std::string& name) const { return get_lisp(name.c_str()); } // for debugging void print(int indent = 0) const; private: friend class Parser; Lisp(LispType newtype); LispType type; union { struct { Lisp* car; Lisp* cdr; } cons; char* string; int integer; bool boolean; float real; } v; }; } // end of namespace lisp #endif --- NEW FILE: list_iterator.cpp --- #include <config.h> #include "list_iterator.h" #include <stdexcept> namespace lisp { ListIterator::ListIterator(const lisp::Lisp* newlisp) : current_lisp(0), cur(newlisp) { } bool ListIterator::next() { if(cur == 0) return false; const lisp::Lisp* child = cur->get_car(); if(!child) throw new std::runtime_error("child is 0 in list entry"); if(child->get_type() != lisp::Lisp::TYPE_CONS) throw new std::runtime_error("Expected CONS"); const lisp::Lisp* name = child->get_car(); if(!name || name->get_type() != lisp::Lisp::TYPE_SYMBOL) throw new std::runtime_error("Expected symbol"); name->get(current_item); current_lisp = child->get_cdr(); cur = cur->get_cdr(); return true; } } --- NEW FILE: writer.cpp --- // $Id: writer.cpp,v 1.1 2004/11/28 14:57:45 matzebraun Exp $ // // SuperTux - A Jump'n Run // Copyright (C) 2004 Matthias Braun <ma...@br... // // 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 <config.h> #include <iostream> #include "writer.h" namespace lisp { Writer::Writer(std::ostream& newout) : out(newout), indent_depth(0) { } Writer::~Writer() { if(lists.size() > 0) { std::cerr << "Warning: Not all sections closed in lispwriter!\n"; } } void Writer::write_comment(const std::string& comment) { out << "; " << comment << "\n"; } void Writer::start_list(const std::string& listname) { indent(); out << '(' << listname << '\n'; indent_depth += 2; lists.push_back(listname); } void Writer::end_list(const std::string& listname) { if(lists.size() == 0) { std::cerr << "Trying to close list '" << listname << "', which is not open.\n"; return; } if(lists.back() != listname) { std::cerr << "Warning: trying to close list '" << listname << "' while list '" << lists.back() << "' is open.\n"; return; } lists.pop_back(); indent_depth -= 2; indent(); out << ")\n"; } void Writer::write_int(const std::string& name, int value) { indent(); out << '(' << name << ' ' << value << ")\n"; } void Writer::write_float(const std::string& name, float value) { indent(); out << '(' << name << ' ' << value << ")\n"; } void Writer::write_string(const std::string& name, const std::string& value) { indent(); out << '(' << name << " \"" << value << "\")\n"; } void Writer::write_bool(const std::string& name, bool value) { indent(); out << '(' << name << ' ' << (value ? "#t" : "#f") << ")\n"; } void Writer::write_int_vector(const std::string& name, const std::vector<int>& value) { indent(); out << '(' << name; for(std::vector<int>::const_iterator i = value.begin(); i != value.end(); ++i) out << " " << *i; out << ")\n"; } void Writer::write_int_vector(const std::string& name, const std::vector<unsigned int>& value) { indent(); out << '(' << name; for(std::vector<unsigned int>::const_iterator i = value.begin(); i != value.end(); ++i) out << " " << *i; out << ")\n"; } void Writer::indent() { for(int i = 0; i<indent_depth; ++i) out << ' '; } } // end of namespace lisp --- NEW FILE: parser.h --- // $Id: parser.h,v 1.1 2004/11/28 14:57:45 matzebraun Exp $ // // TuxKart - a fun racing game with go-kart // Copyright (C) 2004 Matthias Braun <ma...@br...> // 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. #ifndef __LISPPARSER_H__ #define __LISPPARSER_H__ #include <string> #include "lexer.h" namespace lisp { class Lisp; class Parser { public: Parser(); ~Parser(); Lisp* parse(const std::string& filename); Lisp* parse(std::istream& stream); private: Lisp* read(); Lexer* lexer; Lexer::TokenType token; }; } // end of namespace lisp #endif --- NEW FILE: list_iterator.h --- #ifndef __LISP_ITERATOR_H__ #define __LISP_ITERATOR_H__ #include "lisp/lisp.h" namespace lisp { /** * Small and a bit hacky helper class that helps parsing lisp lists where all * entries are lists again themselves */ class ListIterator { public: ListIterator(const lisp::Lisp* cur); const std::string& item() const { return current_item; } lisp::Lisp* lisp() const { return current_lisp; } lisp::Lisp* value() const { return current_lisp->get_car(); } bool next(); private: std::string current_item; lisp::Lisp* current_lisp; const lisp::Lisp* cur; }; } #endif --- NEW FILE: lexer.h --- // $Id: lexer.h,v 1.1 2004/11/28 14:57:45 matzebraun Exp $ // // Copyright (C) 2004 Matthias Braun <ma...@br...> // 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. #ifndef __LISPLEXER_H__ #define __LISPLEXER_H__ namespace lisp { class Lexer { public: enum TokenType { TOKEN_EOF, TOKEN_OPEN_PAREN, TOKEN_CLOSE_PAREN, TOKEN_SYMBOL, TOKEN_STRING, TOKEN_INTEGER, TOKEN_REAL, TOKEN_TRUE, TOKEN_FALSE }; Lexer(std::istream& stream); ~Lexer(); TokenType getNextToken(); const char* getString() const { return token_string; } int getLineNumber() const { return linenumber; } private: enum { MAX_TOKEN_LENGTH = 16384, BUFFER_SIZE = 1024 }; inline void nextChar(); std::istream& stream; bool eof; int linenumber; char buffer[BUFFER_SIZE+1]; char* bufend; char* c; char token_string[MAX_TOKEN_LENGTH + 1]; int token_length; }; } // end of namespace lisp #endif --- NEW FILE: writer.h --- // $Id: writer.h,v 1.1 2004/11/28 14:57:45 matzebraun Exp $ // // SuperTux - A Jump'n Run // Copyright (C) 2004 Matthias Braun <ma...@br... // // 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. #ifndef SUPERTUX_LISPWRITER_H #define SUPERTUX_LISPWRITER_H #include <iostream> #include <string> #include <vector> namespace lisp { class Writer { public: Writer(std::ostream& out); ~Writer(); void write_comment(const std::string& comment); void start_list(const std::string& listname); void write_int(const std::string& name, int value); void write_float(const std::string& name, float value); void write_string(const std::string& name, const std::string& value); void write_bool(const std::string& name, bool value); void write_int_vector(const std::string& name, const std::vector<int>& value); void write_int_vector(const std::string& name, const std::vector<unsigned int>& value); // add more write-functions when needed... void end_list(const std::string& listname); private: void indent(); std::ostream& out; int indent_depth; std::vector<std::string> lists; }; } //namespace lisp #endif //SUPERTUX_LISPWRITER_H --- NEW FILE: parser.cpp --- // $Id: parser.cpp,v 1.1 2004/11/28 14:57:45 matzebraun Exp $ // // TuxKart - a fun racing game with go-kart // Copyright (C) 2004 Matthias Braun <ma...@br...> // 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 <config.h> #include <sstream> #include <stdexcept> #include <fstream> #include "parser.h" #include "lisp.h" namespace lisp { Parser::Parser() : lexer(0) { } Parser::~Parser() { delete lexer; } Lisp* Parser::parse(const std::string& filename) { std::ifstream in(filename.c_str()); if(!in.good()) { std::stringstream msg; msg << "Parser problem: Couldn't open file '" << filename << "'."; throw std::runtime_error(msg.str()); } return parse(in); } Lisp* Parser::parse(std::istream& stream) { delete lexer; lexer = new Lexer(stream); token = lexer->getNextToken(); Lisp* result = new Lisp(Lisp::TYPE_CONS); result->v.cons.car = read(); result->v.cons.cdr = 0; delete lexer; lexer = 0; return result; } Lisp* Parser::read() { Lisp* result; switch(token) { case Lexer::TOKEN_EOF: { std::stringstream msg; msg << "Parse Error at line " << lexer->getLineNumber() << ": " << "Unexpected EOF."; throw std::runtime_error(msg.str()); } case Lexer::TOKEN_CLOSE_PAREN: { std::stringstream msg; msg << "Parse Error at line " << lexer->getLineNumber() << ": " << "Unexpected ')'."; throw std::runtime_error(msg.str()); } case Lexer::TOKEN_OPEN_PAREN: { result = new Lisp(Lisp::TYPE_CONS); token = lexer->getNextToken(); if(token == Lexer::TOKEN_CLOSE_PAREN) { result->v.cons.car = 0; result->v.cons.cdr = 0; break; } Lisp* cur = result; do { cur->v.cons.car = read(); if(token == Lexer::TOKEN_CLOSE_PAREN) { cur->v.cons.cdr = 0; break; } cur->v.cons.cdr = new Lisp(Lisp::TYPE_CONS); cur = cur->v.cons.cdr; } while(1); break; } case Lexer::TOKEN_SYMBOL: { result = new Lisp(Lisp::TYPE_SYMBOL); size_t len = strlen(lexer->getString()) + 1; result->v.string = new char[len]; memcpy(result->v.string, lexer->getString(), len); break; } case Lexer::TOKEN_STRING: { result = new Lisp(Lisp::TYPE_STRING); size_t len = strlen(lexer->getString()) + 1; result->v.string = new char[len]; memcpy(result->v.string, lexer->getString(), len); break; } case Lexer::TOKEN_INTEGER: result = new Lisp(Lisp::TYPE_INTEGER); sscanf(lexer->getString(), "%d", &result->v.integer); break; case Lexer::TOKEN_REAL: result = new Lisp(Lisp::TYPE_REAL); sscanf(lexer->getString(), "%f", &result->v.real); break; case Lexer::TOKEN_TRUE: result = new Lisp(Lisp::TYPE_BOOLEAN); result->v.boolean = true; break; case Lexer::TOKEN_FALSE: result = new Lisp(Lisp::TYPE_BOOLEAN); result->v.boolean = false; break; default: // this should never happen assert(false); } token = lexer->getNextToken(); return result; } } // end of namespace lisp --- NEW FILE: lexer.cpp --- // $Id: lexer.cpp,v 1.1 2004/11/28 14:57:45 matzebraun Exp $ // // Copyright (C) 2004 Matthias Braun <ma...@br...> // 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 <config.h> #include <sstream> #include <stdexcept> #include "lexer.h" namespace lisp { class EOFException { }; Lexer::Lexer(std::istream& newstream) : stream(newstream), eof(false), linenumber(0) { try { // trigger a refill of the buffer c = 0; bufend = c + 1; nextChar(); } catch(EOFException& e) { } } Lexer::~Lexer() { } void Lexer::nextChar() { ++c; if(c >= bufend) { if(eof) throw EOFException(); std::streamsize n = stream.readsome(buffer, BUFFER_SIZE); c = buffer; bufend = 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 == 0 || stream.eof()) { eof = true; *bufend = ' '; ++bufend; } } } Lexer::TokenType Lexer::getNextToken() { static const char* delims = "\"();"; try { while(isspace(*c)) { if(*c == '\n') ++linenumber; nextChar(); }; token_length = 0; switch(*c) { case ';': // comment while(!stream.eof()) { nextChar(); if(*c == '\n') { ++linenumber; break; } } return getNextToken(); // and again case '(': nextChar(); return TOKEN_OPEN_PAREN; case ')': nextChar(); return TOKEN_CLOSE_PAREN; case '"': { // string int startline = linenumber; try { while(1) { if(stream.eof()) { std::stringstream msg; msg << "Parse Error in line " << startline << ": " << "Couldn't find end of string."; throw std::runtime_error(msg.str()); } nextChar(); if(*c == '"') break; else if(*c == '\n') linenumber++; else if(*c == '\\') { nextChar(); switch(*c) { case 'n': *c = '\n'; break; case 't': *c = '\t'; break; } } if(token_length < MAX_TOKEN_LENGTH) token_string[token_length++] = *c; } token_string[token_length] = 0; } catch(EOFException& ) { std::stringstream msg; msg << "Parse error in line " << startline << ": " << "EOF while parsing string."; throw std::runtime_error(msg.str()); } nextChar(); return TOKEN_STRING; } case '#': // constant try { nextChar(); while(isalnum(*c) || *c == '_') { if(token_length < MAX_TOKEN_LENGTH) token_string[token_length++] = *c; nextChar(); } token_string[token_length] = 0; } catch(EOFException& ) { std::stringstream msg; msg << "Parse Error in line " << linenumber << ": " << "EOF while parsing constant."; throw std::runtime_error(msg.str()); } if(strcmp(token_string, "t") == 0) return TOKEN_TRUE; if(strcmp(token_string, "f") == 0) return TOKEN_FALSE; // we only handle #t and #f constants at the moment... { std::stringstream msg; msg << "Parse Error in line " << linenumber << ": " << "Unknown constant '" << token_string << "'."; throw std::runtime_error(msg.str()); } default: if(isdigit(*c) || *c == '-') { bool have_nondigits = false; bool have_digits = false; int have_floating_point = 0; do { if(isdigit(*c)) have_digits = true; else if(*c == '.') ++have_floating_point; else if(isalnum(*c) || *c == '_') have_nondigits = true; if(token_length < MAX_TOKEN_LENGTH) token_string[token_length++] = *c; nextChar(); } while(!isspace(*c) && !strchr(delims, *c)); token_string[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(token_length < MAX_TOKEN_LENGTH) token_string[token_length++] = *c; nextChar(); } while(!isspace(*c) && !strchr(delims, *c)); token_string[token_length] = 0; // no nextChar return TOKEN_SYMBOL; } } } catch(EOFException& ) { return TOKEN_EOF; } } } // end of namespace lisp --- NEW FILE: lisp.cpp --- // $Id: lisp.cpp,v 1.1 2004/11/28 14:57:45 matzebraun Exp $ // // TuxKart - a fun racing game with go-kart // Copyright (C) 2004 Matthias Braun <ma...@br...> // 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 <config.h> #include "lisp.h" namespace lisp { Lisp::Lisp(LispType newtype) : type(newtype) { } Lisp::~Lisp() { if(type == TYPE_SYMBOL || type == TYPE_STRING) delete[] v.string; if(type == TYPE_CONS) { delete v.cons.cdr; delete v.cons.car; } } Lisp* Lisp::get_lisp(const char* name) const { for(const Lisp* p = this; p != 0; p = p->get_cdr()) { Lisp* child = p->get_car(); if(!child || child->get_type() != TYPE_CONS) continue; Lisp* childname = child->get_car(); if(!childname) continue; std::string childName; if(!childname->get(childName)) continue; if(childName == name) { return child->get_cdr(); } } return 0; } void Lisp::print(int indent) const { for(int i = 0; i < indent; ++i) printf(" "); if(type == TYPE_CONS) { printf("(\n"); const Lisp* lisp = this; while(lisp) { if(lisp->v.cons.car) lisp->v.cons.car->print(indent + 1); lisp = lisp->v.cons.cdr; } for(int i = 0; i < indent; ++i) printf(" "); printf(")"); } if(type == TYPE_STRING) { printf("'%s' ", v.string); } if(type == TYPE_INTEGER) { printf("%d", v.integer); } if(type == TYPE_REAL) { printf("%f", v.real); } if(type == TYPE_SYMBOL) { printf("%s ", v.string); } if(type == TYPE_BOOLEAN) { printf("%s ", v.boolean ? "true" : "false"); } printf("\n"); } } // end of namespace lisp |