
Pairs
The Pair class is the result of a cons operation. The object has
two pointers to Evaluable objects, the car and the cdr,
and methods to extract them.
//*****************************************************************************
//*** Pair
//***
//*** The only Lisp compound data structure. It's a car and cdr pointer,
//*** with needed methods. The list is evaluable, which means to treat
//*** as a function call.
//*****************************************************************************
#include <iostream>
using namespace std;
#include "refct.h"
#include "atoms.h"
#include "context.h"
#include "evaluable.h"
#ifndef _pair_h_
#define _pair_h_
/* This is the pair (result of cons). It represents a non-empty list. */
class Pair: public Evaluable {
// The contents of a pair are pointers to two other things.
Ptr<Evaluable> _car, _cdr;
private:
// Create. The public alloc() provides defaults of nil.
Pair(Ptr<Evaluable> cd, Ptr<Evaluable> cr):
_car(cd), _cdr(cr) { }
public:
// Allocate a pair, return a (smart) pointer.
static RefPtr alloc(Ptr<Evaluable> cd = the_nil,
Ptr<Evaluable> cr = the_nil) {
return RefCtObj::newRefPtr(new Pair(cd, cr));
}
// This evaluates the pair as a (func args), if possible. Will
// return an error atom if something goes wrong.
Ptr<Evaluable> eval(Ptr<Context> c);
// Print the list. The two-argument form is essentially a helper
// function for the regular print. Listmode indicates the pair
// should be printed as a list, otherwise it may be printed as a
// dotted pair, though this can change to listmode.
virtual void print(ostream &s, bool listmode);
virtual void print(ostream &s) { print(s, false); }
// Names should be short, even when printing is long.
virtual string name() { return "A Pair"; }
// The parts of the pair.
virtual Ptr<Evaluable> car() { return _car; }
virtual Ptr<Evaluable> cdr() { return _cdr; }
// Equality means both sub-pointers point to equal things.
virtual bool equal_same(RefCtObj& other) {
return _car.same_value(((Pair *)&other)->_car) &&
_cdr.same_value(((Pair *)&other)->_cdr);
}
};
#endif
#include "func.h"
#include "pair.h"
/* Function evaluation. Pair must be a function and an argument list. This
takes a list like (f x1 x2 x3 ... ), separates f, then applies
(x1 x2 x3 ... ) to f. Note that if eval gets an error from any of the
evaluations or applies, it returns that error, but it adds trace information
to its history. */
Ptr<Evaluable> Pair::eval(Ptr<Context> c)
{
// Eval the car (the function to run). If this fails, add the
// form which we were trying to evaluate, and pass the error on
// up the stack.
Ptr<Evaluable> func = car()->eval(c);
if(func.points_to(typeid(Error)))
return Ptr<Error>(func)->hist(this->newref());
// Check that the function is really an applyable thing (such as the
// the car builtin or a Closure, rather than a string or integer.)
// If not, create an error here to reaturn.
if(!func->applyable())
return Error::alloc(BADOP,
"Attempt to apply on " + func->name(),
newref());
// Apply the arguments to the function. If this fails, we use
// histinc() to add the name of whatever we were were applying
// to the history. We add that here because closures don't know
// their own names (they're bound in the context, not contained
// in the objects). We let apply add the arguments themselves to
// the history so we can get the evaluated versions. That makes
// a more useful trace, usually.
Ptr<Evaluable> e = Ptr<Applyable>(func)->apply(cdr(), c);
if(e.points_to(typeid(Error)))
Ptr<Error>(e)->histinc(car());
return e;
}