// Author: Gabriel J. Ferrer
// This program is in the public domain.
// BoolExprEval uses BoolExprParser.java to parse simple boolean expressions

import java.util.*;
import java.io.*;
import squirrel.*;

public class BoolExprEval {
    private BoolExprParser bep;
    private Map<String,Boolean> symbols;
    private Set<String> reservedWords;
    
    public BoolExprEval() {
        bep = new BoolExprParser();
        //bep.debug();
        symbols = new LinkedHashMap<String,Boolean>();
        reservedWords = new LinkedHashSet<String>();
        reservedWords.add("true");
        reservedWords.add("false");
    }
    
    public String interpretLine(String line) {
        Tree pt = bep.parse(line);
        return pt.isError() ? pt.toString() : interp(pt);
    }
    
    private String getInput(Scanner in) {
        System.out.print("> ");
        return in.nextLine();
    }
    
    public void interpreter() {
        Scanner in = new Scanner(System.in);
        String line = getInput(in);
        while (!line.equals("quit")) {
            System.out.println(interpretLine(line));
            line = getInput(in);
        }
    }
    
    public void interpretScript(File f) throws IOException {
        Scanner in = new Scanner(f);
        while (in.hasNextLine()) {
            String line = in.nextLine();
            if (line.length() > 0) {
                System.out.println("> " + line);
                System.out.println(interpretLine(line));
            }
        }
    }
    
    public String interp(Tree t) {
        if (t.numChildren() == 1) {
            return interp(t.nthChild(0));
            
        } else if (t.name().equals("<setter>")) {
            return set(t.namedChild("<var>"), t.namedChild("<expr>"));
            
        } else if (t.name().equals("<expr>")) {
            return evalOr(t.namedChild("<expr>"), t.namedChild("<andExpr>"));
            
        } else if (t.name().equals("<andExpr>")) {
            return evalAnd(t.namedChild("<andExpr>"), t.namedChild("<notExpr>"));
            
        } else if (t.name().equals("<notExpr>")) {
            return evalNot(t.namedChild("<value>"));
            
        } else if (t.name().equals("<value>")) {
            return interp(t.namedChild("<expr>"));
            
        } else if (t.name().equals("<var>")) {
            String var = t.toString();
            if (var.equals("true") || var.equals("false")) {
                return var;
            } else if (symbols.containsKey(var) && symbols.get(var)) {
                return "true";
            } else {
                return "false";
            }
            
        } else {
            throw new IllegalArgumentException("Unrecognized node: " + t.name());
        }
    }
    
    private static String b2s(boolean b) {return Boolean.toString(b);}
    
    private String set(Tree var, Tree val) {
        String v = var.toString();
        String b = interp(val);
        symbols.put(v, b.equals("true"));
        return v + " = " + b;
    }
    
    private String evalOr(Tree left, Tree right) {
        String leftVal = interp(left);
        String rightVal = interp(right);
        return b2s(leftVal.equals("true") || rightVal.equals("true"));
    }
    
    private String evalAnd(Tree left, Tree right) {
        String leftVal = interp(left);
        String rightVal = interp(right);
        return b2s(leftVal.equals("true") && rightVal.equals("true"));
    }
    
    private String evalNot(Tree t) {
        String val = interp(t);
        return b2s(!val.equals("true"));
    }
    
    public static void main(String[] args) throws IOException {
        BoolExprEval bee = new BoolExprEval();
        if (args.length == 0) {
            bee.interpreter();
        } else {
            bee.interpretScript(new File(args[0]));
        }
    }
}
