package IRT;

import java.io.PrintWriter;

/**
 * Print the various parts of an intermediate representation tree
 * (with components given by the Tree and Temp packages).
 * <p>
 * History
 * <dl>
 * <dt>Version 1.0 (11 December 1998)
 * <dd>Created in support of the Tiger compiler project
 * </dl>
 *
 * @author Samuel A. Rebelsky
 * @version 1.0 of 11 December 1998
 */
public class Print {

   // +---------------------------------+-------------------------
   // | Printing temporaries and labels |
   // +---------------------------------+

   /**
    * Print a label.
    *
    * @exception Exception
    *    If the label is undefined.
    */
   public static void print(PrintWriter out, Temp.Label label)
      throws Exception
   {
      if (label == null) throw new Exception("Undefined label");
      out.print("L(" + label.toString() + ")");
   } // print(PrintWriter, Temp.Label)

   /**
    * Print a list of labels.
    *
    * @exception Exception
    *    If any of the labels is null.
    */
   public static void print(PrintWriter out, Temp.LabelList labels)
      throws Exception

   {
      if (labels == null) return;
      print(out, labels.head);
      out.print(" ");
      print(out, labels.tail);
   } // print(PrintWriter, Temp.LabelList)

   /**
    * Print a temporary.  Relies on the modifed Temp.toString method.
    *
    * @exception Exception
    *    If the temporary is undefined.
    */
   public static void print(PrintWriter out, Temp.Temp temp)
      throws Exception
   {
      if (temp == null) throw new Exception("Undefined temporary");
      out.print(temp.toString());
   } // print(PrintWriter, Temp.Temp)

   /**
    * Print a list of temporaries.
    *
    * @exception Exception
    *    If any of the temporaries is null.
    */
   public static void print(PrintWriter out, Temp.TempList temps)
      throws Exception
   {
      if (temps == null) return;
      print(out, temps.head);
      out.print(" ");
      print(out, temps.tail);
   } // print(PrintWriter, Temp.TempList)


   // +----------------------+------------------------------------
   // | Printing expressions |
   // +----------------------+

   /**
    * Print any expression.
    *
    * @exception Exception
    *    If the expression is unprintable (see the various kinds of
    *    expressions for more information) or if it's not one of
    *    the standard kinds of expressions.
    */
   public static void print(PrintWriter out, Tree.Exp exp)
      throws Exception
   {
      if (exp instanceof Tree.BINOP)
         print(out, (Tree.BINOP) exp);
      else if (exp instanceof Tree.BUILTIN)
         print(out, (Tree.BUILTIN) exp);
      else if (exp instanceof Tree.CALL)
         print(out, (Tree.CALL) exp);
      else if (exp instanceof Tree.CONST)
         print(out, (Tree.CONST) exp);
      else if (exp instanceof Tree.ESEQ)
         print(out, (Tree.ESEQ) exp);
      else if (exp instanceof Tree.MEM)
         print(out, (Tree.MEM) exp);
      else if (exp instanceof Tree.NAME)
         print(out, (Tree.NAME) exp);
      else if (exp instanceof Tree.TEMP)
         print(out, (Tree.TEMP) exp);
      else
         throw new Exception("Unknown type of expression");
   } // print(PrintWriter, Tree.Exp)

   /**
    * Print a list of expressions.
    *
    * @exception Exception
    *    If any of the parts of the list is invalid.
    */
   public static void print(PrintWriter out, Tree.ExpList explist)
      throws Exception
   {
      if (explist == null) return;
      print(out, explist.head);
      print(out, explist.tail);
   } // print(PrintWriter, Tree.ExpList)

   /**
    * Print a binary operator.
    *
    * @exception Exception
    *    When the operator is incorrect or one of the arguments
    *    is invalid.
    */
   public static void print(PrintWriter out, Tree.BINOP binop)
      throws Exception
   {
      // Sanity check
      if (binop == null) return;

      // Print the instruction name
      out.print("BINOP ");

      // Print the operator
      switch (binop.binop) {
         case Tree.BINOP.PLUS:    out.print("PLUS "); break;
         case Tree.BINOP.MINUS:   out.print("MINUS "); break;
         case Tree.BINOP.MUL:     out.print("MUL "); break;
         case Tree.BINOP.DIV:     out.print("DIV "); break;
         case Tree.BINOP.AND:     out.print("AND "); break;
         case Tree.BINOP.OR:      out.print("OR "); break;
         case Tree.BINOP.LSHIFT:  out.print("LSHIFT "); break;
         case Tree.BINOP.RSHIFT:  out.print("RSHIFT "); break;
         case Tree.BINOP.ARSHIFT: out.print("ARSHIFT "); break;
         case Tree.BINOP.XOR:     out.print("XOR "); break;
         default:
            out.print("BOZO ");
            throw new Exception("Invalid operator");
      } // switch

      // Print the arguments
      print(out, binop.left);
      out.print(" ");
      print(out, binop.right);
      out.println();
   } // print(PrintWriter,Tree.BINOP)

