Skip to content

Commit

Permalink
[lang] Create compiler for inline calls with variadic paremeters.
Browse files Browse the repository at this point in the history
Signed-off-by: Stéphane Galland <galland@arakhne.org>
  • Loading branch information
gallandarakhneorg committed Jun 23, 2016
1 parent 4fd08e5 commit 9095e8d
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 5 deletions.
4 changes: 2 additions & 2 deletions plugins/io.sarl.lang/src/io/sarl/lang/SARLRuntimeModule.java
Expand Up @@ -25,7 +25,6 @@
import com.google.inject.Singleton;
import com.google.inject.name.Names;
import org.eclipse.xtend.core.compiler.UnicodeAwarePostProcessor;
import org.eclipse.xtend.core.compiler.XtendCompiler;
import org.eclipse.xtend.core.compiler.XtendGenerator;
import org.eclipse.xtend.core.conversion.IntUnderscoreValueConverter;
import org.eclipse.xtend.core.conversion.JavaIDValueConverter;
Expand Down Expand Up @@ -115,6 +114,7 @@
import io.sarl.lang.bugfixes.bug277.Bug277SARLContextPDAProvider;
import io.sarl.lang.bugfixes.bug335.Bug335TypeDeclarationAwareBatchTypeResolver;
import io.sarl.lang.bugfixes.bug356.Bug356ImportedNamespaceScopeProvider;
import io.sarl.lang.compiler.SarlCompiler;
import io.sarl.lang.compiler.SarlOutputConfigurationProvider;
import io.sarl.lang.controlflow.SARLEarlyExitComputer;
import io.sarl.lang.controlflow.SARLExtendedEarlyExitComputer;
Expand Down Expand Up @@ -207,7 +207,7 @@ public Class<? extends FeatureCallValidator> bindFeatureCallValidator() {
* @return the type of the Xbase compiler.
*/
public Class<? extends XbaseCompiler> bindXbaseCompiler() {
return XtendCompiler.class;
return SarlCompiler.class;
}

/** Replies the type of the provider of SARL action signatures.
Expand Down
161 changes: 161 additions & 0 deletions plugins/io.sarl.lang/src/io/sarl/lang/compiler/SarlCompiler.java
@@ -0,0 +1,161 @@
/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2015 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.sarl.lang.compiler;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.collect.Lists;
import com.google.inject.Inject;
import org.eclipse.xtend.core.compiler.XtendCompiler;
import org.eclipse.xtext.common.types.JvmAnnotationReference;
import org.eclipse.xtext.common.types.JvmAnnotationValue;
import org.eclipse.xtext.common.types.JvmExecutable;
import org.eclipse.xtext.common.types.JvmFormalParameter;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmStringAnnotationValue;
import org.eclipse.xtext.common.types.JvmTypeAnnotationValue;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable;
import org.eclipse.xtext.xbase.typesystem.IBatchTypeResolver;
import org.eclipse.xtext.xbase.typesystem.IResolvedTypes;
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference;
import org.eclipse.xtext.xbase.util.XExpressionHelper;


/** The compiler from SARL to the target language.
*
* <p>This compiler provide a specific support for inline annotations. Indeed, the Xbase inline evaluation does
* not support variadic parameters. This SARL compiler provides a support for variadic feature calls.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 0.4
*/
public class SarlCompiler extends XtendCompiler {

private static final String INLINE_VARIABLE_PREFIX = "$"; //$NON-NLS-1$

private static final String INLINE_VALUE_NAME = "value"; //$NON-NLS-1$

private static final String INLINE_IMPORTED_NAME = "imported"; //$NON-NLS-1$

private static final Pattern INLINE_VARIABLE_PATTERN = Pattern.compile("\\" + INLINE_VARIABLE_PREFIX //$NON-NLS-1$
+ "(\\" + INLINE_VARIABLE_PREFIX + "|[0-9]+)"); //$NON-NLS-1$ //$NON-NLS-2$

@Inject
private XExpressionHelper expressionHelper;

@Inject
private IBatchTypeResolver batchTypeResolver;

@Override
@SuppressWarnings({"checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity"})
protected synchronized void appendInlineFeatureCall(XAbstractFeatureCall call, ITreeAppendable target) {
final JvmAnnotationReference inlineAnnotation = this.expressionHelper.findInlineAnnotation(call);

String formatString = null;
final List<JvmTypeReference> importedTypes = Lists.newArrayListWithCapacity(2);
for (JvmAnnotationValue annotationValue: inlineAnnotation.getValues()) {
if (INLINE_VALUE_NAME.equals(annotationValue.getValueName())) {
formatString = ((JvmStringAnnotationValue) annotationValue).getValues().get(0);
} else if (INLINE_IMPORTED_NAME.equals(annotationValue.getValueName())) {
JvmTypeAnnotationValue typeAnnotationValue = (JvmTypeAnnotationValue) annotationValue;
importedTypes.addAll(typeAnnotationValue.getValues());
}
}

if (formatString == null) {
throw new IllegalStateException();
}

final IResolvedTypes resolvedTypes = this.batchTypeResolver.resolveTypes(call);

final List<XExpression> arguments = getActualArguments(call);
final JvmIdentifiableElement calledFeature = call.getFeature();
int numberVariadicParameter = 0;
final int numberFormalParameters;
JvmFormalParameter formalVariadicParameter = null;
if (calledFeature instanceof JvmExecutable) {
JvmExecutable jvmexec = (JvmExecutable) calledFeature;
numberFormalParameters = jvmexec.getParameters().size();
formalVariadicParameter = jvmexec.getParameters().get(numberFormalParameters - 1);
if (jvmexec.isVarArgs()) {
numberVariadicParameter = 1;
}
} else {
numberFormalParameters = arguments.size();
}
final Matcher matcher = INLINE_VARIABLE_PATTERN.matcher(formatString);
int prevEnd = 0;


while (matcher.find()) {
final int start = matcher.start();
if (start != prevEnd) {
target.append(formatString.substring(prevEnd, start));
}
final String indexOrDollar = matcher.group(1);
if (INLINE_VARIABLE_PREFIX.equals(indexOrDollar)) {
target.append(INLINE_VARIABLE_PREFIX);
} else {
final int index = Integer.parseInt(indexOrDollar) - 1;
final int numberImports = importedTypes.size();
final int numberFormalParametersImports = numberFormalParameters + numberImports;
if (numberVariadicParameter != 0 && index < arguments.size() && index == (numberFormalParameters - 1)) {
XExpression argument = arguments.get(index);
appendArgument(argument, target, index > 0);
for (int i = index + 1; i < arguments.size(); ++i) {
target.append(", "); //$NON-NLS-1$
argument = arguments.get(i);
appendArgument(argument, target, true);
}
} else if (index > numberFormalParametersImports) {
final List<LightweightTypeReference> typeArguments = resolvedTypes.getActualTypeArguments(call);
final LightweightTypeReference typeArgument = typeArguments.get(index - numberFormalParametersImports - 1);
serialize(typeArgument.getRawTypeReference().toTypeReference(), call, target);
} else if (index >= numberFormalParameters && index < numberFormalParametersImports) {
serialize(importedTypes.get(index - numberFormalParameters), call, target);
} else if (index == numberFormalParametersImports) {
appendTypeArguments(call, target);
} else if (index < arguments.size()) {
final XExpression argument = arguments.get(index);
appendArgument(argument, target, index > 0);
} else if (formalVariadicParameter != null) {
appendNullValue(formalVariadicParameter.getParameterType(), calledFeature, target);
} else {
throw new IllegalStateException();
}
}
prevEnd = matcher.end();
}
if (prevEnd != formatString.length()) {
target.append(formatString.substring(prevEnd));
}
}

}
Expand Up @@ -50,6 +50,7 @@ public Set<OutputConfiguration> getOutputConfigurations() {
defaultOutput.setOutputDirectory(SARLConfig.FOLDER_SOURCE_GENERATED);
defaultOutput.setOverrideExistingResources(true);
defaultOutput.setCreateOutputDirectory(true);
defaultOutput.setCanClearOutputDirectory(false);
defaultOutput.setCleanUpDerivedResources(true);
defaultOutput.setSetDerivedProperty(true);
defaultOutput.setKeepLocalHistory(false);
Expand Down
Expand Up @@ -1590,7 +1590,7 @@ protected void transform(SarlCapacityUses source, JvmGenericType container) {
// The Xtext inline evaluator is considering the function arguments, not the
// function formal parameters. Consequently, inline cannot be used for functions
// with variadic parameters.
if (!isVarArgs && !Utils.hasAnnotation(operation, Inline.class)) {
if (!Utils.hasAnnotation(operation, Inline.class)) {
JvmDeclaredType declaringType = entry.getValue().getDeclaringType();
StringBuilder it = new StringBuilder();
it.append("getSkill("); //$NON-NLS-1$
Expand Down Expand Up @@ -1954,11 +1954,12 @@ protected void appendGeneratedAnnotation(JvmAnnotationTarget target, String sarl
protected void appendEventGuardEvaluators(JvmGenericType container) {
final GenerationContext context = getContext(container);
if (context != null) {
Collection<Pair<SarlBehaviorUnit, Collection<Procedure1<ITreeAppendable>>>> allEvaluators = context.getGuardEvaluationCodes();
Collection<Pair<SarlBehaviorUnit, Collection<Procedure1<ITreeAppendable>>>> allEvaluators
= context.getGuardEvaluationCodes();
if (allEvaluators == null || allEvaluators.isEmpty()) {
return;
}

final JvmTypeReference voidType = this._typeReferenceBuilder.typeRef(Void.TYPE);
final JvmTypeReference runnableType = this._typeReferenceBuilder.typeRef(Runnable.class);
final JvmTypeReference collectionType = this._typeReferenceBuilder.typeRef(Collection.class, runnableType);
Expand Down
Expand Up @@ -1585,6 +1585,75 @@ public void inlinedCapacityFunctionCall_classParameter_typeof() throws Exception
});
}

@Test
public void inlinedCapacityFunctionCall_variadicParameter() throws Exception {
String source = "capacity C1 { def myfunction(v1 : Class<?>, v2 : int*) } agent A1 { uses C1 def caller { "
+ "myfunction(typeof(Integer))"
+ "myfunction(typeof(Float), 1)"
+ "myfunction(typeof(String), 2, 3)"
+ "myfunction(typeof(String), #[])"
+ " } }";
final String expectedC1 = multilineString(
"import io.sarl.lang.core.Capacity;",
"",
"@SuppressWarnings(\"all\")",
"public interface C1 extends Capacity {",
" public abstract void myfunction(final Class<?> v1, final int... v2);",
"}",
""
);
final String expectedA1 = multilineString(
"import io.sarl.lang.annotation.ImportedCapacityFeature;",
"import io.sarl.lang.annotation.SarlSpecification;",
"import io.sarl.lang.core.Agent;",
"import io.sarl.lang.core.BuiltinCapacitiesProvider;",
"import java.util.UUID;",
"import javax.annotation.Generated;",
"import javax.inject.Inject;",
"import org.eclipse.xtext.xbase.lib.Inline;",
"",
"@SarlSpecification(\"0.4\")",
"@SuppressWarnings(\"all\")",
"public class A1 extends Agent {",
" protected void caller() {",
" this.getSkill(C1.class).myfunction(Integer.class, (int[])null);",
" this.getSkill(C1.class).myfunction(Float.class, 1);",
" this.getSkill(C1.class).myfunction(String.class, 2, 3);",
" this.getSkill(C1.class).myfunction(String.class, new int[] {});",
" }",
" ",
" /**",
" * See the capacity {@link C1#myfunction(java.lang.Class<? extends java.lang.Object>,int[])}.",
" * ",
" * @see C1#myfunction(java.lang.Class<? extends java.lang.Object>,int[])",
" */",
" @Inline(value = \"getSkill(C1.class).myfunction($1, $2)\", imported = C1.class)",
" @Generated(\"io.sarl.lang.jvmmodel.SARLJvmModelInferrer\")",
" @ImportedCapacityFeature(C1.class)",
" private void myfunction(final Class<?> v1, final int... v2) {",
" getSkill(C1.class).myfunction(v1, v2);",
" }",
" ",
" /**",
" * Construct an agent.",
" * @param builtinCapacityProvider - provider of the built-in capacities.",
" * @param parentID - identifier of the parent. It is the identifier of the parent agent and the enclosing contect, at the same time.",
" * @param agentID - identifier of the agent. If <code>null</code> the agent identifier will be computed randomly.",
" */",
" @Inject",
" @Generated(\"io.sarl.lang.jvmmodel.SARLJvmModelInferrer\")",
" public A1(final BuiltinCapacitiesProvider builtinCapacityProvider, final UUID parentID, final UUID agentID) {",
" super(builtinCapacityProvider, parentID, agentID);",
" }",
"}",
""
);
this.compiler.compile(source, (r) -> {
assertEquals(expectedC1,r.getGeneratedCode("C1"));
assertEquals(expectedA1,r.getGeneratedCode("A1"));
});
}

@Test
public void duplicateEventHandler() throws Exception {
final String source = multilineString(
Expand Down

0 comments on commit 9095e8d

Please sign in to comment.