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.
End Of Line