CommonValueFactory.java

package dev.civl.abc.ast.value.common;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

import dev.civl.abc.ast.entity.IF.Entity;
import dev.civl.abc.ast.entity.IF.Entity.EntityKind;
import dev.civl.abc.ast.entity.IF.Function;
import dev.civl.abc.ast.entity.IF.Variable;
import dev.civl.abc.ast.node.IF.ASTNode;
import dev.civl.abc.ast.node.IF.IdentifierNode;
import dev.civl.abc.ast.node.IF.expression.AlignOfNode;
import dev.civl.abc.ast.node.IF.expression.ArrowNode;
import dev.civl.abc.ast.node.IF.expression.CastNode;
import dev.civl.abc.ast.node.IF.expression.CharacterConstantNode;
import dev.civl.abc.ast.node.IF.expression.CompoundLiteralNode;
import dev.civl.abc.ast.node.IF.expression.ConstantNode;
import dev.civl.abc.ast.node.IF.expression.DotNode;
import dev.civl.abc.ast.node.IF.expression.EnumerationConstantNode;
import dev.civl.abc.ast.node.IF.expression.ExpressionNode;
import dev.civl.abc.ast.node.IF.expression.IdentifierExpressionNode;
import dev.civl.abc.ast.node.IF.expression.OperatorNode;
import dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator;
import dev.civl.abc.ast.node.IF.expression.SizeofNode;
import dev.civl.abc.ast.node.IF.expression.StringLiteralNode;
import dev.civl.abc.ast.type.IF.ArrayType;
import dev.civl.abc.ast.type.IF.Enumerator;
import dev.civl.abc.ast.type.IF.Field;
import dev.civl.abc.ast.type.IF.FloatingType;
import dev.civl.abc.ast.type.IF.IntegerType;
import dev.civl.abc.ast.type.IF.ObjectType;
import dev.civl.abc.ast.type.IF.PointerType;
import dev.civl.abc.ast.type.IF.StandardBasicType;
import dev.civl.abc.ast.type.IF.StandardBasicType.BasicTypeKind;
import dev.civl.abc.ast.type.IF.StandardSignedIntegerType;
import dev.civl.abc.ast.type.IF.StandardSignedIntegerType.SignedIntKind;
import dev.civl.abc.ast.type.IF.StandardUnsignedIntegerType;
import dev.civl.abc.ast.type.IF.StandardUnsignedIntegerType.UnsignedIntKind;
import dev.civl.abc.ast.type.IF.StructureOrUnionType;
import dev.civl.abc.ast.type.IF.Type;
import dev.civl.abc.ast.type.IF.Type.TypeKind;
import dev.civl.abc.ast.type.IF.TypeFactory;
import dev.civl.abc.ast.value.IF.AddressValue;
import dev.civl.abc.ast.value.IF.ArrayElementReference;
import dev.civl.abc.ast.value.IF.ArrayValue;
import dev.civl.abc.ast.value.IF.CastValue;
import dev.civl.abc.ast.value.IF.CharacterValue;
import dev.civl.abc.ast.value.IF.ComplexValue;
import dev.civl.abc.ast.value.IF.FunctionReference;
import dev.civl.abc.ast.value.IF.IntegerValue;
import dev.civl.abc.ast.value.IF.MemberReference;
import dev.civl.abc.ast.value.IF.OperatorValue;
import dev.civl.abc.ast.value.IF.RealFloatingValue;
import dev.civl.abc.ast.value.IF.StringValue;
import dev.civl.abc.ast.value.IF.StructureValue;
import dev.civl.abc.ast.value.IF.TypeValue;
import dev.civl.abc.ast.value.IF.TypeValue.TypeValueKind;
import dev.civl.abc.ast.value.IF.UnionValue;
import dev.civl.abc.ast.value.IF.Value;
import dev.civl.abc.ast.value.IF.ValueFactory;
import dev.civl.abc.ast.value.IF.VariableReference;
import dev.civl.abc.config.IF.Configuration;
import dev.civl.abc.config.IF.Configuration.Architecture;
import dev.civl.abc.token.IF.ExecutionCharacter;
import dev.civl.abc.token.IF.StringLiteral;
import dev.civl.abc.token.IF.SyntaxException;
import dev.civl.abc.token.IF.UnsourcedException;

