Xtext – DSL Framework, Bölüm 4

Xtext konusunda en son üçüncü bölümde kalmıştık. Bu bölümde, kendi yarattığımız gramer içerisinde fonksiyonları nasıl çağıracağımıza bakmıştık. Çağırma kısmı, eğer parametre göndermiyorsak fonksiyonumuza gayat basitti. Fakat kendi dilimizde bir fonksiyon yarattıysak, elbet ona parametre aktarmak isteyeceğiz. İşte bu bölümde yarattığımız fonksiyonlara önceden tanımlanmış parametreleri nasıl göndereceğimize bakacağız.

Hazırlık

Eğer daha önce üçüncü bölümü okumadıysanız. Şimdi geri dönüp neler yapıldığına baksanız çok iyi olur. Çünkü bu bölümde üçüncü bölümde oluşturduğumuz projeleri değiştirerek başlayacağız.

Daha önce üçüncü bölümü  okuyanlar içinse nasıl bir proje yarattığımızı hatırlatalım.

Proje Bilgileri

  • Dosya Uzantısı: func
  • Proje Adı: org.xtext.bhdrkn.function
  • Gramer Adı: org.xtext.bhdrkn.function.Function

Düzenlemeler

Öncelikle her zamanki gibi gramerimizin neye benzeyeceğine bakalım. Giriş kısmında açıkladığım gibi öncelikle bir değişken tanımlama ksımımızın olması gerekiyor. İlk önce bu alanı yaratacağım. Ardından ise fonksiyonlarımı çağırırken kullandığım grameri değiştirmem gerekecek. Bunları ekleyince aşağıdakine benzer bir gramer yapısı elde ediyorum. Şimdiden söylemekte fayda var, örneğin basit olması açısından, sadece tek parametre aktarılan fonksiyonlar çağrılabilecek.

bhdrkn.func

[DEFINITIONS]
Type1 Name
Type2 name2
Type1 name3
[/DEFINITIONS]
[FUNCTIONS]
function myFirstFunction(Type1 arg0)
[/FUNCTIONS]
[CALLS]
call myFirstFunction(Name)
call myFirstFunction(name3)
call myFirstFunction(name2)
[/CALLS]

Farkettiyseniz, fonksiyon çağrıldığı yerde, “call myFirstFunction(name2)” kısmının hata vermesini istiyorum. Çünkü Name ve name3 değişkenlerinin tipi doğruyken, name2 değişkeninin tipi yanlış.

Gramerin Gerçeklenmesi

Şimdi ise ilgili xtext dosyasını aşağıdaki gibi yaparak, gramerimizi gerçekleyelim.

Function.xtext


grammar org.xtext.bhdrkn.function.Function with org.eclipse.xtext.common.Terminals

generate function "http://www.xtext.org/bhdrkn/function/Function"

FUNCFile:
defines = Defines
functions = Functions
calls = Calls;

Defines:
'[DEFINITIONS]'
definitions+=Define*
'[/DEFINITIONS]';

Define:
keyword=Keyword name=ID;

Keyword:
"Type1" | "Type2";

Functions:
'[FUNCTIONS]'
functions+=Function*
'[/FUNCTIONS]' ;

Function:
'function' name=ID '(' (args+=Argument)* ')';

Argument:
keyword=Keyword name=ID;

Calls:
'[CALLS]'
calls+=Call*
'[/CALLS]' ;

Call:
'call' name=[Function] '('args=[Define]')';

Fonksiyonların çağırılırken tek parametre aldığına dikkat etmenizi istiyorum. Eğer bu şekilde başka bir değişiklik yapmadan çalıştıracak olursanız, az önce verdiğimiz örnek grameri doğru olarak algılayacaktır. Halbuki verilen arguman tipleri yanlış. Şimdi yeni bişeyler yapmamız gerekiyor.

Validator

Xtext dosyanızın bulunduğu projenin içerisinde, src klasörünün altında farklı java sınıflarıda var. Eğer bu zamana kadar kullanmadıysanız artık kullanmanızın tam zamanı. Kısaca ne olduklarına bakacak olursak:

  • Formatting: (org.xtext.bhdrkn.function.formatting) Bu paketteki sınıflar size, kodunuzu istediğiniz gibi formatlama imkanı sunuyor. Ama yanlış anlaşılmasın, .func uzantılı dosyanızın nasıl formatlanacağını belirliyor. Yoksa generate ettiğiniz kodların formatlanması kısmı başka yerden yapılıyor.
  • Scoping: (org.xtext.bhdrkn.function.scoping) Bu paketteki sınıflar size, scop tanımlama olanağı sunuyor. Yani isterseniz süslü parantezler kullanarak isterseniz kendi belirlediğiniz şekillerde bunu tanımlayabilirsiniz. Bu örnekte sadece parametre aktarımını göreceğiz, fakat fonksiyonların gövdeleri yaratılırken buna ihtiyacımız olacak.
  • Validation: (org.xtext.bhdrkn.function.validation) Bu paketteki sınıflar size, kodunuzu yazılırken kontrol etmeniz olanağı sağlıyor. Yani isterseniz her değişken büyük harfle başlayacak diyip kontrolünü yapabilirsiniz, isterseniz de fonksiyonlara aktarılan parametreleri kontrol edebilirsiniz.

