Generate DSL file with XText from Java model

913 Views Asked by At

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?

1

There are 1 best solutions below

0
On BEST ANSWER

with xtext it is ususally no problem to

  • create the model as ast
  • add it to a resource
  • save the resource to get it serialized

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)
    }

}