/**
 * Flyweight pattern is used for those values which are immutable. Compound
 * values are mutable.
 * 
 * @author siegel
 * 
 */
public class CommonValueFactory implements ValueFactory {

	// Fields...

	private TypeFactory typeFactory;

	private Map<Value, Value> valueMap = new HashMap<Value, Value>();

	private StandardSignedIntegerType SINT;

	private IntegerType CHAR;

	private BigInteger MAX_JINT = new BigInteger(
			Integer.toString(Integer.MAX_VALUE));

	private Value SINT_ONE, SINT_ZERO;

	private Configuration configuration;

	// Constructors...

	public CommonValueFactory(Configuration configuration,
			TypeFactory typeFactory) {
		this.typeFactory = typeFactory;
		SINT = typeFactory.signedIntegerType(SignedIntKind.INT);
		CHAR = (IntegerType) typeFactory.basicType(BasicTypeKind.CHAR);
		SINT_ONE = integerValue(SINT, 1);
		SINT_ZERO = integerValue(SINT, 0);
		this.configuration = configuration;
	}

	// Exported methods...

	@Override
	public Value evaluate(ExpressionNode expr) throws SyntaxException {
		try {
			return evaluateHelper(expr);
		} catch (UnsourcedException err) {
			throw error(err, expr);
		}
	}

	/**
	 * Evaluates a constant expression.
	 * 
	 * Should apply conversions to result.
	 * 
	 * @param expr
	 * @return
	 * @throws SyntaxException
	 * @throws UnsourcedException
	 *                                if expr is not a constant expression
	 */
	private Value evaluateHelper(ExpressionNode expr)
			throws SyntaxException, UnsourcedException {
		if (expr instanceof AlignOfNode) {
			return alignofValue(((AlignOfNode) expr).getArgument().getType());
		} else if (expr instanceof ArrowNode) {
			ArrowNode arrowNode = (ArrowNode) expr;
			ExpressionNode structOrUnionPointer = arrowNode
					.getStructurePointer();
			Field[] navseq = arrowNode.getNavigationSequence();
			Value value = evaluateDereference(structOrUnionPointer);

			for (Field field : navseq)
				value = evaluateMemberAccess(value, field);
			return value;
		} else if (expr instanceof CastNode) {
			CastNode castNode = (CastNode) expr;

			return evaluateCast(castNode.getCastType().getType(),
					evaluate(castNode.getArgument()));
		} else if (expr instanceof CharacterConstantNode) {
			return ((CharacterConstantNode) expr).getConstantValue();
		} else if (expr instanceof CompoundLiteralNode) {
			return evaluateCompoundLiteral((CompoundLiteralNode) expr);
		} else if (expr instanceof ConstantNode) {
			return ((ConstantNode) expr).getConstantValue();
		} else if (expr instanceof DotNode) {
			DotNode dotNode = (DotNode) expr;
			ExpressionNode structOrUnion = dotNode.getStructure();
			IdentifierNode fieldIdentifier = dotNode.getFieldName();
			Field field = (Field) fieldIdentifier.getEntity();
			Value structOrUnionValue = evaluate(structOrUnion);

			return evaluateMemberAccess(structOrUnionValue, field);
		} else if (expr instanceof OperatorNode) {
			OperatorNode opNode = (OperatorNode) expr;
			Operator operator = opNode.getOperator();

			if (operator == Operator.ADDRESSOF)
				return addressValue(opNode.getArgument(0));
			else { // evaluate arguments and call apply
				int numArgs = opNode.getNumberOfArguments();
				Value[] argValues = new Value[numArgs];
				Type type = expr.getInitialType();

				for (int i = 0; i < numArgs; i++) {
					ExpressionNode arg = opNode.getArgument(i);
					Value argValue = evaluate(arg);

					argValues[i] = applyConversions(arg, argValue);
				}
				return apply(type, operator, argValues);
			}
		} else if (expr instanceof SizeofNode) {
			return sizeofValue((((SizeofNode) expr).getArgument()).getType());
		} else if (expr instanceof StringLiteralNode) {
			return ((StringLiteralNode) expr).getConstantValue();
		} else if (expr instanceof EnumerationConstantNode) {
			IdentifierNode identNode = ((EnumerationConstantNode) expr)
					.getName();
			Entity entity = identNode.getEntity();

			if (entity.getEntityKind() == EntityKind.ENUMERATOR) {
				return ((Enumerator) entity).getValue();
			}
			return null;
		} else if (expr instanceof IdentifierExpressionNode) {
			throw new UnsourcedException("Variable in constant expression");
		}
		return null;
	}

