Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » Unexpected behavior on linkage between Xtext resource and inferred JVM type(My DSL inferres two JVM types from a grammar element. Xtext only recognizes one of them)
Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771309] Wed, 23 August 2017 21:20 Go to next message
Miguel Jimenez is currently offline Miguel JimenezFriend
Messages: 40
Registered: July 2015
Member
Hi,

I'm generating two Java classes (Entity.java & EntityEntity.java) from a grammar element (Entity). These are the grammar definition and the JVM model inferrer:
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"

DomainModel:
	importSection=XImportSection?
	elements+=AbstractElement*;
	
AbstractElement:
	PackageDeclaration | Entity;

PackageDeclaration:
	'package' name=QualifiedName '{'
		elements+=AbstractElement*
	'}';

Entity:
	'entity' name=ValidID body=XBlockExpression;


package org.xtext.example.mydsl.jvmmodel

import com.google.inject.Inject
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.xtext.example.mydsl.myDsl.Entity

class MyDslJvmModelInferrer extends AbstractModelInferrer {

	@Inject extension JvmTypesBuilder
	@Inject extension IQualifiedNameProvider

	def dispatch infer(Entity entity,
		extension IJvmDeclaredTypeAcceptor acceptor, boolean prelinkingPhase) {
		accept(entity.toClass('''hard.coded.Entity«entity.name»'''))
		accept(entity.toClass( entity.fullyQualifiedName )) [
			documentation = entity.documentation
			members += entity.toMethod("body", typeRef(void)) [
				body = entity.body
			]
		]
	}
}


These are two example files:

test1.mydsl
package foo {
	entity Computer {
	}
}


test2.mydsl
package hello {
	entity Laptop {
		println(new EntityComputer())
	}
}


if I change the constructor call to "new Entity()", Xtext does not find the class and I need to import it. Why does that happen, and how can I fix it? (i.e., I do not want to import the class, just like when I instantiate "EntityComputer").

Thank you.
Miguel
Re: Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771321 is a reply to message #1771309] Thu, 24 August 2017 04:22 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
Hi,

i dont understand.
you dont inferr a class called entity.
you inferr stuff to a hardcode package and stuff to the package declared.

so what should "new Entity()" call to?


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771322 is a reply to message #1771321] Thu, 24 August 2017 05:03 Go to previous messageGo to next message
Miguel Jimenez is currently offline Miguel JimenezFriend
Messages: 40
Registered: July 2015
Member
Hi,
I made a mistake in the question. I mean "new Computer()". Here is the complete explanation:

The two examples I posted generate four Java classes: hello.Laptop, hard.coded.EntityLaptop, foo.Computer, and hard.coded.EntityComputer.
In test2.mydsl, I have "println(new EntityComputer())". In that case, Xtext has no problem finding the class hard.coded.EntityComputer without adding an import to the File.
If I change that statement to "println(new Computer())", Xtext cannot find the class foo.Computer and I have to import it, even though both classes are inferred from the same model.
Re: Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771323 is a reply to message #1771322] Thu, 24 August 2017 05:42 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
But new computer is in a different package?!?

Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771326 is a reply to message #1771323] Thu, 24 August 2017 06:26 Go to previous messageGo to next message
Miguel Jimenez is currently offline Miguel JimenezFriend
Messages: 40
Registered: July 2015
Member
Both classes are instantiated from a different package (the class calling the constructor is hello.Laptop):

hard.coded.EntityComputer
foo.Computer

