我有一个DSL,其中包含需要包装的块作为在生成的代码创建的匿名类中返回的方法。例如:
model {
task {
val x = 2*5;
Math.pow(2, x)
}
}
应编译为(注意task
成为Runnable
的实例,任务的主体成为Runnable.run()
方法的主体:
import java.util.Collection;
@SuppressWarnings("all")
public class MyFile {
public Collection<Runnable> tasks() {
ArrayList<Runnable> tasks = new ArrayList<>();
tasks.add(getTask0());
return tasks;
}
public static Runnable getTask0() {
Runnable _runnable = new Runnable() {
public void run() {
final int x = (2 * 5);
Math.pow(2, x);
}
}
return _runnable;
}
}
在this question的讨论之后,我能够让这个特定的例子起作用。 (Github repo包括单元测试。)但我必须通过将语法中的Task元素表示为XExpression
s(source)的序列来实现,XbaseCompiler
子类必须迭代(source)。
相反,能够让Task
在属性XBlockExpression
中包含action
,然后在编译器中只执行doInternalToJavaStatement(expr.action, it, isReferenced)
会很不错。我的感觉是,这真的是&#34;对&#34;在我的情况下解决方案,但是当我尝试它时,这将导致生成的run
方法的空体,就像块根本没有被处理一样。发生了什么,我是否遗漏了一些必要的设置/连线事件/这些必要的绑定工作?
答案 0 :(得分:0)
您通常会尝试通过使用更好的推理策略来避免这种情况,例如
语法
Model:
{Model}"model" "{"
vars+=Variable*
tasks+=Task*
"}"
;
Variable:
"var" name=ID ":" type=JvmParameterizedTypeReference
;
Task:
{Task} "task" content=XBlockExpression
;
推断器
class MyDslJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(element.toClass("test.Model2")) [
for (v : element.vars) {
members+=v.toField(v.name, v.type.cloneWithProxies) [
]
}
var i = 0;
for (t : element.tasks) {
val doRunName = "doRun"+i
members += t.toMethod("task"+i, Runnable.typeRef()) [
body = '''
return new «Runnable» () {
public void run() {
«doRunName»();
}
};
'''
]
members += t.toMethod(doRunName, Void.TYPE.typeRef()) [
body = t.content
]
i = i + 1
}
]
}
}
基本上就是这样。
您可以关注https://bugs.eclipse.org/bugs/show_bug.cgi?id=481992
如果您真的想要调整可能需要更多工作的xbase类型系统,例如(仅涵盖最小的案例)
语法
Model:
{Model}"model" "{"
vars+=Variable*
tasks+=Task*
"}"
;
Variable:
"var" name=ID ":" type=JvmParameterizedTypeReference
;
Task:
{Task} "task" content=XTaskContent
;
XTaskContent returns xbase::XExpression:
{XTaskContent} block=XBlockExpression
;
推断器
class MyDslJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(element.toClass("test.Model")) [
for (v : element.vars) {
members+=v.toField(v.name, v.type.cloneWithProxies) [
]
}
var i = 0;
for (t : element.tasks) {
members += t.toMethod("task"+i, Runnable.typeRef()) [
body = t.content
]
i = i + 1
}
]
}
}
键入计算机
class MyDslTypeComputer extends XbaseTypeComputer {
override computeTypes(XExpression expression, ITypeComputationState state) {
if (expression instanceof XTaskContent) {
_computeTypes(expression as XTaskContent, state);
} else {
super.computeTypes(expression, state)
}
}
protected def void _computeTypes(XTaskContent object, ITypeComputationState state) {
state.withExpectation(getPrimitiveVoid(state)).computeTypes(object.block)
state.acceptActualType(getTypeForName(Runnable, state), ConformanceFlags.CHECKED_SUCCESS )
}
}
编译器
class MyDslCompiler extends XbaseCompiler {
override protected internalToConvertedExpression(XExpression obj, ITreeAppendable appendable) {
if (obj instanceof XTaskContent) {
appendable.append("new ").append(Runnable).append("() {").newLine
appendable.increaseIndentation
appendable.append("public void run()").newLine
reassignThisInClosure(appendable, null)
internalToJavaStatement(obj.block, appendable, false)
appendable.newLine
appendable.decreaseIndentation
appendable.newLine.append("}")
} else {
super.internalToConvertedExpression(obj, appendable)
}
}
}
绑定
class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {
def Class<? extends ITypeComputer> bindITypeComputer() {
return MyDslTypeComputer
}
def Class<? extends XbaseCompiler> bindXbaseCompiler() {
return MyDslCompiler
}
}