CommonValueFactory.java

package edu.udel.cis.vsl.abc.ast.value.common;

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

import edu.udel.cis.vsl.abc.ast.entity.IF.Entity;
import edu.udel.cis.vsl.abc.ast.entity.IF.Entity.EntityKind;
import edu.udel.cis.vsl.abc.ast.entity.IF.Function;
import edu.udel.cis.vsl.abc.ast.entity.IF.Variable;
import edu.udel.cis.vsl.abc.ast.node.IF.ASTNode;
import edu.udel.cis.vsl.abc.ast.node.IF.IdentifierNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.AlignOfNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.ArrowNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.CastNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.CharacterConstantNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.CompoundLiteralNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.ConstantNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.DotNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.EnumerationConstantNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.ExpressionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.IdentifierExpressionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.OperatorNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.OperatorNode.Operator;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.SizeofNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.StringLiteralNode;
import edu.udel.cis.vsl.abc.ast.type.IF.ArrayType;
import edu.udel.cis.vsl.abc.ast.type.IF.Enumerator;
import edu.udel.cis.vsl.abc.ast.type.IF.Field;
import edu.udel.cis.vsl.abc.ast.type.IF.FloatingType;
import edu.udel.cis.vsl.abc.ast.type.IF.IntegerType;
import edu.udel.cis.vsl.abc.ast.type.IF.ObjectType;
import edu.udel.cis.vsl.abc.ast.type.IF.PointerType;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardBasicType;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardBasicType.BasicTypeKind;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardSignedIntegerType;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardSignedIntegerType.SignedIntKind;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardUnsignedIntegerType;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardUnsignedIntegerType.UnsignedIntKind;
import edu.udel.cis.vsl.abc.ast.type.IF.StructureOrUnionType;
import edu.udel.cis.vsl.abc.ast.type.IF.Type;
import edu.udel.cis.vsl.abc.ast.type.IF.Type.TypeKind;
import edu.udel.cis.vsl.abc.ast.type.IF.TypeFactory;
import edu.udel.cis.vsl.abc.ast.value.IF.AddressValue;
import edu.udel.cis.vsl.abc.ast.value.IF.ArrayElementReference;
import edu.udel.cis.vsl.abc.ast.value.IF.ArrayValue;
import edu.udel.cis.vsl.abc.ast.value.IF.CastValue;
import edu.udel.cis.vsl.abc.ast.value.IF.CharacterValue;
import edu.udel.cis.vsl.abc.ast.value.IF.ComplexValue;
import edu.udel.cis.vsl.abc.ast.value.IF.FunctionReference;
import edu.udel.cis.vsl.abc.ast.value.IF.IntegerValue;
import edu.udel.cis.vsl.abc.ast.value.IF.MemberReference;
import edu.udel.cis.vsl.abc.ast.value.IF.OperatorValue;
import edu.udel.cis.vsl.abc.ast.value.IF.RealFloatingValue;
import edu.udel.cis.vsl.abc.ast.value.IF.StringValue;
import edu.udel.cis.vsl.abc.ast.value.IF.StructureValue;
import edu.udel.cis.vsl.abc.ast.value.IF.TypeValue;
import edu.udel.cis.vsl.abc.ast.value.IF.TypeValue.TypeValueKind;
import edu.udel.cis.vsl.abc.ast.value.IF.UnionValue;
import edu.udel.cis.vsl.abc.ast.value.IF.Value;
import edu.udel.cis.vsl.abc.ast.value.IF.ValueFactory;
import edu.udel.cis.vsl.abc.ast.value.IF.VariableReference;
import edu.udel.cis.vsl.abc.config.IF.Configuration;
import edu.udel.cis.vsl.abc.config.IF.Configuration.Architecture;
import edu.udel.cis.vsl.abc.token.IF.ExecutionCharacter;
import edu.udel.cis.vsl.abc.token.IF.StringLiteral;
import edu.udel.cis.vsl.abc.token.IF.SyntaxException;
import edu.udel.cis.vsl.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();
			IdentifierNode fieldIdentifier = arrowNode.getFieldName();
			Field field = (Field) fieldIdentifier.getEntity();
			Value structOrUnionValue = evaluateDereference(
					structOrUnionPointer);

			return evaluateMemberAccess(structOrUnionValue, field);
		} 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());
	}

}