	@Override
	public IntegerValue plusOne(IntegerValue value) {
		return integerValue(value.getType(),
				value.getIntegerValue().add(BigInteger.ONE));
	}

	@Override
	public AddressValue addressValue(ExpressionNode lhs)
			throws SyntaxException {
		if (lhs instanceof IdentifierExpressionNode) {
			Entity entity = ((IdentifierExpressionNode) lhs).getIdentifier()
					.getEntity();
			EntityKind kind = entity.getEntityKind();

			if (kind == EntityKind.VARIABLE) {
				return variableReference((Variable) entity);
			} else if (kind == EntityKind.FUNCTION) {
				return functionReference((Function) entity);
			} else {
				throw error("Operand of & not variable or function identifier",
						lhs);
			}
		} else if (lhs instanceof DotNode) {
			AddressValue structureOrUnionReference = addressValue(
					((DotNode) lhs).getStructure());
			Field field = (Field) ((DotNode) lhs).getFieldName().getEntity();

			return memberReference(structureOrUnionReference, field);
		} else if (lhs instanceof OperatorNode) {
			OperatorNode opNode = (OperatorNode) lhs;
			Operator operator = ((OperatorNode) lhs).getOperator();

			if (operator == Operator.SUBSCRIPT) {
				AddressValue arrayReference = addressValue(
						opNode.getArgument(0));
				Value index = evaluate(opNode.getArgument(1));

				return arrayElementReference(arrayReference, index);
			} else if (operator == Operator.DEREFERENCE) {
				return (AddressValue) evaluate(opNode.getArgument(0));
			}
		}
		throw error("Cannot take address of expression", lhs);
	}

	@Override
	public IntegerValue integerValue(IntegerType type,
			BigInteger integerValue) {
		return (IntegerValue) canonic(
				new CommonIntegerValue(type, integerValue));
	}

	@Override
	public IntegerValue integerValue(IntegerType type, int intValue) {
		return integerValue(type, BigInteger.valueOf(intValue));
	}

	@Override
	public RealFloatingValue realFloatingValue(FloatingType type, int radix,
			BigInteger wholePartValue, BigInteger fractionPartValue,
			int fractionLength, BigInteger exponentValue) {
		return (RealFloatingValue) canonic(
				new CommonRealFloatingValue(type, radix, wholePartValue,
						fractionPartValue, fractionLength, exponentValue));
	}

	@Override
	public ComplexValue complexValue(FloatingType type,
			RealFloatingValue realPart, RealFloatingValue imaginaryPart) {
		return (ComplexValue) canonic(
				new CommonComplexValue(type, realPart, imaginaryPart));
	}

	@Override
	public Value sizeofValue(Type type) {
		if (this.configuration.getSVCOMP()) {
			int sizeofType = this.sizeofType(type);

			if (sizeofType > 0)
				return integerValue(
						(IntegerType) typeFactory.basicType(BasicTypeKind.INT),
						BigInteger.valueOf(sizeofType));

		}
		return (TypeValue) canonic(new CommonTypeValue(typeFactory.size_t(),
				TypeValueKind.SIZEOF, type));
	}

