Xtext – DSL Framework, Bölüm 2

İlk bölümde elimden geldiğince, Xtext framework’unun ne olduğunu, ne için Xtext framework’unu kullanmakta olduğumu, kurulumunun nasıl yapılacağını anlatmaya çalıştım. Bu bölümde ise, ilk bölümde yapmış olduğum örnekten devam ederek başka ne şekilde oluşturduğunuz dili test edebileceğinizi anlatmaya çalışacağım.

Previously on Xtext

Öncelikle Xtext projemizi oluşturmuştuk. Projemizi oluşturduğumuzda bizim için 3 farklı projenin oluşturulacağından bahsetmiştik. Bu projeler:

  1. org.xtext.bhdrkn.fouroperation, dilin özelliklerini ve nasıl işleyeceğini bu proje altında belirledik.
  2. org.xtext.bhdrkn.fouroperation.generator, ilk dilimizi bu klasörün altına .java dosyaları oluşturarak işledik. Şimdi ise yine bu klasörün altında Xpand ve Xtend dillerini kullanarak dilimizin dosyasını nasıl işleyeceğimize bakacağız.
  3. org.xtext.bhdrkn.fouroperation.ui

Sonra, tam sayılar üzerinde dört işlemi gerçekleştirebilecek bir dili nasıl oluşturacağımıza bakmıştık. İlk örneğimiz olduğundan olabildiğince sade tutup sadece iki sayı üzerinden bu dört işlemin nasıl yapılacağını incelemiştik. Dilimizin dosya uzantısını .op olarak belirlemiş ve örneklerimizde bhdrkn.op dosyasını kullanmıştık. Şimdide yine bu oluşturduğumuz dilin kurallarını destekleyen bhdrkn.op dosyasından yardım alarak devam edeceğiz.

bhdrkn.op / Example.op


operation bhdrkn {
 8 + 4
 14 - 2
 3 * 4
 24 / 2
}

Xpand ve Xtend

Öncelikle Xpand ve Xtend’in neler olduğuna bakalım. Xpand ve Xtend, Xtext framework’ü ile birlikte gelen iki ayrı dil. Biz bunları oluşturduğumuz dili yorumlamak için kullanıyoruz. Aslında temelde ilk bölümde java ile yaptığımız işi bu bölümde Xpand ve Xtend ile yapacağız. Basit olması açısından, hazırda oluşmuş olan 2. projeyi yani generator ile biten projeyi kullanacağız.Şu adımları takip edeceğiz:

  1. Öncelikle oluşturduğumuz dilde bir dosya yazacağız. Oluşturduğumuz dilde doğru bir şekilde yazdığımızdan emin olduktan sonra bu dosyayı kullanacağımızı belirteceğiz.
  2. .mwe2 uzantılı bir generator dosyasında kullanacağımız dosyayı ve bu dosyayı nerden, hangi tip class kullanarak okuyacağımızı belirteceğiz.
  3. Dosya okunduktan sorna ne şekilde çalışacağını söyleyecek fonksiyonları yazacağız. Bunun için Xpand dilini kullanacağız.
  4. Xpand ile yazılmış fonksiyonların çalışırken kullanacağı ve/ve ya classlara eklemek istediğimiz fonksiyonları yazacağız. Bunun için ise Xtend dilini kullanacağız.
  5. Sonra oluşturduğumuz kodu generate edip sonuçlarımızı göreceğiz.

Dosyamız zaten hazırda oluşturulmuş olduğundan (bhdrkn.op), sadece dosyanın içeriğini generator porjesinin altında src/model/Example.op dosyasının içerisine kopyalayın.

Generator Dosyasının Olusturulması

Yine generator projesinin altında src/workflow klasörünün altındaki FouroperationGenerator.mwe2 dosyayı açalım ve içeriğini aşağıdaki gibi değiştirelim.

FouroperationGenerator.mwe2


module workflow.FouroperationGenerator

import org.eclipse.emf.mwe.utils.*

var targetDir = "src-gen"
var fileEncoding = "UTF-8"
var modelPath = "src/model"