Biz validation işlemi yapacağız. Bunun için projemiz adına yaratılan FunctionJavaValidator sınıfını kullanacağız. Eğer daha önce JUnit gibi test araçları kullandıysanız hiç zorlanmayacaksınız. Dikkat ettiyseniz zaten yorum halinde bir şeyler yazıyor. Bizim yapacağımızda bunun benzeri. İlk başta kolay anlaşılsın diye bende değişkenlerin büyük harf başlamasını ekleyeceğim. Bunu yapmak için aşağıdaki kod parçasını FuncitionJavaValidator.java dosyasına ekliyoruz.

FunctionJavaValidator.java


...

@Check
public void checkDefineNames(Define define){
if(!Character.isUpperCase(define.getName().charAt(0))){
warning("Name should start with a capital", FunctionPackage.DEFİNE);
}
}

...

Burada “Define” sınıfı benim dilimde tanımladığım bir sınıf. Bunu daha net görmek için xtext dosyasını kontrol edebilirsiniz. Burada yapılan ilk karakterin büyük haftle başlayıp başlamadığını kontrol etmek. Eğer başlamıyorsa. Kaynak dosyası içerisinde (bhdrkn.func) “Define” tanımına karşı gelen yere bir uyarı mesajı yazdırıyoruz.

Şimdi ise asıl yapmak istediğimiz parametre kontrolünü yapalım. Bu kısmıda ekleyince FuncitionJavaValidator.java dosyası aşağıdaki gibi oluyor.

FunctionJavaValidator.java


package org.xtext.bhdrkn.function.validation;

import org.eclipse.emf.common.util.EList;
import org.eclipse.xtext.validation.Check;
import org.xtext.bhdrkn.function.function.Argument;
import org.xtext.bhdrkn.function.function.Call;
import org.xtext.bhdrkn.function.function.Define;
import org.xtext.bhdrkn.function.function.Function;
import org.xtext.bhdrkn.function.function.FunctionPackage;

public class FunctionJavaValidator extends AbstractFunctionJavaValidator {

@Check
public void checkDefineNames(Define define){
if(!Character.isUpperCase(define.getName().charAt(0))){
warning("Name should start with a capital", FunctionPackage.DEFİNE);
}
}

@Check
public void checkCallArguments(Call call){
boolean isOk = false;
Function function = call.getName();
EList<Argument> args = function.getArgs();
String keyword = call.getArgs().getKeyword();
for (Argument argument : args) {
if(argument.getKeyword().compareTo(keyword)==0){
isOk = true;
break;
}
}
if(!isOk){
error("There is no function, which takes such arguments", FunctionPackage.CALL__ARGS);
}
}

}

Son

Projenin son halini derleyip, eclipse uygulamamızı çalıştırdığımızda ise hem hataları görebiliyoruz, hem de uyarıları. Eğer ilk yaptığınızda değişikliği göremezseniz, projenizi temizleyip tekrar tekrar sınıflarınızı generate edin. İlk yapmaya çalıştığımda böyle bir hatayla karşılaşmıştım.

İtiraf etmeliyim ki parametre aktarımı beni en çok zorlayan kısımlardan biriydi. Tüm içi gramer kısmında halletmeye çalışınca gerçekten çok zor bir hal alıyor. Fakat sonradan Xtext forumlarında sorunumu dile getirdim ve öğrendim ki sadece gramer kısmından bunu yapmak mümkün değilmiş. Bundan dolayı size tavsiyem, bir konuda bir günden fazla uğraşmış ve hala yapamamışsanız, hala uğraşacak olsanız bile ilgili forumlara danışın.

Git Hub Adresi

Parametre aktarımınıda projeye ekledikten sonra, ilgili git reposuna yükledim. Git reposuna aşağıdaki adresten ulaşabilirsiniz.

https://github.com/bhdrkn/Xtext-Examples

End Of Line