	/**
	 * For svcomp, there are two architectures: 32-bit and 64-bit.
	 * 
	 * https://wiki.debian.org/ArchitectureSpecificsMemo For 32-bit, uses i386
	 * in the above link; for 64-bit, amd64.
	 * 
	 * short int long long long float double long double void*
	 * 
	 * The size of types is factoring out here:
	 * <ul>
	 * <li>sizeof(short)=2</li>
	 * <li>sizeof(int)=4</li>
	 * <li>sizeof(long)=sizeof(void*)</li>
	 * <li>sizeof(long long)=8</li>
	 * <li>sizeof(float)=4</li>
	 * <li>sizeof(double)=8</li>
	 * <li>sizeof(void*)=4 if 32-bit; 8 if 64-bit.</li>
	 * </ul>
	 * 
	 * @return returns the size (a positive integer) of the given type; or -1 if
	 *         the size of that type is not specified or can't be decided
	 *         statically.
	 */
	private int sizeofType(Type type) {
		if (this.configuration.getArchitecture() == Architecture.UNKNOWN)
			return -1;

		TypeKind typeKind = type.kind();

		switch (typeKind) {
			case BASIC : {
				StandardBasicType basicType = (StandardBasicType) type;
				BasicTypeKind basicKind = basicType.getBasicTypeKind();

				switch (basicKind) {
					case SIGNED_CHAR :
					case UNSIGNED_CHAR :
					case CHAR :
						return 1;
					case DOUBLE :
						return 8;
					case FLOAT :
						return 4;
					case UNSIGNED :
					case INT :
						return 4;
					case UNSIGNED_LONG :
					case LONG :
						if (this.configuration
								.getArchitecture() == Architecture._32_BIT)
							return 4;
						else
							return 8;
					case UNSIGNED_LONG_LONG :
					case LONG_LONG :
						return 8;
					case UNSIGNED_SHORT :
					case SHORT :
						return 2;
					default :
						return -1;
				}
			}
			case ARRAY : {
				ArrayType arrayType = (ArrayType) type;
				int sizeOfEleType = this.sizeofType(arrayType.getElementType());
				IntegerValue size = arrayType.getConstantSize();

				if (size == null || sizeOfEleType < 0)
					return -1;
				return size.getIntegerValue().intValue() * sizeOfEleType;
			}
			case OTHER_INTEGER :
				return 4;
			case POINTER : {
				if (this.configuration
						.getArchitecture() == Architecture._32_BIT)
					return 4;
				else
					return 8;
			}
			default : {
				return -1;
			}
		}
	}

	@Override
	public TypeValue alignofValue(Type type) {
		return (TypeValue) canonic(new CommonTypeValue(typeFactory.size_t(),
				TypeValueKind.ALIGNOF, type));
	}

	@Override
	public CastValue castValue(Type castType, Value argument) {
		return (CastValue) canonic(new CommonCastValue(castType, argument));
	}

	@Override
	public OperatorValue operatorValue(Type type, Operator operator,
			Value[] arguments) {
		return (OperatorValue) canonic(
				new CommonOperatorValue(type, operator, arguments));
	}

	@Override
	public StructureValue newStructureValue(StructureOrUnionType type) {
		return new CommonStructureValue(type);
	}

	@Override
	public UnionValue newUnionValue(StructureOrUnionType unionType, Field field,
			Value memberValue) {
		return (UnionValue) canonic(
				new CommonUnionValue(unionType, field, memberValue));
	}

	@Override
	public ArrayValue newArrayValue(ArrayType type) {
		return new CommonArrayValue(type);
	}

	@Override
	public Answer isZero(Value value) {
		return value.isZero();
	}

	@Override
	public CharacterValue characterValue(ExecutionCharacter character) {
		IntegerType type;

		switch (character.getCharacterKind()) {
			case CHAR :
				type = CHAR;
				break;
			case WCHAR :
				type = typeFactory.wchar_t();
				break;
			case CHAR16 :
				type = typeFactory.char16_t();
				break;
			case CHAR32 :
				type = typeFactory.char32_t();
				break;
			default :
				throw new RuntimeException("unreachable");
		}
		return (CharacterValue) canonic(
				new CommonCharacterValue(type, character));
	}

