Home » Modeling » TMF (Xtext) » Compile Xbase expression at arbitrary position using JvmModelInferrer and XbaseCompiler in Xtext 2.4
Compile Xbase expression at arbitrary position using JvmModelInferrer and XbaseCompiler in Xtext 2.4 [message #1043528] |
Wed, 17 April 2013 20:45 |
Wolfgang Schwitzer Messages: 2 Registered: April 2013 |
Junior Member |
|
|
At the moment, we are using Xtext/Xtend to build a language for parallel software architectures.
A first prototype was promising, so we decided to extend our language with more powerful expressions by building on Xbase.
At the moment, we are struggling with the following:
How can a Xbase expression be generated at an arbitrary position e.g. in the body part of a method within the JvmModelInferrer?
Is an injected XbaseCompiler a way to do that?
I know, many times there are other good solutions like e.g. adding initialized fields for each variable or argument, but sometimes there is no other obvious or convenient solution than generating an expression at a certain position.
Here is a little (well... very simplified) example language to demonstrate the general problem in a more abstract way:
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase
generate myDsl "..."
Model:
(vars+=XVariableDeclaration | vertices+=Vertex | edges+=Edge)*;
Vertex:
name=ID '[' multiplicity=XExpression ']';
Edge:
'forall' name=ID 'in' range=XExpression 'connect'
srcVertex=[Vertex] '[' srcIndex=XExpression ']' 'to'
dstVertex=[Vertex] '[' dstIndex=XExpression ']';
With this language you could write something like:
val n = 100
senders[n]
receivers[n]
forall i in 0 ..< n
connect senders[i] to receivers[n-1-i]
In the MyDslJvmModelInferrer you would typically find something like:
// ...
@Inject XbaseCompiler compiler
// ...
// Initialize vertices and edges in main method.
members += model.toMethod("main", model.newTypeRef(Void::TYPE)) [
parameters += model.toParameter("args",
model.newTypeRef(typeof(String)).addArrayTypeDimension)
static = true
varArgs = true
body = [
// Create and fill a new list for each vertex.
for (v : model.vertices) {
append('''«v.name» = new ''')
v.newTypeRef(typeof(ArrayList), v.newTypeRef(typeof(String))).serialize(v, it)
append('''();''')
newLine
append('''for (int i = 0; i < ''')
// (*) Compile multiplicity expression directly to target code.
// Fails with NPE in ...TreeAppendable.append(TreeAppendable.java:320)
// The name that is given to append as 'content' is null.
compiler.toJavaExpression(v.multiplicity, it)
append('''; i++) {''')
append('''«v.name».add("«v.name»" + "-" + i);''')
append('''}''')
}
// Go on with creating edges (removed for simplicity).
// ...
]
]
// ...
The example is very simplified e.g. Strings are used as vertices, but it should be sufficient to show the problem.
The problem occurs at (*): no matter if toJavaExpression, toJavaStatement, compile(...) is used, this fails with different kinds of exceptions.
Maybe it is just that some initialization of the compiler is missing?
One solution could be to add "v.multiplicity" as an "synthetic" initialized field in advance, but imagine how difficult (impossible?) this could be for creating the edges depending on the "range" expression (e.g. an IntegerRange) when the "srcIndex" and "dstIndex" expressions usually reference the index variable ("i" in the example) in the surrounding for-loop that implements the "forall" behavior.
Thank you for your answers in advance.
|
|
|
Re: Compile Xbase expression at arbitrary position using JvmModelInferrer and XbaseCompiler in Xtext [message #1044600 is a reply to message #1043528] |
Fri, 19 April 2013 05:36 |
Sebastian Zarnekow Messages: 3118 Registered: July 2009 |
Senior Member |
|
|
Hi Wolfgang,
yes, the compiler has to be initialized prior to compiling an
expression, e.g. it has to know which variable names are already used in
the current scope.
Have you considered to make your Edge an XExpression and implement the
stuff directly in the compiler.
Future versions of Xtext will have something like a logical expression
block, where you would compose the complete body of the method of
existing expressions and assign the synthesized body to an operation.
Currently there's some effort to call the compiler from within the
inferrer. You may want to look at the JvmModelGenerator in order to see
how to initialize the compiler.
Hope that helps,
Sebastian
--
Looking for professional support for Xtext, Xtend or Eclipse Modeling?
Go visit: http://xtext.itemis.com
Am 18.04.13 14:42, schrieb Wolfgang Schwitzer:
> At the moment, we are using Xtext/Xtend to build a language for parallel
> software architectures.
> A first prototype was promising, so we decided to extend our language
> with more powerful expressions by building on Xbase.
>
> At the moment, we are struggling with the following:
> How can a Xbase expression be generated at an arbitrary position e.g. in
> the body part of a method within the JvmModelInferrer?
> Is an injected XbaseCompiler a way to do that?
>
> I know, many times there are other good solutions like e.g. adding
> initialized fields for each variable or argument, but sometimes there is
> no other obvious or convenient solution than generating an expression at
> a certain position.
>
> Here is a little (well... very simplified) example language to
> demonstrate the general problem in a more abstract way:
>
> grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase
>
> generate myDsl "..."
>
> Model:
> (vars+=XVariableDeclaration | vertices+=Vertex | edges+=Edge)*;
>
> Vertex:
> name=ID '[' multiplicity=XExpression ']';
>
> Edge:
> 'forall' name=ID 'in' range=XExpression 'connect'
> srcVertex=[Vertex] '[' srcIndex=XExpression ']' 'to'
> dstVertex=[Vertex] '[' dstIndex=XExpression ']';
>
>
> With this language you could write something like:
>
> val n = 100
>
> senders[n]
> receivers[n]
>
> forall i in 0 ..< n connect senders[i] to receivers[n-1-i]
>
>
> In the MyDslJvmModelInferrer you would typically find something like:
>
> // ...
> @Inject XbaseCompiler compiler
> // ...
> // Initialize vertices and edges in main method.
> members += model.toMethod("main", model.newTypeRef(Void::TYPE)) [
> parameters += model.toParameter("args",
> model.newTypeRef(typeof(String)).addArrayTypeDimension)
> static = true
> varArgs = true
> body = [
> // Create and fill a new list for each vertex.
> for (v : model.vertices) {
> append('''«v.name» = new ''')
> v.newTypeRef(typeof(ArrayList),
> v.newTypeRef(typeof(String))).serialize(v, it)
> append('''();''')
> newLine
> append('''for (int i = 0; i < ''')
>
> // (*) Compile multiplicity expression directly to target
> code.
> // Fails with NPE in
> ...TreeAppendable.append(TreeAppendable.java:320)
> // The name that is given to append as 'content' is null.
> compiler.toJavaExpression(v.multiplicity, it)
>
> append('''; i++) {''')
> append('''«v.name».add("«v.name»" + "-" + i);''')
> append('''}''')
> }
> // Go on with creating edges (removed for simplicity).
> // ...
> ]
> ]
> // ...
>
>
> The example is very simplified e.g. Strings are used as vertices, but it
> should be sufficient to show the problem.
> The problem occurs at (*): no matter if toJavaExpression,
> toJavaStatement, compile(...) is used, this fails with different kinds
> of exceptions.
> Maybe it is just that some initialization of the compiler is missing?
>
> One solution could be to add "v.multiplicity" as an "synthetic"
> initialized field in advance, but imagine how difficult (impossible?)
> this could be for creating the edges depending on the "range" expression
> (e.g. an IntegerRange) when the "srcIndex" and "dstIndex" expressions
> usually reference the index variable ("i" in the example) in the
> surrounding for-loop that implements the "forall" behavior.
>
> Thank you for your answers in advance.
|
|
|
Re: Compile Xbase expression at arbitrary position using JvmModelInferrer and XbaseCompiler in Xtext [message #1045080 is a reply to message #1044600] |
Fri, 19 April 2013 18:43 |
Wolfgang Schwitzer Messages: 2 Registered: April 2013 |
Junior Member |
|
|
Hi Sebastian,
thank you for your helpful and instant reply.
I have a solution that works for me now, which I describe step by step it in the following, in case it could be useful for others, too.
(1) From the JvmModelGenerator (and my stack traces ) I learned that the XbaseCompiler resolves many names in the "jvm world", so expressions need to be introduced first as "synthetic" members e.g. by
members += v.toField("_" + v.name + "_multiplicity", v.newTypeRef(typeof(int))) [
initializer = v.multiplicity
]
Afterwards, in the JvmModelInferrer of your language, you can use the injected XbaseCompiler directly like this
append(...)
compiler.compileAsJavExpression(v.multiplicity, it, v.newTypeRef(typeof(int)))
append(...)
(2) But... the expression is now already "precomputed" as a field and by respecting the DRY principle (even in generated code ) instead of calling the compiler you would simply reference the field itself
append(...)
append('''_«v.name»_multiplicity''')
append(...)
(3) The more complicated problem with the index-variables of the sources and destinations of edges can be solved by introducing "synthetic" methods, which take the loop-variable as single parameter
members += e.toMethod("_" + e.name + "_srcIndex", e.newTypeRef(typeof(int))) [
static = true
parameters += e.toParameter(e.name, e.newTypeRef(typeof(Integer)))
body = e.srcIndex
]
// ...
Now these methods can be referenced like this
for (e : model.edges) {
newLine
append(
'''
for (Integer «e.name» : _«e.name»_range) {
edges.add(«e.srcVertex.name».get(_«e.name»_srcIndex(«e.name»)) +
"->" + «e.dstVertex.name».get(_«e.name»_dstIndex(«e.name»)));
}
''')
}
Conclusions
Without using the XbaseCompiler directly, the generated code appears very readable and by using appropriate names for synthetic fields and methods the code even documents itself well to a certain degree.
This is an excerpt from the generated code for the original example with n senders and n receivers
public class Model {
private final static int n = 100;
private final static int _senders_multiplicity = Model.n;
private final static int _receivers_multiplicity = Model.n;
private final static Iterable<Integer> _i_range = new Function0<Iterable<Integer>>() {
public Iterable<Integer> apply() {
ExclusiveRange _doubleDotLessThan = new ExclusiveRange(0, Model.n, true);
return _doubleDotLessThan;
}
}.apply();
public static int _i_srcIndex(final Integer i) {
return (i).intValue();
}
public static int _i_dstIndex(final Integer i) {
int _minus = (Model.n - 1);
int _minus_1 = (_minus - (i).intValue());
return _minus_1;
}
private static List<String> senders;
private static List<String> receivers;
private static List<String> edges;
public static void main(final String... args) {
senders = new ArrayList<String>();
for (int i = 0; i < _senders_multiplicity; i++) {
senders.add("senders" + "-" + i);
}
receivers = new ArrayList<String>();
for (int i = 0; i < _receivers_multiplicity; i++) {
receivers.add("receivers" + "-" + i);
}
edges = new ArrayList<String>();
for (Integer i : _i_range) {
edges.add(senders.get(_i_srcIndex(i)) +
"->" + receivers.get(_i_dstIndex(i)));
}
}
}
Cheers from Munich
Wolfgang
[Updated on: Fri, 19 April 2013 19:25] Report message to a moderator
|
|
|
Goto Forum:
Current Time: Thu Apr 25 20:32:51 GMT 2024
Powered by FUDForum. Page generated in 0.03122 seconds
|