LocalScope.java

package edu.udel.cis.vsl.tass.model.impl.scope;

import java.util.Collection;
import java.util.Vector;

import edu.udel.cis.vsl.tass.model.IF.FunctionIF;
import edu.udel.cis.vsl.tass.model.IF.SyntaxException;
import edu.udel.cis.vsl.tass.model.IF.location.LocationIF;
import edu.udel.cis.vsl.tass.model.IF.scope.LocalScopeIF;
import edu.udel.cis.vsl.tass.model.IF.scope.ScopeIF;
import edu.udel.cis.vsl.tass.model.IF.type.TypeIF.TypeKind;
import edu.udel.cis.vsl.tass.model.IF.variable.FormalVariableIF;
import edu.udel.cis.vsl.tass.model.IF.variable.LocalVariableIF;

public class LocalScope extends Scope implements LocalScopeIF {

	/**
	 * The scope ID, a unique ID number among all scopes belonging to the
	 * function. It is not necessarily set until the function is completed.
	 */
	private int id = -1;

	private int depth;

	private FunctionIF function;

	private Collection<LocalVariableIF> properLocals = new Vector<LocalVariableIF>();

	private Collection<LocationIF> locations = new Vector<LocationIF>();

	/**
	 * The list of local scope ancestors of this scope (including this scope).
	 * The outermost scope is in index 0, and this scope is in the last position
	 * (ancestors.length -1). This is computed once, at creation, and stored
	 * here. The top-most scope is the first ancestor whose parent is null.
	 */
	private LocalScopeIF[] ancestors;

	/**
	 * Constructor to be used for creating outermost scope. The parent must be a
	 * non-null scope. The depth and id number for the new scope will both be 0.
	 * 
	 * @param parent
	 * @param function
	 */
	public LocalScope(ScopeIF parent, FunctionIF function) {
		super(parent, ScopeKind.LOCAL, parent.model());
		assert function != null;
		this.depth = 0;
		this.function = function;

		int numFormals = function.numFormals();

		for (int i = 0; i < numFormals; i++) {
			variables.add(null);
		}
		this.id = 0;
		name = "outermost local scope for function " + function + " of "
				+ function.process();
		ancestors = new LocalScopeIF[] { this };
	}

	/**
	 * Constructor to be used for creating any scope that is not the outermost
	 * scope. The function is the function of the given parent. Assigns an id
	 * number which is the current number of local scopes in the function.
	 * 
	 * @parent the local scope that is to immediately contain the new local
	 *         scope
	 */
	public LocalScope(LocalScopeIF parent) {
		super(parent, ScopeKind.LOCAL, parent.model());
		this.function = parent.function();
		this.id = function.numScopes();
		this.depth = parent.depth() + 1;
		this.name = "internal local scope " + id + " for function " + function
				+ " of " + function.process() + " with parent scope "
				+ parent.localId();

		{
			LocalScopeIF[] parentAncestors = parent.localAncestors();
			int numParentAncestors = parentAncestors.length;

			ancestors = new LocalScopeIF[numParentAncestors + 1];
			for (int i = 0; i < numParentAncestors; i++)
				ancestors[i] = parentAncestors[i];
			ancestors[numParentAncestors] = this;
		}
	}

	@Override
	public int localId() {
		return id;
	}

	public void setLocalId(int id) {
		this.id = id;
	}

	public void setFormal(FormalVariableIF formal) throws SyntaxException {
		int index = formal.idInScope();
		LocalVariableIF oldFormal = (LocalVariableIF) variables.get(index);

		// this can be checked in factory when formal is created...
		if (index < 0 || index >= function.numFormals())
			throw new SyntaxException(formal, "Index " + index
					+ " is out of range for function " + function
					+ ", numFormals=" + function.numFormals());
		if (oldFormal != null)
			throw new SyntaxException(formal, "Formal " + index
					+ " of function " + function + " already set: " + oldFormal);
		addName(formal);
		variables.set(index, formal);
		variableSet.add(formal);
	}

	public void addProperLocalVariable(LocalVariableIF variable)
			throws SyntaxException {
		if (variable instanceof FormalVariableIF)
			throw new SyntaxException(
					"Cannot use addProperLocalVariable to add formal variable: "
							+ variable);
		addVariable(variable);
		properLocals.add(variable);
	}

	public void complete() throws SyntaxException {
		int numFormals = function.numFormals();

		for (int i = 0; i < numFormals; i++) {
			if (variables.get(i) == null)
				throw new SyntaxException(function, "formal parameter " + i
						+ " has not been defined");
		}
		for (LocalVariableIF variable : properLocals()) {
			if (variable.type().kind() == TypeKind.ARRAY) {
				if (variable.dimensionExpressions() == null) {
					throw new SyntaxException(variable,
							"Need to set dimensions of this array variable");
				}
			}
		}
	}

	@Override
	public int depth() {
		return depth;
	}

	@Override
	public FunctionIF function() {
		return function;
	}

	@Override
	public Collection<LocalVariableIF> properLocals() {
		return properLocals;
	}

	/**
	 * The variables in this scope (including formals) are assigned ID numbers
	 * 0, 1, 2, ..., at completion time. This method returns the variable with
	 * the given ID.
	 */
	@Override
	public LocalVariableIF variableWithId(int id) {
		return (LocalVariableIF) super.variableWithId(id);
	}

	/**
	 * Returns the variable in this scope with the given name, or null if there
	 * isn't one.
	 */
	@Override
	public LocalVariableIF variableWithName(String name) {
		return (LocalVariableIF) super.variableWithName(name);
	}

	public void addLocation(LocationIF location) {
		assert location.scope() == this;
		locations.add(location);
	}

	@Override
	public Collection<LocationIF> locations() {
		return locations;
	}

	@Override
	public LocalScopeIF[] localAncestors() {
		return ancestors;
	}
}