	@Override
	/**
	 * Precondition: string literal should already have the \0 appended. In
	 * particular, it has at least one character.
	 * 
	 */
	public StringValue stringValue(StringLiteral literal) {
		int length = literal.getNumCharacters();
		IntegerValue size = integerValue(SINT, length);
		IntegerType characterType;
		ArrayType type;

		switch (literal.getStringKind()) {
			case CHAR :
			case UTF_8 :
				characterType = CHAR;
				break;
			case WCHAR :
				characterType = typeFactory.wchar_t();
				break;
			case CHAR16 :
				characterType = typeFactory.char16_t();
				break;
			case CHAR32 :
				characterType = typeFactory.char32_t();
				break;
			default :
				throw new RuntimeException("unreachable");
		}
		type = typeFactory.arrayType(characterType, size);
		return (StringValue) canonic(new CommonStringValue(type, literal));
	}

	public VariableReference variableReference(Variable variable) {
		PointerType pointerType = typeFactory.pointerType(variable.getType());

		return (VariableReference) canonic(
				new CommonVariableReference(pointerType, variable));
	}

	public FunctionReference functionReference(Function function) {
		PointerType pointerType = typeFactory.pointerType(function.getType());

		return (FunctionReference) canonic(
				new CommonFunctionReference(pointerType, function));
	}

	public ArrayElementReference arrayElementReference(
			AddressValue arrayReference, Value index) {
		PointerType arrayReferenceType = arrayReference.getType();
		ArrayType arrayType = (ArrayType) arrayReferenceType.referencedType();
		// might need to strip qualifiers?
		ObjectType elementType = arrayType.getElementType();
		PointerType elementReferenceType = typeFactory.pointerType(elementType);

		return (ArrayElementReference) canonic(new CommonArrayElementReference(
				elementReferenceType, arrayReference, index));
	}

	public MemberReference memberReference(
			AddressValue structureOrUnionReference, Field field) {
		ObjectType memberType = field.getType();
		PointerType memberReferenceType = typeFactory.pointerType(memberType);

		return (MemberReference) canonic(new CommonMemberReference(
				memberReferenceType, structureOrUnionReference, field));
	}

	// Helper methods.......................................................

	private Value canonic(Value value) {
		Value result = valueMap.get(value);

		if (result == null) {
			valueMap.put(value, value);
			return value;
		}
		return result;
	}

	/**
	 * Given expression e of pointer type, evaluate *e. If e had type
	 * [qualified] pointer to t, resulting value has type t.
	 * 
	 * @param pointerExpression
	 * @return
	 */
	private Value evaluateDereference(ExpressionNode pointerExpression)
			throws SyntaxException {
		return null; // how can this happen in a constant expression?
	}

	private Value evaluateCast(Type castType, Value value)
			throws UnsourcedException {
		// TODO: cast concrete numeric types if you can, pointer types, ...
		if (value == null)
			return null;
		if (castType.compatibleWith(value.getType()))
			return value;
		return canonic(new CommonCastValue(castType, value));
	}

	private Value evaluateCompoundLiteral(CompoundLiteralNode node) {
		// TODO
		return null;
	}

	private Value evalMinus(Type type, Value arg0) throws UnsourcedException {
		if (arg0 instanceof IntegerValue) {
			BigInteger big = ((IntegerValue) arg0).getIntegerValue();
			BigInteger neg = big.negate();
			Value result = integerValue((IntegerType) type, neg);

			return result;
		}
		throw new UnsourcedException(
				"Unsupported feature: non-integer negative");
	}

