ljr/wcmtools/s2/danga/s2/Checker.java

368 lines
9.8 KiB
Java
Executable File

package danga.s2;
import java.util.Hashtable;
import java.util.ListIterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.Collections;
import java.util.TreeSet;
import java.util.Iterator;
public class Checker
{
// combined (all layers)
private Hashtable classes; // class name -> NodeClass
private Hashtable props; // property name -> Type
private Hashtable funcs; // FuncID -> return type
private Hashtable funcBuiltin; // FuncID -> Boolean (is builtin)
private LinkedList localblocks; // NodeStmtBlock scopes .. last is deepest (closest)
private Type returnType;
private String funcClass; // current function class
private Hashtable derclass; // classname -> LinkedList<classname>
private boolean inFunction; // checking in a function now?
// per-layer
private Hashtable funcDist; // FuncID -> [ distance, NodeFunction ]
private Hashtable funcIDs; // NodeFunction -> Set<FuncID>
private boolean hitFunction; // true once a function has been declared/defined
// per function
private int funcNum = 0;
private Hashtable funcNums; // FuncID -> Integer(funcnum)
private LinkedList funcNames; // Strings
public Checker ()
{
classes = new Hashtable();
props = new Hashtable();
funcs = new Hashtable();
funcBuiltin = new Hashtable();
derclass = new Hashtable();
localblocks = new LinkedList();
}
// class functions
public void addClass (String name, NodeClass nc) {
classes.put(name, nc);
// make sure that the list of classes that derive from this
// one exists.
if (derclass.get(name) == null) {
derclass.put(name, new LinkedList());
}
// and if this class derives from another, add ourselves
// to that list.
String parent = nc.getParentName();
if (parent != null) {
LinkedList l = (LinkedList) derclass.get(parent);
l.add(name);
}
}
public NodeClass getClass (String name) {
if (name == null) return null;
return (NodeClass) classes.get(name);
}
public String getParentClassName (String name) {
NodeClass nc = getClass(name);
if (nc == null) return null;
return nc.getParentName();
}
public boolean isValidType (Type t) {
if (t == null) return false;
if (t.isPrimitive()) return true;
if (getClass(t.baseType()) != null) return true;
return false;
}
// property functions
public void addProperty (String name, Type t) {
props.put(name, t);
}
public Type propertyType (String name) {
return (Type) props.get(name);
}
// return type of null means no return type.
public void setReturnType (Type t) {
returnType = t;
}
public Type getReturnType () {
return returnType;
}
// function functions
public void addFunction (String funcid, Type t, boolean builtin)
throws Exception
{
// make sure function doesn't mask a lower one with a different type
Type existing = functionType(funcid);
if (existing != null && ! existing.equals(t)) {
throw new Exception("Can't override function '" + funcid + "' with new "+
"return type.");
}
funcs.put(funcid, t);
funcBuiltin.put(funcid, new Boolean(builtin));
}
public Type functionType (String funcid) {
return (Type) funcs.get(funcid);
}
public boolean isFuncBuiltin (String funcid) {
Boolean b = (Boolean)funcBuiltin.get(funcid);
return b == null ? false : b.booleanValue();
}
// returns true if there's a string -> t class constructor
public boolean isStringCtor (Type t) {
if (t == null) return false;
if (! t.isSimple()) return false;
String cname = t.baseType();
String ctorid = cname+"::"+cname+"(string)";
Type rt = functionType(ctorid);
if (rt == null || ! rt.isSimple() || ! rt.baseType().equals(cname) ||
! isFuncBuiltin(ctorid))
return false;
return true;
}
// setting/getting the current function class we're in
public void setCurrentFunctionClass (String f) {
funcClass = f;
}
public String getCurrentFunctionClass () {
return funcClass;
}
// setting/getting whether in a function now
public void setInFunction (boolean in) {
inFunction = in;
}
public boolean getInFunction () {
return inFunction;
}
// variable lookup
public void pushLocalBlock (NodeStmtBlock nb) {
localblocks.addLast(nb);
}
public void popLocalBlock () {
localblocks.removeLast();
}
public NodeStmtBlock getLocalScope () {
if (localblocks.size() == 0)
return null;
return (NodeStmtBlock) localblocks.getLast();
}
public Type localType (String local)
{
if (localblocks.size() == 0)
return null;
ListIterator li = localblocks.listIterator(localblocks.size());
while (li.hasPrevious()) {
NodeStmtBlock nb = (NodeStmtBlock) li.previous();
Type t = nb.getLocalVar(local);
if (t != null)
return t;
}
return null;
}
public Type memberType (String clas, String member)
{
NodeClass nc = getClass(clas);
if (nc == null) return null;
return nc.getMemberType(member);
}
public void setHitFunction (boolean b) {
hitFunction = b;
}
public boolean getHitFunction () {
return hitFunction;
}
public boolean hasDerClasses (String clas) {
LinkedList l = (LinkedList) derclass.get(clas);
return l.size() > 0;
}
public ListIterator getDerClassesIter (String clas) {
LinkedList l = (LinkedList) derclass.get(clas);
return l.listIterator();
}
public void setFuncDistance (String funcID, DerItem df)
{
//System.err.println("setFuncDistance(\""+funcID+"\", "+df+")");
DerItem existing = (DerItem) funcDist.get(funcID);
if (existing == null || df.dist < existing.dist) {
funcDist.put(funcID, df);
///// keep the funcIDs hashes -> FuncID set up-to-date
// removing the existing funcID from the old set first
if (existing != null) {
Set oldset = (Set) funcIDs.get(existing.nf);
oldset.remove(funcID);
}
// first, make sure the set exists
Set idset = (Set) funcIDs.get(df.nf);
if (idset == null) {
idset = Collections.synchronizedSortedSet(new TreeSet());
funcIDs.put(df.nf, idset);
}
// now, insert this funcID for this function.
idset.add(funcID);
}
}
public Iterator getFuncIDsIter (NodeFunction nf)
{
Set s = (Set) funcIDs.get(nf);
if (s == null) {
System.err.println("WARN: no funcids for nf="+nf);
return null;
}
return s.iterator();
}
// per function
public void resetFunctionNums () {
funcNum = 0;
funcNums = new Hashtable();
funcNames = new LinkedList();
}
public int functionNum (String funcID) {
Integer num = (Integer) funcNums.get(funcID);
if (num == null) {
num = new Integer(++funcNum);
funcNums.put(funcID, num);
funcNames.add(funcID);
}
return num.intValue();
}
public Hashtable getFuncNums () {
return funcNums;
}
public LinkedList getFuncNames () {
return funcNames;
}
// check if type 't' is a subclass of 'w'
public boolean typeIsa (Type t, Type w)
{
if (! Type.sameMods(t, w))
return false;
String is = t.baseType();
String parent = w.baseType();
while (is != null) {
if (is.equals(parent))
return true;
NodeClass nc = getClass(is);
is = nc != null ? nc.getParentName() : null;
};
return false ;
}
// check to see if a class or parents has a
// "toString()" method
public boolean classHasToString (String clas) {
Type et = functionType(clas+"::toString()");
if (et != null && et.equals(Type.STRING))
return true;
return false;
}
// check to see if a class or parents has an
// "as_string" string member
public boolean classHasAsString (String clas) {
Type et = memberType(clas, "as_string");
if (et != null && et.equals(Type.STRING))
return true;
return false;
}
// ------
public void checkLayer (Layer lay) throws Exception
{
// initialize layer-specific data structures
funcDist = new Hashtable();
funcIDs = new Hashtable();
hitFunction = false;
// check to see that they declared the layer type, and that
// it isn't bogus.
{
// what the S2 source says the layer is
String dtype = lay.getDeclaredType();
if (dtype == null)
throw new Exception("Layer type not declared.");
// what type s2compile thinks it is
String type = lay.getType();
if (! dtype.equals(type)) {
throw new Exception("Layer is declared " + dtype +
" but expecting a "+type+" layer.");
}
// now that we've validated their type is okay
lay.setType(dtype);
}
LinkedList nodes = lay.getNodes();
ListIterator li = nodes.listIterator();
while (li.hasNext()) {
Node n = (Node) li.next();
n.check(lay, this);
}
if (lay.getType().equals("core")) {
String mv = lay.getLayerInfo("majorversion");
if (mv == null) {
throw new Exception("Core layers must declare 'majorversion' layerinfo.");
}
}
}
// static stuff
// returns the signature of a function and its arguments, in the form
// of: Classname::funcName(String,UserPage,int)
public static String functionID (String clas, String func, Object o)
{
StringBuffer sb = new StringBuffer(70);
if (clas != null) {
sb.append(clas); sb.append("::");
}
sb.append(func);
sb.append("(");
// where Object can be a NodeFormals or FIXME: other stuff
if (o == null) {
// do nothing.
} else if (o instanceof NodeFormals) {
NodeFormals nf = (NodeFormals) o;
sb.append(nf.typeList());
} else if (o instanceof String) {
String s = (String) o;
sb.append(s);
} else {
sb.append("[-----]");
}
sb.append(")");
return sb.toString();
}
}