                     //WrenAS.java

class Program
{		     // Program ::= Identifier Block
   private Identifier ide;
   private Block body;

   Program(Identifier i,Block b)
   {  ide = i;  body = b;  }

   Identifier getIdentifier()
   { return ide;	 }

   Block getBlock()
   { return body;  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Program");
      ide.show(n+3);
      body.show(n+3);
   }
}


class Block
{		     // Block ::= Declarations Commands
   private Declarations decs;
   private Commands cmds;

   Block(Declarations d,Commands c)
   {  decs = d;	 cmds = c;  }

   Declarations getDeclarations()
   { return decs;  }

   Commands getCommands()
   { return cmds;  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Block");
      decs.show(n+3);
      cmds.show(n+3);
   }
}


class Declarations extends java.util.ArrayList
{		     // Declarations ::= Declaration*
   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Declarations");
      for (int k=0; k<size(); k++)
	 ((Declaration)get(k)).show(n+3);
   }
}



class Declaration
{		     // Declaration ::= VarList Type
   private VarList vlist;
   private Type type;

   Declaration(VarList vl, Type t)
   {  vlist = vl;  type = t;  }

   VarList getVarList()
   { return vlist;  }

   Type getType()
   { return type;  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Declaration");
      vlist.show(n+3);
      type.show(n+3);
   }
}


class VarList extends java.util.ArrayList
{		     // VarList ::= Identifier+
   void show(int n)
   {
      System.out.println(Token.spaces(n)+"VarList");
      for (int k=0; k<size(); k++)
	 ((Identifier)get(k)).show(n+3);
   }
}


class Type
{		     // Type ::= integer | boolean
   private String type;

   Type(String t) { type = t; }

   public String toString()
   {  return type;  }

   public boolean equals(Object obj)
   { 
     String s = ((Type)obj).type;
     return type.equals(s);
   }

   public int hashCode()
   {  return type.hashCode();  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Type = " + type);
   }

   public boolean isBoolean()
   { return type.equals("boolean"); }

   public boolean isInteger()
   { return type.equals("integer"); }

   public boolean isUndefined()
   { return type.equals("undefined"); }
}


/*************************************************************************/


class Commands extends java.util.ArrayList
{		     // Commands ::= Command+
   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Commands");
      for (int k=0; k<size(); k++)
	 ((Command)get(k)).show(n+3);
   }
}


abstract class Command
{		       // Command ::= Read | Write | Skip | Loop
		       //	    | Assignment | Conditional
    abstract void show(int n);
}


class Skip extends Command
{			  // Skip ::=
   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Skip");
   }
}


class Read extends Command
{			  // Read ::= Identifier
   private Identifier ide;

   Read(Identifier i)
   {  ide = i;	}

   Identifier getIdentifier()
   {  return ide;  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Read");
      ide.show(n+3);
   }
}


class Write extends Command
{			  // Write ::= Expression
   private Expression exp;

   Write(Expression e)
   {  exp = e;	}

   Expression getExpression()
   {  return exp;  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Write");
      exp.show(n+3);
   }
}


class Assignment extends Command
{			  // Assignment ::= Identifier Expression
   private Identifier target;
   private Expression source;

   Assignment(Identifier id, Expression e)
   {  target = id;  source = e;	 }

   Identifier getIdentifier()
   {  return target;  }

   Expression getExpression()
   {  return source;  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Assignment");
      target.show(n+3);
      source.show(n+3);
   }
}


class Conditional extends Command
{			  // Conditional ::= Expression Commands Commands?
   private Expression test;
   private Commands thenPart, elsePart;

   Conditional(Expression t, Commands tp, Commands ep)
   {  test = t; thenPart = tp; elsePart = ep;  }

   Expression getTest()
   {  return test;  }

   Commands getThenPart()
   {  return thenPart;	}

   Commands getElsePart()
   {  return elsePart;	}

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"If");
      test.show(n+3);
      thenPart.show(n+3);
      if (elsePart != null) elsePart.show(n+3);
   }
}


class Loop extends Command
{			  // Loop ::= Expression Commands
   private Expression test;
   private Commands body;

   Loop(Expression t, Commands b)
   {  test = t; body = b;  }

   Expression getTest()
   {  return test;  }

   Commands getBody()
   {  return body;  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"While");
      test.show(n+3);
      body.show(n+3);
   }
}


/*************************************************************************/

abstract class Expression
{			 // Expression ::= Identifier | Value | Binary
			 //			    | Minus | Not
     abstract void show(int n);
}


class Identifier extends Expression
{
   private String ide;