   /**
    * Print a call to a builtin operator.
    *
    * @exception Exception
    *    When one of the arguments is incorrect.
    */
   public static void print(PrintWriter out, Tree.BUILTIN builtin)
      throws Exception
   {
      // Sanity check
      if (builtin == null) return;
      // Print the instruction
      out.print("BUILTIN ");
      // Print the function name
      print(out, builtin.func);
      // Print the arguments to the function
      print(out, builtin.args);
      // Print the carriage return
      out.println();
   } // print(PrintWriter,Tree.BUILTIN)

   /**
    * Print a function call.
    *
    * @exception Exception
    *    When one of the arguments is incorrect.
    */
   public static void print(PrintWriter out, Tree.CALL call)
      throws Exception
   {
      // Sanity check
      if (call == null) return;
      // Print the instruction
      out.print("CALL ");
      // Print the function name
      print(out, call.func);
      // Print the arguments to the function
      print(out, call.args);
      // Print the carriage return
      out.println();
   } // print(PrintWriter,Tree.CALL)

   /**
    * Print a constant.  Since this should only be used as an
    * argument to something, it does not include a carriage return.
    * <p>
    * This is one of the few <code>print</code> methods that does
    * not throw an exception.
    */
   public static void print(PrintWriter out, Tree.CONST c) {
      // Sanity check
      if (c == null) out.print(0);
      // Print the value
      else out.print(c.value);
   } // print(PrintWriter,Tree.CONST)

   /**
    * Print an ESEQ.  Since straight line IRTs should not have
    * ESEQs, this should never be called.  Nonetheless, it is
    * included for safety.
    *
    * @exception Exception
    *   If it's ever called
    */
   public static void print(PrintWriter out, Tree.ESEQ eseq)
      throws Exception
   {
      // Sanity check
      if (eseq == null) return;
      // Print the two parts
      print(out, eseq.stm);
      print(out, eseq.exp);
      // Report the error
      throw new Exception("Attempt to print an ESEQ");
   } // print(out,Tree.ESEQ)

   /**
    * Print a memory reference.
    *
    * @exception Exception
    *    When the subreference is invalid or the whole thing is null.
    */
   public static void print(PrintWriter out, Tree.MEM mem)
      throws Exception
   {
     if (mem == null) {
        throw new Exception("Undefined MEM");
     }
     out.print("M(");
     print(out, mem.exp);
     out.print(")");
   } // print(PrintWriter, Tree.MEM)

   /**
    * Print a label used as part of an argument.
    *
    * @exception Exception
    *    If the label is unspecified.
    */
   public static void print(PrintWriter out, Tree.NAME name)
      throws Exception
   {
      if (name == null) {
        throw new Exception("Undefined NAME");
      }
      print(out, name.label);
   } //  print(PrintWriter, Tree.NAME)

   /**
    * Print a temporary.
    *
    * @exception Exception
    *    If the temporary is unspecified.
    */
   public static void print(PrintWriter out, Tree.TEMP temp)
      throws Exception
   {
      if (temp == null) {
        throw new Exception("Undefined TEMP");
      }
      print(out, temp.temp);
   } //  print(PrintWriter, Tree.TEMP)


   // +---------------------+-------------------------------------
   // | Printing statements |
   // +---------------------+

   /**
    * Print a statement.  This method figures out which related
    * method to call, depending on which type of statement it is.
    *
    * @exception Exception
    *    If there's a problem printing the specific type of statement.
    */
   public static void print(PrintWriter out, Tree.Stm stm)
      throws Exception
   {
      if (stm instanceof Tree.CJUMP)
         print(out, (Tree.CJUMP) stm);
      else if (stm instanceof Tree.EXP)
         print(out, (Tree.EXP) stm);
      else if (stm instanceof Tree.JUMP)
         print(out, (Tree.JUMP) stm);
      else if (stm instanceof Tree.LABEL)
         print(out, (Tree.LABEL) stm);
      else if (stm instanceof Tree.MOVE)
         print(out, (Tree.MOVE) stm);
      else if (stm instanceof Tree.SEQ)
         print(out, (Tree.SEQ) stm);
      else if (stm instanceof Tree.STRING)
         print(out, (Tree.STRING) stm);
      else
         throw new Exception("Unknown type of statement");
   } // print(PrintWriter, Tree.Stm)

