// Copyright (C) 1998 by // Krzysztof Czarnecki (Czarnecki@acm.org) and // Ulrich W. Eisenecker (Ulrich.Eisenecker@T-Online.de) // // METALISP.CPP contains a rudimentary LISP implementation as a template // metaprogram. All the basic primitives and some convenience functions // are provided. You can use it to write functional programs interpreted by // the compiler at compile time. // This implementation uses member templates and has been tested with the // Microsoft Visual C++ 5.0 compiler. Partial specialization and partial ordering of // templates are not used. Some minor changes of the implementation might become // necessary once the existing compilers conform to the upcoming C++ // standard. // METACTRL is required. // At some point, we will separate the examples from the header file. /* IMPORTANT NOTE FROM THE AUTHORS METALISP will be part of our upcoming book "Generative Programming - Towards a New Paradigm of Software-Engineering" published by Addison Wesley Longman. Any suggestions for improvements and implementations of further LISP functions are welcomed. If you have any problems with METALISP, feel free to contact us for help or advice. If you find a bug in METALISP, try first to fix it. If you are successful in fixing the bug, send us the bug report along with the fix. If you are unable to fix it, please send us the bug report including the detailed information about the compiler and the options you use. We will see what we can do. Feel free to extend METALISP in whatever way suits your needs. However, PLEASE DO NOT RELEASE ENHANCED VERSIONS WITHOUT CHECKING WITH US FIRST!! We would like to be the clearing house for new features added to METALISP. If you want to add features for your own personal use, go ahead. But, if you want to distribute your enhanced version, contact us first. */ #include #include #include "metactrl" namespace metalisp { using namespace std; using namespace metactrl; // The functions which are not currently not available are documented // as comments. Many of them cannot be implemented, e.g. destructive // list functions or input during compile-time. // EVALUATION FUNCTIONS // EVAL // APPLY // FUNCALL // QUOTE // FUNCTION // BACKQUOTE // LAMBDA // SYMBOL FUNCTIONS // SET // SETQ // SETF // DEFUN // DEFMACRO // GENSYM // INTERN // MAKE-SYMBOL // SYMBOL-NAME // SYMBOL-VALUE // SYMBOL-PLIST // HASH // PROPERTY LIST FUNCTIONS // GET // PUTPROP // REMPROP // ARRAY FUNCTIONS // AREF // MAKE-ARRAY // LIST FUNCTIONS template struct CAR; template struct CDR; // CXXR // CXXXR // CXXXXR template struct CONS; template struct LIST; template struct APPEND; template struct REVERSE; template struct LAST; // MEMBER // ASSOC // REMOVE template struct LENGTH; template struct NTH; // NTHCDR // MAPC // MAPCAR // MAPL // MAPLIST // SUBST // SUBLIS // DESTRUCTIVE LIST FUNCTIONS [ ARE ALL NON-FUNCTIONAL AND HENCE CANNOT BE IMPLEMENTED! ] // RPLACA // RPLACD // NCONC // DELETE // PREDICATE FUNCTIONS template struct ATOM; template struct SYMBOLP; template struct NUMBERP; template struct NULLP; // ORIGINAL NAME IS NULL ! template struct NOT; template struct LISTP; template struct CONSP; // BOUNDP // MINUSP // ZEROP // PLUSP // EVENP // ODDP template struct EQ; template struct EQL; template struct EQUAL; // CONTROL CONSTRUCTS // COND // AND // OR // IF // CASE // LET // LET* // CATCH // THROW // LOOPING CONSTRUCTS // DO // DO* // DOLIST // DOTIMES // THE PROGRAM FEATURE [ CANNOT BE IMPLEMENTED ! ] // PROG // PROG* // GO // RETURN // PROG1 // PROG2 // PROGN // DEBUGGING AND ERROR-HANDLING [ CANNOT BE IMPLEMENTED ! ] // ERROR // CERROR // BREAK // CLEAN-UP // TOP-LEVEL // CONTINUE // ERRSET // BAKTRACE // EVALHOOK // ARITHMETIC FUNCTIONS // TRUNCATE // FLOAT template struct ADD; // ORIGINAL NAME IS + template struct SUBTRACT; // ORIGINAL NAME IS - template struct MULTIPLY; // ORIGINAL NAME IS * template struct DIVIDE; // ORIGINAL NAME IS / // 1+ // 1- // REM // MIN // MAX // ABS // RANDOM // SIN // COS // TAN // EXPT // EXP // SQRT // BITWISE LOGICAL FUNCTIONS // LOGAND // LOGIOR // LOGXOR // LOGNOT // RELATIONAL FUNCTIONS // < // <= // = // /= // >= // > // STRING FUNCTIONS // CHAR // STRING // STRCAT // SUBSTR // INTPUT/OUTPUT FUNCTIONS // READ template struct PRINT; // PRIN1 // PRINC // TERPRI // FLATSIZE // FLATC // FILE I/O FUNCTIONS // OPENI // OPENO // CLOSE // READ-CHAR // PEEK-CHAR // WRITE-CHAR // READ-LINE // SYSTEM FUNCTIONS // LOAD // TRANSCRIPT // GC // EXPAND // ALLOC // MEM // TYPE-OF // PEEK // POKE // ADDRESS-OF // EXIT // ----------------------------------------------------------------- // T and NIL are implemented as const int's here. // Thus all predicate functions don't work on them! const int T = 1; // value True in lisp const int NIL = 0; // value False in lisp struct NILT; typedef NILT LISTT; // LISTT is the empty list which is type NILT in lisp! It's called NILT since it would otherwise collide with NIL value // Basic types: // - SEXPRESSIONT (for convenience of code sharing) // - NUMBERT // - SYMBOLT // - LISTT (empty list, equals NILT) // T stands for TYPE - These identifiers do not exist in Lisp. // Otherwise we had conflicts with functions like LIST struct SEXPRESSIONT { enum { N_VAL = NIL, ID = NIL, IS_NUMBER = NIL, IS_SYMBOL = NIL, IS_ATOM = T, IS_LIST = NIL, IS_EMPTY = T, }; static const char* const name() { return "SEXPRESSIONT"; } static void print(ostream& os) { os << name() << flush; } typedef NILT FIRST; // required for termination of EQUAL typedef NILT REST; // required for termination of EQUAL }; namespace intimate { template struct SAME // not part of lisp { enum { RET = X1::N_VAL == X2::N_VAL && X1::ID == X2::ID && X1::IS_NUMBER == X2::IS_NUMBER && X1::IS_SYMBOL == X2::IS_SYMBOL && X1::IS_ATOM == X2::IS_ATOM && X1::IS_LIST == X2::IS_LIST && X1::IS_EMPTY == X2::IS_EMPTY }; }; template struct _REVERSE; }; // end namespace intimate template struct NUMBERT : public SEXPRESSIONT { enum { IS_NUMBER=T, N_VAL = value }; typedef NUMBERT RET; // required for being treated as function static const char* const name() { return "NUMBERT"; } static void print(ostream& os) { os << value << flush; } }; template // symbols are always bound here struct SYMBOLT : public SEXPRESSIONT { enum { IS_SYMBOL=T, ID = SymbolID // warning: it is not checked whether SymbolID is already in use }; typedef SYMBOLT RET; // required for being treated as function typedef value S_VAL; static const char* const name() { return "SYMBOLT"; } static void print(ostream& os) { os << "'" << SymbolID << flush; // SymbolIDs are printed with a preceding quote } }; struct NILT : public SEXPRESSIONT { enum { IS_LIST=T }; typedef NILT RET; // required for being treated as function static const char* const name() { return "NILT"; } static void print(ostream& os) { os << "NIL" << flush; } }; // List functions template struct CAR // return the CAR of a list node { typedef typename List::FIRST RET; }; template struct CDR // return the CDR of a list node { typedef typename List::REST RET; }; // CXXR, CXXXR, CXXXXR list functions are missing. // The most simple way is to code them explicitly when required. template struct CONS: public NILT // construct a new list node { enum { IS_EMPTY = NIL, IS_ATOM = NIL }; typedef X1 FIRST; typedef X2 REST; typedef CONS RET; static void print(ostream& os) // lists are printed with redundant parentheses { os << "("; FIRST::print(os); os << " . "; REST::print(os); os << ")" << flush; } }; template struct LIST: public CONS // create a list of values {}; template struct REVERSE // reverse a list - different from standard lisp this implementation of { // reverse works also on atoms; they are returned unchanged. typedef IF::RET,X,intimate::_REVERSE::RET>::RET RET; }; // Predicate functions template struct NUMBERP // is X a list? { enum { RET = X::IS_NUMBER}; }; template struct SYMBOLP // is X a symbol? { enum { RET = X::IS_SYMBOL }; }; template struct LISTP // is X a list? { enum { RET = X::IS_LIST }; }; template struct NULLP // is X an empty list? // usual name is NULL - had to be changed to NULLP due the // presence of NULL in C++ { enum { RET = X::IS_LIST && X::IS_EMPTY }; }; template // struct NOT // returns T is X is NILT - for negating NIL C++ operator ! must be used { enum { RET = NIL }; }; template<> struct NOT { enum { RET = T }; }; template struct CONSP // is this a non-empty list? { enum { RET = !NULLP::RET }; }; template struct ATOM // is X an atom? - The answer is T also for empty lists { enum { RET = X::IS_ATOM }; }; template // private struct EQ // are S1 and S2 equal symbols? { enum { RET = (SYMBOLP::RET && SYMBOLP::RET)?(S1::ID==S2::ID):0 }; }; template struct EQL // are N1 and N2 equal numbers? { // EQL normally works for strings too, but we don't have strings in this enum { RET = (NUMBERP::RET && NUMBERP::RET)?(N1::N_VAL == N2::N_VAL):0 }; }; template struct EQUAL { enum { RET = (LISTP::RET && LISTP::RET) ? EQUAL::RET, CAR::RET>::RET && EQUAL::RET, CDR::RET>::RET : intimate::SAME::RET }; }; template<> struct EQUAL // terminate recursion of EQUAL { enum { RET = T }; }; template struct LENGTH // length of the list? (strings are not supported) { // different from original lisp length can be applied // here also to numbers and symbols, for which it returns NIL enum { RET = ATOM::RET ? 0 : LISTP::RET>::RET ? LENGTH::RET>::RET + 1 : 1 }; }; template<> struct LENGTH // terminate recursion of LENGTH { enum { RET = 0 }; }; template // first node is node 0! struct NTH // caution: strictly following the XLISP implementation is very hard { // in case of invalid n the last node is returned; if not applied to a list returns the object itself typedef IF<(LENGTH::RET > 0 && n > 0), NTH::RET,(n)?n-1:NIL>::RET, IF<(LENGTH::RET == 0),X,CAR::RET>::RET >::RET RET; }; template<> struct NTH { typedef NILT RET; }; template struct LAST // Return the last node of a list or, if not applied to a list, the object itself { typedef NTH::RET>::RET RET; }; // System functions // --- // Arithmetic functions template struct ADD // add two numbers, in original lisp +; other than in original lisp works on any type of element { enum { N1 = X1::N_VAL, N2 = X2::N_VAL }; typedef NUMBERT RET; }; template struct SUBTRACT // subtract two numbers, in original lisp -; other than in original lisp works on any type of element { enum { N1 = X1::N_VAL, N2 = X2::N_VAL }; typedef NUMBERT RET; }; template struct MULTIPLY // multiplies two numbers, in original lisp *; other than in original lisp works on any type of element { enum { N1 = X1::N_VAL, N2 = X2::N_VAL }; typedef NUMBERT RET; }; template struct DIVIDE // divides two numbers, in original lisp /; other than in original lisp works on any type of element { enum { N1 = X1::N_VAL, N2 = X2::N_VAL }; typedef NUMBERT RET; }; // Input/Output-Functions template // 2 deviations from lisp: symbols are preded by a quote and lists print with redundant parentheses struct PRINT // prints X on SINK - must be called by EXEC() since RET can't have side effects { static void EXEC(SINK& sink = cout) { X::print(sink); sink << flush; } }; template struct APPEND { typedef IF::RET, X1::FIRST, X2::FIRST >::RET N; typedef IF::RET, NILT, X1::REST >::RET N1; typedef IF::RET, X2::REST, X2 >::RET N2; typedef IF::RET && ATOM::RET, NILT, CONS::RET>::RET >::RET RET; }; template<> // terminate recursion of APPEND struct APPEND { typedef NILT RET; }; // Implementations namespace intimate { template struct _REVERSE { typedef CONS REV; typedef IF<(ATOM::RET || ATOM::RET), REV, _REVERSE::RET,NILT,X::REST>::RET, IF::RET,NILT,REV>::RET>::RET >::RET RET; }; template<> struct _REVERSE // terminate recursion of _REVERSE { typedef NILT RET; }; } // end namespace intimate } // end namespace metalisp // -------------------------------------------------------------------------- // Stuff for testing: using namespace std; void wait() { char c; cout << endl << "Press a key and Enter to continue ..." << flush; cin >> c; cout << endl; } using namespace metalisp; using namespace metactrl; template struct Test { static void print() { cout << "N_VAL\t\t" << EXPR::N_VAL << endl << "ID\t\t" << EXPR::ID << endl << "IS_NUMBER\t" << bool(EXPR::IS_NUMBER) << endl << "IS_SYMBOL\t" << bool(EXPR::IS_SYMBOL) << endl << "IS_ATOM\t\t" << bool(EXPR::IS_ATOM) << endl << "IS_LIST\t\t" << bool(EXPR::IS_LIST) << endl << "IS_EMPTY\t" << bool(EXPR::IS_EMPTY) << endl; wait(); } static void predicates() { cout << "NUMBERP\t" << bool(NUMBERP::RET) << endl << "SYMBOL\t" << bool(SYMBOLP::RET) << endl << "LISTP\t" << bool(LISTP::RET) << endl << "NULLP\t" << bool(NULLP::RET) << endl << "ATOM\t" << bool(ATOM::RET) << endl; wait(); } template struct eq { static void print() { cout << "(EQ " << EXPR::name() << " " << X2::name() << ") -> " << bool(EQ::RET) << endl; } }; template struct eql { static void print() { cout << "(EQL " << EXPR::name() << " " << X2::name() << ") -> " << bool(EQL::RET) << endl; } }; template struct _equal { static void print() { cout << "(EQUAL " << EXPR::name() << " " << X2::name() << ") -> " << bool(EQUAL::RET) << endl; } }; }; // some types typedef NUMBERT<99> number_99; typedef SYMBOLT symbol_void_42; typedef LISTT empty_list; typedef CONS cons_number_99_symbol_void_42; typedef CONS,SYMBOLT >,CONS,CONS,NUMBERT<4> > > > complex_cons; typedef CONS,CONS,CONS,NILT> > > pretty_list; typedef LIST,LIST,LIST > > > same_as_pretty_list; // type mapping template struct type {}; template<> struct type<0> { typedef number_99 RET; }; template<> struct type<1> { typedef symbol_void_42 RET; }; template<> struct type<2> { typedef empty_list RET; }; template<> struct type<3> { typedef cons_number_99_symbol_void_42 RET; }; template<> struct type<4> { typedef complex_cons RET; }; // checking eq's struct eq_check { template struct Code { struct innerLoop { template struct Code { static void execute() { Test::RET>::eq::RET>::print(); } }; }; static void execute() { FOR<0,Less,5,+1,innerLoop>::EXEC(); } }; }; struct eql_check { template struct Code { struct innerLoop { template struct Code { static void execute() { Test::RET>::eql::RET>::print(); } }; }; static void execute() { FOR<0,Less,5,+1,innerLoop>::EXEC(); } }; }; struct equal_check { template struct Code { struct innerLoop { template struct Code { static void execute() { Test::RET>::_equal::RET>::print(); } }; }; static void execute() { FOR<0,Less,5,+1,innerLoop>::EXEC(); } }; }; struct length_check { template struct Code { static void execute() { cout << type::RET::name() << '\t' << LENGTH::RET>::RET << endl; } }; }; struct nth_check { template struct Code { struct innerLoop { template struct Code { static void execute() { cout << NTH::RET,j>::RET::name() << '\t'; } }; }; static void execute() { FOR <0,Less,LENGTH::RET>::RET+2,+1,innerLoop>::EXEC(); cout << endl; wait(); } }; }; struct last_check { template struct Code { static void execute() { cout << LAST::RET>::RET::name() << endl; } }; }; struct print_check { template struct Code { static void execute() { PRINT::RET>::EXEC(); cout << endl; } }; }; struct reverse_check { template struct Code { static void execute() { PRINT::RET>::EXEC(); cout << " -> "; PRINT::RET>::RET>::EXEC(); cout << endl; } }; }; struct append_check { template struct Code { struct innerLoop { template struct Code { static void execute() { PRINT::RET>::EXEC(); cout << " + "; PRINT::RET>::EXEC(); cout << " -> "; PRINT::RET,type::RET>::RET>::EXEC(); cout << endl; } }; }; static void execute() { FOR<0,Less,5,+1,innerLoop>::EXEC(); wait(); } }; }; struct consp_check { template struct Code { static void execute() { cout << "CONSP<"; PRINT::RET>::EXEC(); cout << "> = " << CONSP::RET>::RET << endl; } }; }; int main() { // Testing // The stuff for testing above and the tests below are in a terrible state. // They follow exactly the bootstrap process when developing the // basic types and then adding more and more functions. cout << boolalpha; cout << "SEXPRESSIONT" << endl; Test::print(); cout << "number_99" << endl; Test::print(); cout << "symbol_void_42" << endl; Test::print(); cout << "empty_list" << endl; Test::print(); cout << "cons_number_99_symbol_void_42" << endl; Test::print(); cout << "CAR::RET" << endl; Test::RET>::print(); cout << "CDR::RET" << endl; Test::RET>::print(); cout << "Predicates for number_99" << endl; Test::predicates(); cout << "Predicates for symbol_void_42" << endl; Test::predicates(); cout << "Predicates for empty_list" << endl; Test::predicates(); cout << "Predicates for cons_number_99_symbol_void_42" << endl; Test::predicates(); cout << "*EQ*" << endl; FOR<0,Less,5,+1,eq_check>::EXEC(); wait(); cout << "*EQL*" << endl; FOR<0,Less,5,+1,eql_check>::EXEC(); wait(); cout << "*EQUAL*" << endl; FOR<0,Less,5,+1,equal_check>::EXEC(); wait(); cout << "*LENGTH*" << endl; FOR<0,Less,5,+1,length_check>::EXEC(); wait(); cout << "*NTH*" << endl; FOR<0,Less,5,+1,nth_check>::EXEC(); wait(); cout << "*LAST*" << endl; FOR<0,Less,5,+1,last_check>::EXEC(); wait(); cout << "*PRINT*" << endl; FOR<0,Less,5,+1,print_check>::EXEC(); wait(); cout << "*REVERSE*" << endl; FOR<0,Less,5,+1,reverse_check>::EXEC(); wait(); cout << "pretty_list" << endl; PRINT::EXEC(); cout << endl; cout << "same_as_pretty_list" << endl; PRINT::EXEC(); cout << endl; cout << "The two lists are really equal? " << EQUAL::RET << endl; typedef REVERSE::RET rev; PRINT::EXEC(); cout << endl; cout << "*APPEND*" << endl; FOR<0,Less,5,+1,append_check>::EXEC(); wait(); typedef LIST,LIST > > A; typedef LIST,LIST > > B; PRINT::EXEC(); cout << endl; PRINT::EXEC(); cout << endl; PRINT::RET,ostream>::EXEC(cerr); cout << endl; cout << "*CONSP*" << endl; FOR<0,Less,5,+1,consp_check>::EXEC(); wait(); typedef NUMBERT<6> n1; typedef NUMBERT<2> n2; typedef NUMBERT<24> n3; cout << "6 + (2 + 24) = "; PRINT::RET>::RET>::EXEC(); cout << endl; cout << "24 - (6 - 1) = "; PRINT::RET>::RET>::EXEC(); cout << endl; cout << "6 * (2 * 24) = "; PRINT::RET>::RET>::EXEC(); cout << endl; cout << "24 / (6 / 2) = "; PRINT::RET>::RET>::EXEC(); cout << endl; typedef LIST,LIST, LIST,LIST > > > > part_1; typedef LIST,LIST > > part_2; typedef APPEND::RET all_types; PRINT::EXEC(); cout << " + "; PRINT::EXEC(); cout << " -> "; PRINT::EXEC(); cout << endl; return 0; }