   Identifier(String i)
   {   ide = i;	}

   public String toString()
   {  return ide;  }

   public boolean equals(Object obj)
   {
     String s = ((Identifier)obj).ide;
     return ide.equals(s);
   }

   public int hashCode()
   {  return ide.hashCode();  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Identifier = "+ide);
   }
}

class Value extends Expression
{				 // Value ::= Numeral | true | false
   private Type type;
   private int intValue;
   private boolean boolValue;
   private boolean undefined = false;

   Value()
   {  undefined = true;  }

   Value(int i)
   { type = new Type("integer"); intValue = i; undefined = false;  }

   Value(boolean b)
   { type = new Type("boolean"); boolValue = b; undefined = false;  }

   int getInteger()
   {  return intValue;  }

   boolean getBoolean()
   {  return boolValue;  }

   boolean isInitialized()
   {  return !undefined;  }

   Type getType()
   {  return type;  }

   public String toString()
   {
       if (undefined) return "undef";
       if (type.isInteger()) return "" + intValue;
       if (type.isBoolean()) return "" + boolValue;
       return "";
   }

   void show(int n)
   {
      if (type.isInteger())
	System.out.println(Token.spaces(n)+"Value = " + intValue);
      else
	System.out.println(Token.spaces(n)+"Value = " + boolValue);
   }

   static Value negate(Value operand)
   {  return new Value(-operand.getInteger());  }

   static Value not(Value operand)
   {  return new Value(!operand.getBoolean());  }

   static Value compute(Operator opr, Value left, Value right)
   {
       int val = opr.getOperator();
       if (val == Token.PLUS)
         return new Value(left.getInteger() + right.getInteger());
       if (val == Token.MINUS)
         return new Value(left.getInteger() - right.getInteger());
       if (val == Token.TIMES)
         return new Value(left.getInteger() * right.getInteger());
       if (val == Token.DIVIDES)
         return new Value(left.getInteger() / right.getInteger());
       if (val == Token.LT)
         return new Value(left.getInteger() < right.getInteger());
       if (val == Token.LE)
         return new Value(left.getInteger() <= right.getInteger());
       if (val == Token.GT)
         return new Value(left.getInteger() > right.getInteger());
       if (val == Token.GE)
         return new Value(left.getInteger() >= right.getInteger());
       if (val == Token.EQ)
         return new Value(left.getInteger() == right.getInteger());
       if (val == Token.NE)
         return new Value(left.getInteger() != right.getInteger());
       if (val == Token.AND)
         return new Value(left.getBoolean() && right.getBoolean());
       if (val == Token.OR)
         return new Value(left.getBoolean() || right.getBoolean());
       return new Value();
   }
}



class Binary extends Expression
{				   // Binary ::= Operator Expression Expression
   private Operator opr;
   private Expression term1, term2;

   Binary(Operator p, Expression t1, Expression t2)
   {  opr = p;	term1 = t1;  term2 = t2;  }

   Operator getOperator()
   {  return opr;  }

   Expression getLeftOperand()
   {  return term1;  }

   Expression getRightOperand()
   {  return term2;  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Binary");
      opr.show(n+3);
      term1.show(n+3);
      term2.show(n+3);
   }
}

class Minus extends Expression
{				    // Minus ::= Expression
   private Expression exp;

   Minus(Expression e)
   {  exp = e;	}

   Expression getExpression()
   {  return exp;  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Minus");
      exp.show(n+3);
   }
}


class Not extends Expression
{				    // Not ::= Expression
   private Expression exp;

   Not(Expression e)
   {  exp = e;	}

   Expression getExpression()
   {  return exp;  }

   void show(int n)
   {
      System.out.println(Token.spaces(n)+"Not");
      exp.show(n+3);
   }
}


class Operator
{		 // Operator ::= + | - | * | / | < | <= | = | <>
		 //	       | >= | > | and | or
    private int val;

    Operator(int opr)
    {  val = opr;  }

    int getOperator()
    {  return val;  }

    boolean isArithmeticOp()
    {
       return val==Token.PLUS || val==Token.MINUS ||
	      val==Token.TIMES || val==Token.DIVIDES;
    }

    boolean isRelationalOp()
    {
       return val==Token.LE || val==Token.LT || val==Token.EQ ||
	      val==Token.GE || val==Token.GT || val==Token.NE;
    }

    boolean isBooleanOp()
    {
       return val==Token.AND || val==Token.OR;
    }

    void show(int n)
    {
	System.out.println(Token.spaces(n)+"Operator = "+Token.token(val));
    }
}
/*************************************************************************/
