Package xtc.typical

Source Code of xtc.typical.Analyzer

/*
* xtc - The eXTensible Compiler
* Copyright (C) 2007 New York University
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package xtc.typical;

import java.util.ArrayList;
import java.util.Hashtable;
import xtc.tree.Node;
import xtc.util.Runtime;
import xtc.util.SymbolTable;
import xtc.util.Pair;
import xtc.util.Function;

/**
* The base class of all Typical-generated type checkers.
*
* @author Laune Harris, Anh Le
* @version $Revision: 1.145 $
*/
public abstract class Analyzer {
 
  /** The runtime. */
  protected final Runtime runtime;
 
  /** The symbol table. */
  protected final SymbolTable gamma;

  /** The property to check if enter a scope. */
  protected final String ENTERSCOPE = "enterScope" ;

  /** The property of check if exit a scope. */
  protected final String EXITSCOPE = "exitScope";

  /**
   * The property to store the times of not entering a new scope because
   *   the new scope is also the current scope.
   */
  protected final String MAGICNUMBER = "magicNumber";
 
  /** The hash table */
  protected Hashtable<Object, Object> hashTable;
   
  /** The analyzer. */
  protected Function.F1<?, Node> analyzer;

  /** The tree root. */
  protected Node root;

  /** The visited node path.*/
  protected final ArrayList<Node> matching_nodes = new ArrayList<Node>();
 
  /** The list of names of the nodes that trigger scope changes. */
  protected final ArrayList<String> processScopeNodes = new ArrayList<String>();
 
  /** Interface for pattern matches. */
  public static interface Match<T> extends Function.F0<T> { /*empty*/ }

  /** Interface for ancestor Matches. */
  public static interface NodeMatch extends Function.F1<Boolean, Node> { /* empty */ }
                                                   
  /** Interface for require expressions. */
  public static interface Require<T> extends Function.F0<T> { /*empty*/ }

  /** Interface for let expressions. */
  public static interface Let<T> extends Function.F0<T> { /*empty*/ }
 
  /** Interface for guard expressions. */
  public static interface Guard<T> extends Function.F0<T> { /*empty*/ }
 
  /** Get the names of the nodes that trigger scope changes. */
  protected abstract void getScopeNodes();
 
