import pascal.PascalNonterminals;
import pascal.PascalTokens;

import rebelsky.compiler.lexer.Identifier;
import rebelsky.compiler.misc.Checker;
import rebelsky.compiler.misc.Symbol;
import rebelsky.compiler.misc.Traverser;
import rebelsky.compiler.parser.Node;
import rebelsky.compiler.types.BasicTypes;
import rebelsky.compiler.types.Type;

/**
 * Devin checks declarations.
 *
 * @author Samuel A. Rebelsky
 * @version 1.0 of April 2004
 */
public class Devin
{
  /**
   * Undefined types.  Useful when you can't find another type.
   */
  public static final Type UNDEFINED = new Type("Undefined");

  /**
   * Add all the checkers I'm responsible for.
   */
  public static void addCheckers(Traverser t)
  {
    // Insert some standard types.
    try {
      t.setType((Identifier) PascalTokens.TTRUE, BasicTypes.BOOLEAN);
      t.setType((Identifier) PascalTokens.TFALSE, BasicTypes.BOOLEAN);
    }
    catch (Exception e) {
    }

    // For type identifiers, we set the type based on the identifier.
    t.setChecker(PascalNonterminals.TYPE_IDENTIFIER, new Checker() {
        public void check(Traverser t, Node n) 
          throws Exception
        {
          // Traverse the child, just in case.
          t.traverse(n.getChild(0));
          // Extract the type and add it to this node.
          Symbol typesym = n.getChild(0).getSymbol();
          if (typesym == PascalTokens.TBOOLEAN)
            n.setAttribute("type", BasicTypes.BOOLEAN);
          else if (typesym == PascalTokens.TCHAR)
            n.setAttribute("type", BasicTypes.CHAR);
          else if (typesym == PascalTokens.TINTEGER)
            n.setAttribute("type", BasicTypes.INTEGER);
          else if (typesym == PascalTokens.TREAL)
            n.setAttribute("type", BasicTypes.REAL);
          else if (typesym == PascalTokens.TSTRING)
            n.setAttribute("type", BasicTypes.STRING);
          else
            n.setAttribute("type", UNDEFINED);
        } // check(Traverser);
      }); // TYPE_IDENTIFIER

    // For variable declarations, we enter the types of all the IDS in
    // the symbol table.
    t.setChecker(PascalNonterminals.VARIABLE_DECLARATION, new Checker() {
        public void check(Traverser t, Node n) 
          throws Exception
        {
          // Count the number of variables declared.
          int numvars = n.numChildren() - 1;
          // Visit all the children, just in case.
          for (int i = 0; i <= numvars; i++)
            t.traverse(n.getChild(i));
          // Get the type
          Type type = (Type) n.getChild(numvars).getAttribute("type");
          // Enter the types
          for (int i = 0; i < numvars; i++) {
            t.setType((Identifier) n.getChild(i).getSymbol(), type);
          } // for
        } // check(Traverser)
      });

    // For procedure declarations, we must check both variable declarations
    // and parameters.  This checker is not yet completely implemented.
    t.setChecker(PascalNonterminals.PROCEDURE_DECLARATION, new Checker() {
      public void check(Traverser t, Node n)
        throws Exception
      {
        // Begin a new scope
        t.beginScope();
        // Count the number of variables declared.
        int numchildren = n.numChildren();
        // Visit all the children
        for (int i = 0; i < numchildren; i++)
          t.traverse(n.getChild(i));
        // End the scope we just created
        t.endScope();
      } // check(Traverser, Node)
    });
  } // addCheckers()
} // class Devin