	private Value evalBinaryNumericOp(Type type, Operator operator, Value arg0,
			Value arg1) throws UnsourcedException {
		Object val0, val1;

		if (arg0 instanceof IntegerValue) {
			val0 = ((IntegerValue) arg0).getIntegerValue();
		} else if (arg0 instanceof RealFloatingValue) {
			val0 = ((RealFloatingValue) arg0).getDoubleValue();
		} else
			throw new UnsourcedException(
					"Expected integer or real constant, not " + arg0);
		if (arg1 instanceof IntegerValue) {
			val1 = ((IntegerValue) arg1).getIntegerValue();
		} else if (arg1 instanceof RealFloatingValue) {
			val1 = ((RealFloatingValue) arg1).getDoubleValue();
		} else
			throw new UnsourcedException(
					"Expected integer or real constant, not " + arg1);
		if (val0 instanceof BigInteger && val1 instanceof BigInteger) {
			BigInteger big0 = ((BigInteger) val0);
			BigInteger big1 = ((BigInteger) val1);
			BigInteger bigVal;

			switch (operator) {
				case TIMES :
					bigVal = big0.multiply(big1);
					break;
				case PLUS :
					bigVal = big0.add(big1);
					break;
				case MINUS :
					bigVal = big0.subtract(big1);
					break;
				case DIV :
					bigVal = big0.divide(big1);
					break;
				case MOD :
					bigVal = big0.mod(big1);
					break;
				default :
					throw new UnsourcedException(
							"Unexpected operator: " + operator);
			}
			return integerValue((IntegerType) type, bigVal);
		} else {
			throw new UnsourcedException(
					"multiplication of floating constants");
		}

	}

	/**
	 * Applies an operator to some arguments to yield a constant expression.
	 * 
	 * @param type
	 * @param operator
	 * @param args
	 * @return
	 * @throws UnsourcedException
	 *                                if result is not a constant expression
	 */
	private Value apply(Type type, Operator operator, Value[] args)
			throws UnsourcedException {
		int numArgs = args.length;

		switch (operator) {
			case BITAND : // & bit-wise and
			case BITCOMPLEMENT : // ~ bit-wise complement
			case BITOR : // | bit-wise inclusive or
			case BITXOR : // ^ bit-wise exclusive or
			case CONDITIONAL : // ?: the conditional operator
			case DEREFERENCE : // * pointer dereference
			case SUBSCRIPT : // [] array subscript
				break;

			case SHIFTLEFT : // << shift left
			case SHIFTRIGHT : // >> shift right
			case EQUALS : // == equality
			case GT : // > greater than
			case GTE : // >= greater than or equals
			case LT : // < less than
			case LTE : // <= less than or equals
			case NEQ : // != not equals
				if (numArgs == 2)
					return evalBinaryIntegerOp(operator, args[0], args[1]);
				else
					throw new UnsourcedException(
							"Expected two arguments for operator " + operator);

			case LAND : // && logical and
				if (numArgs == 2) {
					Value lval = evalBinaryIntegerOp(Operator.EQUALS, args[0],
							SINT_ONE);
					if (!lval.equals(SINT_ONE))
						return SINT_ZERO;
					Value rval = evalBinaryIntegerOp(Operator.EQUALS, args[0],
							SINT_ONE);
					if (rval.equals(SINT_ONE))
						return SINT_ONE;
					else
						return SINT_ZERO;

				} else
					throw new UnsourcedException(
							"Expected two arguments for operator " + operator);

			case LOR : // || logical or
				if (numArgs == 2) {
					Value lval = evalBinaryIntegerOp(Operator.EQUALS, args[0],
							SINT_ONE);
					if (lval.equals(SINT_ONE))
						return SINT_ONE;
					Value rval = evalBinaryIntegerOp(Operator.EQUALS, args[0],
							SINT_ONE);
					if (rval.equals(SINT_ONE))
						return SINT_ONE;
					else
						return SINT_ZERO;

				} else
					throw new UnsourcedException(
							"Expected two arguments for operator " + operator);

			case NOT : // ! logical not
				if (numArgs == 1)
					return evalBinaryIntegerOp(Operator.EQUALS, args[0],
							SINT_ZERO);
				else
					throw new UnsourcedException(
							"Expected one arguments for operator " + operator);

			case PLUS : // + binary addition, numeric or pointer
			case DIV : // / numerical division
			case TIMES : // numeric multiplication
			case MOD : // % integer modulus
			case MINUS : // - binary subtraction (numbers and pointers)
				if (numArgs == 2)
					return evalBinaryNumericOp(type, operator, args[0],
							args[1]);
				else
					throw new UnsourcedException(
							"Expected two arguments for operator " + operator);
			case UNARYMINUS : // - numeric negative
				return evalMinus(type, args[0]);
			case UNARYPLUS : // + numeric no-op</li>
				return args[0];
			default :
				throw new UnsourcedException(
						"Illegal operator in constant expression: " + operator);
		}
		// TODO: handle specials cases for all of above
		return canonic(new CommonOperatorValue(type, operator, args));
	}

