All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.xtext.xbase.interpreter.AbstractConstantExpressionsInterpreter.xtend Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
 *******************************************************************************/
package org.eclipse.xtext.xbase.interpreter

import com.google.inject.Inject
import java.util.Map
import java.util.Set
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtend.lib.annotations.Data
import org.eclipse.xtext.common.types.JvmField
import org.eclipse.xtext.common.types.JvmIdentifiableElement
import org.eclipse.xtext.common.types.JvmType
import org.eclipse.xtext.common.types.JvmTypeReference
import org.eclipse.xtext.common.types.TypesFactory
import org.eclipse.xtext.common.types.access.impl.ClassFinder
import org.eclipse.xtext.nodemodel.util.NodeModelUtils
import org.eclipse.xtext.xbase.XBinaryOperation
import org.eclipse.xtext.xbase.XBooleanLiteral
import org.eclipse.xtext.xbase.XCastedExpression
import org.eclipse.xtext.xbase.XExpression
import org.eclipse.xtext.xbase.XStringLiteral
import org.eclipse.xtext.xbase.XTypeLiteral
import org.eclipse.xtext.xbase.XUnaryOperation
import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotation
import org.eclipse.xtext.xbase.XAbstractFeatureCall
import org.eclipse.xtext.resource.persistence.StorageAwareResource
import org.eclipse.xtext.xbase.scoping.featurecalls.OperatorMapping
import org.eclipse.xtext.naming.QualifiedName

/**
 * @author Anton Kosyakov - Initial contribution and API
 */
class AbstractConstantExpressionsInterpreter {
	
	@Accessors(PROTECTED_GETTER)
	@Inject ConstantOperators constantOperators
	@Inject OperatorMapping operatorMapping

	def protected evaluate(XExpression expression, Context ctx) {
		if (ctx.alreadyEvaluating.add(expression)) {
			try {
				internalEvaluate(expression, ctx)
			} finally {
				ctx.alreadyEvaluating.remove(expression)
			}
		} else {
			throw notConstantExpression(expression)
		}
			
	}

	def dispatch Object internalEvaluate(XExpression expression, Context ctx) {
		throw notConstantExpression(expression)
	}
	
	def dispatch Object internalEvaluate(Void nullValue, Context ctx) {
		throw notConstantExpression(null)
	}
	
	def notConstantExpression(XExpression expression) {
		new ConstantExpressionEvaluationException("Not a constant expression : '" + expression?.toText + "'", expression)
	}
	
	def dispatch Object internalEvaluate(XCastedExpression expression, Context ctx) {
		evaluate(expression.target, ctx)
	}

	def dispatch Object internalEvaluate(XStringLiteral it, Context ctx) {
		value
	}

	def dispatch Object internalEvaluate(XBooleanLiteral it, Context ctx) {
		isTrue
	}

	def dispatch Object internalEvaluate(XAnnotation literal, Context ctx) {
		literal
	}

	def dispatch Object internalEvaluate(XTypeLiteral it, Context ctx) {
		toTypeReference(type, arrayDimensions.size)
	}

	protected def toTypeReference(JvmType type, int arrayDimensions) {
		if (type == null)
			return null
		var JvmTypeReference resultTypeRef = TypesFactory.eINSTANCE.createJvmParameterizedTypeReference => [
			it.type = type
		]
		for (i : 0 ..< arrayDimensions) {
			val arrayRef = TypesFactory.eINSTANCE.createJvmGenericArrayTypeReference
			arrayRef.setComponentType(resultTypeRef);
			resultTypeRef = arrayRef
		}
		return resultTypeRef
	}

	def dispatch Object internalEvaluate(XBinaryOperation it, Context ctx) {
		val context = ctx.cloneWithExpectation(null)
		 
		val left = leftOperand.evaluate(context) 
		val right = rightOperand.evaluate(context)

		evaluateBinaryOperation(it, left, right)
	}
	
	protected def evaluateBinaryOperation(XBinaryOperation binaryOperation, Object left, Object right) {
		val op = binaryOperation.operator
		switch op {
			case '+': constantOperators.plus(left, right)
			case '-': constantOperators.minus(left, right)
			case '*': constantOperators.multiply(left, right)
			case '/': constantOperators.divide(left, right)
			case '%': constantOperators.modulo(left, right)
			case '&&': constantOperators.and(left, right)
			case '||': constantOperators.or(left, right)
			case '<<': constantOperators.shiftLeft(left, right)
			case '>>': constantOperators.shiftRight(left, right)
			case '>>>': constantOperators.shiftRightUnsigned(left, right)
			case '<': constantOperators.lessThan(left, right)
			case '>': constantOperators.greaterThan(left, right)
			case '<=': constantOperators.lessEquals(left, right)
			case '>=': constantOperators.greaterEquals(left, right)
			case '==', 
			case '===': constantOperators.same(left, right)
			case '!=',
			case '!==': constantOperators.notSame(left, right)
			default: throw new ConstantExpressionEvaluationException("Couldn't evaluate binary operator '" + op + "' on values " + left + " and " + right)
		}
	}

	def dispatch Object internalEvaluate(XUnaryOperation it, Context ctx) {
		val value = operand.evaluate(ctx)
		val op = operator
		switch op {
			case '-': constantOperators.minus(value)
			case op=='!' && value instanceof Boolean: !(value as Boolean)
			case op=='+' && value instanceof Number: value
			default: throw new ConstantExpressionEvaluationException("Couldn't evaluate unary operator '" + op + "' on value " + value)
		}
	}
	
	protected def String getOperator(XAbstractFeatureCall call) {
		switch res : call.eResource {
			StorageAwareResource case res.isLoadedFromStorage : {
				// we don't have a node model, but can resolve proxies
				return operatorMapping.getOperator(QualifiedName.create(call.feature.simpleName))?.toString
			}
			default: call.concreteSyntaxFeatureName
		}
	}

	protected def toText(XExpression expression) {
		NodeModelUtils.getNode(expression).text
	}

}

@Data class Context {
	JvmTypeReference expectedType
	ClassFinder classFinder
	Map visibleFeatures
	Set alreadyEvaluating
	
	def cloneWithExpectation(JvmTypeReference newExpectation) {
		return new Context(newExpectation, classFinder, visibleFeatures, alreadyEvaluating)
	}
	
}

/**
 * Indicates a problem during evaluation
 */
class ConstantExpressionEvaluationException extends RuntimeException {

	XExpression expression
	
	new(String message) {
		super(message)
	}
	
	new(String message, XExpression expression) {
		this(message)
		this.expression = expression
	}

	def getExpression() {
		expression
	}
}

class UnresolvableFeatureException extends ConstantExpressionEvaluationException {
	
	new(String message, XExpression expression) {
		super(message, expression)
	}
	
}

class StackedConstantExpressionEvaluationException extends ConstantExpressionEvaluationException {
	
	JvmField field
	ConstantExpressionEvaluationException cause
	
	new(XExpression expression, JvmField field, ConstantExpressionEvaluationException cause) {
		super('Error during call to '+field.simpleName+' : ' +cause.message, expression)
		this.field = field
		this.cause = cause
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy