Well, since groovy supports java syntax, I thought this would go on the fly, but I was very, very wrong.
Nevertheless the speed of Groovy in handling files and many other things forced me to get my fingers dirty in the console compiler system, an IT magic created in coorperation with Andreas Hamm last year.
So the problems found were basically 3:
1) java blocks without labels are not supported in groovy, because of their ambiguity with closures
2) the direct assignment array construct is not supported: String[] str = {"a", "b"};
3) the Classloader works differently from the one of beanshell, and we don't see the needed classes
So Hamm and I spent an evening together in chat to make me understand how the internals of the console work.
And here I want to share that with you, by showing you how I solved problem 2, which contains also 1.It is not for the weak of heart (also because the code snippets are not well formatted), but believe me, after a starting desperation, I had quite some fun :)
0) identification of the problem
Groovy doesn't support an array creation like:
String[] __arg_v = {"a", "b" };
we need to change it to:
Argument[] __arg_v = new Argument[2];
__arg_v[0] = "a";
__arg_v[1] = "b";
and this should be general also to other types of arrays.
The problem in this case that no Abstract Syntax Tree operator was available to deal with array and therefor a new piece of syntac had to be created.
Step by step this was done like that:
1) add a new enumeration type to eu.hydrologis.jgrass.console.core.runtime.analysis.ASTs.java
In this case I added:
/**
*
* The identifier of an operator of the
array
keyword for the creation of an* array e.g., "array[2]; array[0] = string1; array[1] = string2;
*
*
* @see eu.hydrologis.jgrass.console.core.runtime.nodes.AST_while
*/
AST_ARRAY(null, "ARRAY"); //$NON-NLS-1$
2) add a new class in eu.hydrologis.jgrass.console.core.runtime.nodes
ex. AST_array with a Default constructor and a Copyconstruktor
public final class AST_array extends AbstractAST
// Construction
/**
*
*
*/
public AST_array() {
super(ASTs.AST_ARRAY.expression(), ASTs.AST_ARRAY.annotation(), ASTs.AST_ARRAY);
} // AST_array
/**
*
* Constructs this object with the specified operands.
*
*
* @param operands - can be
null
or either a single operand or a list of operands.*/
public AST_array( AST
super(ASTs.AST_ARRAY.expression(), ASTs.AST_ARRAY.annotation(), ASTs.AST_ARRAY, operands);
} // AST_array
} // AST_array
3) add the grammar to the NativeML4j
Add the Argument array type:
final SYM_type_array __typedef_argument = new SYM_type_array("Argument"); //$NON-NLS-1$
Search for the part in which the proper child is added in the method __native_model.
Before:
Argument[] args = {arg1, arg2}
Created by:
new AST_expression(
new AST_variable_definition(
new AST_type(__typedef_argv.type()),
new AST_identifier(__variable_argv),
new AST_assign_statement(
new AST_block(
__parameter_definition(
symtable,
operator))))),
Now we need an array, which changes things to the following:
new AST_variable_definition(
new AST_type(__typedef_argv.type()),
new AST_identifier(__variable_argv),
new AST_assign_statement(
new AST_array(
__parameter_definition(
symtable,
operator),
new AST_type(
__typedef_argument.type()),
new AST_identifier(__variable_argv))))
note how to the AST_array we pass the normal operators throught __parameter_definition, but also an AST_type, which will tell us at generation time which class to use for the array creation and also the name of the variable needed. The last said is needed to create the different array elements:
varname[0] = ...
varname[1] = ...
...
4) add the grammar to the JavaML4j
Exactly the same way done for grass models, we proceed for java models.
In __java_model I change the expression that was creating arrays the wrong way for groovy:
new AST_block(
__argument_definition(
( APT_argument_definition
)operator.argument_defs()
)
)
and change it with:
new AST_array(
__argument_definition((APT_argument_definition) operator
.argument_defs()),
new AST_type(__typedef_argv.type()),
new AST_identifier(__variable_argv))
The same is needed in __model_input and __model_output.
5) In eu.hydrologis.jgrass.console.core.runtime.compiler.AbstractML4j.java add the case.
In the method generate, crate a case based on the enum of 1) and create the proper generation of code there:
case AST_ARRAY:
/*
* this should be a set of strings (also concatenated) divided by commas
*/
targetCode.append("new "); //$NON-NLS-1$
AST
AST
AST
for( int i = 0; i <> child = op.getChild(i);
if (child.identifier().annotation().equals(ASTs.AST_TYPE.annotation())) {
typeChild = child;
}
if (child.identifier().annotation().equals(ASTs.AST_COMMA.annotation())) {
commaChild = child;
}
if (child.identifier().annotation().equals(ASTs.AST_IDENTIFIER.annotation())) {
variableChild = child;
}
}
if (typeChild != null && commaChild != null && variableChild != null) {
int nums = commaChild.size();
String arrayString = typeChild.expression();
arrayString = arrayString.substring(0, arrayString.length() - 1) + nums + "]";
targetCode.append(arrayString).append(";\n"); //$NON-NLS-1$
// now the arrays
String varName = variableChild.expression();
for( int i = 0; i < j =" 0;"> child = commaChild.getChild(i);
generate(indentCount + 1, child, targetCode);
targetCode.append(";\n"); //$NON-NLS-1$
}
}
break;
The result?
Now the JGrass Console can use Beanshell and Groovy by defining the language as a session setting (in the settings lines that start with # in the script).
But hei, images tell more than thousand words:
Go, closure, go!!!
No comments:
Post a Comment