/*******************************************************************************
 * Copyright (c) 2008, 2012 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Markus Schorn - initial API and implementation
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser;

import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_alignof;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_nothrow_constructor;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_nothrow_copy;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_trivial_assign;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_trivial_constructor;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_trivial_copy;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_trivial_destructor;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_virtual_destructor;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_abstract;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_class;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_empty;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_enum;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_literal_type;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_pod;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_polymorphic;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_standard_layout;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_trivial;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_union;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_sizeof;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_typeid;
import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_typeof;

import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTBinaryTypeIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTCastExpression;
import org.eclipse.cdt.core.dom.ast.IASTConditionalExpression;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateNonTypeParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator.SizeAndAlignment;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.TypeTraits;
import org.eclipse.cdt.internal.core.parser.scanner.ExpressionEvaluator;
import org.eclipse.cdt.internal.core.parser.scanner.ExpressionEvaluator.EvalException;
import org.eclipse.cdt.internal.core.pdom.db.TypeMarshalBuffer;
import org.eclipse.core.runtime.CoreException;

/**
 * Represents values of variables, enumerators or expressions. The primary purpose of
 * the representation is to support instantiation of templates with non-type template parameters.
 */
public class Value implements IValue {
	public static final int MAX_RECURSION_DEPTH = 25;
	public static final Value UNKNOWN= new Value("<unknown>".toCharArray(), null); //$NON-NLS-1$
	public static final Value NOT_INITIALIZED= new Value("<__>".toCharArray(), null); //$NON-NLS-1$

	private static final char UNIQUE_CHAR = '_';

	private final static IValue[] TYPICAL= {
		new Value(new char[] {'0'}, null),
		new Value(new char[] {'1'}, null),
		new Value(new char[] {'2'}, null),
		new Value(new char[] {'3'}, null),
		new Value(new char[] {'4'}, null),
		new Value(new char[] {'5'}, null),
		new Value(new char[] {'6'}, null)};


	private static class UnknownValueException extends Exception {}
	private static UnknownValueException UNKNOWN_EX= new UnknownValueException();
	private static int sUnique= 0;

	// The following invariant always holds: (fFixedValue == null) != (fEvaluation == null)
	private final char[] fFixedValue;
	private final ICPPEvaluation fEvaluation;
	private char[] fSignature;

	private Value(char[] fixedValue, ICPPEvaluation evaluation) {
		assert (fixedValue == null) != (evaluation == null);
		fFixedValue = fixedValue;
		fEvaluation = evaluation;
	}

	@Override
	public Long numericalValue() {
		return fFixedValue == null ? null : parseLong(fFixedValue);
	}

	@Override
	public ICPPEvaluation getEvaluation() {
		return fEvaluation;
	}

	@Override
	public char[] getSignature() {
		if (fSignature == null) {
			fSignature = fFixedValue != null ? fFixedValue : fEvaluation.getSignature();
		}
		return fSignature;
	}

	@Deprecated
	@Override
	public char[] getInternalExpression() {
		return CharArrayUtils.EMPTY_CHAR_ARRAY;
	}

	@Deprecated
	@Override
	public IBinding[] getUnknownBindings() {
		return IBinding.EMPTY_BINDING_ARRAY;
	}

	public void marshall(ITypeMarshalBuffer buf) throws CoreException {
		if (UNKNOWN == this) {
			buf.putByte((byte) (ITypeMarshalBuffer.VALUE | ITypeMarshalBuffer.FLAG1));
		} else {
			Long num= numericalValue();
			if (num != null) {
				long lv= num;
				if (lv >= Integer.MIN_VALUE && lv <= Integer.MAX_VALUE) {
					buf.putByte((byte) (ITypeMarshalBuffer.VALUE | ITypeMarshalBuffer.FLAG2));
					buf.putInt((int) lv);
				} else {
					buf.putByte((byte) (ITypeMarshalBuffer.VALUE | ITypeMarshalBuffer.FLAG3));
					buf.putLong(lv);
				}
			} else if (fFixedValue != null) {
				buf.putByte((byte) (ITypeMarshalBuffer.VALUE | ITypeMarshalBuffer.FLAG4));
				buf.putCharArray(fFixedValue);
			} else {
				buf.putByte((ITypeMarshalBuffer.VALUE));
				fEvaluation.marshal(buf, true);
			}
		}
	}

