Skip to content

Commit

Permalink
[lang] Allow references to $0, $1 into lambdas.
Browse files Browse the repository at this point in the history
close #919

Signed-off-by: Stéphane Galland <galland@arakhne.org>
  • Loading branch information
gallandarakhneorg committed Jun 16, 2019
1 parent c6fe8a1 commit 1056c08
Show file tree
Hide file tree
Showing 7 changed files with 468 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Like a method declaration, a lambda expression may declare parameters.
}
[:End:]

The lambda above has one parameter called e which is of type [:actionevent:].
The lambda above has one parameter called [:e:] which is of type [:actionevent:].
The code after the [:tube:] operator is the internal code of the lambda expression.


Expand Down Expand Up @@ -61,7 +61,9 @@ For example, when using inferred type, the code in the previous section becomes:
The type of [:e:] is inferred according to its usage.


## Implicit Parameters: it
## Implicit Parameters

### Case of a single parameter: it

As lambdas with one parameter are a common case, there a special short hand notation
for these parameters, which is to leave the declaration including the vertical bar out.
Expand All @@ -87,6 +89,45 @@ The name of the single parameter becomes [:it:].
}
[:End:]

### Case of multiple parameters

When a lambda has multiple parameters, and no name is provided by the SARL developer,
the compiler generates default names for each of the formal parameters.
The implicit name for the first parameter is `$0`, `$1` for the second,
`$2` for the third, etc.

Let the following interface declaration:

[:Success:]
package io.sarl.docs.reference.gsr
[:On]
interface [:MyInterfacename](MyInterface) {
def [:MyInterfacenamefct](myfct)(a : int, b : int, c : int)
}
[:Off]
[:End:]

The following code is a lambda expression implementing the [:MyInterfacename:] interface.
Because the [:MyInterfacenamefct:] function has four formal parameters, the lambda expression
has four parameters with implicit the following implicit names: `$0`, `$1`, and `$2`.

[:Success:]
package io.sarl.docs.reference.gsr
interface MyInterface {
def myfct(a : int, b : int, c : int) : int
}
agent A {
def called(p : MyInterface) {}
def example {
called(
[:On]
[
$0 + $1 + $2
]
[:Off])
}
}
[:End:]

## Empty List of Parameters

