Dev (Back & Front)ARTIGO

Estendendo o Xbase

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:

  1. No tipo atual como operator_plus (OperandType1, OperandType2)
  2. Na classe do primeiro operando, como OperandType1.operator_plus(OperandType2)
  3. 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

trabalha como desenvolvedor e arquiteto de software. É especialista no desenvolvimento de software orientado a modelo e na modelagem de ferramentas na plataforma Eclipse, além de ser core committer no projeto Xtext.

Ver perfil