javaxtextxbase

Generate DSL file with XText from Java model


I have recently started working with XText. So far I have been able to define a simple grammar, complete the JvmModelInferrer and generate the corresponding java classes and .java files.

Is it possible to generate automatically the DSL file (taking into account its grammar) from a set of custom Java classes?

Let me provide a simple example.

I have the following grammar:

MODEL:
    entities+=ENTITY*
;

ENTITY:
    'entity' name=ValidID 'as'
        (elements+=PROPERTY)*
    'end'
;

PROPERTY:
    (many?='many')? 'property' name=ID 'of' type=JvmTypeReference
;

If I have the following sample.myDsl

entity Book as
    property title of String
    property numPages of Integer
end

entity Author as
    property name of String
    property surname of String
end

I get the Book.java and the Author.java files. In my project I have a processor that analyzes java files and creates objects from them, so if I run the processor on the previous Book.java and Author.java I would get two instances of a custom Entity java type. Each Entity instance would have a set of Property instances. So, the Java model is very similar to the xtext grammar.

Is it possible to "feed" these two objects to XText, maybe define an Inferrer to specify the translations, and taking into account the same .xtext grammar file, generate automatically a .myDsl file?


Solution

  • with xtext it is ususally no problem to

    if you use xbase and jvmmodelinferrrer building the ast might be a kind of pain if you reference from a model to a inferred jvm element or try to build xbase expressions as ast here is a simple complex example using the domainmodel example

    package org.eclipse.xtext.example.domainmodel.tests
    
    import com.google.inject.Injector
    import org.eclipse.emf.common.util.URI
    import org.eclipse.emf.ecore.resource.Resource
    import org.eclipse.emf.ecore.resource.ResourceSet
    import org.eclipse.xtext.common.types.JvmParameterizedTypeReference
    import org.eclipse.xtext.common.types.util.TypeReferences
    import org.eclipse.xtext.example.domainmodel.DomainmodelStandaloneSetup
    import org.eclipse.xtext.example.domainmodel.domainmodel.DomainmodelFactory
    import org.eclipse.xtext.resource.DerivedStateAwareResource
    import org.eclipse.xtext.resource.SaveOptions
    import org.eclipse.xtext.xbase.jvmmodel.JvmTypeReferenceBuilder
    
    class Main {
        var static extension DomainmodelFactory factory = DomainmodelFactory.eINSTANCE
    
        def static void main(String[] args) {
            var Injector injector = new DomainmodelStandaloneSetup().createInjectorAndDoEMFRegistration()
            val ResourceSet resourceSet = injector.getInstance(ResourceSet)
            val Resource r0 = resourceSet.createResource(URI.createURI("base/Base.dmodel"))
            val Resource r1 = resourceSet.createResource(URI.createURI("model/Person.dmodel"))
            val typeReferenceBuilder = injector.getInstance(JvmTypeReferenceBuilder.Factory).create(resourceSet)
            val typeReferences = injector.getInstance(TypeReferences)
            val model = createDomainModel
            r1.contents += model
            val model0 = createDomainModel
            r0.contents += model0
            // build the ast using xtends with clause
            model0 => [
                elements += createPackageDeclaration => [
                    name = "base"
                    elements += createEntity => [
                        name = "Base"
                        features+= createProperty => [
                            name = "id"
                            type = typeReferenceBuilder.typeRef("java.lang.String")
                            println(type)
                        ]
                    ]
                ]
            ]
            //trigger the inferrer on resource 0
            (r0 as DerivedStateAwareResource) => [
                fullyInitialized = false
                installDerivedState(false)
            ]
    
            // build the ast of the second resource
            model => [
                elements += createPackageDeclaration => [
                    name = "model"
                    elements += createEntity => [
                        val base = typeReferences.findDeclaredType("base.Base", resourceSet)
                        println(base)
                        superType = typeReferenceBuilder.typeRef(base) as JvmParameterizedTypeReference
                        println(superType)
    
                        name = "Person"
                        features+= createProperty => [
                            name = "name"
                            type = typeReferenceBuilder.typeRef("java.lang.String")
                            println(type)
                        ]
                    ]
                ]
            ]
            //save the resources
            r0.save(SaveOptions.defaultOptions.toOptionsMap)
            r1.save(SaveOptions.defaultOptions.toOptionsMap)
        }
    
    }