	private Value applyConversions(ExpressionNode expr, Value value) {
		// TODO
		return value;
	}

	/**
	 * 
	 * C11 Sec. 6.3.1.2: "When any scalar value is converted to _Bool, the
	 * result is 0 if the value compares equal to 0; otherwise, the result is
	 * 1."
	 * 
	 * C11 Sec. 6.3.1.3:
	 * 
	 * <blockquote> When a value with integer type is converted to another
	 * integer type other than _Bool, if the value can be represented by the new
	 * type, it is unchanged.
	 * 
	 * Otherwise, if the new type is unsigned, the value is converted by
	 * repeatedly adding or subtracting one more than the maximum value that can
	 * be represented in the new type until the value is in the range of the new
	 * type.
	 * 
	 * Otherwise, the new type is signed and the value cannot be represented in
	 * it; either the result is implementation-defined or an
	 * implementation-defined signal is raised. </blockquote>
	 * 
	 * 
	 * @param value
	 * @param newType
	 * @return
	 */
	@SuppressWarnings("unused")
	private Value evaluateIntegerConversion(Value value, IntegerType newType) {
		IntegerType oldType = (IntegerType) value.getType();

		if (oldType.equals(newType))
			return value;
		if (value instanceof IntegerValue) {
			BigInteger intVal = ((IntegerValue) value).getIntegerValue();

			// first: if intVal representable in new type, that's it:
			if (newType instanceof StandardSignedIntegerType) {
				StandardSignedIntegerType newSigned = (StandardSignedIntegerType) newType;

				if (newSigned.inMinimumRange(intVal)) {
					return integerValue(newType, intVal);
				} else if (oldType instanceof StandardSignedIntegerType) {
					SignedIntKind newKind = newSigned.getIntKind();
					SignedIntKind oldKind = ((StandardSignedIntegerType) oldType)
							.getIntKind();

					if (newKind.compareTo(oldKind) > 0)
						return integerValue(newType, intVal);
				}
			} else if (newType instanceof StandardUnsignedIntegerType) {
				StandardUnsignedIntegerType newSigned = (StandardUnsignedIntegerType) newType;

				if (newSigned.inMinimumRange(intVal)) {
					return integerValue(newType, intVal);
				} else if (oldType instanceof StandardUnsignedIntegerType) {
					UnsignedIntKind newKind = newSigned.getIntKind();
					UnsignedIntKind oldKind = ((StandardUnsignedIntegerType) oldType)
							.getIntKind();

					if (newKind.compareTo(oldKind) > 0)
						return integerValue(newType, intVal);
				}
			}
		}
		return castValue(newType, value);
	}

	// private Value evaluateAndorOr(Operator operator, ExpressionNode expr1,
	// ExpressionNode expr2) throws SyntaxException {
	// boolean isAnd = operator == Operator.LAND;
	// Value v1 = evaluate(expr1);
	//
	// return null;
	// }

