Browse Source

Operation overloading (#2003)

* ArgumentSorter should not resolve parameter proxies

* Operation Overloading linker
Andreas Mülder 7 years ago
parent
commit
f272d540bc

+ 1 - 0
plugins/org.yakindu.base.expressions/META-INF/MANIFEST.MF

@@ -31,6 +31,7 @@ Export-Package: org.yakindu.base.expressions,
  org.yakindu.base.expressions.formatting,
  org.yakindu.base.expressions.generator,
  org.yakindu.base.expressions.inferrer,
+ org.yakindu.base.expressions.linking,
  org.yakindu.base.expressions.parser.antlr,
  org.yakindu.base.expressions.parser.antlr.internal,
  org.yakindu.base.expressions.scoping,

+ 102 - 0
plugins/org.yakindu.base.expressions/src/org/yakindu/base/expressions/linking/OperationOverloadingLinkingService.java

@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2018 committers of YAKINDU 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:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.base.expressions.linking;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.xtext.linking.impl.DefaultLinkingService;
+import org.eclipse.xtext.linking.impl.IllegalNodeException;
+import org.eclipse.xtext.naming.IQualifiedNameConverter;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.nodemodel.INode;
+import org.eclipse.xtext.resource.IEObjectDescription;
+import org.eclipse.xtext.scoping.IScope;
+import org.yakindu.base.expressions.expressions.ArgumentExpression;
+import org.yakindu.base.expressions.expressions.ElementReferenceExpression;
+import org.yakindu.base.expressions.expressions.FeatureCall;
+import org.yakindu.base.types.Operation;
+import org.yakindu.base.types.TypesPackage;
+
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+
+/**
+ * 
+ * Supports operation overloading
+ * 
+ * @author andreas muelder - Initial contribution and API
+ * 
+ */
+public class OperationOverloadingLinkingService extends DefaultLinkingService {
+
+	@Inject
+	private IQualifiedNameConverter qualifiedNameConverter;
+	@Inject
+	private OperationOverloadingResolver operationsLinker;
+
+	@Override
+	public List<EObject> getLinkedObjects(EObject context, EReference ref, INode node) throws IllegalNodeException {
+		if (context instanceof ArgumentExpression && isOperationCall(context)) {
+			return getLinkedOperation((ArgumentExpression) context, ref, node);
+		}
+		return super.getLinkedObjects(context, ref, node);
+	}
+
+	public List<EObject> getLinkedOperation(ArgumentExpression context, EReference ref, INode node) {
+		final EClass requiredType = ref.getEReferenceType();
+		if (requiredType == null) {
+			return Collections.<EObject>emptyList();
+		}
+		final String crossRefString = getCrossRefNodeAsString(node);
+		if (crossRefString == null || crossRefString.equals("")) {
+			return Collections.<EObject>emptyList();
+		}
+		final IScope scope = getScope(context, ref);
+		final QualifiedName qualifiedLinkName = qualifiedNameConverter.toQualifiedName(crossRefString);
+		// Adoption to super class implementation here to return multi elements
+		final Iterable<IEObjectDescription> eObjectDescription = scope.getElements(qualifiedLinkName);
+		int size = Iterables.size(eObjectDescription);
+		if (size == 0)
+			return Collections.emptyList();
+		if (size == 1)
+			return Collections.singletonList(Iterables.getFirst(eObjectDescription, null).getEObjectOrProxy());
+		// Two operation with same name found here
+		List<IEObjectDescription> candidates = new ArrayList<>();
+		for (IEObjectDescription currentDescription : eObjectDescription) {
+			if (currentDescription.getEClass().isSuperTypeOf(TypesPackage.Literals.OPERATION)) {
+				candidates.add(currentDescription);
+			}
+		}
+		Optional<Operation> operation = operationsLinker.linkOperation(candidates, context);
+		if (operation.isPresent()) {
+			return Collections.singletonList(operation.get());
+		}
+		//Link to first operation to get parameter errors instead of linking errors
+		return Collections.singletonList(Iterables.getFirst(eObjectDescription, null).getEObjectOrProxy());
+	}
+
+	protected boolean isOperationCall(EObject context) {
+		if (context instanceof ElementReferenceExpression) {
+			return ((ElementReferenceExpression) context).isOperationCall();
+		}
+		if (context instanceof FeatureCall) {
+			return ((FeatureCall) context).isOperationCall();
+		}
+		return false;
+	}
+
+}

+ 109 - 0
plugins/org.yakindu.base.expressions/src/org/yakindu/base/expressions/linking/OperationOverloadingResolver.java

@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2018 committers of YAKINDU 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:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.base.expressions.linking;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.xtext.resource.IEObjectDescription;
+import org.yakindu.base.expressions.expressions.ArgumentExpression;
+import org.yakindu.base.expressions.expressions.Expression;
+import org.yakindu.base.expressions.expressions.util.ArgumentSorter;
+import org.yakindu.base.types.Operation;
+import org.yakindu.base.types.Parameter;
+import org.yakindu.base.types.Type;
+import org.yakindu.base.types.TypesPackage;
+import org.yakindu.base.types.inferrer.ITypeSystemInferrer;
+import org.yakindu.base.types.typesystem.ITypeSystem;
+
+import com.google.inject.Inject;
+
+/**
+ * 
+ * @author andreas muelder - Initial contribution and API
+ * 
+ */
+public class OperationOverloadingResolver {
+
+	protected class PolymorphicComparator implements Comparator<Operation> {
+
+		public int compare(Operation operation1, Operation operation2) {
+			List<Parameter> parameters1 = operation1.getParameters();
+			List<Parameter> parameters2 = operation2.getParameters();
+
+			if (parameters1.size() > parameters2.size()) {
+				return -1;
+			}
+			if (parameters1.size() < parameters2.size()) {
+				return 1;
+			}
+
+			for (int i = 0; i < parameters1.size(); i++) {
+				final Type type1 = parameters1.get(i).getType();
+				final Type type2 = parameters2.get(i).getType();
+
+				if (typeSystem.isSame(type1, type2))
+					continue;
+				if (typeSystem.isSuperType(type1, type2)) {
+					return 1;
+				}
+				if (typeSystem.isSuperType(type2, type1)) {
+					return -1;
+				}
+			}
+			return 0;
+		}
+	}
+
+	@Inject
+	protected ITypeSystemInferrer inferrer;
+	@Inject
+	protected ITypeSystem typeSystem;
+
+	public Optional<Operation> linkOperation(List<IEObjectDescription> candidates, ArgumentExpression call) {
+		if (candidates.size() == 1 && candidates.get(0).getEClass().isSuperTypeOf(TypesPackage.Literals.OPERATION)) {
+			return Optional.of((Operation) candidates.get(0).getEObjectOrProxy());
+		}
+
+		List<Operation> operations = candidates.stream().map(it -> (Operation) it.getEObjectOrProxy())
+				.collect(Collectors.toList());
+		Collections.sort(operations, new PolymorphicComparator());
+		for (Operation operation : operations) {
+			if (isCallable(operation, call)) {
+				return Optional.of(operation);
+			}
+		}
+
+		return Optional.empty();
+	}
+
+	protected boolean isCallable(Operation operation, ArgumentExpression expression) {
+		EList<Expression> orderedExpressions = ArgumentSorter.getOrderedExpressions(expression.getArguments(),
+				operation);
+		List<Type> argumentTypes = orderedExpressions.stream().map(it -> inferrer.infer(it).getType())
+				.filter(t -> t != null).collect(Collectors.toList());
+		List<Type> parameterTypes = operation.getParameters().stream().map(it -> it.getType())
+				.collect(Collectors.toList());
+		if (argumentTypes.size() != parameterTypes.size())
+			return false;
+		for (int i = 0; i < argumentTypes.size(); i++) {
+			Type type1 = argumentTypes.get(i);
+			Type type2 = parameterTypes.get(i);
+			if (!typeSystem.isSuperType(type2, type1))
+				return false;
+		}
+		return true;
+	}
+}

+ 7 - 0
plugins/org.yakindu.sct.model.stext/src/org/yakindu/sct/model/stext/STextRuntimeModule.java

@@ -13,12 +13,14 @@ package org.yakindu.sct.model.stext;
 import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.xtext.Constants;
 import org.eclipse.xtext.linking.ILinker;
+import org.eclipse.xtext.linking.ILinkingService;
 import org.eclipse.xtext.naming.IQualifiedNameProvider;
 import org.eclipse.xtext.parser.antlr.IReferableElementsUnloader;
 import org.eclipse.xtext.parsetree.reconstr.ITransientValueService;
 import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
 import org.eclipse.xtext.validation.CompositeEValidator;
 import org.eclipse.xtext.validation.INamesAreUniqueValidationHelper;
+import org.yakindu.base.expressions.linking.OperationOverloadingLinkingService;
 import org.yakindu.base.types.inferrer.ITypeSystemInferrer;
 import org.yakindu.base.types.typesystem.GenericTypeSystem;
 import org.yakindu.base.types.typesystem.ITypeSystem;
@@ -109,4 +111,9 @@ public class STextRuntimeModule extends org.yakindu.sct.model.stext.AbstractSTex
 		return STextTransientValueService.class;
 	}
 
+	@Override
+	public Class<? extends ILinkingService> bindILinkingService() {
+		return OperationOverloadingLinkingService.class;
+	}
+
 }

+ 1 - 0
plugins/org.yakindu.sct.model.stext/src/org/yakindu/sct/model/stext/expressions/STextExpressionParser.java

@@ -102,6 +102,7 @@ public class STextExpressionParser implements IExpressionParser {
 			sc.setSpecification(specification);
 		}
 		resource.getContents().add(sc);
+		resource.getLinkingDiagnostics().clear();
 		linker.linkModel(sc, diagnosticsConsumer);
 		linker.linkModel(rootASTElement, diagnosticsConsumer);
 		resource.resolveLazyCrossReferences(CancelIndicator.NullImpl);