Skip to content

Instantly share code, notes, and snippets.

@eush77
Created October 8, 2017 17:47
Show Gist options
  • Save eush77/29a82560ec19a666257eb1a8266a9fd0 to your computer and use it in GitHub Desktop.
Save eush77/29a82560ec19a666257eb1a8266a9fd0 to your computer and use it in GitHub Desktop.
Toy interpreter using libCello https://github.com/orangeduck/Cello
#include <Cello.h>
/*
* `Eval' is a type class that describes a node that can be evaluated.
*/
struct Eval {
var (*eval)(var);
};
static var Eval = Cello(Eval);
// Polymorphic function.
static var eval (var ast) {
return method(ast, Eval, eval);
}
/*
* `Const' is a node that represents a complex numeric constant and evaluates
* to itself.
*/
struct Const {
var real;
var imag;
};
static void Const_Assign (var self, var obj) {
struct Const *s = self;
struct Const *o = obj;
s->real = copy(o->real);
s->imag = copy(o->imag);
}
static int Const_Show (var self, var output, int pos) {
const double epsilon = 1e-14;
struct Const *s = self;
double real = c_float(s->real);
double imag = c_float(s->imag);
if (fabs(imag) < epsilon) {
return print_to(output, pos, "Const(%f)", $F(real));
}
else if (fabs(real) < epsilon) {
return print_to(output, pos, "Const(%f * I)", $F(imag));
}
else {
return print_to(output, pos, "Const(%f + %f * I)", $F(real), $F(imag));
}
}
static var Const_Eval (var self) {
return self;
}
static var Const = Cello(
Const,
Instance(Assign, Const_Assign),
Instance(Show, Const_Show),
Instance(Eval, Const_Eval));
/*
* `Add' is a node that performs addition.
*/
struct Add {
var left;
var right;
};
static void Add_Assign (var self, var obj) {
struct Add *s = self;
struct Add *o = obj;
s->left = copy(o->left);
s->right = copy(o->right);
}
static int Add_Show (var self, var output, int pos) {
struct Add *s = self;
return print_to(output, pos, "Add(%$, %$)", s->left, s->right);
}
static var Add_Eval (var self) {
struct Add *s = self;
struct Const *left = eval(s->left);
struct Const *right = eval(s->right);
return new(Const, $(Const,
$F(c_float(left->real) + c_float(right->real)),
$F(c_float(left->imag) + c_float(right->imag))));
}
static var Add = Cello(
Add,
Instance(Assign, Add_Assign),
Instance(Show, Add_Show),
Instance(Eval, Add_Eval));
/*
* `Mul' is a node that performs multiplication.
*/
struct Mul {
var left;
var right;
};
static void Mul_Assign (var self, var obj) {
struct Mul *s = self;
struct Mul *o = obj;
s->left = copy(o->left);
s->right = copy(o->right);
}
static int Mul_Show (var self, var output, int pos) {
struct Mul *s = self;
return print_to(output, pos, "Mul(%$, %$)", s->left, s->right);
}
static var Mul_Eval (var self) {
struct Mul *s = self;
struct Const *left = eval(s->left);
struct Const *right = eval(s->right);
double left_r = c_float(left->real);
double left_i = c_float(left->imag);
double right_r = c_float(right->real);
double right_i = c_float(right->imag);
return new(Const, $(Const,
$F(left_r * right_r - left_i * right_i),
$F(left_r * right_i + left_i * right_r)));
}
static var Mul = Cello(
Mul,
Instance(Assign, Mul_Assign),
Instance(Show, Mul_Show),
Instance(Eval, Mul_Eval));
/*
* `Div' is a node that performs division by a real factor.
*/
struct Div {
var left;
var right;
};
static void Div_Assign (var self, var obj) {
struct Div *s = self;
struct Div *o = obj;
s->left = copy(o->left);
s->right = copy(o->right);
}
static int Div_Show (var self, var output, int pos) {
struct Div *s = self;
return print_to(output, pos, "Div(%$, %$)", s->left, s->right);
}
static var Div_Eval (var self) {
struct Div *s = self;
struct Const *left = eval(s->left);
struct Float *right = s->right;
return new(Const, $(Const,
$F(c_float(left->real) / c_float(right)),
$F(c_float(left->imag) / c_float(right))));
}
static var Div = Cello(
Div,
Instance(Assign, Div_Assign),
Instance(Show, Div_Show),
Instance(Eval, Div_Eval));
/*
* `Exp' is a node that performs (natural-base) exponentiation.
*/
struct Exp {
var power;
};
static void Exp_Assign (var self, var obj) {
struct Exp *s = self;
struct Exp *o = obj;
s->power = copy(o->power);
}
static int Exp_Show (var self, var output, int pos) {
return print_to(output, pos, "Exp(%$)", ((struct Exp*) self)->power);
}
static var Exp_Eval (var self) {
enum {
num_terms = 30,
};
var arg = ((struct Exp*) self)->power;
var term = new(Const, $(Const, $F(1), $F(0)));
var sum = term;
// Build up a partial sum of the exponential series:
// 1 + x/1! + x^2/2! + x^3/3! + x^4/4! + ...
for (unsigned termno = 1; termno < num_terms; ++termno) {
term = new(Div, $(Div, $(Mul, term, arg), $F(termno)));
sum = new(Add, $(Add, sum, term));
}
return eval(sum);
}
static var Exp = Cello(
Exp,
Instance(Assign, Exp_Assign),
Instance(Show, Exp_Show),
Instance(Eval, Exp_Eval));
/*
* Construct and evaluate the AST showing the Euler's identity using the nodes
* above.
*/
int main () {
var ast = $(Exp, $(Const, $F(0), $F(M_PI)));
println("eval(%$) = %$", ast, eval(ast));
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment