A nova biblioteca e expressão de linguagem Xbase permite integrar expressões aos suas próprias DSLs Xtext 2.0. O Xbase oferece uma gramática, uma grande biblioteca de tempo de execução e um compilador como intérprete para executar essas expressões. Assim, o Xbase abre o caminho para uma classe muito maior de DSLs e aniquila vantagens anteriores de DSLs internas por DSLs externas baseadas em Xtext.
Em uma série de workshops internos sobre Xbase, muitas vezes eu encontrei uma pergunta: podemos estender essas expressões do Xbase? Neste artigo, irei mostrar como fazer isso. É baseado no exemplo de modelo de domínio contido no Xtext 2.0, que você pode instanciar ao selecionar File > New > Example… > Xtext > Xtext Domain-Model Example.
A gramática
Por padrão, o Xbase somente conhece os inteiros literais. Neste exemplo, estamos adicionando decimais literais.
Se você quiser adicionar um novo conceito para as expressões do Xbase sintaticamente, você terá que adicionar a regra correta do Xbase. Como o nome sugere, um literal pode ser adicionado na regra Xbase XLiteral. Então no Dominamodel.xtext você terá que adicionar:
import "http://www.eclipse.org/xtext/xbase/Xbase"
...
XLiteral returns XExpression:
XClosure |
XBooleanLiteral |
XIntLiteral |
XNullLiteral |
XStringLiteral |
XTypeLiteral |
DecimalLiteral; // our new alternative
DecimalLiteral:
intPart=INT '.' decimalPart=INT;
Como o EPackage da linguagem de modelo de domínio é gerada, isso irá introduzir uma nova EClass DecimalLiteral assim que você regenerar a infraestrutura da linguagem.
Type provider
Para o bem da simplicidade, nossa decimal literal será mapeada para java.lang.Double. Temos que adaptar o type provider de acordo:
public DomainmodelTypeProvider {
@Inject
private TypeReferences typeReferences;
...
protected JvmTypeReference _type(DecimalLiteral literal,
boolean rawType) {
return typeReferences.getTypeForName(Double.class,
literal);
}
}
Compilador
O compilador precisa entender o que fazer com as decimais literais. Temos que adicionar um método que anexe a sintaxe concreta do Java double literal para um DecimalLiteral.
public class DomainmodelCompiler {
...
protected void _toJavaExpression(DecimalLiteral expr,
IAppendable b) {
b.append(expr.getIntPart() + "."
+ expr.getDecimalPart());
}
protected void _toJavaStatement(DecimalLiteral expr,
IAppendable b,
boolean isReferenced) {
generateComment(expr, b, isReferenced);
}
}
Podemos agora usar decimais literais em nossas expressões, e elas são convertidas corretamente para java.lang.Doubles. O modelo de domínio a seguir deve compilar:
entity DecimalLiteralTest {
op approxPi() : double {
3.1415926535
}
}
Note que o autoboxing/unboxing funciona no Xbase da mesma maneira que no Java. Mesmo assim, nós não temos nenhuma operação para decimais literais. A próxima sessão mostra como adicioná-los.
Operadores
Os operadores em Xbase são implementados como métodos Java seguindo uma convenção de nomes. Por exemplo, o operador + será mapeado para invocar um método chamado named operator_plus(). Existem vários lugares onde o Xbase tenta encontrar esse método:
- No tipo atual como operator_plus (OperandType1, OperandType2)
- Na classe do primeiro operando, como OperandType1.operator_plus(OperandType2)
- No escopo da extensão como operator_plus(OperandType1, OperandType2)
A primeira alternativa é descartada, uma vez que queremos fornecer operações globais. A segunda não se aplica por não podermos mudar a classe final java.lang.Double, então eventualmente estamos lidando com a alternativa 3.
Vamos escrever uma nova extensão de classe de biblioteca contendo as operações como métodos estáticos. Esses métodos precisam estar disponíveis no momento da execução também, uma vez que o código Java gerado irá chamá-lo. É por isso que é uma boa ideia colocar tais classes de extensão em um plug-in separado org.eclipse.xtext.example.domainmodel.lib.
package org.eclipse.xtext.example.domainmodel.lib;
public class DecimalExtensions {
public static Double operator_plus(Double x,
Double y) {
return x + y;
}
public static Double operator_minus(Double x,
Double y) {
return x - y;
}
public static Double operator_multiply(Double x,
Double y) {
return x * y;
}
public static Double operator_divide(Double x,
Double y) {
return x / y;
}
}
Classes de extensão globalmente disponíveis e classes literais têm que ser registradas no StaticMethodsFeatureForTypeProvider. Nós o substituímos:
package org.eclipse.xtext.example.domainmodel.scoping;
...
public class DomainmodelStaticMethodsProvider
extends StaticMethodsFeatureForTypeProvider {
@Override
protected Iterable getVisibleTypesContainingStaticMethods
(JvmTypeReference reference) {
Iterable resultFromSuper =
super.getVisibleTypesContainingStaticMethods
(reference);
if (reference != null && reference.getType() != null
&& "java.lang.Double"
.equals(reference.getType().getIdentifier())) {
return Iterables.concat(Collections.singletonList
("org.eclipse.xtext.example.domainmodel.lib.DecimalExtensions"),
resultFromSuper);
}
return resultFromSuper;
}
}
e amarrar nossa implementação personalizada
public class DomainmodelRuntimeModule
extends AbstractDomainmodelRuntimeModule {
...
public Class
bindStaticMethodsFeatureForTypeProvider() {
return DomainmodelStaticMethodsProvider.class;
}
}
Se você adicionar o plug-in org.eclipse.xtext.example.domainmodel.lib ao classpath do projeto na área de trabalho do tempo de execução, o seguinte modelo de domínio deve ser compilado:
entity DecimalLiteralTest2 {
op circleArea(double radius) : double {
3.1415926535 * radius * radius
}
}
?
Texto original disponível em http://koehnlein.blogspot.com/2011/07/extending-xbase.html







