Created
October 8, 2017 17:47
-
-
Save eush77/29a82560ec19a666257eb1a8266a9fd0 to your computer and use it in GitHub Desktop.
Toy interpreter using libCello https://github.com/orangeduck/Cello
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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