// Semantics.java

import java.util.*;

class State extends Hashtable { 
  // Defines the set of variables and their associated values 
  // that are active during interpretation: State is implemented as 
  // a Java Hashtable.
    
  // construct the empty state
  public State() { }
    
  // construct a new state with a single key-value pair
  public State(Variable key, Value val) {
    put(key, val);
  }
    
  // Overriding union function "onion" on two states s and t;
  // returns a state composed of all elements in t together
  // with all elements in s that are not in t.    
  public State onion (State t) {
    for (Enumeration e = t.keys(); e.hasMoreElements(); ) {
      Variable key = (Variable)e.nextElement();
      put(key, t.get(key));
    }
    return this;
  }


  public void display () {
    System.out.print("{ ");
    for (Enumeration e = keys(); e.hasMoreElements(); ) {
      Variable key = (Variable)e.nextElement();
      Value val = (Value)get(key);
      System.out.print("<" + key.id + ", ");
      if (val.type.isInteger())
	System.out.print (val.intValue + ">");
      else if (val.type.isBoolean())
	System.out.print (val.boolValue + ">");
      else System.out.print ("undefined>");
      if (e.hasMoreElements())
	System.out.print(", ");
    }
    System.out.println(" }");
  }

}

// Following is the semantics class, characterized by the meaning
// function M and its auxiliary function applyBinary.  

public class Semantics {
    
   State M (Program p) { 
       return M (p.body, initialState(p.decpart)); 
   }
  
   State initialState (Declarations d) {
     State sigma = new State();
     Value undef = new Value();
     for (int i = 0; i < d.size(); i++)
       sigma.put(((Declaration)(d.elementAt(i))).v, undef);
     return sigma;
   }
  
   Value applyBinary (Operator op, Value v1, Value v2) {
    if (v1.type.isUndefined() || v2.type.isUndefined()) {
      return new Value();
    } 
    else if (op.ArithmeticOp( )) {
      if (op.val.equals(Operator.PLUS)) 
	    return new Value(v1.intValue + v2.intValue);
      if (op.val.equals(Operator.MINUS)) 
	    return new Value(v1.intValue - v2.intValue);
      if (op.val.equals(Operator.TIMES)) 
	    return new Value(v1.intValue * v2.intValue);
      if (op.val.equals(Operator.DIV)) 
	    return new Value(v1.intValue / v2.intValue);
    } 
    else if (op.RelationalOp( )) {
      if (op.val.equals(Operator.LT)) 
	    return new Value(v1.intValue < v2.intValue);
      if (op.val.equals(Operator.LE)) 
	    return new Value(v1.intValue <= v2.intValue);
      if (op.val.equals(Operator.EQ)) 
	    return new Value(v1.intValue == v2.intValue);
      if (op.val.equals(Operator.NE)) 
	    return new Value(v1.intValue != v2.intValue);
      if (op.val.equals(Operator.GE)) 
	    return new Value(v1.intValue >= v2.intValue);
      if (op.val.equals(Operator.GT)) 
	    return new Value(v1.intValue > v2.intValue);
    } 
    else if (op.BooleanOp( )) {
      if (op.val.equals(Operator.AND)) 
	    return new Value(v1.boolValue && v2.boolValue);
      if (op.val.equals(Operator.OR)) 
	    return new Value(v1.boolValue || v2.boolValue);
    }
    return null;
  } 
    
  Value applyUnary (Operator op, Value v) {
    if (v.type.isUndefined()) return new Value();
    else if (op.val.equals("!")) return new Value(!v.boolValue);
    return null;
  } 
  
  Value M (Expression e, State sigma) {
    if (e instanceof Value) 
      return (Value)e;
    if (e instanceof Variable)
      if (!sigma.containsKey((Variable)e)) 
	return new Value();
      else return (Value)(sigma.get((Variable)e));
    if (e instanceof Binary)
      return applyBinary (((Binary)e).op, 
                          M(((Binary)e).term1, sigma), 
			              M(((Binary)e).term2, sigma));
    if (e instanceof Unary)
      return applyUnary(((Unary)e).op, M(((Unary)e).term, sigma));
    return null;
  }
  
  State M (Statement s, State sigma) {
    if (s instanceof Skip) return M((Skip)s, sigma);
    if (s instanceof Assignment)  return M((Assignment)s, sigma);
    if (s instanceof Conditional)  return M((Conditional)s, sigma);
    if (s instanceof Loop)  return M((Loop)s, sigma);
    if (s instanceof Block)  return M((Block)s, sigma);
    return null;
  }
  
  State M (Skip s, State sigma) {
    return sigma;
  }
  
  State M (Assignment a, State sigma) {
    return sigma.onion(new State(a.target, M (a.source, sigma)));
  }
  
  State M (Block b, State sigma) {
    for (int i=0; i<b.members.size(); i++) {
      sigma = M ((Statement)(b.members.elementAt(i)), sigma);
    } 
    return sigma;
  }
  
  State M (Conditional c, State sigma) {
    if (M(c.test, sigma).boolValue)
      return M (c.thenbranch, sigma);
    else
      return M (c.elsebranch, sigma);
  }
  
  State M  (Loop l, State sigma) {
    if (M (l.test, sigma).boolValue)
      return M(l, M (l.body, sigma));
    else return sigma;
  }
}

