将Xtext与StringTemplate代码生成器链接

在我当前的项目中,我试图链接用xtext编写的DSL规范和用StringTemplate编写的代码生成器。

例如,我的DSL规范的语法如下。 我通过xText提供的很好的编辑器工具输入这些信息。

structs: TempStruct tempValue : double; unitOfMeasurement : String; abilities : sensors: TemperatureSensor generate tempMeasurement : TempStruct; attribute responseFormat : String; 

上述DSL规范的语法如下:

  VocSpec: 'structs' ':' (structs += Struct)+ 'abilities' ':' ('sensors' ':' (sensors += Sensor)+ )+ ; Sensor: name = ID ((attributes += Attributes ) | (sources += Sources))* ; Sources: 'generate' name=ID ':' type = Type ';' ; Attributes: 'attribute' name=ID ':' type = Type ';' ; Struct: name = ID (fields += Field)+ ; Field: name=ID ':' type += Type ';' ; 

xText生成对应于上述规范的语义模型。 在我们的示例中,xText生成语义模型,其中包含struct.javaField.javaAttribute.javaSensor.java等文件。

我可以清楚地看到这个语义模型可以与StringTemplate文件链接。 StringTemplate文件获取类的对象。 例如, StringTemplate文件将TemperatureSensor (Sensor实例)作为Input并生成Java代码。

我的问题是我如何实例化语义模型(由xText生成)以及如何与StringTemplate文件链接?

如果你想从Eclipse中使用StringTemplate生成代码:

在DSL的运行时项目中找到生成器存根。 应该有一个实现IGenerator接口的类。 将使用资源和IFileSystemAccess的实例调用方法#doGenerator。 资源是EMF概念 – 基本上是对象物理位置的抽象。 它提供getContents,反过来它将提供对VocSpec实例列表的访问(如果语法片段完整)。 这些实例可以传递给您的字符串模板,它将产生输出。 输出应该通过IFileSystemAccess#generateFile写出

如果您希望将其作为独立进程执行,则应遵循Xtext常见问题解答中的步骤。 他们解释了如何加载EMF资源。 之后,您可以像在基于Eclipse的解决方案中那样做。 也就是说,实现IGenerator并将结果传递给IFileSystemAccess。

为了给你一个简短的例子,这是在几分钟内开始应该做的事情:

首先,您应该在“GenerateMyDsl.mwe2”工作流文件中启用以下代码段并运行工作流。

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

您将在运行时项目的包中找到带有后缀.generator的新工件。 即’Main.java’文件。

第二步是实现发电机。 以下代码段可以在’MyDslGenerator.xtend’类中使用:

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

Java等价物将是这样的:

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

接下来,必须更改存根’Main.java’的内容以反映输入文件的位置和预期的输出路径。

 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 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 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."); } } 

输入文件位于运行时项目中新创建的文件夹“input”中。 文件’Sample.mydsl’的内容是

 Hello Pankesh! 

现在你可以运行主类了,在Eclipse中快速刷新之后,你会在我的运行时项目中找到一个带有单个文件’Sample.txt’的新’output’文件夹:

 Generated with StringTemplate, Pankesh! 

顺便说一句:Xtext文档包含一个关于如何使用Xtend生成代码的教程 – 它比StringTemplate好,因为它与Eclipse和现有Java实用程序无缝集成:

 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»! ''') } }