Isn't that the same case? If it requires to import classes that are in different packages, why is "new EntityComputer" working?
Re: Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771327 is a reply to message #1771326] Thu, 24 August 2017 06:35 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
no, the hard coded package is known to both (since you inferrer a class inside hard.coded for both entities.

Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771333 is a reply to message #1771327] Thu, 24 August 2017 07:26 Go to previous messageGo to next message
Miguel Jimenez is currently offline Miguel JimenezFriend
Messages: 40
Registered: July 2015
Member
Is there a way to avoid importing the class?
Re: Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771336 is a reply to message #1771333] Thu, 24 August 2017 07:54 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
i dont understand. if you have

package X {
entity Entity
}

package X2 {
entity Entity
}

whichone should be picked?


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771341 is a reply to message #1771336] Thu, 24 August 2017 08:32 Go to previous messageGo to next message
Miguel Jimenez is currently offline Miguel JimenezFriend
Messages: 40
Registered: July 2015
Member
You're right about that. The reason I'm trying to avoid the Java import is because in the actual language
I already have a cross reference to the model (used for other things). Something like this:

include X.Entity // a cross reference
package X2 {
    entity SomeEntity {
        new Entity() // I don't want to have both include and import
    }
}


In that case, is it possible to use that cross reference to make the inferred type known?
I tried inserting an empty method returning the included type just to insert the Java import in the inferred class but it sill requires the import in the model.
Re: Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771343 is a reply to message #1771341] Thu, 24 August 2017 08:37 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
Have a look at the ximportsection*scopeprovider class

Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Unexpected behavior on linkage between Xtext resource and inferred JVM type [message #1771411 is a reply to message #1771343] Thu, 24 August 2017 19:41 Go to previous message
Miguel Jimenez is currently offline Miguel JimenezFriend
Messages: 40
Registered: July 2015
Member
Thank you Christian.
I'm posting the solution in case it's useful to anyone:

MyDsl.xtext
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase

generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"

DomainModel:
	extensions=ExtensionSection?
	importSection=XImportSection?
	elements+=AbstractElement*;

ExtensionSection:
	declarations+=IncludeDeclaration+;

IncludeDeclaration:
	'includes' entity=[Entity|QualifiedName];

AbstractElement:
	PackageDeclaration | Entity;

PackageDeclaration:
	'package' name=QualifiedName '{'
	elements+=AbstractElement*
	'}';

Entity:
	'entity' name=ValidID body=XBlockExpression;


MyDslJvmModelInferrer.xtend
package org.xtext.example.mydsl.jvmmodel

import com.google.common.collect.Lists
import com.google.inject.Inject
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.xtext.example.mydsl.myDsl.DomainModel
import org.xtext.example.mydsl.myDsl.Entity
import org.xtext.example.mydsl.myDsl.PackageDeclaration

class MyDslJvmModelInferrer extends AbstractModelInferrer {

	@Inject extension JvmTypesBuilder
	@Inject extension IQualifiedNameProvider

	def dispatch infer(DomainModel model,
		extension IJvmDeclaredTypeAcceptor acceptor, boolean prelinkingPhase) {
		if (prelinkingPhase)
			return
		val entities = model.elements.filter(Entity) + model.elements.filter(PackageDeclaration).entities
		entities.forEach[entity|
			accept(entity.toClass(entity.fullyQualifiedName)) [
				documentation = entity.documentation
				members += entity.toMethod("body", typeRef(void)) [
					body = entity.body
				]
			]
		]
	}

	def Iterable<Entity> entities(Iterable<PackageDeclaration> packages) {
		val entities = Lists.newArrayList
		packages.forEach[^package|
			entities.addAll(^package.elements.filter(Entity))
			entities.addAll(^package.elements.filter(PackageDeclaration).entities)
		]
		entities
	}
}


MyDslImportSectionNamespaceScopeProvider.xtend
package org.xtext.example.mydsl.scoping

import com.google.common.collect.Lists
import com.google.inject.Inject
import java.util.Collections
import java.util.List
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.scoping.impl.ImportNormalizer
import org.eclipse.xtext.xbase.XConstructorCall
import org.eclipse.xtext.xbase.scoping.XImportSectionNamespaceScopeProvider
import org.xtext.example.mydsl.myDsl.DomainModel
import org.xtext.example.mydsl.myDsl.ExtensionSection

// With a little help from https://stackoverflow.com/a/28027025/738968
class MyDslImportSectionNamespaceScopeProvider extends XImportSectionNamespaceScopeProvider {

	@Inject extension IQualifiedNameProvider

	override List<ImportNormalizer> internalGetImportedNamespaceResolvers(EObject context, boolean ignoreCase) {
		switch(context) {
			XConstructorCall: {
				val container = context.model as DomainModel
				if (container.extensions != null)
					container.extensions.provideImportNormalizerList(ignoreCase)
				else
					Collections.emptyList
			}
			default:
				super.internalGetImportedNamespaceResolvers(context, ignoreCase)
		}
	}

	/*
	 * Iterates upwards through the AST until a DomainModel is found.
	 */
	def EObject model(EObject o) {
        switch (o) {
            DomainModel: o
            default: o.eContainer.model
        }
    }

	/*
	 * Creates the list of all imports of an ExtensionSection. This implementation is similar to 
	 * getImportedNamespaceResolvers(XImportSection, boolean)
	 */
	def List<ImportNormalizer> provideImportNormalizerList(ExtensionSection extensionSection, boolean ignoreCase) {
        val List<ImportNormalizer> result = Lists.newArrayList
        extensionSection.declarations.forEach[includeDecl|
        	result.add(
        		includeDecl.entity
        			.fullyQualifiedName
        			.toString
        			.createImportedNamespaceResolver(ignoreCase)
        	)
        ]
        result
    }

}


MyDslRuntimeModule.xtend
package org.xtext.example.mydsl

import com.google.inject.Binder
import com.google.inject.name.Names
import org.eclipse.xtext.scoping.IScopeProvider
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider
import org.xtext.example.mydsl.scoping.MyDslImportSectionNamespaceScopeProvider

class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {

	override void configureIScopeProviderDelegate(Binder binder) {
		binder.bind(IScopeProvider)
			.annotatedWith(Names.named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE))
			.to(MyDslImportSectionNamespaceScopeProvider);
	}

}


And the example code:
test.mydsl
includes foo.Computer

package hello {
	entity Laptop {
		println(new Computer)
	}
}


test2.mydsl
package foo {
	entity Computer {
		
	}
}
Previous Topic:Reference ecore model from other ecore model?
Next Topic:"Xtext validation" task causing Eclipse to collapse
Goto Forum:
  


Current Time: Tue Mar 19 11:00:31 GMT 2024

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

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

Back to the top