Workflow {

component = org.eclipse.xtext.mwe.Reader {
// lookup all resources on the classpath
// useJavaClassPath = true

// or define search scope explicitly
path = modelPath

// this class will be generated by the xtext generator
register = org.xtext.bhdrkn.FouroperationStandaloneSetup {}
load = {
slot = "operation" //greetings to operation
type = "Operation" //Greeting to Operation
}
}

component = org.eclipse.xpand2.Generator {
expand = "templates::Template::main FOREACH operation" // greetings to operation
outlet = {
path = targetDir
}
fileEncoding = fileEncoding
}
}

Olabildiğince yaptığım değişiklikleri belirttim. Genel olarak oluşturduğumuz dosya yüklenirken hangi konumdan başlanıldığını(operation) ve başlanılan konumun hangi class’a karşılık(Operation) geldiğini belirtiyoruz. Dosya içerisinde karşılaşılan her belirttiğimiz class(Operation) için, hangi fonksiyonun çağırılacağını(templates paketi altında Template dosyası içinde main fonksiyonu) belirtiyoruz. Fonksiyonu ise az sonra Xpand ile yazacağız.

Xpand ile Fonksiyonun Olusturulması

Proje oluşurken yine bizim için bir Xpand dosyası oluşturuyor. Bu dosyayı generator projesinin altında src/templates klasörü içinde bulabilirsiniz. Template.xpt dosyasını açıp içeriğini aşağıdaki gibi değiştiriyoruz.

Template.xpt

«IMPORT org::xtext::bhdrkn::fouroperation»

«EXTENSION templates::Extensions»

«DEFINE main FOR Operation»
«EXPAND statement FOREACH this.statements»
«ENDDEFINE»

«DEFINE statement FOR Statement»
«EXPAND expression FOR this.expression»
«ENDDEFINE»

«DEFINE expression FOR Expression»
«FILE "bhdrkn" + this.eClass().name + ".txt"»
«IF this.eClass().name == "Addition"»
«((Addition)this).calculate()»
«ELSEIF this.eClass().name == "Minus"»
«((Minus)this).calculate()»
«ELSEIF this.eClass().name == "Multi"»
«((Multi)this).calculate()»
«ELSEIF this.eClass().name == "Div"»
«((Div)this).calculate()»
«ENDIF»
«ENDFILE»
«ENDDEFINE»

İlk başta oluşturduğumuz dilimizi içeren projeyi import ediyoruz. Ardından hangi Xtend dosyalarını kullanacağımızı belirtiyoruz. Sonra ise main fonksiyonumuzu yazuyoruz. Main fonksiyonumuz, Statement listesi şeklindeki statements değişkeninin içindeki her Statement için statement isminde başka bir fonksiyona dallanıyor.

“statement” fonksiyonu ise sahip olduğu Expression türündeki expression değişkeni için başka bir fonksiyon çağırıyor. “expression” ismindeki bu fonksiyon ise hangi türden olduğunu belirleyip ona göre dışarıda tanımlı başka bir fonksiyon çağırıyor. Bu fonksiyonsa ilgili değerleri hesaplayıp bize bir string döndürüyor. Bizde ‘”bhdrkn”+classAdı.txt‘ isimli bir dosyaya bu değerleri yazacağız. Dört farklı class kullanacağımız için, generetor projemizin altında src-gen klasörünün altında dört farklı dosya oluşacak.

İlk başta harici foksiyon çağrıları ve başktaki Xtend eklenti kısmı hata verebilir. Birazdan Xtend kısmını yazınca hatalar ortadan kalkacaktır. Eğer import kısmında ya da başka kısımlarda hata ile karşılaşıyorsanız proje ve paket isimlerini doğru verdiğinizden emin olun.İlk bölüme bakıp karşılaştırabilirsiniz.

Xtend ile Harici Fonksiyonların Yazılması

Biz class değilde txt dosyası türettiğimiz için, sadece harici fonksiyonlar yazacağız. Fakat kullanırken sizde göreceksiniz, bu yazdığımız harici fonksiyonlar, dilimizi oluştururken oluşan class’larla bütünleşecek. Şimdi generator projesinin altında src/templates klasörünün içindeki Extensions.ext dosyasını açıyoruz ve içeriğini aşağıdaki gibi yapıyoruz.

