在 Xtend 循环中使用唯一的变量名称(代码生成)
Using unique variable names in Xtend loop (Code Generation)
我用 Xtext 创建了一个自定义 DSL,这对描述层次结构很有用(在本例中固定高度为 2)。我现在要做的是生成一个简单的 Java Swing App,它可以使用 JTree 显示这样的层次结构。我通过使用 Xtend 从 Xtext 扩展代码生成示例来做到这一点。一切正常,但可以做得更好。
到目前为止我的模板中非常难看的部分:
def compile(Level a) '''
DefaultMutableTreeNode «a.name» = new DefaultMutableTreeNode("«a.name»");
«FOR b:a.sublevels»
DefaultMutableTreeNode «b.name» = new DefaultMutableTreeNode("«b.name»");
«a.name».add(«b.name»);
«ENDFOR»
'''
如您所见,我使用 DSL 用户定义的实体名称作为代码生成的变量名称,这是不好的。如果用户选择的名称不是 Java 中的有效变量名称,应用程序将不会在以后编译。
我这样做的原因是因为在创建 JTree 及其节点时,我需要唯一的变量名。生成的代码如下所示:
DefaultMutableTreeNode a = new DefaultMutableTreeNode("a");
DefaultMutableTreeNode a1 = new DefaultMutableTreeNode("a1");
a.add(a1);
DefaultMutableTreeNode a2 = new DefaultMutableTreeNode("a2");
a.add(a2);
DefaultMutableTreeNode a3 = new DefaultMutableTreeNode("a2");
a.add(a3);
...
由于所有内容都在同一范围内,因此变量名称需要不同 - 在本例中为 a、a1、a2 和 a3。但是我如何创建有效的唯一变量名称而不是使用用户的输入(这可能是无效的)?感谢您的帮助,谢谢。
您需要节点的人工变量名和计数器变量。最简单的方法是将它作为一个字段添加到生成器中,并在调用编译方法之前重置它。
int count
def compile(Level a) {
count = 0
return compile(a, -1)
}
def compile(Level a, int parentCount) {
var aCount = count++
return '''
DefaultMutableTreeNode node«aCount» = new DefaultMutableTreeNode("«a.name»");
«IF parentCount > -1»
node«parentCount».add(node«aCount»);
«ENDIF»
«FOR b:a.sublevels»
«compile(b, aCount)»
«ENDFOR»
'''
}
如果您可以节省变量名,我更愿意使用 Java 鲜为人知的非静态初始化器生成代码,因为它们反映了 Java 代码中树的结构,例如
new DefaultMutableTreeNode("a") {{
add(new DefaultMutableTreeNode("a1") {{
add(new DefaultMutableTreeNode("b1"))
}})
add(new DefaultMutableTreeNode("a2"))
add(new DefaultMutableTreeNode("a2"))
}}
通过非常简单的生成器
def CharSequence compile(Level a) '''
new DefaultMutableTreeNode("«a.name»")«IF !a.sublevels.empty» {{
«FOR b:a.sublevels»
add(«compile(b)»);
«ENDFOR»
}}«ENDIF»'''
我用 Xtext 创建了一个自定义 DSL,这对描述层次结构很有用(在本例中固定高度为 2)。我现在要做的是生成一个简单的 Java Swing App,它可以使用 JTree 显示这样的层次结构。我通过使用 Xtend 从 Xtext 扩展代码生成示例来做到这一点。一切正常,但可以做得更好。
到目前为止我的模板中非常难看的部分:
def compile(Level a) '''
DefaultMutableTreeNode «a.name» = new DefaultMutableTreeNode("«a.name»");
«FOR b:a.sublevels»
DefaultMutableTreeNode «b.name» = new DefaultMutableTreeNode("«b.name»");
«a.name».add(«b.name»);
«ENDFOR»
'''
如您所见,我使用 DSL 用户定义的实体名称作为代码生成的变量名称,这是不好的。如果用户选择的名称不是 Java 中的有效变量名称,应用程序将不会在以后编译。
我这样做的原因是因为在创建 JTree 及其节点时,我需要唯一的变量名。生成的代码如下所示:
DefaultMutableTreeNode a = new DefaultMutableTreeNode("a");
DefaultMutableTreeNode a1 = new DefaultMutableTreeNode("a1");
a.add(a1);
DefaultMutableTreeNode a2 = new DefaultMutableTreeNode("a2");
a.add(a2);
DefaultMutableTreeNode a3 = new DefaultMutableTreeNode("a2");
a.add(a3);
...
由于所有内容都在同一范围内,因此变量名称需要不同 - 在本例中为 a、a1、a2 和 a3。但是我如何创建有效的唯一变量名称而不是使用用户的输入(这可能是无效的)?感谢您的帮助,谢谢。
您需要节点的人工变量名和计数器变量。最简单的方法是将它作为一个字段添加到生成器中,并在调用编译方法之前重置它。
int count
def compile(Level a) {
count = 0
return compile(a, -1)
}
def compile(Level a, int parentCount) {
var aCount = count++
return '''
DefaultMutableTreeNode node«aCount» = new DefaultMutableTreeNode("«a.name»");
«IF parentCount > -1»
node«parentCount».add(node«aCount»);
«ENDIF»
«FOR b:a.sublevels»
«compile(b, aCount)»
«ENDFOR»
'''
}
如果您可以节省变量名,我更愿意使用 Java 鲜为人知的非静态初始化器生成代码,因为它们反映了 Java 代码中树的结构,例如
new DefaultMutableTreeNode("a") {{
add(new DefaultMutableTreeNode("a1") {{
add(new DefaultMutableTreeNode("b1"))
}})
add(new DefaultMutableTreeNode("a2"))
add(new DefaultMutableTreeNode("a2"))
}}
通过非常简单的生成器
def CharSequence compile(Level a) '''
new DefaultMutableTreeNode("«a.name»")«IF !a.sublevels.empty» {{
«FOR b:a.sublevels»
add(«compile(b)»);
«ENDFOR»
}}«ENDIF»'''