package edu.neu.ccs.demeterf.examples;
import edu.neu.ccs.demeterf.*;

/** Expression Eval Test. We evaluate simple expressions, find the 
 *    max nested depth, and do a few simple transformations on the Exps */
public class EvalTest{
    static public void main(String[] args){
        Exp e = new Bin(new Div(), new Bin(new Plus(), new Bin(new Plus(),
                new Bin(new Times(),new Num(9),new Num(6)),new Num(8)),
                new Bin(new Minus(),new Num(5),new Num(7))),new Num(4));
        System.out.println("  Before: "+e);
        System.out.println("  AddOne: "+new Traversal(new AddOne()).traverse(e));
        System.out.println("    Eval: "+new Traversal(new Eval()).traverse(e));
        System.out.println(" Num2Str: "+new Traversal(new Num2Str()).traverse(e));
        System.out.println("   Depth: "+new Traversal(new Depth()).traverse(e));

    // Adds one to each leaf, and reconstruct the Tree
    static class AddOne extends Bc{ int combine(int i){ return i+1; } }

    // Evaluates the Tree, 'Unifies' to an int
    static class Eval extends ID{
        int combine(Num n, int i){ return i; }
        Op combine(Op o){ return o; }
        int combine(Bin b, Plus p, int l, int r){ return l+r; }
        int combine(Bin b, Minus p, int l, int r){ return l-r; }
        int combine(Bin b, Times p, int l, int r){ return l*r; }
        int combine(Bin b, Div p, int l, int r){ return l/r; }

    // Calculated the Max depth of the Tree
    static class Depth extends ID{
        Op combine(Op o){ return o; }
        int combine(Exp u, int i){ return 0; }
        int combine(Exp b, Op o, int l, int r){ return 1+Math.max(l,r); }

    // Converts a Number Expression into a String + Depth
    static class Num2Str extends Bc{
        Str combine(Num n, int i){ return new Str(""+n); }    

    // Simple Arithmetic Expressions
    static abstract class Exp{
        public String toString(){
            return new Traversal(new ID(){
                String combine(Num n, int i){ return ""+i; }
                String combine(Bin b, String op, String l, String r){ return "("+l+" "+op+" "+r+")"; }
                String combine(Plus p){ return "+"; }
                String combine(Minus p){ return "-"; }
                String combine(Times p){ return "*"; }
                String combine(Div p){ return "/"; }
                String combine(Str se, String s){ return "\""+s+"\""; }

    // Integer Literals
    static class Num extends Exp{
        int num;
        Num(int i){ num = i; } 
        public String toString(){ return ""+num; }
    // A form of String Literals
    static class Str extends Exp{
        String str;
        Str(String s){ str = s; } 
    // The usual operators for Binary Expressions... 
    static class Bin extends Exp{
        Op op; 
        Exp left, right;
        Bin(Op o, Exp l, Exp r){ op = o; left = l; right = r; }
    static abstract class Op{}
    static class Plus extends Op{}
    static class Minus extends Op{}
    static class Times extends Op{}
    static class Div extends Op{}