Tuesday, November 15

Implementing Lisp in C++

I've been spending some time implementing a small Lisp for embedding and bootstrapping in C++, somewhat in the spirit of Scheme from Scratch.

The brief I've given myself is a bit contradictory in that I'm eschewing the use of virtual functions (as they make linking C to C++ more complicated) but, for now I'm still allowing boost as a dependency.

It turns out that Boost is highly useful for implementing polymorphic objects, since it contains variant - which can be used instead of the usual tagged unions.

To implement the basic Lisp Object I've created a template that implements the properties of the lisp object (copyable, assignable, and freely convertible to a base C type) and created a boost variant of all the different specialisations of this template which is the base class of our object.

This template should save writing boatloads of boilerplate code when contrasted with C and it should be much more easy to plug in new types. This has to be a win over C. The other big advantage (as noted before when I wrote the simpler Lisp) is that shared_ptr gives reference counted garbage collection more - or - less for free.

As usual, here's the code.

#ifndef H_LISP_OBJ
#define H_LISP_OBJ
#include <cassert>
#include <ostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <boost/variant.hpp>
#include <boost/shared_ptr.hpp>
namespace Lisp {
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
typedef signed long s64;
typedef signed long long s128;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long u64;
typedef unsigned long long u128;
typedef float f32;
typedef double f64;
// forward declaration
class LispObj;
typedef boost::shared_ptr<LispObj> LispObjRef;
typedef s32 CFixnum;
typedef u8 CChar;
typedef std::string CString;
typedef f32 CFloatnum;
// placeholder type
class NullType {
private:
const u32 dead_;
public:
NullType() : dead_(0xDEADBEEF) {
}
};
/* This is a template for an unboxed type that can be represented by a simple scalar */
template <typename T> class TLispType {
private:
T data_;
public:
TLispType() {
}
explicit TLispType(const T& other) : data_(other) {
}
T& operator=(const T& other) {
data_ = other;
}
TLispType(const TLispType<T>& other) : data_(other.data_) {
}
T& operator=(const TLispType<T>& other) {
data_ = other.data_;
return *this;
}
// unboxed types can be freely converted to C++ type
operator T() {
return data_;
}
};
/**
* LispPrimitive is a pointer to a function that returns a reference to
* a lisp obj and takes in a lisp obj.
*/
typedef LispObjRef (*CPrim)(LispObjRef args);
typedef std::pair< std::string, LispObjRef > LispSymbol;
/* byte */
typedef TLispType<CChar> CharType;
/* fixnum */
typedef TLispType<CFixnum> FixnumType;
/* float */
typedef TLispType<CFloatnum> FloatnumType;
/* string */
typedef TLispType< CString > StringType;
/* symbol */
typedef TLispType< LispSymbol > SymbolType;
/* cons cell */
typedef std::pair< LispObjRef, LispObjRef > CCons;
typedef TLispType< CCons > ConsType;
/** primitive */
typedef TLispType< CPrim > PrimType;
enum LispObjectType {
NIL = 0,
CHAR,
FIXNUM,
FLOATNUM,
SYMBOL,
STRING,
CONS,
PRIM
};
typedef boost::variant< NullType, CharType, FixnumType, FloatnumType, SymbolType, StringType, ConsType, PrimType > LispObjBase;
class LispObj : public LispObjBase {
public:
LispObj() : LispObjBase() {
}
LispObj(const FixnumType& fnum) : LispObjBase(fnum) {
}
LispObj(const FloatnumType& fnum) : LispObjBase(fnum) {
}
LispObj(const CharType& ch) : LispObjBase(ch) {
}
LispObj(const ConsType& cons) : LispObjBase(cons) {
}
LispObj(const StringType& string) : LispObjBase(string) {
}
LispObj(const SymbolType& symbol) : LispObjBase(symbol) {
}
LispObj(const PrimType& primitive) : LispObjBase(primitive) {
}
};
} // end namespace lisp
#endif
view raw LispObj.h hosted with ❤ by GitHub