Linking Xtext with StringTemplate code generator

后端 未结 1 1932
臣服心动
臣服心动 2021-02-10 15:08

In my current project, I am trying to link the DSL specification written in xtext and code generator written in StringTemplate.

for instance, the syntax of my DSL specif

相关标签:
1条回答
  • 2021-02-10 15:34

    Iff you want to generate the code with StringTemplate from within Eclipse:

    Locate the generator stub in the runtime project of your DSL. There should be a class that implements the IGenerator interface. The method #doGenerator will be invoked with a resource and an instance of the IFileSystemAccess. The resource is an EMF concepts - basically an abstraction over the physical location of your objects. It offer getContents which in turn will provide access to a list of instances of VocSpec (if the grammar snippet is complete). These instances can be passed to your string template thing which will produce the output. The output should be written by means of IFileSystemAccess#generateFile

    If you want to do that as a standalone process, you should follow the steps in the Xtext FAQ. They explain how to load the EMF resource. Afterwards you can do pretty much the same as in the Eclipse-based solution. That is, implement the IGenerator and pass the result to the IFileSystemAccess.

    To give you a short example, this is what should be done to get started in a few minutes:

    First you should enable the following code snippet in the 'GenerateMyDsl.mwe2' workflow file and run the workflow.

    fragment = generator.GeneratorFragment {
        generateMwe = false
        generateJavaMain = true
    }
    

    You'll find a new artifact in the runtime project's package with the suffix .generator. Namely the 'Main.java' file.

    The second step is to implement the generator. The following snippet can be used in the 'MyDslGenerator.xtend' class:

    package org.xtext.example.mydsl.generator
    
    import org.eclipse.emf.ecore.resource.Resource
    import org.eclipse.xtext.generator.IGenerator
    import org.eclipse.xtext.generator.IFileSystemAccess
    import org.antlr.stringtemplate.StringTemplate
    import org.antlr.stringtemplate.language.DefaultTemplateLexer
    import org.xtext.example.mydsl.myDsl.Model
    
    class MyDslGenerator implements IGenerator {
    
        override void doGenerate(Resource resource, IFileSystemAccess fsa) {
            val hello = new StringTemplate("Generated with StringTemplate, $greeting.name$!", typeof(DefaultTemplateLexer))
            val model = resource.contents.head as Model
            hello.setAttribute("greeting", model.greetings.head)
            fsa.generateFile("Sample.txt", hello.toString())
        }
    }
    

    The Java equivalent would be something along these lines:

    package org.xtext.example.mydsl.generator;
    
    import org.antlr.stringtemplate.StringTemplate;
    import org.antlr.stringtemplate.language.DefaultTemplateLexer;
    import org.eclipse.emf.ecore.resource.Resource;
    import org.eclipse.xtext.generator.IFileSystemAccess;
    import org.eclipse.xtext.generator.IGenerator;
    import org.xtext.example.mydsl.myDsl.Model;
    
    public class StringTemplateGenerator implements IGenerator {
    
        public void doGenerate(Resource input, IFileSystemAccess fsa) {
            StringTemplate hello = new StringTemplate("Generated with StringTemplate, $greeting.name$!", DefaultTemplateLexer.class);
            Model model = (Model) input.getContents().get(0);
            hello.setAttribute("greeting", model.getGreetings().get(0));
            fsa.generateFile("Sample.txt", hello.toString());
        }
    
    }
    

    Next up one has to change the content of the stub 'Main.java' to reflect the location of the input file and the expected output path.

    package org.xtext.example.mydsl.generator;
    
    import java.util.List;
    
    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.generator.IGenerator;
    import org.eclipse.xtext.generator.JavaIoFileSystemAccess;
    import org.eclipse.xtext.util.CancelIndicator;
    import org.eclipse.xtext.validation.CheckMode;
    import org.eclipse.xtext.validation.IResourceValidator;
    import org.eclipse.xtext.validation.Issue;
    
    import com.google.inject.Inject;
    import com.google.inject.Injector;
    import com.google.inject.Provider;
    
    public class Main {
    
        public static void main(String[] args) {
            Injector injector = new MyDslStandaloneSetupGenerated().createInjectorAndDoEMFRegistration();
            Main main = injector.getInstance(Main.class);
            main.runGenerator("input/Sample.mydsl");
        }
    
        @Inject 
        private Provider<ResourceSet> resourceSetProvider;
    
        @Inject
        private IResourceValidator validator;
    
        @Inject
        private IGenerator generator;
    
        @Inject 
        private JavaIoFileSystemAccess fileAccess;
    
        protected void runGenerator(String string) {
            // load the resource
            ResourceSet set = resourceSetProvider.get();
            Resource resource = set.getResource(URI.createURI(string), true);
    
            // validate the resource
            List<Issue> list = validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl);
            if (!list.isEmpty()) {
                for (Issue issue : list) {
                    System.err.println(issue);
                }
                return;
            }
    
            // configure and start the generator
            fileAccess.setOutputPath("output/");
            generator.doGenerate(resource, fileAccess);
    
            System.out.println("Code generation finished.");
        }
    }
    

    The input file is located in the runtime project in a newly created folder 'input'. The content of the file 'Sample.mydsl' is

    Hello Pankesh!
    

    Now you can run the main class and after a quick refresh in Eclipse, you find the new 'output' folder in my runtime project with a single file 'Sample.txt':

    Generated with StringTemplate, Pankesh!
    

    Btw: the Xtext documentation contains a tutorial on how to generate code with Xtend - it is nice than StringTemplate because it integrates seemlessly with Eclipse and existing Java utilities:

    package org.xtext.example.mydsl.generator
    
    import org.eclipse.emf.ecore.resource.Resource
    import org.eclipse.xtext.generator.IFileSystemAccess
    import org.eclipse.xtext.generator.IGenerator
    import org.xtext.example.mydsl.myDsl.Model
    
    class MyDslGenerator implements IGenerator {
    
        override void doGenerate(Resource resource, IFileSystemAccess fsa) {
            val model = resource.contents.head as Model
            fsa.generateFile("Sample.txt", '''
                Generated with Xtend, «model.greetings.head»!
            ''')
        }
    }
    
    0 讨论(0)
提交回复
热议问题