  /** The Typical load function to load predefined values and data types. */
  protected final Function.F3<Void,String,String,Object> load =
    new Function.F3<Void,String,String,Object>() {
      public Void apply(String s, String ns, Object t) {
        if (null == s || null == ns) return null;
        gamma.current().define(SymbolTable.toNameSpace(s, ns), t);
        return null;
     
    }

  /** The ancestor function.*/
  protected final Function.F1<Node, NodeMatch> ancestor =
    new Function.F1<Node,NodeMatch>() {
      public final Node apply(NodeMatch pattern) {
        for (int i = matching_nodes.size() - 1; i >=0; i--) {
          final Node node = matching_nodes.get(i);
          if (pattern.apply(node)) return node;
        }
        return null;
      }
    };
 
  /** The parent function.*/
  protected final Function.F1<Node, NodeMatch> parent =
    new Function.F1<Node,NodeMatch>() {
      public final Node apply(NodeMatch pattern) {
        final Node node = matching_nodes.get(matching_nodes.size() -1);
        if (pattern.equals(node)) return node;
        return null;
      }
    };

  /** A Typical function to lookup a node, without error messages. */
  protected final Function.F2<Object, Node, Function.F1<?, Node>> lookup2 =
    new Function.F2<Object, Node, Function.F1<?, Node>>() {
      public final Object apply(Node n, Function.F1<?, Node> getNameSpace) {
        Tuple.T3<Name<?>,String,String> tup = cast(getNameSpace.apply(n));
        assert (null != tup) : "Unable to map namespace for node " +
                               (null == n ? "Null" : n.getName());
        checkEnterScope(n);

        final Object res = gamma.lookup(tup.get1().mangle(tup.get2()));
        if (null == res) showMessage("error""Undefined: " +
                                     tup.get1().mangle(tup.get2()), n);
        checkExitScope(n);
        return res;
     
    };

  /** A Typical function to lookup a node, with an error message. */
  protected final Function.F4<Object, Node, String, String,
                              Function.F1<?, Node>> lookup4 =
    new Function.F4<Object, Node, String, String, Function.F1<?, Node>>() {
      public final Object apply(Node n, String tag, String err,
                             Function.F1<?, Node> getNameSpace) {
        Tuple.T3<Name<?>,String,String> tup = cast(getNameSpace.apply(n));
        assert (null != tup) : "Unable to map namespace for node " +
                               (null == n ? "Null" : n.getName());       
        checkEnterScope(n);
        final Object res = gamma.lookup(tup.get1().mangle(tup.get2()));
        if (null == res) showMessage(tag, err, n);
        checkExitScope(n);
        return res;
      }
    };

  /** A Typical function to lookup a node locally, without error messages. */
  protected final Function.F2<Object, Node, Function.F1<?, Node>>
    lookupLocally2 = new Function.F2<Object, Node, Function.F1<?, Node>>() {
      public final Object apply(Node n, Function.F1<?, Node> getNameSpace) {
        Tuple.T3<Name<?>,String,String> tup = cast(getNameSpace.apply(n));
        assert (null != tup) : "Unable to map namespace for node " +
                               (null == n ? "Null" : n.getName());

        checkEnterScope(n);
        final Object res = gamma.lookup(tup.get1().mangle(tup.get2()));
        if (null == res) showMessage("error", "Undefined in the current scope: "
                                     + tup.get1().mangle(tup.get2()), n);
        checkExitScope(n);
        return res;
     
    };

  /** A Typical function to lookup a node locally, with an error message. */
  protected final Function.F4<Object, Node, String, String,
                              Function.F1<?, Node>> lookupLocally4 =
    new Function.F4<Object, Node, String, String, Function.F1<?, Node>>() {
      public final Object apply(Node n, String tag, String err,
                             Function.F1<?, Node> getNameSpace) {
        Tuple.T3<Name<?>,String,String> tup = cast(getNameSpace.apply(n));
        assert (null != tup) : "Unable to map namespace for node " +
                               (null == n ? "Null" : n.getName());
      
        checkEnterScope(n);
        final Object res = gamma.lookup(tup.get1().mangle(tup.get2()));
        if (null == res) showMessage(tag,err,n);
        checkExitScope(n);
        return res;
      }
    };

  /** A Typical function to define a node, without error messages. */
  protected final Function.F3<Void, Node, Object, Function.F1<?, Node>>
    define3 = new Function.F3<Void, Node, Object, Function.F1<?, Node>>() {
      public final Void apply(Node n, Object t,
                              Function.F1<?, Node> getNameSpace) {
        Tuple.T3<Name<?>,String,String> tup = cast(getNameSpace.apply(n));
     
        assert (null != tup) : "define : no namespace for " +
                               (null == n ? "Null" : n.getName());     
        checkEnterScope(n);
        gamma.current().define(tup.get1().mangle(tup.get2()), t);
        checkExitScope(n);
        return null;
      }    
    };

  /** A Typical function to define a node, with an error message. */
  protected final Function.F5<Void, Node, Object, String, String,
                              Function.F1<?, Node>> define5 =
    new Function.F5<Void, Node, Object, String, String,
                    Function.F1<?, Node>>() {
      public final Void apply(Node n, Object t, String tag,
                         String err, Function.F1<?, Node> getNameSpace) {
        Tuple.T3<Name<?>,String,String> tup = cast(getNameSpace.apply(n));
   
        assert (null != tup) : "define : no namespace for " +
                               (null == n ? "Null" : n.getName())
   
        checkEnterScope(n);
        final String newName = tup.get1().mangle(tup.get2());
        if (gamma.current().isDefined(newName)) {
          showMessage(tag,err,n);         
        }
        gamma.current().define(newName, t);
        checkExitScope(n);
        return null;
      }
    };
 
  /** A Typical function to redefine a node. */
  protected final Function.F3<Void, Node, Object, Function.F1<?, Node>>
    redefine = new Function.F3<Void, Node, Object, Function.F1<?, Node>>() {
      public final Void apply(Node n, Object t,
                              Function.F1<?, Node> getNameSpace) {
        Tuple.T3<Name<?>,String,String> tup = cast(getNameSpace.apply(n));
   
        assert (null != tup) : "redefine : no namespace for " +
                               (null == n ? "Null" : n.getName())
         
        checkEnterScope(n);
        String newName = tup.get1().mangle(tup.get2());
        gamma.current().define(newName, t);
        checkExitScope(n);
        return null;       
      }  
    };

  /** A typical function for checking if a node has been defined */
  protected final Function.F2<Boolean, Node, Function.F1<?,Node>> isDefined =
    new Function.F2<Boolean, Node, Function.F1<?,Node>>() {
      public final Boolean apply(Node n, Function.F1<?,Node> getNameSpace) {
        Tuple.T3<Name<?>,String,String> tup = cast(getNameSpace.apply(n));
       
        assert (null != tup) : "is_define : no namespace for " +
                               (null == n ? "Null" : n.getName());
        checkEnterScope(n);
        final boolean res = gamma.isDefined(tup.get1().mangle(tup.get2()));         
        checkExitScope(n);
        return res;
      }      
    };
 
  /** A typical function for checking if a node has been locally defined */
  protected final Function.F2<Boolean, Node, Function.F1<?,Node>>
    isDefinedLocally = new Function.F2<Boolean, Node, Function.F1<?,Node>>() {
      public final Boolean apply(Node n, Function.F1<?,Node> getNameSpace) {
        Tuple.T3<Name<?>,String,String> tup = cast(getNameSpace.apply(n));
       
        assert (null != tup) : "is_define_locally : no namespace for " +
                               (null == n ? "Null" : n.getName());
       
        checkEnterScope(n);
        boolean res = gamma.current().isDefinedLocally(tup.get1().mangle(tup.get2()));
        checkExitScope(n);
        return res;
      }   
    }

  /** The Typical function to print a symbol. */
  protected final Function.F1<Boolean, String> show_symbols =
    new Function.F1<Boolean, String>() {
      public Boolean apply(String s) {
        if ("local".equals(s)) {
          gamma.current().dump(runtime.console());
          runtime.console().flush();       
        } else if ("all".equals(s)) {
          gamma.root().dump(runtime.console());
          runtime.console().flush();       
        } else {
          runtime.error("local or all required to use show_symbol function")
        }
        return Boolean.TRUE;
     
    }

  /** The Typical function to get a new name. */
  protected final Function.F1<String, String> freshName  =
    new Function.F1<String, String>() {
      public String apply(String s){
        return gamma.freshName(s);
      }
    }

  /**
   * The Typical function to check if a value if bottom.
   *   Return <code>true</code> if not bottom, otherwise bottom
   *   (It will be removed once guard is done)
   */
  protected final Function.F1<Boolean, Object> notBottom =
    new Function.F1<Boolean, Object>() {
      public Boolean apply(Object t) {
        return null == t ? null : Boolean.TRUE;
      }
    };

  /**
   * Create a new TypeChecker base.
   *
   * @param runt The runtime.
   */
  public Analyzer(Runtime runt) {
    runtime = runt;
    gamma = new SymbolTable();
    hashTable = new Hashtable<Object, Object>();
    getScopeNodes();   
  }

  /**
   * Check a node and enter a scope.
   *
   * @param n The node to check.
   */
  protected void checkEnterScope(Node n) {  
    if (null != n && n.hasProperty(ENTERSCOPE)) {
      final String scopeName = (String)n.getProperty(ENTERSCOPE);
      if (!scopeName.equals(gamma.current().getName())){
        gamma.enter(scopeName);     
      } else {
        // magicNumber is the number of times not entering a new scope
        //   at this node because the new scope is the current scope
        if (!n.hasProperty(MAGICNUMBER)) {
          n.setProperty(MAGICNUMBER, 1);
        } else {
          final Integer num = (Integer)n.getProperty(MAGICNUMBER);
          n.setProperty(MAGICNUMBER, num + 1);
        }
      }
    }
   
  }
 
  /**
   * Check a node and exit scope.
   *
   * @param n The node to check.
   */
  protected void checkExitScope(Node n){
    if (null != n && n.hasProperty(EXITSCOPE)) {
      if (!n.hasProperty(MAGICNUMBER)) gamma.exit();
      else {
        final Integer num = (Integer)n.getProperty(MAGICNUMBER);
        if (1 == num) n.removeProperty(MAGICNUMBER);
        else n.setProperty(MAGICNUMBER, num - 1);
      }
    }
    if (null != n && n.hasProperty("deleteScope")) {
      gamma.delete((String)n.getProperty("deleteScope"));
    }
  }
 
  /**
   * Print a error message.
   *
   * @param s The msg.
   * @param n The location.
   * @return null
   */ 
  protected Object error(String s, Node n) {
    if (null == n) {
      n = matching_nodes.get(matching_nodes.size() -1);
    }   
    runtime.error(s, n);
    return null;   
  }
 
  /**
   * Print a warning message.
   *
   * @param s The msg.
   * @param n The location.
   * @return null
   */
  protected Object warning(String s, Node n) {
    if (null == n) {
      n = matching_nodes.get(matching_nodes.size() -1);
    }
    runtime.warning(s, n);
    return null;  
 

  /**
   * Print a error message.
   *
   * @param tag The message class.
   * @param msg The message.
   * @param o The node generating the message.
   */
  protected void showMessage(String tag, String msg, Object o){
    Node n = (Node)o;
    if (null == n) {
      if ("error".equals(tag)) {
        if (matching_nodes.size() - 1 < 0) {
          runtime.error(msg);         
        } else {
          Node nod = matching_nodes.get(matching_nodes.size() - 1);
          runtime.error(msg,nod);         
        }       
      } else {
        if (matching_nodes.size() -1 < 0) {
          runtime.warning(msg);         
        } else {
          Node nod = matching_nodes.get(matching_nodes.size() - 1);
          runtime.warning(msg,nod);         
        }
      }
    } else {
      if ("error".equals(tag)) {
        runtime.error(msg,n);
      } else {
        runtime.warning(msg,n);
      }
    }
  } 
  
  /**
   * Process scope.
   *
   * @param n The node to process.
   * @param getScope The generated getScope function.
   */
  protected void processScope(Node n, Function.F1<?, Node> getScope) {
    // the function getScope(Node n) will be created
    // after visiting the scope declaration
    Scope scop = cast(getScope.apply(n));
    if (null == scop) {
      throw new AssertionError("unable to get scope for : " + n.getName());
    }
    // get the scope name
    ScopeKind<?> scopename = scop.getTuple().get1();
    String str;
    if (scopename.isNamed()) {
      // Cast to object first to avoid inconvertible types error with Java 5.
      Name<?> strname = ((ScopeKind.Named)(Object)scopename).getTuple().get1();
      if (strname.isSimpleName()) {
        // Cast to object first to avoid inconvertible types error with Java 5.
        str = ((Name.SimpleName)(Object)strname).getTuple().get1();
      } else {
        str = "QualifiedName";
      }     
    } else if (scopename.isAnonymous()) {
      // Cast to object first to avoid inconvertible types error with Java 5.
      str = gamma.
        freshName(((ScopeKind.Anonymous)(Object)scopename).getTuple().get1());
    } else {
      // Cast to object first to avoid inconvertible types error with Java 5.
      str = gamma.
        freshName(((ScopeKind.Temporary)(Object)scopename).getTuple().get1());
   
    Pair<Node> nodes = scop.getTuple().get2();
   
    ArrayList<Node> nodeList = (ArrayList<Node>)nodes.list();
    int index;
    int prob = -1;
    // check and remove set scope property in those nodes
    for (index = 0; index < nodeList.size(); index++) {
      Node node = nodeList.get(index);
      if ( node == null) {
        continue;
      }
      if (!node.hasProperty(ENTERSCOPE)) {
        node.setProperty(ENTERSCOPE,str);
        node.setProperty(EXITSCOPE,true);
        prob = index;
      }
    }
    if ((prob!=-1) && (scopename.isTemporary())) {
      Node node = nodeList.get(prob);
      node.setProperty("deleteScope",str);
    }
  }
 
  /**
   * Run this typechecker on the tree rooted at n.
   *
   * @param n The tree root.
   * @return The Symbol table of the ast.
   */
  public SymbolTable run(Node n) {
    if (null == analyzer) {
      runtime.error("Analyzer is null");
      runtime.exit();
    }
   
    if (null == n) {
      runtime.error("Tree root is null");
      runtime.exit();
    } else {
      root = n;
    }

    matching_nodes.add(n);
    analyzer.apply(n);

    return gamma;
 

  /**
   * Returns the (possibly annotated and modified) AST root.
   *
   * @return The root of the ast.
   */
  public Node getASTRoot() {
    return root;
  }

  /**
   * Utility function to print a nicely formatted ast for debugging.
   *
   * @param n The ast root.
   */
  protected void printAST(Node n){
    runtime.console().pln().format(n).pln().flush();
  }

  /**
   * Convert the specified object to a string.
   *
   * @param o The object.
   * @return The corresponding string.
   */
  public static final String toString(Object o) {
    return null == o ? "?" : o.toString();
  }

   /**
   * Test for equality between two objects.
   *
   * @param o1 The first object.
   * @param o2 The second object.
   * @return <code>true</code> if both are equal, false otherwise.
   */
  public static Boolean equal(Object o1, Object o2) {
    return null == o1 ? null == o2 : o1.equals(o2);
  }

  /**
   * Test for equality between two objects.
   *
   * @param o1 The first object.
   * @param o2 The second object.
   * @return <code>true</code> if the o1 and o2 not equal, false otherwise.
   */
  protected static Boolean not_equal(Object o1, Object o2) {
    return null == o1 ? null != o2 : (! o1.equals(o2));
  }

  /**
   * Ignore the specified value.
   *
   * @param o The value to ignore.
   */
  protected static final void discard(Object o) {
    // Nothing to do.
  }
 
  /**
   * Cast an object to type T.
   *
   * @param arg The object to cast.
   * @return The object cast to T
   */
  @SuppressWarnings("unchecked")
  public static final <T> T cast(Object arg) {
    return (T)arg;
  }
}
TOP

Related Classes of xtc.typical.Analyzer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.