Home » Modeling » TMF (Xtext) » Exploiting types in a DSL: creating new types and referring to existing ones
Exploiting types in a DSL: creating new types and referring to existing ones [message #649544] |
Wed, 19 January 2011 16:16 |
Victor Noël Messages: 112 Registered: June 2010 |
Senior Member |
|
|
Hello,
I am trying to integrate my DSL with JavaVMTypes and I am stuck with some problems.
The idea is the following:
I have some elements named Component, inside a Namespace with imports, and I want these elements to have a name and a list of type parameters.
These components contains stuffs named Port that have the particularity to have a member referring to a real Java type.
And these real Java types can have type parameter, and for these, I would like to be either able to use other real Java types, or the type parameter of the enclosing component as type arguments.
Furthermore, component can contains also stuffs named val, that are instance of other components, i.e. referred to by their name along with type arguments for their type parameters.
Of course, every time I am referring to a type, be it a Component or a real Java type, I would like to verify that the number of arguments is correct (and maybe later add constraints for the type parameters...).
Finally, I want, for one instance of a component, inside of another component, to be able to bind ports together, and that this binding is respecting the semantics of the Java assignment with respect to types!
Here is an example of instance of my dsl that should work:
component C1[T] {
required p1: java.lang.String
required p2: java.util.List[T]
}
component C2[T] {
provided p1: java.lang.String
provided p2: java.util.List[T]
}
component C3 {
val ci1: C1[java.lang.String] {
bind p1 to ci2.p1
bind p2 to ci2.p2
}
val ci2: C2[java.lang.String]
}
I have a first shot at answering that:
import "http://www.eclipse.org/xtext/common/JavaVMTypes" as types
File returns Namespace:
( imports+=Import
| elements+=Element
)*;
Namespace:
'namespace' name=FQN '{'
( imports+=Import
| elements+=Element
)*
'}';
FQN:
ID ('.' ID)*;
ImportedFQN:
FQN ('.' '*')?;
Import:
'import' importedNamespace=ImportedFQN;
Element:
Namespace | Component;
Component:
"component" type=ComponentTypeWithParam "{"
( "required" requireds+=Port
| "provided" provideds+=Port
| "val" vals+=Instance
)*
"}";
ComponentTypeWithParam returns types::JvmGenericType:
fullyQualifiedName=ID ("[" typeParameters+=TypeParam ("," typeParameters+=TypeParam)* "]")?;
TypeParam returns types::JvmTypeParameter:
name=ID;
Port:
name=ID ":" interface=Interface;
Interface:
javaType=TypeWithParamRef; // here, only keep interfaces
TypeWithParamRef returns types::JvmParameterizedTypeReference:
type=[types::JvmType|FQN] ("[" arguments+=TypeParamRef ("," arguments+=TypeParamRef)* "]")?;
TypeParamRef returns types::JvmReferenceTypeArgument:
typeReference=TypeWithParamRef;
Instance:
name=ID ":" type=TypeWithParamRef // only keep the
("{"
(bindings+=Binding)*
"}")?;
Binding:
"bind" from=[Port]
"to" instance=[Instance]
"." port=[Port];
But I encountered a set of problems:
1) ComponentTypeWithParam doesn't seems to be understood as referrable type by TypeWithParamRef in Instance... I guess it is linked to the way I declare NEW types with my DSL (see ComponentTypeWithParam)
2) It seems that since Composite does not declare any name member, I lost the nice differentiation between stuff with the same name enclosed in it (i.e. the DSL editor tells me that the different T in my example have the same name and can't. Also the same happens with the name of the port).
3) I don't know how to exploit the types to check the bindings, I guess when the rest will be ok, this will be straightforward !
4) It seems that the default scope provider for is not very good behaving using
At least, it seems that I can use one of the TypeParam as an argument to the type parameters of the TypeWithParamRef!
Thank you for any help,
I am fighting with this for a long time :)
|
|
|
Re: Exploiting types in a DSL: creating new types and referring to existing ones [message #649575 is a reply to message #649544] |
Wed, 19 January 2011 18:50 |
|
Hello Victor,
doing some adaptions to the grammar and introducing a QualifiedNameProvider and Scoping should help
grammar org.xtext.example.mydsl1.MyDsl1 with org.eclipse.xtext.common.Terminals
generate myDsl1 "http://www.xtext.org/example/mydsl1/MyDsl1"
import "http://www.eclipse.org/xtext/common/JavaVMTypes" as types
File returns Namespace:
( imports+=Import
| elements+=Element
)*;
Namespace:
'namespace' name=FQN '{'
( imports+=Import
| elements+=Element
)*
'}';
FQN:
ID ('.' ID)*;
ImportedFQN:
FQN ('.' '*')?;
Import:
'import' importedNamespace=ImportedFQN;
Element:
Namespace | Component;
Component:
"component" type=ComponentTypeWithParam "{"
( "required" requireds+=Port
| "provided" provideds+=Port
| "val" vals+=Instance
)*
"}";
ComponentTypeWithParam returns types::JvmGenericType:
{ComponentTypeWithParam} fullyQualifiedName=ID ("[" typeParameters+=TypeParam ("," typeParameters+=TypeParam)* "]")?;
TypeParam returns types::JvmTypeParameter:
{TypeParam} name=ID;
Port:
name=ID ":" interface=Interface;
Interface:
javaType=TypeWithParamRef; // here, only keep interfaces
TypeWithParamRef returns types::JvmParameterizedTypeReference:
type=[types::JvmType|FQN] ("[" arguments+=TypeParamRef ("," arguments+=TypeParamRef)* "]")?;
ComponentRefWithParamRef:
type=[Component|FQN] ("[" arguments+=TypeParamRef ("," arguments+=TypeParamRef)* "]")?;
TypeParamRef returns types::JvmReferenceTypeArgument:
typeReference=TypeWithParamRef;
Instance:
name=ID ":" type=ComponentRefWithParamRef // only keep the
("{"
(bindings+=Binding)*
"}")?;
Binding:
"bind" from=[Port]
"to" instance=[Instance]
"." port=[Port];
package org.xtext.example.mydsl1;
import org.eclipse.xtext.naming.DefaultDeclarativeQualifiedNameProvider;
import org.xtext.example.mydsl1.myDsl1.Component;
import org.xtext.example.mydsl1.myDsl1.ComponentTypeWithParam;
import org.xtext.example.mydsl1.myDsl1.TypeParam;
public class MyQNP extends DefaultDeclarativeQualifiedNameProvider {
public String qualifiedName(Component component) {
return component.getType().getFullyQualifiedName();
}
public String qualifiedName(TypeParam typeParam) {
return getQualifiedName((ComponentTypeWithParam)typeParam.eContainer())+"."+typeParam.getName();
}
public String qualifiedName(ComponentTypeWithParam componentTypeWithParam) {
return componentTypeWithParam.getFullyQualifiedName();
}
}
/*
* generated by Xtext
*/
package org.xtext.example.mydsl1;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
*/
public class MyDsl1RuntimeModule extends org.xtext.example.mydsl1.AbstractMyDsl1RuntimeModule {
@Override
public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
return MyQNP.class;
}
}
/*
* generated by Xtext
*/
package org.xtext.example.mydsl1.scoping;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.Scopes;
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
import org.xtext.example.mydsl1.myDsl1.Binding;
import org.xtext.example.mydsl1.myDsl1.Instance;
import org.xtext.example.mydsl1.myDsl1.Port;
/**
* This class contains custom scoping description.
*
* see : http://www.eclipse.org/Xtext/documentation/latest/xtext.html#scoping
* on how and when to use it
*
*/
public class MyDsl1ScopeProvider extends AbstractDeclarativeScopeProvider {
public IScope scope_Binding_from(Binding binding, EReference ref) {
List<Port> ports = new ArrayList<Port>();
ports.addAll(((Instance)binding.eContainer()).getType().getType().getProvideds());
ports.addAll(((Instance)binding.eContainer()).getType().getType().getRequireds());
return Scopes.scopeFor(ports);
}
public IScope scope_Binding_port(Binding binding, EReference ref) {
List<Port> ports = new ArrayList<Port>();
ports.addAll(binding.getInstance().getType().getType().getProvideds());
ports.addAll(binding.getInstance().getType().getType().getRequireds());
return Scopes.scopeFor(ports);
}
}
~Christian
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
|
Re: Exploiting types in a DSL: creating new types and referring to existing ones [message #649669 is a reply to message #649544] |
Thu, 20 January 2011 08:50 |
Victor Noël Messages: 112 Registered: June 2010 |
Senior Member |
|
|
Hello Christian,
Thank you for your quick reply, it is very helpful :)
Now to complete your solution, I changed and added some stuffs:
In particular, I reduced the scopes for the bindings only to a subset of the ports
public class MyDslScopeProvider extends AbstractDeclarativeScopeProvider {
public IScope scope_Binding_from(Binding binding, EReference ref) {
List<Port> ports = new ArrayList<Port>();
ports.addAll(((Instance)binding.eContainer()).getType().getType().getRequireds());
return Scopes.scopeFor(ports);
}
public IScope scope_Binding_port(Binding binding, EReference ref) {
List<Port> ports = new ArrayList<Port>();
ports.addAll(binding.getInstance().getType().getType().getProvideds());
return Scopes.scopeFor(ports);
}
}
And I added a constraint to check types.
public class MyDslJavaValidator extends AbstractMyDslJavaValidator {
@Inject
private IAssignabilityComputer assComp;
@Check
public void checkBinding(Binding b) {
if (!assComp.isAssignableFrom(b.getFrom().getInterface().getJavaType(), b.getPort().getInterface().getJavaType())) {
error("Incompatible interfaces", MyDslPackage.BINDING);
}
}
}
Then, with the current code:
component C1[T] {
required p1: java.lang.String
required p2: java.util.List[T]
}
namespace ns1 {
component C2[T] {
provided p1: java.lang.String
provided p2: java.util.List[T]
}
}
component C3 {
val c: ns1.C2[java.lang.String] // pb namespace
}
component C3 {} // pb C3 duplicate
component C4a {
val ci1: C1[java.lang.String] {
bind p1 to ci2.p1
bind p2 to ci2.p2 // pb types
}
val ci2: C2[java.lang.String]
}
component C4b[T] {
val ci1: C1[T] {
bind p1 to ci2.p1
bind p2 to ci2.p2 // pb types
}
val ci2: C2[T]
}
My problems are the following:
1) As demonstrated with C3, the namespace is not taken into account, I am not sure of what I should do to have the same behaviour as with out-of-the-box namespace and names.
2) Maybe linked to 1, as demonstrated with C3, the out-of-the-box check for duplicates does not seem to work either.
3) As demonstrated with C4b and C4b, the types of the ports are not taking the type arguments into account in ci1 for p2.
4) It seems that the completion fails to work when typing a TypeWithParamRef: it doesn't complete the namespace part of the types.
5) How to override the completion in order to have also type parameter with default arguments when typing TypeWithParamRef or ComponentRefWithParamRef?
Would you have any idea regarding these things, I know it start to get complex, but I am sure that with the correct knowledge, it should be doable :)
Thank you very much.
Victor
|
|
| | |
Goto Forum:
Current Time: Thu Apr 25 11:47:58 GMT 2024
Powered by FUDForum. Page generated in 0.02865 seconds
|