	public static IValue unmarshal(ITypeMarshalBuffer buf) throws CoreException {
		int firstByte= buf.getByte();
		if (firstByte == TypeMarshalBuffer.NULL_TYPE)
			return null;
		if ((firstByte & ITypeMarshalBuffer.FLAG1) != 0)
			return Value.UNKNOWN;
		if ((firstByte & ITypeMarshalBuffer.FLAG2) != 0) {
			int val= buf.getInt();
			return Value.create(val);
		}
		if ((firstByte & ITypeMarshalBuffer.FLAG3) != 0) {
			long val= buf.getLong();
			return Value.create(val);
		}
		if ((firstByte & ITypeMarshalBuffer.FLAG4) != 0) {
			char[] fixedValue = buf.getCharArray();
			return new Value(fixedValue, null);
		}
		ISerializableEvaluation eval= buf.unmarshalEvaluation();
		if (eval instanceof ICPPEvaluation)
			return new Value(null, (ICPPEvaluation) eval);
		return Value.UNKNOWN;
	}

	@Override
	public int hashCode() {
		return CharArrayUtils.hash(getSignature());
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof Value)) {
			return false;
		}
		final Value rhs = (Value) obj;
		if (fFixedValue != null)
			return CharArrayUtils.equals(fFixedValue, rhs.fFixedValue);
		return CharArrayUtils.equals(getSignature(), rhs.getSignature());
	}

	@Override
	public String toString() {
		return new String(getSignature());
	}

	/**
	 * Creates a value representing the given number.
	 */
	public static IValue create(long value) {
		if (value >=0 && value < TYPICAL.length)
			return TYPICAL[(int) value];
		return new Value(toCharArray(value), null);
	}

	/**
	 * Creates a value object representing the given boolean value.  
	 */
	public static IValue create(boolean value) {
		return create(value ? 1 : 0);
	}

	/**
	 * Creates a value representing the given template parameter.
	 */
	public static IValue create(ICPPTemplateNonTypeParameter tntp) {
		EvalBinding eval = new EvalBinding(tntp, null);
		return new Value(null, eval);
	}

	/**
	 * Create a value wrapping the given evaluation.
	 */
	public static IValue create(ICPPEvaluation eval) {
		return new Value(null, eval);
	}

	public static IValue evaluateBinaryExpression(final int op, final long v1, final long v2) {
		try {
			return create(applyBinaryOperator(op, v1, v2));
		} catch (UnknownValueException e) {
		}
		return UNKNOWN;
	}

	public static IValue evaluateUnaryExpression(final int unaryOp, final long value) {
		try {
			return create(applyUnaryOperator(unaryOp, value));
		} catch (UnknownValueException e) {
		}
		return UNKNOWN;
	}

	public static IValue evaluateUnaryTypeIdExpression(int operator, IType type, IASTNode point) {
		try {
			return create(applyUnaryTypeIdOperator(operator, type, point));
		} catch (UnknownValueException e) {
		}
		return UNKNOWN;
	}

	public static IValue evaluateBinaryTypeIdExpression(IASTBinaryTypeIdExpression.Operator operator,
			IType type1, IType type2, IASTNode point) {
		try {
			return create(applyBinaryTypeIdOperator(operator, type1, type2, point));
		} catch (UnknownValueException e) {
		}
		return UNKNOWN;
	}

	private static long applyUnaryTypeIdOperator(int operator, IType type, IASTNode point) throws UnknownValueException{
		switch (operator) {
			case op_sizeof:
				return getSizeAndAlignment(type, point).size;
			case op_alignof:
				return getSizeAndAlignment(type, point).alignment;
			case op_typeid:
				break;  // TODO(sprigogin): Implement
			case op_has_nothrow_copy:
				break;  // TODO(sprigogin): Implement
			case op_has_nothrow_constructor:
				break;  // TODO(sprigogin): Implement
			case op_has_trivial_assign:
				break;  // TODO(sprigogin): Implement
			case op_has_trivial_constructor:
				break;  // TODO(sprigogin): Implement
			case op_has_trivial_copy:
				return !(type instanceof ICPPClassType) ||
						TypeTraits.hasTrivialCopyCtor((ICPPClassType) type, point) ? 1 : 0;
			case op_has_trivial_destructor:
				break;  // TODO(sprigogin): Implement
			case op_has_virtual_destructor:
				break;  // TODO(sprigogin): Implement
			case op_is_abstract:
				return type instanceof ICPPClassType &&
						TypeTraits.isAbstract((ICPPClassType) type, point) ? 1 : 0;
			case op_is_class:
				return type instanceof ICompositeType &&
						((ICompositeType) type).getKey() != ICompositeType.k_union ? 1 : 0;
			case op_is_empty:
				break;  // TODO(sprigogin): Implement
			case op_is_enum:
				return type instanceof IEnumeration ? 1 : 0;
			case op_is_literal_type:
				break;  // TODO(sprigogin): Implement
			case op_is_pod:
				return TypeTraits.isPOD(type, point) ? 1 : 0;
			case op_is_polymorphic:
				return type instanceof ICPPClassType &&
						TypeTraits.isPolymorphic((ICPPClassType) type, point) ? 1 : 0;
			case op_is_standard_layout:
				return TypeTraits.isStandardLayout(type, point) ? 1 : 0;
			case op_is_trivial:
				return type instanceof ICPPClassType &&
						TypeTraits.isTrivial((ICPPClassType) type, point) ? 1 : 0;
			case op_is_union:
				return type instanceof ICompositeType &&
						((ICompositeType) type).getKey() == ICompositeType.k_union ? 1 : 0;
			case op_typeof:
				break;  // TODO(sprigogin): Implement
		}
		throw UNKNOWN_EX;
	}

	public static long applyBinaryTypeIdOperator(IASTBinaryTypeIdExpression.Operator operator,
			IType type1, IType type2, IASTNode point) throws UnknownValueException {
		switch (operator) {
		case __is_base_of:
			if (type1 instanceof ICPPClassType && type1 instanceof ICPPClassType) {
				return ClassTypeHelper.isSubclass((ICPPClassType) type2, (ICPPClassType) type1) ? 1 : 0;
			}
		}
		throw UNKNOWN_EX;
	}

	private static SizeAndAlignment getSizeAndAlignment(IType type, IASTNode point) throws UnknownValueException {
		SizeAndAlignment sizeAndAlignment = SizeofCalculator.getSizeAndAlignment(type, point);
		if (sizeAndAlignment == null)
			throw UNKNOWN_EX;
		return sizeAndAlignment;
	}

	/**
	 * Tests whether the value is a template parameter (or a parameter pack).
	 * 
	 * @return the parameter id of the parameter, or <code>-1</code> if it is not a template
	 *         parameter.
	 */
	public static int isTemplateParameter(IValue tval) {
		ICPPEvaluation eval = tval.getEvaluation();
		if (eval instanceof EvalBinding) {
			IBinding binding = ((EvalBinding) eval).getBinding();
			if (binding instanceof ICPPTemplateParameter) {
				return ((ICPPTemplateParameter) binding).getParameterID();
			}
		}
		return -1;
	}

	/**
	 * Tests whether the value references some template parameter.
	 */
	public static boolean referencesTemplateParameter(IValue tval) {
		ICPPEvaluation eval = tval.getEvaluation();
		if (eval == null)
			return false;
		return eval.referencesTemplateParameter();
	}

	/**
	 * Tests whether the value depends on a template parameter.
	 */
	public static boolean isDependentValue(IValue nonTypeValue) {
		return nonTypeValue.getEvaluation() != null;
	}

	/**
	 * Creates the value for an expression.
	 */
	public static IValue create(IASTExpression expr, int maxRecursionDepth) {
		try {
			Object obj= evaluate(expr, maxRecursionDepth);
			if (obj instanceof Long)
				return create(((Long) obj).longValue());

			if (expr instanceof ICPPASTInitializerClause) {
				ICPPEvaluation evaluation = ((ICPPASTInitializerClause) expr).getEvaluation();
				return new Value(null, evaluation);
			}
		} catch (UnknownValueException e) {
		}
		return UNKNOWN;
	}

	/**
	 * Creates a value off its canonical representation.
	 */
	public static IValue fromInternalRepresentation(ICPPEvaluation evaluation) {
		return new Value(null, evaluation);
	}

	/**
	 * Creates a unique value needed during template instantiation.
	 */
	public static IValue unique() {
		StringBuilder buf= new StringBuilder(10);
		buf.append(UNIQUE_CHAR);
		buf.append(++sUnique);
		return new Value(CharArrayUtils.extractChars(buf), null);
	}

	/**
	 * Computes the canonical representation of the value of the expression.
	 * Returns a {@code Long} for numerical values or {@code null}, otherwise.
	 * @throws UnknownValueException
	 */
	private static Long evaluate(IASTExpression exp, int maxdepth) throws UnknownValueException {
		if (maxdepth < 0 || exp == null)
			throw UNKNOWN_EX;

		if (exp instanceof IASTArraySubscriptExpression) {
			throw UNKNOWN_EX;
		}
		if (exp instanceof IASTBinaryExpression) {
			return evaluateBinaryExpression((IASTBinaryExpression) exp, maxdepth);
		}
		if (exp instanceof IASTCastExpression) { // must be ahead of unary
			return evaluate(((IASTCastExpression) exp).getOperand(), maxdepth);
		}
		if (exp instanceof IASTUnaryExpression) {
			return evaluateUnaryExpression((IASTUnaryExpression) exp, maxdepth);
		}
		if (exp instanceof IASTConditionalExpression) {
			IASTConditionalExpression cexpr= (IASTConditionalExpression) exp;
			Long v= evaluate(cexpr.getLogicalConditionExpression(), maxdepth);
			if (v == null)
				return null;
			if (v.longValue() == 0) {
				return evaluate(cexpr.getNegativeResultExpression(), maxdepth);
			}
			final IASTExpression pe = cexpr.getPositiveResultExpression();
			if (pe == null) // gnu-extension allows to omit the positive expression.
				return v;
			return evaluate(pe, maxdepth);
		}
		if (exp instanceof IASTIdExpression) {
			IBinding b= ((IASTIdExpression) exp).getName().resolvePreBinding();
			return evaluateBinding(b, maxdepth);
		}
		if (exp instanceof IASTLiteralExpression) {
			IASTLiteralExpression litEx= (IASTLiteralExpression) exp;
			switch (litEx.getKind()) {
			case IASTLiteralExpression.lk_false:
			case IASTLiteralExpression.lk_nullptr:
				return Long.valueOf(0);
			case IASTLiteralExpression.lk_true:
				return Long.valueOf(1);
			case IASTLiteralExpression.lk_integer_constant:
				try {
					return ExpressionEvaluator.getNumber(litEx.getValue());
				} catch (EvalException e) {
					throw UNKNOWN_EX;
				}
			case IASTLiteralExpression.lk_char_constant:
				try {
					final char[] image= litEx.getValue();
					if (image.length > 1 && image[0] == 'L')
						return ExpressionEvaluator.getChar(image, 2);
					return ExpressionEvaluator.getChar(image, 1);
				} catch (EvalException e) {
					throw UNKNOWN_EX;
				}
			}
		}
		if (exp instanceof IASTTypeIdExpression) {
			ASTTranslationUnit ast = (ASTTranslationUnit) exp.getTranslationUnit();
			final IType type = ast.createType(((IASTTypeIdExpression) exp).getTypeId());
			if (type instanceof ICPPUnknownType)
				return null;
			return applyUnaryTypeIdOperator(((IASTTypeIdExpression) exp).getOperator(), type, exp);
		}

		if (exp instanceof IASTBinaryTypeIdExpression) {
			
		}
		throw UNKNOWN_EX;
	}

	/**
	 * Extract a value off a binding.
	 */
	private static Long evaluateBinding(IBinding b, int maxdepth) throws UnknownValueException {
		if (b instanceof IType) {
			throw UNKNOWN_EX;
		}
		if (b instanceof ICPPTemplateNonTypeParameter) {
			return null;
		}

		if (b instanceof ICPPUnknownBinding) {
			return null;
		}

		IValue value= null;
		if (b instanceof IInternalVariable) {
			value= ((IInternalVariable) b).getInitialValue(maxdepth - 1);
		} else if (b instanceof IVariable) {
			value= ((IVariable) b).getInitialValue();
		} else if (b instanceof IEnumerator) {
			value= ((IEnumerator) b).getValue();
		}
		if (value != null && value != Value.UNKNOWN) {
			return value.numericalValue();
		}

		throw UNKNOWN_EX;
	}

	private static Long evaluateUnaryExpression(IASTUnaryExpression exp, int maxdepth)
			throws UnknownValueException {
		final int unaryOp= exp.getOperator();

		if (unaryOp == IASTUnaryExpression.op_sizeof) {
			final IASTExpression operand = exp.getOperand();
			if (operand != null) {
				IType type = operand.getExpressionType();
				if (type instanceof ICPPUnknownType)
					return null;
				ASTTranslationUnit ast = (ASTTranslationUnit) exp.getTranslationUnit();
				SizeofCalculator calculator = ast.getSizeofCalculator();
				SizeAndAlignment info = calculator.sizeAndAlignment(type);
				if (info != null)
					return info.size;
			}
			throw UNKNOWN_EX;
		}

		if (unaryOp == IASTUnaryExpression.op_amper || unaryOp == IASTUnaryExpression.op_star ||
				unaryOp == IASTUnaryExpression.op_sizeofParameterPack) {
			throw UNKNOWN_EX;
		}

		final Long value= evaluate(exp.getOperand(), maxdepth);
		if (value == null)
			return null;
		return applyUnaryOperator(unaryOp, value);
	}

	private static long applyUnaryOperator(final int unaryOp, final long value) throws UnknownValueException {
		switch (unaryOp) {
		case IASTUnaryExpression.op_bracketedPrimary:
		case IASTUnaryExpression.op_plus:
			return value;
		}

		switch (unaryOp) {
		case IASTUnaryExpression.op_prefixIncr:
		case IASTUnaryExpression.op_postFixIncr:
			return value + 1;
		case IASTUnaryExpression.op_prefixDecr:
		case IASTUnaryExpression.op_postFixDecr:
			return value - 1;
		case IASTUnaryExpression.op_minus:
			return -value;
		case IASTUnaryExpression.op_tilde:
			return ~value;
		case IASTUnaryExpression.op_not:
			return value == 0 ? 1 : 0;
		}
		throw UNKNOWN_EX;
	}

	private static Long evaluateBinaryExpression(IASTBinaryExpression exp, int maxdepth)
			throws UnknownValueException {
		final int op= exp.getOperator();
		switch (op) {
		case IASTBinaryExpression.op_equals:
			if (exp.getOperand1().equals(exp.getOperand2()))
				return Long.valueOf(1);
			break;
		case IASTBinaryExpression.op_notequals:
			if (exp.getOperand1().equals(exp.getOperand2()))
				return Long.valueOf(0);
			break;
		}

		final Long o1= evaluate(exp.getOperand1(), maxdepth);
		if (o1 == null)
			return null;
		final Long o2= evaluate(exp.getOperand2(), maxdepth);
		if (o2 == null)
			return null;

		return applyBinaryOperator(op, o1, o2);
	}

	private static long applyBinaryOperator(final int op, final long v1, final long v2)
			throws UnknownValueException {
		switch (op) {
		case IASTBinaryExpression.op_multiply:
			return v1 * v2;
		case IASTBinaryExpression.op_divide:
			if (v2 == 0)
				throw UNKNOWN_EX;
			return v1 / v2;
		case IASTBinaryExpression.op_modulo:
			if (v2 == 0)
				throw UNKNOWN_EX;
			return v1 % v2;
		case IASTBinaryExpression.op_plus:
			return v1 + v2;
		case IASTBinaryExpression.op_minus:
			return v1 - v2;
		case IASTBinaryExpression.op_shiftLeft:
			return v1 << v2;
		case IASTBinaryExpression.op_shiftRight:
			return v1 >> v2;
		case IASTBinaryExpression.op_lessThan:
			return v1 < v2 ? 1 : 0;
		case IASTBinaryExpression.op_greaterThan:
			return v1 > v2 ? 1 : 0;
		case IASTBinaryExpression.op_lessEqual:
			return v1 <= v2 ? 1 : 0;
		case IASTBinaryExpression.op_greaterEqual:
			return v1 >= v2 ? 1 : 0;
		case IASTBinaryExpression.op_binaryAnd:
			return v1 & v2;
		case IASTBinaryExpression.op_binaryXor:
			return v1 ^ v2;
		case IASTBinaryExpression.op_binaryOr:
			return v1 | v2;
		case IASTBinaryExpression.op_logicalAnd:
			return v1 != 0 && v2 != 0 ? 1 : 0;
		case IASTBinaryExpression.op_logicalOr:
			return v1 != 0 || v2 != 0 ? 1 : 0;
		case IASTBinaryExpression.op_equals:
			return v1 == v2 ? 1 : 0;
		case IASTBinaryExpression.op_notequals:
			return v1 != v2 ? 1 : 0;
        case IASTBinaryExpression.op_max:
			return Math.max(v1, v2);
        case IASTBinaryExpression.op_min:
			return Math.min(v1, v2);
		}
		throw UNKNOWN_EX;
	}

	/**
	 * Parses a long, returns <code>null</code> if not possible
	 */
	private static Long parseLong(char[] value) {
		final long maxvalue= Long.MAX_VALUE / 10;
		final int len= value.length;
		boolean negative= false;
		long result = 0;
		int i= 0;

		if (len > 0 && value[0] == '-') {
			negative = true;
			i++;
		}
		if (i == len)
			return null;

		for (; i < len; i++) {
			if (result > maxvalue)
				return null;

			final int digit= (value[i] - '0');
			if (digit < 0 || digit > 9)
				return null;
			result= result * 10 + digit;
		}
		return negative ? -result : result;
	}

	/**
	 * Converts long to a char array
	 */
	private static char[] toCharArray(long value) {
		StringBuilder buf= new StringBuilder();
		buf.append(value);
		return CharArrayUtils.extractChars(buf);
	}
}
