Get the effective type of JvmField in parametrized context [message #1067582] |
Tue, 09 July 2013 12:49 |
Kevin SERIN Messages: 19 Registered: May 2013 |
Junior Member |
|
|
Hi,
I'm creating a DSL where we can specify components (which are classes). These components can be parametrized (like a class can be). Inside components, we can use instances of other declared components. This is a short example of what we can do:
component Comp1[A] {
port p : A
}
component Comp2 {
instance p1 : Comp1[String]
}
As you can see, components can also have typed ports.
What I'm trying to do is to know the EFFECTIVE type of the port 'p' inside the instance 'p1'. In this case, it's 'String' because the instance 'p1' is parametrized by the type 'String', and the port 'p' has the parametrized type as type.
This the grammar I use:
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
Model:
components+=Component*;
Component:
'component' name=ValidID
(("<"|"[") typeParameters+=JvmTypeParameter ("," typeParameters+=JvmTypeParameter)* (">"|"]"))?
'{'
(
ports+=Port
|parts+=Part
)*
'}';
Part:
'instance' name=ValidID ':' reference=ParameterizedComponentReference;
ParameterizedComponentReference:
component=[Component] (=>("<"|"[") arguments+=JvmArgumentTypeReference ("," arguments+=JvmArgumentTypeReference)* ("<"|"]"))?;
Port:
"port" name=ValidID ":" typeReference=JvmParameterizedTypeReference;
And this is my JvmModelInferrer:
def dispatch void infer(Component component, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(component.toClass("mypackage."+component.name)[
typeParameters += component.typeParameters.map[cloneWithProxies]
]).initializeLater[
//ports
for(port : component.ports) {
members += port.toField(port.name, port.typeReference)
}
//parts
for(part : component.parts) {
members += part.toMethod(part.name, part.reference.toType)[
visibility = JvmVisibility::PROTECTED
];
}
]
}
def dispatch private JvmTypeReference toType(Component component) {
val ref = component.jvmElements.head
if(ref != null && ref instanceof JvmType) {
return (ref as JvmType).newTypeRef()
}
return null
}
def dispatch private JvmTypeReference toType(ParameterizedComponentReference ref) {
val type = ref.component.jvmElements.head as JvmType
if(type != null) {
val res = type.newTypeRef(ref.arguments)
ref.associate(res)
res
}
}
So I would like to get the effective type of my ports "inside" my instances in my validator. But of course, if I do this:
@Check
def checkInstancePortType(Part instance) {
for(port : instance.reference.component.ports) {
println(getActualType(port.jvmElements.head as JvmField))
}
}
the returned type is 'A'. But I would like to have the type in the parametrized context (I want 'String') and i don't know how to do it (I hope I'm clear :/). Is there a way to do it ?
|
|
|
Re: Get the effective type of JvmField in parametrized context [message #1067589 is a reply to message #1067582] |
Tue, 09 July 2013 13:31 |
Hallvard Traetteberg Messages: 673 Registered: July 2009 Location: Trondheim, Norway |
Senior Member |
|
|
Hi,
I have a similar DSL. To compute the actual type, I have a custom copier
that resolves type parameters. I've inserted the code below, hopefully
it is helpful. Note that my model uses the existing JvmType objects,
rather than implementing my own.
Hallvard
package org.ptolemy.xtext.generator;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
import org.eclipse.xtext.common.types.JvmTypeParameter;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations;
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder;
import org.ptolemy.ecore.actor.ActorRef;
import org.ptolemy.ecore.actor.TypeParameter;
import org.ptolemy.ecore.actor.TypeParameterized;
import org.ptolemy.ecore.kernel.Entity;
import org.ptolemy.ecore.kernel.EntityRef;
import com.google.inject.Inject;
class TypeParameterResolvingCopier extends Copier {
private boolean generic = false;
public boolean isGeneric() {
return generic;
}
public static class Result {
public final JvmTypeReference typeRef;
public boolean generic;
public Result(JvmTypeReference typeRef, boolean generic) {
super();
this.typeRef = typeRef;
this.generic = generic;
}
}
static Result getCachedResult(JvmTypeReference typeRef) {
return WrappingAdapter.getWrapped(typeRef, Result.class);
}
Result copyTypeReference(JvmTypeReference typeRef) {
JvmTypeReference copy = (JvmTypeReference) copy(typeRef);
copyReferences();
Result result = new Result(copy, generic);
new WrappingAdapter<Result>(result).attach(typeRef);
if (! generic) {
new WrappingAdapter<Result>(result).attach(copy);
}
return result;
}
@Override
public EObject copy(EObject eObject) {
if (eObject instanceof JvmTypeReference) {
JvmTypeReference typeRef = (JvmTypeReference) eObject;
if (typeRef.getType() instanceof JvmTypeParameter) {
JvmTypeReference resolveTypeRef = resolveTypeParameter(typeRef, true);
if (resolveTypeRef.getType() instanceof JvmTypeParameter) {
generic = true;
}
return resolveTypeRef;
}
}
return super.copy(eObject);
}
@Inject
private IJvmModelAssociations jvmModelAssociations;
@Inject
private JvmTypesBuilder jvmTypesBuilder;
JvmTypeReference resolveTypeParameter(JvmTypeReference typeRef, boolean
clone) {
while (typeRef != null && typeRef.getType() instanceof JvmTypeParameter) {
JvmTypeReference resolvedTypeRef = resolveTypeParameter1(typeRef);
if (resolvedTypeRef == null) {
break;
// return typeRef;
}
typeRef = resolvedTypeRef;
}
return (clone ? jvmTypesBuilder.cloneWithProxies(typeRef) : typeRef);
}
JvmTypeReference resolveTypeParameter1(JvmTypeReference typeRef) {
EObject container = typeRef.eContainer();
while (container != null) {
if (container instanceof Entity<?>) {
EntityRef<?> entityRef = ((Entity<?>) container).getSuperEntity();
if (entityRef != null && entityRef.getRef() != null) {
Entity<?> superEntity = entityRef.getRef();
if (entityRef instanceof ActorRef<?> && superEntity instanceof
TypeParameterized) {
EList<JvmTypeReference> typeArguments = ((ActorRef<?>)
entityRef).getTypeArguments();
int pos = 0;
for (TypeParameter typeParameter : ((TypeParameterized)
superEntity).getTypeParameters()) {
for (EObject jvmElement :
jvmModelAssociations.getJvmElements(typeParameter)) {
if (typeRef.getType() == jvmElement && pos < typeArguments.size()) {
return typeArguments.get(pos);
}
}
pos = pos + 1;
}
}
}
}
container = container.eContainer();
}
return null;
}
}
On 09.07.13 14.49, Kevin SERIN wrote:
> Hi,
>
> I'm creating a DSL where we can specify components (which are classes).
> These components can be parametrized (like a class can be). Inside
> components, we can use instances of other declared components. This is a
> short example of what we can do:
>
> component Comp1[A] {
> port p : A
> }
>
> component Comp2 {
> instance p1 : Comp1[String]
> }
>
> As you can see, components can also have typed ports.
> What I'm trying to do is to know the EFFECTIVE type of the port 'p'
> inside the instance 'p1'. In this case, it's 'String' because the
> instance 'p1' is parametrized by the type 'String', and the port 'p' has
> the parametrized type as type.
>
> This the grammar I use:
> grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase
>
> generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
>
> Model:
> components+=Component*;
>
> Component:
> 'component' name=ValidID (("<"|"[")
> typeParameters+=JvmTypeParameter ("," typeParameters+=JvmTypeParameter)*
> (">"|"]"))?
> '{'
> (
> ports+=Port
> |parts+=Part
> )*
> '}';
>
> Part:
> 'instance' name=ValidID ':' reference=ParameterizedComponentReference;
>
> ParameterizedComponentReference:
> component=[Component] (=>("<"|"[")
> arguments+=JvmArgumentTypeReference (","
> arguments+=JvmArgumentTypeReference)* ("<"|"]"))?;
>
> Port:
> "port" name=ValidID ":" typeReference=JvmParameterizedTypeReference;
>
>
> And this is my JvmModelInferrer:
> def dispatch void infer(Component component, IJvmDeclaredTypeAcceptor
> acceptor, boolean isPreIndexingPhase) {
> acceptor.accept(component.toClass("mypackage."+component.name)[
> typeParameters +=
> component.typeParameters.map[cloneWithProxies]
> ]).initializeLater[
>
> //ports
> for(port : component.ports) {
> members += port.toField(port.name, port.typeReference)
> }
> //parts
> for(part : component.parts) {
> members += part.toMethod(part.name,
> part.reference.toType)[
> visibility = JvmVisibility::PROTECTED
> ];
> }
> ]
> }
>
> def dispatch private JvmTypeReference toType(Component component) {
> val ref = component.jvmElements.head
> if(ref != null && ref instanceof JvmType) {
> return (ref as JvmType).newTypeRef()
> }
> return null
> }
>
> def dispatch private JvmTypeReference
> toType(ParameterizedComponentReference ref) {
> val type = ref.component.jvmElements.head as JvmType
> if(type != null) {
> val res = type.newTypeRef(ref.arguments)
> ref.associate(res)
> res
> }
> }
>
> So I would like to get the effective type of my ports "inside" my
> instances in my validator. But of course, if I do this:
>
> @Check
> def checkInstancePortType(Part instance) {
> for(port : instance.reference.component.ports) {
> println(getActualType(port.jvmElements.head as JvmField))
> }
>
> }
>
> the returned type is 'A'. But I would like to have the type in the
> parametrized context (I want 'String') and i don't know how to do it (I
> hope I'm clear :/). Is there a way to do it ?
|
|
|
|
|
|
Re: Get the effective type of JvmField in parametrized context [message #1067790 is a reply to message #1067762] |
Wed, 10 July 2013 15:11 |
Victor Noël Messages: 112 Registered: June 2010 |
Senior Member |
|
|
Hi, yes it does contain them, it is not a problem about type erasure as we are not in the JVM itself but manipulating object representing Jvm elements.
Where we were wrong and you pointed us in the right direction is that we are actually interested in instance.jvmElements.head, which contains the reference with the type parameters.
So I am in the good direction to solve that but I'm still missing some pieces:
@Check
def checkInstancePortType(Part instance) {
for(port : instance.reference.component.ports) {
val partR = (instance.jvmElements.head as JvmField).type
val portO = (port.jvmElements.head as JvmField)
println(getRealType(partR, portO))
}
}
def getRealType(JvmTypeReference type, JvmField f) {
val part = type.toLightweightTypeReference;
val mapping = new ConstraintAwareTypeArgumentCollector(part.owner).getTypeParameterMapping(part);
new StandardTypeParameterSubstitutor(mapping, part.owner).substitute(f.type);
}
What we are looking for is something like ConstraintAwareTypeArgumentCollector and StandardTypeParameterSubstitutor, but the current code is wrong, because we are not considering the type arguments collected from partR as different than those of the thing that contain instance, so if we have a recursive thing, the substitutor just goes crazy in the recursion.
Maybe we are in the good direction, it is difficult to dig into xtext code
|
|
|
|
Powered by
FUDForum. Page generated in 0.03245 seconds