Expand Down
64 changes: 39 additions & 25 deletions main/coreplugins/io.sarl.lang/src/io/sarl/lang/util/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ public final class Utils {

private static boolean checkSarlVersionClass = true;

private static final Pattern IMPLICIT_LAMBDA_PARAMETER_PATTERN = Pattern.compile("^\\$[0-9]+$"); //$NON-NLS-1$

static {
final StringBuilder name = new StringBuilder();
final String[] components = EarlyExit.class.getPackage().getName().split("\\."); //$NON-NLS-1$
Expand Down Expand Up @@ -340,7 +342,7 @@ && isVisible(jvmElement, feature)
*
* @param fromType - the type from which the feature visibility is tested.
* @param target - the feature to test for the visibility.
* @return <code>true</code> if the given type can see the target feature.
* @return {@code true} if the given type can see the target feature.
*/
public static boolean isVisible(JvmDeclaredType fromType, JvmMember target) {
switch (target.getVisibility()) {
Expand All @@ -358,7 +360,7 @@ public static boolean isVisible(JvmDeclaredType fromType, JvmMember target) {
/** Replies if the last parameter is a variadic parameter.
*
* @param params - parameters.
* @return <code>true</code> if the late parameter is variadic.
* @return {@code true} if the late parameter is variadic.
*/
public static boolean isVarArg(List<? extends XtendParameter> params) {
assert params != null;
Expand All @@ -376,12 +378,24 @@ public static boolean isVarArg(List<? extends XtendParameter> params) {
* compiler, and that cannot be defined by the SARL user.
*
* @param name - the name to test.
* @return <code>true</code> if the given name is reserved by SARL.
* @return {@code true} if the given name is reserved by SARL.
*/
public static boolean isHiddenMember(String name) {
return name.contains(HIDDEN_MEMBER_CHARACTER);
}

/** Replies if the given name is related to an hidden parameter for closures.
*
* <p>An hidden parameter for closures is the name assigned to a formal parameter
* for a closure when no name is explicitly provided.
*
* @param name - the name to test.
* @return {@code true} if the given name is reserved by SARL.
*/
public static boolean isImplicitLambdaParameterName(String name) {
return IMPLICIT_LAMBDA_PARAMETER_PATTERN.matcher(name).matches();
}

/** Replies a fixed version of the given name assuming
* that it is an hidden action, and reformating
* the reserved text.
Expand Down Expand Up @@ -429,7 +443,7 @@ public static String createNameForHiddenCapacityImplementationCallingMethodFromF
/** Replies if the given simple name is the name of the hidden method that is calling a capacity implementation.
*
* @param simpleName the simple name.
* @return <code>true</code> if the given simple name if for the hidden method for capacuty uses.
* @return {@code true} if the given simple name if for the hidden method for capacuty uses.
*/
public static boolean isNameForHiddenCapacityImplementationCallingMethod(String simpleName) {
return simpleName != null && simpleName.startsWith(PREFIX_CAPACITY_IMPLEMENTATION)
Expand Down Expand Up @@ -478,7 +492,7 @@ public static String createNameForHiddenEventHandlerMethod(String eventId, int h
/** Replies if the given reference is pointing to a class type.
*
* @param typeRef - the type reference to test.
* @return <code>true</code> if the pointed element is a class type.
* @return {@code true} if the pointed element is a class type.
*/
public static boolean isClass(LightweightTypeReference typeRef) {
final JvmType t = typeRef.getType();
Expand All @@ -491,7 +505,7 @@ public static boolean isClass(LightweightTypeReference typeRef) {
/** Replies if the given type is a class type.
*
* @param type - the type to test.
* @return <code>true</code> if the element is a class type.
* @return {@code true} if the element is a class type.
*/
public static boolean isClass(Class<?> type) {
return !type.isInterface();
Expand All @@ -500,7 +514,7 @@ public static boolean isClass(Class<?> type) {
/** Replies if the given reference is referencing a final type.
*
* @param expressionTypeRef - the type reference to test.
* @return <code>true</code> if the given type is final.
* @return {@code true} if the given type is final.
*/
public static boolean isFinal(LightweightTypeReference expressionTypeRef) {
if (expressionTypeRef.isArray()) {
Expand All @@ -516,7 +530,7 @@ public static boolean isFinal(LightweightTypeReference expressionTypeRef) {
/** Replies if the given type is a final type.
*
* @param expressionType - the type to test.
* @return <code>true</code> if the given type is final.
* @return {@code true} if the given type is final.
*/
public static boolean isFinal(Class<?> expressionType) {
if (expressionType.isArray()) {
Expand All @@ -532,7 +546,7 @@ public static boolean isFinal(Class<?> expressionType) {
/** Replies if the given type is an interface.
*
* @param type - the type to test.
* @return <code>true</code> if the given type is an interface.
* @return {@code true} if the given type is an interface.
*/
public static boolean isInterface(LightweightTypeReference type) {
return type.getType() instanceof JvmGenericType
Expand Down Expand Up @@ -1022,7 +1036,7 @@ public static QualifiedName getQualifiedName(JvmIdentifiableElement element) {
/** Replies if the given declaration has an abstract member.
*
* @param declaration - the declaration.
* @return <code>true</code> if the given type has an abstract function.
* @return {@code true} if the given type has an abstract function.
*/
public static boolean hasAbstractMember(XtendTypeDeclaration declaration) {
if (declaration != null) {
Expand All @@ -1041,8 +1055,8 @@ public static boolean hasAbstractMember(XtendTypeDeclaration declaration) {
*
* @param typeReferences - the accessor to the types.
* @param context - the context that is providing the access to the classpath.
* @return <code>true</code> if a compatible SARL library was found.
* Otherwise <code>false</code>.
* @return {@code true} if a compatible SARL library was found.
* Otherwise {@code false}.
*/
public static boolean isCompatibleSARLLibraryOnClasspath(TypeReferences typeReferences, Notifier context) {
final OutParameter<String> version = new OutParameter<>();
Expand All @@ -1056,8 +1070,8 @@ public static boolean isCompatibleSARLLibraryOnClasspath(TypeReferences typeRefe
/** Check if a version is compatible with the expected SARL library.
*
* @param version - the version to test.
* @return <code>true</code> if a compatible SARL library was found.
* Otherwise <code>false</code>.
* @return {@code true} if a compatible SARL library was found.
* Otherwise {@code false}.
*/
public static boolean isCompatibleSARLLibraryVersion(String version) {
if (version != null) {
Expand All @@ -1072,8 +1086,8 @@ public static boolean isCompatibleSARLLibraryVersion(String version) {
/** Check if a version of the JRE is compatible with the SARL library.
*
* @param version - the version to test.
* @return <code>true</code> if this version is for a compatible JRE.
* Otherwise <code>false</code>.
* @return {@code true} if this version is for a compatible JRE.
* Otherwise {@code false}.
*/
public static boolean isCompatibleJREVersion(String version) {
if (version != null && !version.isEmpty()) {
Expand All @@ -1093,8 +1107,8 @@ public static boolean isCompatibleJREVersion(String version) {

/** Check if a version of the current JRE is compatible with the SARL library.
*
* @return <code>true</code> if this version is for a compatible JRE.
* Otherwise <code>false</code>.
* @return {@code true} if this version is for a compatible JRE.
* Otherwise {@code false}.
*/
public static boolean isCompatibleJREVersion() {
return isCompatibleJREVersion(System.getProperty("java.specification.version")); //$NON-NLS-1$
Expand All @@ -1103,8 +1117,8 @@ public static boolean isCompatibleJREVersion() {
/** Check if a version of Xtext is compatible with the SARL library.
*
* @param version - the version to test.
* @return <code>true</code> if this version is for a compatible Xtext.
* Otherwise <code>false</code>.
* @return {@code true} if this version is for a compatible Xtext.
* Otherwise {@code false}.
*/
public static boolean isCompatibleXtextVersion(String version) {
return version != null && !version.isEmpty()
Expand All @@ -1113,8 +1127,8 @@ public static boolean isCompatibleXtextVersion(String version) {

/** Check if a version of the current Xtext is compatible with the SARL library.
*
* @return <code>true</code> if this version is for a compatible Xtext.
* Otherwise <code>false</code>.
* @return {@code true} if this version is for a compatible Xtext.
* Otherwise {@code false}.
*/
public static boolean isCompatibleXtextVersion() {
final XtextVersion xtextVersion = XtextVersion.getCurrent();
Expand Down Expand Up @@ -1201,7 +1215,7 @@ public static SarlLibraryErrorCode getSARLLibraryVersionOnClasspath(TypeReferenc
/** Replies if the given annotation is an annotation from the SARL core library.
*
* @param type the type of the annotation
* @return <code>true</code> if the given type is a SARL annotation.
* @return {@code true} if the given type is a SARL annotation.
*/
public static boolean isSARLAnnotation(Class<?> type) {
return (type != null && Annotation.class.isAssignableFrom(type))
Expand All @@ -1211,7 +1225,7 @@ public static boolean isSARLAnnotation(Class<?> type) {
/** Replies if the given annotation is an annotation from the SARL core library.
*
* @param qualifiedName the qualified name of the annotation type.
* @return <code>true</code> if the given type is a SARL annotation.
* @return {@code true} if the given type is a SARL annotation.
*/
public static boolean isSARLAnnotation(String qualifiedName) {
return qualifiedName != null && qualifiedName.startsWith(SARL_PACKAGE_PREFIX);
Expand All @@ -1224,7 +1238,7 @@ public static boolean isSARLAnnotation(String qualifiedName) {
*
* @param type the type to test.
* @param sarlSignatureProvider the provider of SARL operation signatures.
* @return <code>true</code> if the given type is final.
* @return {@code true} if the given type is final.
*/
public static boolean isFunctionalInterface(JvmGenericType type, IActionPrototypeProvider sarlSignatureProvider) {
if (type != null && type.isInterface()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@

import javax.inject.Inject;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtend.core.xtend.XtendAnnotationType;
import org.eclipse.xtend.core.xtend.XtendClass;
import org.eclipse.xtend.core.xtend.XtendEnum;
import org.eclipse.xtend.core.xtend.XtendInterface;
import org.eclipse.xtend.core.xtend.XtendMember;
import org.eclipse.xtend.core.xtend.XtendTypeDeclaration;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmMember;
import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XClosure;
import org.eclipse.xtext.xbase.jvmmodel.ILogicalContainerProvider;
import org.eclipse.xtext.xbase.lib.InputOutput;

Expand Down Expand Up @@ -79,8 +82,10 @@ public boolean isDisallowedCall(XAbstractFeatureCall call) {
return !isInsideOOTypeDeclaration(call);
}
// Avoid any call to the hidden functions (function name contains "$" character).
if (Utils.isHiddenMember(feature.getSimpleName())
&& !Utils.isNameForHiddenCapacityImplementationCallingMethod(feature.getSimpleName())) {
final String simpleName = feature.getSimpleName();
if (Utils.isHiddenMember(simpleName)
&& !Utils.isNameForHiddenCapacityImplementationCallingMethod(simpleName)
&& (!Utils.isImplicitLambdaParameterName(simpleName) || !isInsideClosure(call))) {
return true;
}
// Avoid any reference to private API.
Expand All @@ -91,6 +96,13 @@ public boolean isDisallowedCall(XAbstractFeatureCall call) {
return false;
}

private static boolean isInsideClosure(XAbstractFeatureCall call) {
final EObject container = Utils.getFirstContainerForPredicate(call, it -> {
return it instanceof XClosure || it instanceof XtendMember;
});
return container instanceof XClosure;
}

private boolean isPrivateAPI(JvmIdentifiableElement element) {
JvmMember featureContainer = EcoreUtil2.getContainerOfType(element, JvmMember.class);
while (featureContainer != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public SARLFeatureNameValidator() {
@Override
public boolean isDisallowedName(QualifiedName name) {
final String id = name.getLastSegment();
return Utils.isHiddenMember(id) || super.isDisallowedName(name);
return super.isDisallowedName(name) || (Utils.isHiddenMember(id) && !Utils.isImplicitLambdaParameterName(id));
}

}
Loading

0 comments on commit 1056c08

Please sign in to comment.