/* * Lambda Calculus Interpreter in the C++ Template System * * Copyright 2007, K.D.P.Ross * * 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 * */ // {{{ C++ include nonsense #include #include #include using namespace std ; // }}} // {{{ Interpreter // {{{ Program representations /* Peano Integers */ struct Zero {}; template struct Succ {}; /* characters */ template struct Char {}; /* polymorphic lists */ struct Nil {}; template struct Cons { typedef X head ; typedef Xs tail ;}; /* abstract syntax trees */ /* T1 + T2 */ template struct Sum {}; template struct Cat {}; template struct Var {}; /* lambda X . T */ template struct Abst {}; /* T1 T2 */ template struct Appl {}; /* let X = T1 in T2 */ template struct Let {}; /* bools */ struct True {}; struct False {}; /* X == Y */ template struct Eq {}; template struct Eval {}; /* if C then T else E */ template struct If {}; /* pred X */ template struct Pred {}; // }}} // {{{ Utility / helper functions /* type equality */ template struct eq_types {}; template struct eq_types { typedef X res ;}; /* Peano addition */ template struct PrimAdd {}; template <> struct PrimAdd { typedef Zero res ;}; template struct PrimAdd , Zero> { typedef Succ res ;}; template struct PrimAdd > { typedef Succ res ;}; template struct PrimAdd , Succ > { typedef typename PrimAdd >, Z2> :: res res ;}; /* String concatenation */ template struct PrimCatHelp {}; template struct PrimCatHelp { typedef S res ;}; template struct PrimCatHelp , S1>, S2> { typedef typename PrimCatHelp , S2> > :: res res ;}; template struct RevHelp {}; template struct RevHelp { typedef S res ;}; template struct RevHelp , S1>, S2> { typedef typename RevHelp , S2> > :: res res ;}; template struct Rev { typedef typename RevHelp :: res res ;}; /* reverse the first argument, then copy it over */ template struct PrimCat { typedef typename PrimCatHelp :: res, S2> :: res res ;}; /* user-level equality (False on failure) */ template struct PrimEq { typedef False res ;}; template struct PrimEq { typedef True res ;}; /* Peano predecessor */ template struct PrimPred {}; template struct PrimPred > { typedef X res ;}; /* not sure if we want this, but it's the conventional definition */ /* template <> struct PrimPred { typedef Zero res ;}; */ // }}} // {{{ Evaluator /* Nil in Val */ template struct Eval { typedef Nil res ;}; /* Char[C] in Val */ template struct Eval > { typedef Char res ;}; /* Zero in Val */ template struct Eval { typedef Zero res ;}; /* True in Val */ template struct Eval { typedef True res ;}; /* False in Val */ template struct Eval { typedef False res ;}; /* Env |- Z ==> V ----------------------------------- Env |- Succ ==> Succ */ template struct Eval > { typedef Succ :: res> res ;}; /* Env |- T1 ==> V1 Env |- T2 ==> V2 ---------------------------------- Env |- T1 + T2 ==> V1 V2 */ template struct Eval > { typedef typename PrimAdd :: res, typename Eval :: res> :: res res ;}; /* Env |- T1 ==> V1 Env |- T2 ==> V2 ---------------------------------- Env |- T1 ^ T2 ==> V1 V2 */ template struct Eval > { typedef typename PrimCat :: res, typename Eval :: res> :: res res ;}; template struct Eval > {}; /* ---------------------- Env, |- X ==> T */ template struct Eval , T>, Es>, Var > { typedef T res ;}; /* Env |- X ==> T' ----------------------- Env, |- X ==> T' */ template struct Eval , Var > { typedef typename Eval > :: res res ;}; /* Env |- T1 ==> V1 Env |- T2 ==> V2 Env |- V1 V2 ==> V ------------------------------------------------------ Env |- T1 T2 ==> V */ template struct Eval > { typedef typename Eval , Eval > > :: res res ;}; /* Env |- T2 ==> V2 Env, |- T1 ==> V ------------------------------------------ Env |- (\ X . T1) T2 ==> V */ template struct Eval , T2> > { typedef typename Eval , typename Eval :: res>, E>, T1> :: res res ;}; /* Env |- (\ X . T2) T1 ==> V ----------------------------- Env |- Let X = T1 in T2 ==> V */ template struct Eval > { typedef typename Eval , T1> > :: res res ;}; /* Env |- X ==> V Env |- Xs ==> Vs -------------------------------- Env |- X :: Xs ==> V :: Vs */ template struct Eval > { typedef Cons :: res, typename Eval :: res> res ;}; /* Env |- T1 ==> V1 Env |- T2 ==> V2 ---------------------------------- Env |- T1 == T2 ==> V1 V2 */ template struct Eval > { typedef typename PrimEq :: res, typename Eval :: res> :: res res ;}; // Don't like this much -KDPR /* Env |- T2 ==> V2 ------------------------------------- Env |- If True then T2 else T3 ==> V2 */ template struct Eval > { typedef typename Eval :: res res ;}; /* Env |- T3 ==> V3 ------------------------------------- Env |- If False then T2 else T3 ==> V3 */ template struct Eval > { typedef typename Eval :: res res ;}; /* Env |- T1 ==> V1 Env |- If V1 then T2 else T3 ==> V ---------------------------------------------------- Env |- If T1 then T2 else T3 ==> V */ template struct Eval > { typedef typename Eval :: res, T2, T3> > :: res res ;}; /* Env |- T ==> Succ (V) --------------------- Env |- Pred (T) ==> V */ template struct Eval > { typedef typename PrimPred :: res> :: res res ;}; // }}} // }}} // {{{ Conversions to run-time values // {{{ Mapping between compile-time values and run-time types /* We want to be able to take our compile-time-computed values and convert them to "appropriate" run-time values. To do this in a somewhat-generalisable way (e.g., to define how to convert a compile-time list-of-T to a run-time list-of- in general, assuming we can convert a T). We'll define a traits class where we'll define the mapping between a compile-time value and the run-time type into which it will be projected. */ template struct type_traits {}; template <> struct type_traits { typedef unsigned int rtt ;}; template struct type_traits > { typedef unsigned int rtt ;}; /* have to pick an arbitrary type for [] */ template <> struct type_traits { typedef list rtt ;}; /* convert list to correct run-time list */ template struct type_traits > { typedef list :: rtt> rtt ;}; template struct type_traits > { typedef list :: rtt, typename type_traits :: rtt :: value_type> :: res> rtt ;}; /* but ... convert char lists to run-time strings */ template struct type_traits , Nil> > { typedef string rtt ;}; template struct type_traits , Xs> > { typedef typename eq_types :: rtt> :: res rtt ;}; template struct type_traits > { typedef char rtt ;}; template <> struct type_traits { typedef bool rtt ;}; template <> struct type_traits { typedef bool rtt ;}; // }}} // {{{ Mapping between compile-time values and run-time values /* While type_traits encodes the mapping between a(n untyped) compile-time value and its run-time type, reflect defines the mapping between a compile-time value and a run-time value. The user-level idiom to do this is: // runs our program typedef {{compile-time program}} prog ; // reflects the result of doing so to a run-time value of the "right" type type_traits :: rtt val = reflect () ; */ template typename type_traits :: rtt reflect () ; template struct reflect_help {}; /* rtt Nat = unsigned int */ template <> struct reflect_help { static unsigned int doit () { return 0 ;}}; template struct reflect_help > { static unsigned int doit () { return 1 + reflect () ;}}; /* rtt Char = char */ template struct reflect_help > { static char doit () { return C ;}}; /* rtt [Char] = string */ template struct reflect_help , Nil> > { static string doit () { string tail = " " ; tail [0] = C ; return tail ;}}; template struct reflect_help , Xs> > { static string doit () { string tail = " " + reflect () ; tail [0] = C ; return tail ;}}; /* rtt [T] = list ... we can, of course, use untyped lists, but we have no way to reflect them at run-time */ template struct reflect_help > { static list :: rtt> doit () { typedef typename type_traits :: rtt value_type ; const value_type head = reflect () ; list tail ; tail . push_back (head) ; return tail ;}}; template struct reflect_help > { static typename type_traits > :: rtt doit () { typedef typename type_traits :: rtt value_type ; const value_type head = reflect () ; list tail = reflect () ; tail . push_front (head) ; return tail ;}}; /* rtt [\A . A] = list */ template <> struct reflect_help { static type_traits :: rtt doit () { type_traits :: rtt tail ; return tail ;}}; /* rtt Bool = bool */ template <> struct reflect_help { static type_traits :: rtt doit () { return true ;}}; template <> struct reflect_help { static type_traits :: rtt doit () { return false ;}}; /* user can call as a function rather than dealing with typename and :: */ template typename type_traits :: rtt reflect () { return reflect_help :: doit () ;} // }}} // }}} // {{{ Some functions for convenience /* why aren't lists printable? */ template ostream & operator<< (ostream & out, list xs) { out << '[' ; typename list :: iterator i = xs . begin () ; if (i != xs . end ()) out << * (i ++) ; while (i != xs .end ()) out << ", " << * (i ++) ; return out << ']' ;} /* a bit easier to call, again hides some of the implementation details */ template typename type_traits :: res> :: rtt eval () { return reflect :: res> () ;} template struct eval_type { typedef typename type_traits :: res> :: rtt rtt ;}; // }}} /* some little tests */ int main () { // 'h' : 'e' : 'l' : 'l' : 'o' : [] ==> "hello" cout << eval , Cons , Cons , Cons , Cons , Nil> > > > > > () << endl ; // 2 + 3 ==> 5 cout << eval >, Succ > > > > () << endl ; // (\x . x) (1 + 1) ==> 2 cout << eval >, Sum , Succ > > > () << endl ; // let x = 2 + 2 in x + x ==> 8 cout << eval >, Succ > >, Sum , Var <1u> > > > () << endl ; // ['h', 'e', 'l', 'l', 'o'] ++ ['h', 'e', 'l', 'l', 'o'] ==> "hellohello" cout << eval , Cons , Cons , Cons , Cons , Nil> > > > >, Cat , Var <1u> > > > () << endl ; // (1 + 1) : (pred (2 + 1)) : [] ==> [2, 2] cout << eval , Succ >, Cons >, Succ > >, Nil> > > () << endl ; // if 0 == 1 then False else 1 + 1 ==> 2 cout << eval >, False, Sum , Succ > > > () << endl ; // [] ==> [] cout << eval () << endl ; return EXIT_SUCCESS ;}