Extensions.ext


import org::xtext::bhdrkn::fouroperation;

String calculate(Addition this) :
"Result is " + (this.left.toInteger() + this.right.toInteger()).toString();

String calculate(Minus this) :
"Result is " + (this.left.toInteger() - this.right.toInteger()).toString();

String calculate(Multi this) :
"Result is " + (this.left.toInteger() * this.right.toInteger()).toString();

String calculate(Div this) :
"Result is " + (this.left.toInteger() / this.right.toInteger()).toString();

Burda ise yine önce birinci projemizi ekliyoruz. Ardından ise dilimizi ilk oluşturduğumuzda oluşan class’lar için calculate fonksiyonu yazıyoruz. Bu fonksiyon işlemi hesaplayıp bize string halinde döndürüyor. Yukarıdaki Xpand’de yazılmış Template.xpt dosyası içinde ise farklı dosyalar içerisine sonuçları yazıyoruz.

Kodun Generate Edilmesi

Buraya kadar gelmiş ve hiç bir dosyamızdan hata almıyorsak artık kodumuzu generate edebiliriz. Yapacağımız şey dilimizi ilk oluştururken yaptığımızla aynı. Oluşturduğumuz, genereator projesi içerisindeki src/workflow klasörü altındaki FouroperationGenerator.mwe2 isimli dosyayı seçiyoruz ve MWE2 Workflow şeklinde çalıştırıyoruz. Eğer herşey düzgün giderse Console’da son satır DONE ile bitiyor.

Herşey doğru gittiyse generator projesi içerisindeki src-gen/ klasörünün altında yeni oluşmuş dört farklı .txt dosyası göreceksiniz. Yaptığımız işlemlerin sonucu aynı olduğundan hepsinin içinde şu sonucu göreceksiniz:

Result: 12

Son

Umarım biraz daha yardımcı olabilmişimdir. Bundan sonra benim merak ettiğim fakat, bitime ödevimin kapsamına şimdilik girmeyen otomatik generate etme kısmı var. Sizinde gördüğünüz üzere biz elle generate ediyoruz. Fakat kendi dilimizi yazdıktan sonra, yazdığımız dosyayı çalıştırabilmeli ve sonuçları görebilmeliyiz.

Dört farklı dosya oluşturma ile ilgili olarakta şunu söyleyebilirim. Xpand yaz dediğiniz her dosyaya sıfırdan başlıyor. Yani yenisini oluşturuyor. Xpand’in java dosyaları üretmek için kullanıldığını düşünürsek bu gayet mantıklı bir yaklaşım. Fakat bu yakşalım bizim dört farklı dosyaya ihtiyaç duymamıza neden oluyor. Hatta eğer ilk oluşturduğumuz örnek dil dosyamızda (bhdrkn.op) aynı işlemi birden fazla kullanırsak, sadece son olan işlemin sonucunu görebileceğiz. Sadece örnek olarak değerlendirdiğim için bir sorun çıkaracağını düşünmüyorum.

Birde tavsiyede bulunmak istiyorum. Eğer blog’lardan, forum’lardan ya da herhangibir tutorial’dan bişeyler öğrenmeye çalışıyorsanız, lütfen önce sizin kullandığınız sürümde, örneğin çalıştığından emin olun. Sonra kodu anlamaya çalışın. Xtext ile bir çok örnek gördüm ve çoğu 0.7 versiyonu içindi. Önce anlamaya çalıştığımdan baya bir enerji ve zaman kaybettim. Siz siz olun önce çalıştığına emin olun.

Kaynaklar

Bu projeyi oluştururken şu kaynaklardan faydalandım.

Son olarak benim kullandığım Xtext 1.0.1 sürümü.

GitHub Adresi

İlk iki bölümde anlattğım kısımların birleştirilmiş haline aşağıdaki GitHub adresinden erişebilirsiniz.

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

End Of Line