	/**
	 * Evaluates plus in case where both types are integers. NOTE types of these
	 * values must be converted types from expression.
	 * 
	 * @param a1
	 * @param a2
	 * @return
	 */
	private Value evalBinaryIntegerOp(Operator operator, Value a1, Value a2) {
		IntegerType type1 = (IntegerType) a1.getType();
		IntegerType type;

		switch (operator) {
			case PLUS :
			case TIMES :
			case MINUS :
			case DIV :
			case SHIFTLEFT :
			case SHIFTRIGHT :
				type = type1;
				break;
			case EQUALS :
			case LTE :
			case GTE :
			case NEQ :
			case LT :
			case GT :
				type = SINT;
				break;
			default :
				throw new RuntimeException(
						"This method should not be called with operator "
								+ operator);
		}
		if (a1.equals(a2)) {
			switch (operator) {
				case EQUALS :
				case LTE :
				case GTE :
					return SINT_ONE;
				case NEQ :
				case LT :
				case GT :
					return SINT_ZERO;
				default :
			}
		}
		if (a1 instanceof IntegerValue && a2 instanceof IntegerValue) {
			BigInteger v1 = ((IntegerValue) a1).getIntegerValue();
			BigInteger v2 = ((IntegerValue) a2).getIntegerValue();
			BigInteger v3 = null;

			switch (operator) {
				case PLUS :
					v3 = v1.add(v2);
					break;
				case TIMES :
					v3 = v1.multiply(v2);
					break;
				case MINUS :
					v3 = v1.subtract(v2);
					break;
				case DIV :
					v3 = v1.divide(v2);
					break;
				case EQUALS :
					v3 = BigInteger.ZERO;
					break;
				case NEQ :
					v3 = BigInteger.ONE;
					break;
				case LT :
					v3 = v1.compareTo(v2) < 0
							? BigInteger.ONE
							: BigInteger.ZERO;
					break;
				case GT :
					v3 = v1.compareTo(v2) > 0
							? BigInteger.ONE
							: BigInteger.ZERO;
					break;
				case LTE :
					v3 = v1.compareTo(v2) <= 0
							? BigInteger.ONE
							: BigInteger.ZERO;
					break;
				case GTE :
					v3 = v1.compareTo(v2) >= 0
							? BigInteger.ONE
							: BigInteger.ZERO;
					break;
				case SHIFTLEFT :
				case SHIFTRIGHT :
					if (v1.signum() >= 0 && v2.signum() >= 0
							&& MAX_JINT.compareTo(v2) >= 0) {
						int v2small = v2.intValue();

						if (operator == Operator.SHIFTLEFT)
							v3 = v1.shiftLeft(v2small);
						else
							v3 = v1.shiftRight(v2small);
					}
					break;
				default :
			}
			if (v3 != null && type instanceof StandardSignedIntegerType
					&& ((StandardSignedIntegerType) type).inMinimumRange(v3)
					|| type instanceof StandardUnsignedIntegerType
							&& ((StandardUnsignedIntegerType) type)
									.inMinimumRange(v3)) {
				return integerValue(type, v3);
			}
		}
		return operatorValue(type, operator, new Value[]{a1, a2});
	}

	private Value evaluateMemberAccess(Value structureOrUnionValue, Field field)
			throws UnsourcedException {
		if (structureOrUnionValue instanceof StructureValue) {
			return ((StructureValue) structureOrUnionValue).getMember(field);
		}
		if (structureOrUnionValue instanceof UnionValue) {
			UnionValue unionValue = (UnionValue) structureOrUnionValue;
			Field unionField = unionValue.getField();

			if (!unionField.equals(field))
				throw new UnsourcedException(
						"Union value field differs from requested field:\n"
								+ unionField + "\n" + field);
			return unionValue.getMemberValue();
		}
		// TODO: need to create a new dot value.
		throw new UnsourcedException(
				"Cannot evaluate structure or union value: "
						+ structureOrUnionValue);
	}

	private SyntaxException error(String message, ASTNode node) {
		return new SyntaxException(message, node.getSource());
	}

	private SyntaxException error(UnsourcedException e, ASTNode node) {
		return new SyntaxException(e, node.getSource());
	}

}