Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » How to infer a method with generics?(How to create a JvmOperation with generics)
How to infer a method with generics? [message #1749037] Thu, 01 December 2016 11:14 Go to next message
Peter Luthardt is currently offline Peter LuthardtFriend
Messages: 40
Registered: February 2014
Member
I am trying to enhance my DSL which is derived from XbaseWithAnnotations to enable the use of generic method.

The user should write something like this:

op <T> doSomething (T arg)
{
println("Hallo"+T)
}

1.) Is there a nice way to do this? I am sure I am not the first one who tries to do something like this. but I could not find any example.
2.) I could not figure out how to tell the JvmOpertation about the generics.

Any hint welcome

Thank you



Re: How to infer a method with generics? [message #1749049 is a reply to message #1749037] Thu, 01 December 2016 12:52 Go to previous messageGo to next message
Alex Tugarev is currently offline Alex TugarevFriend
Messages: 14
Registered: July 2011
Junior Member
In YourDslJvmModelInferrer you'll need:
    @Inject private org.eclipse.xtext.common.types.TypesFactory typesFactory
    @Inject private org.eclipse.xtext.common.types.util.TypeReferences references


Then when iterating over features you need to create type parameter first:
    val tp = typesFactory.createJvmTypeParameter()
    tp.name = "T"


And then add this type parameter to JvmOperation instance and also as type of parameter.

        members += operation.toMethod(operation.name, references.createTypeRef(tp)) [
            typeParameters += tp
            parameters += params.head.toParameter(p.name, references.createTypeRef(tp))
            // ...
        ]

[Updated on: Fri, 02 December 2016 12:08]

Report message to a moderator

Re: How to infer a method with generics? [message #1749064 is a reply to message #1749049] Thu, 01 December 2016 14:22 Go to previous messageGo to next message
Peter Luthardt is currently offline Peter LuthardtFriend
Messages: 40
Registered: February 2014
Member
Thanks, but is works only for one Method

        members += operation.toMethod(f.name, references.createTypeRef(tp)) [
            typeParameters += tp
            parameters += params.head.toParameter(p.name, references.createTypeRef(tp))
            // ...
        ]
       members += operation.toMethod(f.name+"X", references.createTypeRef(tp)) [
            typeParameters += tp
            parameters += params.head.toParameter(p.name, references.createTypeRef(tp))
            // ...
        ]

Does not work. "Cannot make a static reference to the non-static type T"

I need to create an Interface and an Implementation so I have to use it twice. (I tried tp.cloneWithProxies, but no success)
Re: How to infer a method with generics? [message #1749071 is a reply to message #1749064] Thu, 01 December 2016 14:59 Go to previous messageGo to next message
Alex Tugarev is currently offline Alex TugarevFriend
Messages: 14
Registered: July 2011
Junior Member
Just create one type parameter per operation. Then they can be named the same.
Re: How to infer a method with generics? [message #1749074 is a reply to message #1749071] Thu, 01 December 2016 15:39 Go to previous messageGo to next message
Peter Luthardt is currently offline Peter LuthardtFriend
Messages: 40
Registered: February 2014
Member
I do not understand.
Creating the genericTypes twice does not solve the problem
	val genericTypes = method.createJvmTypeParameter()
	members += method.toMethod(method.declaration.methodName, method.type().cloneWithProxies) [
				
		genericTypes.forEach[tp|
			typeParameters += tp.cloneWithProxies
		]...
	]

	val genericTypesX = method.createJvmTypeParameter()
	members += method.toMethod(method.declaration.methodName+"X", method.type().cloneWithProxies) [
		genericTypesX.forEach[tp|
			typeParameters += tp.cloneWithProxies	
		]...
	]
Re: How to infer a method with generics? [message #1749075 is a reply to message #1749071] Thu, 01 December 2016 15:39 Go to previous messageGo to next message
Peter Luthardt is currently offline Peter LuthardtFriend
Messages: 40
Registered: February 2014
Member
I do not understand.
Creating the genericTypes twice does not solve the problem
	val genericTypes = method.createJvmTypeParameter()
	members += method.toMethod(method.declaration.methodName, method.type().cloneWithProxies) [
				
		genericTypes.forEach[tp|
			typeParameters += tp.cloneWithProxies
		]...
	]

	val genericTypesX = method.createJvmTypeParameter()
	members += method.toMethod(method.declaration.methodName+"X", method.type().cloneWithProxies) [
		genericTypesX.forEach[tp|
			typeParameters += tp.cloneWithProxies	
		]...
	]
Re: How to infer a method with generics? [message #1749148 is a reply to message #1749075] Fri, 02 December 2016 10:21 Go to previous messageGo to next message
Lorenzo Bettini is currently offline Lorenzo BettiniFriend
Messages: 1735
Registered: July 2009
Location: Firenze, Italy
Senior Member
You're not creating the generic types twice: you're cloning them
Re: How to infer a method with generics? [message #1749150 is a reply to message #1749148] Fri, 02 December 2016 10:48 Go to previous messageGo to next message
Peter Luthardt is currently offline Peter LuthardtFriend
Messages: 40
Registered: February 2014
Member
val genericTypesX = method.createJvmTypeParameter()

calls the method like suggested by Alex
    val tp = typesFactory.createJvmTypeParameter()
    tp.name = "T"

So for every generic parameter I call the code.
I my case this should create a new JvmTypeParameter instance for each JvmOperation.

Mybe this shows it better:
	val tp = typesFactory.createJvmTypeParameter()
	tp.name = "T"
	members += method.toMethod(method.declaration.methodName, method.type().cloneWithProxies) [
				
		typeParameters += tp.cloneWithProxies
		...
	]

	val tpX = typesFactory.createJvmTypeParameter()
	tpX.name = "T"
	members += method.toMethod(method.declaration.methodName+"X", method.type().cloneWithProxies) [
		typeParameters += tpX.cloneWithProxies
		...
	]

results in an error: Cannot make a static reference to the non-static type T
Re: How to infer a method with generics? [message #1749152 is a reply to message #1749150] Fri, 02 December 2016 11:14 Go to previous messageGo to next message
Alex Tugarev is currently offline Alex TugarevFriend
Messages: 14
Registered: July 2011
Junior Member
Why are you cloning the type parameters?

typeParameters += tp // .cloneWithProxies


Please inspect the working example from above.

After reading your first post once again, I'm not sure which signatures you are expecting to be created.

[Updated on: Fri, 02 December 2016 11:15]

Report message to a moderator

Re: How to infer a method with generics? [message #1749154 is a reply to message #1749152] Fri, 02 December 2016 11:51 Go to previous messageGo to next message
Peter Luthardt is currently offline Peter LuthardtFriend
Messages: 40
Registered: February 2014
Member
Thank you for your patience.
I have create a tiny dsl which shows the problem.
When you run the test case you get the error message.
Re: How to infer a method with generics? [message #1749156 is a reply to message #1749154] Fri, 02 December 2016 12:14 Go to previous messageGo to next message
Alex Tugarev is currently offline Alex TugarevFriend
Messages: 14
Registered: July 2011
Junior Member
Okay, I had a look at your test.

In addition to my last comment: please also consider to use a type reference to the actual type parameter as return type of the inferred operation. Cf. example from above. The relevant line is:
  members += operation.toMethod(operation.name, references.createTypeRef(typeParameterT)) [ ...
Re: How to infer a method with generics? [message #1749160 is a reply to message #1749156] Fri, 02 December 2016 13:12 Go to previous messageGo to next message
Peter Luthardt is currently offline Peter LuthardtFriend
Messages: 40
Registered: February 2014
Member
Thanks,

I have change to
	def dispatch void infer(Class element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
		acceptor.accept(element.toClass("my.class."+element.name))[
			element.methods.forEach[method|
				val tp = typesFactory.createJvmTypeParameter()
				tp.name = method.generic
				members += method.toMethod(method.name, references.createTypeRef(tp))[
					typeParameters += tp
				]
				val tpX = typesFactory.createJvmTypeParameter()
				tpX.name = method.generic
				members += method.toMethod(method.name+"X", references.createTypeRef(tpX))[
					typeParameters += tpX
				]
			]
		]
	}

but still the same: "Cannot make a static reference to the non-static type T"
Re: How to infer a method with generics? [message #1749312 is a reply to message #1749160] Mon, 05 December 2016 11:20 Go to previous messageGo to next message
Jan Koehnlein is currently offline Jan KoehnleinFriend
Messages: 746
Registered: July 2009
Location: Hamburg
Senior Member
In your grammar, you say

Method:
	'<' generic=ValidID '>' ...


as such you don't give Xbase a hint that this is a type parameter.

You have to write

Method:
	'<'generic=JvmTypeParameter'>' ...


instead. You can use this type parameter directly in the inferrer, i.e.

element.methods.forEach[method|
	members += method.toMethod(method.name, method.type)[
		typeParameters += method.generic
	]
	members += method.toMethod(method.name+"X", method.type)[
		typeParameters += method.generic
	]
]


---
Get professional support from the Xtext committers at www.typefox.io
Re: How to infer a method with generics? [message #1771289 is a reply to message #1749312] Wed, 23 August 2017 17:15 Go to previous message
Peter Luthardt is currently offline Peter LuthardtFriend
Messages: 40
Registered: February 2014
Member
Sorry but I have to reopen this thread.
I have learned a lot and created a tiny dsl
grammar lutte.typedmethodParameter.LUTTE1 with org.eclipse.xtext.xbase.annotations.XbaseWithAnnotations

generate lUTTE1 "http://www.typedmethodParameter.lutte/LUTTE1"

Model:
	'BusinessClass' name = ValidID
	operations += Operation*
;
	
Operation:
	typedParameters += TypedParameter* 'op' operationType = JvmTypeReference name = ValidID '(' parameters+=JvmFormalParameter(','parameters+=JvmFormalParameter)*')'
	body = XBlockExpression 
;

TypedParameter:
	'<'typedParameter = JvmTypeParameter'>'
;


with Inferer
/*
 * generated by Xtext 2.12.0
 */
package lutte.typedmethodParameter.jvmmodel

import com.google.inject.Inject
import lutte.typedmethodParameter.lUTTE1.Model
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder

class LUTTE1JvmModelInferrer extends AbstractModelInferrer {

	@Inject extension JvmTypesBuilder

	def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
		acceptor.accept(element.toClass("test."+element.name))[
			element.operations.forEach[op|
				members += op.toMethod(op.name, op.operationType.cloneWithProxies)[
					op.typedParameters.forEach[parameter|
						typeParameters += parameter.typedParameter
					]
					op.parameters.forEach[parameter|
						parameters += parameter.cloneWithProxies
					]
					body = op.body
				]
				members += op.toMethod(op.name+"2", op.operationType.cloneWithProxies)[
					op.typedParameters.forEach[parameter|
						typeParameters += parameter.typedParameter
					]
					op.parameters.forEach[parameter|
						parameters += parameter.cloneWithProxies
					]
				]

			]
		]
	}
}


a test case
/*
 * generated by Xtext 2.12.0
 */
package lutte.typedmethodParameter.tests

import com.google.inject.Inject
import com.google.inject.Provider
import java.util.ArrayList
import lutte.typedmethodParameter.lUTTE1.Model
import org.eclipse.xtext.generator.InMemoryFileSystemAccess
import org.eclipse.xtext.resource.XtextResourceSet
import org.eclipse.xtext.testing.InjectWith
import org.eclipse.xtext.testing.XtextRunner
import org.eclipse.xtext.testing.util.ParseHelper
import org.eclipse.xtext.xbase.compiler.JvmModelGenerator
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.eclipse.emf.common.util.URI

@RunWith(XtextRunner)
@InjectWith(LUTTE1InjectorProvider)
class LUTTE1ParsingTest {
	@Inject
	ParseHelper<Model> parseHelper
	@Inject private Provider<XtextResourceSet> resourceSetProvider;
	@Inject private JvmModelGenerator generator;
	
	@Test
	def void loadModel() {
		val resourceSet = resourceSetProvider.get
		resourceSet.classpathURIContext = this
		val result = parseHelper.parse('''
			BusinessClass Test
			<T> op T test(T t)
			{
				t
			}
		''', URI.createURI("test/Test.lutte1"), resourceSet)
		Assert.assertNotNull(result)
		Assert.assertTrue(result.eResource.errors.isEmpty)
		
        val fsa = new InMemoryFileSystemAccess()
        val resources = new ArrayList(resourceSet.resources)
		resources.forEach[resource|
	        generator.doGenerate(resource, fsa)
		]
        
        fsa.allFiles.entrySet.forEach[entry|
        	println(entry.key)
        ]
        val file = fsa.textFiles.get("DEFAULT_OUTPUTtest/Test.java").toString;
		Assert.assertEquals('''package test;

@SuppressWarnings("all")
public class Test {
  public <T> T test(final T t) {
    return t;
  }
  
  public <T> test2(final T t);
}
'''.toString.remove('\r'), file)
	}

	def String remove (String from, String value)
	{
		return from.replaceAll("("+value+")", "");
	}
}


with the result
org.junit.ComparisonFailure: expected:<... t;
  }
  
  public [<T> test2(final T] t);
}
> but was:<... t;
  }
  
  public [? test2(final ?] t);
}
>
	at org.junit.Assert.assertEquals(Assert.java:115)
	at org.junit.Assert.assertEquals(Assert.java:144)
	at lutte.typedmethodParameter.tests.LUTTE1ParsingTest.loadModel(LUTTE1ParsingTest.java:98)
...


What is it I am missing?
I used xtext 2.12.0 .
Previous Topic:How to enable running functionality in a new dsl
Next Topic:SyntaxErrorMessageProvider custom implementation
Goto Forum:
  


Current Time: Tue Oct 17 15:30:40 GMT 2017

Powered by FUDForum. Page generated in 0.03935 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software