package interpreter; /////////////////////////////////////////////////////////////////////////////// // // Symbolic expressions // /////////////////////////////////////////////////////////////////////////////// public abstract class Expr<T> { // Do one step of evaluation public step(env: Env<T>): Expr<T>; // Perform a full step-by-step evaluation public eval(env: Env<T>): (); // Variable constructor (generic) public static variable<U>(name: String): VarExpr<U>; // Constant constructor (generic) public static constant<U>(value: U): CstExpr<U>; // Sum constructor (floats) public static plus<U: float>(e1: Expr<U>, e2: Expr<U>): Expr<U>; // Multiplication constructor (floats) public static mult<U: float>(e1: Expr<U>, e2: Expr<U>): Expr<U>; // Concatenatio constructor (strings) public static concat(e1: Expr<String>, e2: Expr<String>): Expr<String>; // Return true if the expression is evaluated (i.e., is a constant) evaluated(): boolean; } /////////////////////////////////////////////////////////////////////////////// // // Implementation // /////////////////////////////////////////////////////////////////////////////// // Constant expressions (visible only at the package level) class CstExpr<T> extends Expr<T> { value: T; } // Function applications (visible only at the package level) class AppExpr<T> extends Expr<T> { f: Functor<T>; args: Expr<T>[f.arity]; } // Variables (visible only at the package level) class VarExpr<T> extends Expr<T> { name: String; } // Functors (visible only at the package level) class Functor<T> { name: String; arity: int; eval: fun(CstExpr<T>[]): CstExpr<T>; } // Generic display of expressions toString@VarExpr() = name; toString@Functor() = name; toString@CstExpr() = (value instanceof String ? format("\"%a\"", value) : format("%a", value)); toString@AppExpr() = format("%a(%s)", f, str[f.arity]) { str = ["", args[0].toString(), i -> format("%s, %a", str[i-1], args[i-1])]; } // Evaluated expressions evaluated@Expr() = (this instanceof CstExpr); // Perform one step of evaluation step@CstExpr<T>(env) = this; step@VarExpr<T>(env) = env.lookup(name); step@AppExpr<T>(env) = result { // Number of arguments n = f.arity; // Checks that the arguments args[0],..., args[i-1] are evaluated evaluated = [true, i -> evaluated[i-1] && args[i-1].evaluated()]; if (evaluated[n]) { // Apply the functor operation on the evaluated arguments var args' = [i -> (CstExpr) args[i]]; result = f.eval(args': CstExpr<T>[n]); } else { // Evaluate the first non-evaluated argument var args' = [i -> (evaluated[i] && !args[i].evaluated() ? args[i].step(env) : args[i])]; result = clone(args = args': Expr<T>[n]); } } // Full step-by-step evaluation eval@Expr<T>(env) = () { @ print("{%a}\n -> %a\n%s\n", env, this, eval(this)); fun eval(x: Expr<T>) = (x'.evaluated() ? format(" -> %a", x') : format(" -> %a\n%s", x', eval(x'))) { x' = x.step(env); } } // Variable constructor (generic) Expr.variable(name) = new VarExpr(name = name); // Constant constructor (generic) Expr.constant(value) = new CstExpr(value = value); // Expression constructors (floats) Expr.plus(e1, e2) = new AppExpr(f = f, args = [e1, e2]) { f = new Functor(name = "plus", arity = 2, eval = eval); fun eval(args) = Expr.constant(args[0].value + args[1].value); } Expr.mult(e1, e2) = new AppExpr(f = f, args = [e1, e2]) { f = new Functor(name = "mult", arity = 2, eval = eval); fun eval(args) = Expr.constant(args[0].value * args[1].value); } // Expression constructors (strings) Expr.concat(e1, e2) = new AppExpr(f = f, args = [e1, e2]) { f = new Functor(name = "concat", arity = 2, eval = eval); fun eval(args) = Expr.constant(format("%s%s", args[0].value, args[1].value)); }