Variable.java

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

import edu.udel.cis.vsl.tass.model.IF.SyntaxException;
import edu.udel.cis.vsl.tass.model.IF.expression.ExpressionIF;
import edu.udel.cis.vsl.tass.model.IF.type.ArrayTypeIF;
import edu.udel.cis.vsl.tass.model.IF.type.TypeIF;
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.VariableIF;
import edu.udel.cis.vsl.tass.model.impl.scope.Scope;
import edu.udel.cis.vsl.tass.util.Source;
import edu.udel.cis.vsl.tass.util.TASSInternalException;

public class Variable implements VariableIF {

	protected String name;

	protected TypeIF type;

	protected Scope scope;

	protected int idInScope = -1;

	protected Source sourceCode = null;

	protected ExpressionIF[] dimensions = null;

	protected ExpressionIF initializationExpression = null;

	/** Allow null scope for certain variables, such as bound variables. */
	public Variable(String name, TypeIF type, Scope scope) {
		if (name == null)
			throw new NullPointerException("Null variable name");
		if (type == null)
			throw new NullPointerException("Null type");
		if (type.kind() == TypeKind.VOID) {
			throw new RuntimeException("Variable type cannot be void.");
		}
		this.name = name;
		this.type = type;
		this.scope = scope;
	}

	@Override
	public String name() {
		return name;
	}

	@Override
	public TypeIF type() {
		return type;
	}

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

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

	@Override
	public boolean equals(Object object) {
		if (object instanceof Variable) {
			Variable that = (Variable) object;

			return name.equals(that.name) && scope.equals(that.scope);
		}
		return false;
	}

	@Override
	public int hashCode() {
		int result = name.hashCode() + scope.hashCode();

		return result;
	}

	@Override
	public String toString() {
		return name;
	}

	@Override
	public Source getSource() {
		return sourceCode;
	}

	@Override
	public void setSource(Source sourceCode) {
		this.sourceCode = sourceCode;
	}

	@Override
	public Scope scope() {
		return scope;
	}

	@Override
	public ExpressionIF[] dimensionExpressions() {
		return dimensions;
	}

	/**
	 * Do the dimensions of dimensions need to add up to dimension of array? For
	 * now, yes.
	 * 
	 * Are null entries allowed? For now, no.
	 * 
	 * @param dimensions
	 * @throws SyntaxException
	 */
	public void setDimensions(ExpressionIF[] dimensions) throws SyntaxException {
		if (type.kind() != TypeKind.ARRAY)
			throw new SyntaxException(this,
					"Cannot set dimensions of a non-array variable");
		ArrayTypeIF arrayType = (ArrayTypeIF) type;
		int dimension = arrayType.dimension();
		int dimensionCount = 0;
		int dimensionsLength = dimensions.length;

		for (int i = 0; i < dimensionsLength; i++) {
			ExpressionIF extent = dimensions[i];

			if (extent == null) {
				throw new SyntaxException(this, "array extent expression " + i
						+ " is null");
			}

			TypeIF extentType = extent.type();
			TypeKind extentTypeKind = extentType.kind();

			dimensionCount++;
			if (extentTypeKind == TypeKind.INTEGER) {
			} else if (extentTypeKind == TypeKind.ARRAY) {
				ArrayTypeIF extentArrayType = (ArrayTypeIF) extentType;
				TypeIF baseType = extentArrayType.baseType();

				if (baseType.kind() != TypeKind.INTEGER) {
					throw new SyntaxException(this, "array extent expression "
							+ i + " is array of non-integer base type: "
							+ extent);
				}
				dimensionCount += extentArrayType.dimension();
			} else {
				throw new TASSInternalException(
						"Unknown kind of array extent: " + extent);
			}
		}
		if (dimensionCount != dimension) {
			throw new SyntaxException(this,
					"Dimensions array does not match dimension of variable "
							+ this + ": " + dimensionCount + " != " + dimension);
		}
		this.dimensions = dimensions;
	}

	@Override
	public String decl() {
		String result;

		if (type instanceof ArrayTypeIF && !(this instanceof FormalVariableIF)) {
			if (dimensions == null)
				throw new RuntimeException(this.sourceCode + ": "
						+ "no dimensions given for this array variable");
			ArrayTypeIF arrayType = (ArrayTypeIF) type;
			result = arrayType.baseType().toString();
			for (ExpressionIF dimension : dimensions) {
				result += "[" + dimension + "]";
			}
		} else {
			result = type().toString();
		}
		return result;
	}

	@Override
	public ExpressionIF initializationExpression() {
		return initializationExpression;
	}

	public void setInitializationExpression(ExpressionIF expression)
			throws SyntaxException {
		if (expression != null) {
			TypeKind kind = type.kind();
			TypeIF expressionType = expression.type();

			if (!(type.equals(expressionType) || (kind == TypeKind.RATIONAL && expressionType
					.kind() == TypeKind.INTEGER)))
				throw new SyntaxException(expression,
						"Type of initialization expression does not match type of v: "
								+ expressionType + " vs. " + type);
			this.initializationExpression = expression;
		}
	}
}