I would like to construct a simple parser/typechecker/evaluator for the simply typed lambda calculus. I've already implemented such in Rust, but as someone new to C++, I'm unsure what the best way to represent the AST in. As an example, in Rust, I can define a recursive sum type. I can then use pattern matching or the Visitor pattern to visit subterms.
enum Term {
// de Bruijn index
Var(usize),
// Lambda abstraction x: T. t
Abs(Box<Term>, Box<Term>),
// Application t1 t2
App(Box<Term>, Box<Term>),
}
fn ex(term: &mut Term) {
match term {
Term::Var(idx) => {
},
Term::Abs(ty, tm) => {
ex(tm.as_mut());
},
Term::App(t1, t2) => {
// do something
}
}
}
Without sum types, it seems like there are 2 primary ways to represent something similar in C++: an enum/union pair (which I would use in C), or a class hierarchy with the visitor pattern (full implementation not shown)
class Expr {
public:
virtual void visit(Visitor &v) const = 0;
virtual ~Expr() {}
};
class Var : public Expr {
public:
Var(int idx) : debruijn{idx} {}
private:
int debruijn;
};
class App : public Expr {
public:
App(std::unique_ptr<Expr> t1, std::unique_ptr<Expr> t2)
: e1{std::move(t1)}, e2{std::move(t2)} {}
private:
std::unique_ptr<Expr> e1;
std::unique_ptr<Expr> e2;
};
class Abs : public Expr {
public:
Abs(Type ty, std::unique_ptr<Expr> e) : e{std::move(e)}, ty{ty} {}
private:
Type ty;
std::unique_ptr<Expr> e;
};
Is this the best way to do it? I would like to start off my C++ learning by writing code in the most modern and idiomatic style.