   /**
    * Print a list of statements.
    *
    * @exception Exception
    *    If there are problems with the individual statements.
    */
   public static void print(PrintWriter out, Tree.StmList statements)
      throws Exception
   {
      // End of the list.  Print the legendary END statement.
      if (statements == null) {
        out.println("END");
      }
      else {
        print(out, statements.head);
        print(out, statements.tail);
      }
   } // print(PrintWriter, Tree.StmList)

   /**
    * Print a conditional jump.  Note that the second label
    * for the conditional jump is not printed.
    *
    * @exception Exception
    *    If the operator is invalid or one of the arguments is incorrect.
    */
   public static void print(PrintWriter out, Tree.CJUMP cjump)
      throws Exception
   {
      // Sanity check
      if (cjump == null) return;
      // Print the instruction
      out.print("CJUMP ");
      // Print the operator
      switch (cjump.relop) {
         case Tree.CJUMP.EQ:   out.print("EQ "); break;
         case Tree.CJUMP.NE:   out.print("NE "); break;
         case Tree.CJUMP.LT:   out.print("LT "); break;
         case Tree.CJUMP.GT:   out.print("GT "); break;
         case Tree.CJUMP.LE:   out.print("LE "); break;
         case Tree.CJUMP.GE:   out.print("GE "); break;
         default:
            out.print("BOZO ");
            throw new Exception("Invalid operator in CJUMP");
      } // switch
      // Print the arguments
      print(out, cjump.left);
      out.print(" ");
      print(out, cjump.right);
      out.print(" ");
      // Print the label and carriage return
      print(out, cjump.iftrue);
      out.println();
   } //  print(PrintWriter,Tree.CJUMP)

   /**
    * Print an expression instruction.
    *
    * @exception Exception
    *   If it's an invalid type of subexpression or
    *   there's a problem printing the subexpression.
    */
   public static void print(PrintWriter out, Tree.EXP exp)
      throws Exception
   {
      // Sanity check
      if (exp == null) return;
      // Verify that it's a valid type of expression to print
      if ( (exp.exp instanceof Tree.CONST) ||
           (exp.exp instanceof Tree.ESEQ) ||
           (exp.exp instanceof Tree.MEM) ||
           (exp.exp instanceof Tree.NAME) ||
           (exp.exp instanceof Tree.TEMP) ) {
        throw new Exception("Invalid expression in this context");
      }
      // Print the expression
      print(out, exp.exp);
   } // print(PrintWriter, Tree.EXP)

   /**
    * Print a JUMP instruction.
    *
    * @exception Exception
    *    If one of the parts of the jump is invalid.
    */
   public static void print(PrintWriter out, Tree.JUMP jump)
      throws Exception
   {
     // Sanity check
     if (jump == null) return;
     // Print the instruction
     out.print("JUMP ");
     // Print the switch part
     print(out, jump.exp);
     // Print the targets
     print(out, jump.targets);
     //  Print the carriage return
     out.println();
   } // print(PrintWriter, Tree.JUMP)

   /**
    * Print a label instruction.
    *
    * @exception Exception
    *    If the label is unspecified.
    */
   public static void print(PrintWriter out, Tree.LABEL label)
      throws Exception
   {
      if (label == null) return;
      out.print("LABEL ");
      print(out, label.label);
      out.println();
   } //  print(PrintWriter, Tree.LABEL)

   /**
    * Print a move instruction.
    *
    * @exception Exception
    *    If there's an error in one of the arguments.
    */
   public static void print(PrintWriter out, Tree.MOVE move)
      throws Exception
   {
      if (move == null) return;
      out.print("MOVE ");
      print(out, move.dst);
      out.print(" ");
      print(out, move.src);
      out.println();
   } //  print(PrintWriter, Tree.MOVE)

   /**
    * Print a sequence of instructions.  This should never be called, as
    * all the <code>SEQ</code> nodes in a tree should have been removed.
    *
    * @exception Exception
    *    If some element of the sequence is erroneous or if it's called
    *    at all.
    */
   public static void print(PrintWriter out, Tree.SEQ seq)
      throws Exception
   {
      if (seq == null) return;
      print(out, seq.left);
      print(out, seq.right);
   } //  print(PrintWriter, Tree.SEQ)

   /**
    * Print a string declaration.
    * <p>
    * Nope.  No exceptions.
    */
   public static void print(PrintWriter out, Tree.STRING string) {
     if (string == null) return;
     out.println("STRING string.str");
   } //  print(PrintWriter, Tree.STRING)
} // class Print
