#ifndef AST_HPP #define AST_HPP #include #include // ast stuff lul struct Expr { virtual ~Expr() = default; virtual std::unique_ptr clone() const = 0; }; // represents a simple symbol like "x" or even "thisIsACoolFunctionTrustme100" // (ikik the true lambda calculus professionals would now scream at me and say // "lambda calculus has only one letter variables!", to which id say ¯\_(ツ)_/¯ struct Variable : Expr { std::string name; Variable(std::string n) : name(std::move(n)) {} std::unique_ptr clone() const override { return std::make_unique(name); } }; // represents a function itself like \ x . x struct Abstraction : Expr { std::string param; std::unique_ptr body; Abstraction(std::string p, std::unique_ptr b) : param(std::move(p)), body(std::move(b)) {} std::unique_ptr clone() const override { return std::make_unique(param, body->clone()); } }; // wouldnt be turing complete if we couldnt apply to these functions innit // (\ x . x) a would be such an application // note that parenthesis are your savior because (\ x y . x) (\ x . x) b and // (\ x y . x) ((\ x . x) b ) are not the same thing struct Application : Expr { std::unique_ptr left, right; Application(std::unique_ptr l, std::unique_ptr r) : left(std::move(l)), right(std::move(r)) {} std::unique_ptr clone() const override { return std::make_unique(left->clone(), right->clone()); } }; #endif // AST_HPP