Tagless final interp cpp
C++
// From https://i.cs.hku.hk/~bruno/oa/
#include <iostream>
#include <string>
#include <memory>
using namespace std;
/*
* This program has used some C++11 features to get rid of manual memory management.
*
* If you prefer the older C++ version, do the follwing steps:
* 1. Switch all "EvalPtr" to "Eval *"; do the same to "PPrintPtr"
* WARNING: you need to do some additional cleanup for those newed objects
* 2. Replace "make_shared<Closure>" with "new Closure"
* 3. Substitude "to_string" to some other ways of converting int to string
*/
// Initial object algebra interface for expressions: integers and addition
template <typename E>
class ExpAlg
{
public:
virtual E lit(int x) = 0;
virtual E add(E e1, E e2) = 0;
};
// An object algebra implementing that interface (evaluation)
// The evaluation interface
class Eval
{
public:
virtual int eval() = 0;
};
typedef shared_ptr<Eval> EvalPtr;
class EvalLit : public Eval {
public:
EvalLit(int x) : _x(x) {}
virtual int eval() {
return _x;
}
private:
int _x;
};
class EvalAdd : public Eval {
public:
EvalAdd(EvalPtr l, EvalPtr r) : _l(l), _r(r) {}
virtual int eval() {
return _l->eval() + _r->eval();
}
private:
EvalPtr _l;
EvalPtr _r;
};
// The object algebra
class EvalExpAlg : virtual public ExpAlg<EvalPtr>
{
public:
virtual EvalPtr lit(int x) {
return make_shared<EvalLit>(x);
}
virtual EvalPtr add(EvalPtr e1, EvalPtr e2) {
return make_shared<EvalAdd>(e1, e2);
}
};
// Evolution 1: Adding subtraction
template<typename E>
class SubExpAlg : virtual public ExpAlg<E>
{
public:
virtual E sub(E e1, E e2) = 0;
};
class EvalSub : public Eval {
public:
EvalSub(EvalPtr l, EvalPtr r)
: _l(l), _r(r) {}
int eval() {
return _l->eval() - _r->eval();
}
private:
EvalPtr _l;
EvalPtr _r;
};
// Updating evaluation:
class EvalSubExpAlg : public EvalExpAlg, public SubExpAlg<EvalPtr>
{
public:
virtual EvalPtr sub(EvalPtr e1, EvalPtr e2) {
return make_shared<EvalSub>(e1, e2);
}
};
// Evolution 2: Adding pretty printing
class PPrint
{
public:
virtual string print() = 0;
};
typedef shared_ptr<PPrint> PPrintPtr;
class PrintLit : public PPrint
{
public:
PrintLit(int x) : _x(x) {}
virtual string print() {
return to_string(_x);
}
private:
int _x;
};
class PrintAdd : public PPrint
{
public:
PrintAdd(PPrintPtr e1, PPrintPtr e2)
: _e1(e1), _e2(e2) {}
virtual string print() {
return _e1->print() + " + " + _e2->print();
}
private:
PPrintPtr _e1;
PPrintPtr _e2;
};
class PrintSub : public PPrint
{
public:
PrintSub(PPrintPtr e1, PPrintPtr e2)
: _e1(e1), _e2(e2) {}
virtual string print() {
return _e1->print() + " - " + _e2->print();
}
private:
PPrintPtr _e1;
PPrintPtr _e2;
};
class PrintExpAlg : virtual public SubExpAlg<PPrintPtr>
{
public:
virtual PPrintPtr lit(int x) {
return make_shared<PrintLit>(x);
}
virtual PPrintPtr add(PPrintPtr e1, PPrintPtr e2) {
return make_shared<PrintAdd>(e1, e2);
}
virtual PPrintPtr sub(PPrintPtr e1, PPrintPtr e2) {
return make_shared<PrintSub>(e1, e2);
}
};
// An alternative object algebra for pretty printing
class PrintExpAlg2 : virtual public SubExpAlg<string>
{
public:
virtual string lit(int x) {
return to_string(x);
}
virtual string add(string e1, string e2) {
return e1 + " + " + e2;
}
virtual string sub(string e1, string e2) {
return e1 + " - " + e2;
}
};
// Testing
// An expression using the basic ExpAlg
template<typename E>
E exp1(ExpAlg<E>& alg) {
return alg.add(alg.lit(3), alg.lit(4));
}
// An expression using subtraction too
template<typename E>
E exp2(SubExpAlg<E>& alg) {
return alg.sub(exp1(alg), alg.lit(4));
}
int main() {
// Some object algebras:
EvalExpAlg ea;
EvalSubExpAlg esa;
PrintExpAlg pa;
PrintExpAlg2 pa2;
EvalPtr ev = exp1(esa);
// But calling ea with exp2 is an error
// EvalPtr ev_bad = exp2(ea);
cout << "Evaluation of exp1 \"" << exp1(pa)->print() << "\" is: " << ev->eval() << endl;
cout << "Evaluation of exp2 \"" << exp2(pa)->print() << "\" is: " << exp2(esa)->eval() << endl;
cout << "The alternative pretty printer works nicely too!\n"
<< "exp1: " << exp1(pa2) << "\n"
<< "exp2: " << exp2(pa2);
}