pengeunpedi pengeunpedi pengeunpedi
  penguenyuvasi   pengulog   arşiv   bulut   rss


ben de burdayım

Wednesday, March 21, 2007 9:58:00 AM tarihinde Fırat KÜÇÜK tarafından gönderildi.

Macintosh'un "hello i am a pc and i am a mac" ile başlıyan reklamlarını biliyorsunuzdur heralde. Bilmiyorsanız [[http://www.youtube.com/results?search_query=apple+mac+pc+commercial&search=Search|youtube]]'dan hemen izleyin derim. E novell de boş durmamış. Bu reklamlara linux'u da eklemiş :)

eğlencelik, linux, mac, windowskaynak | yorumlar [0]


hesap makinesi

Monday, March 19, 2007 10:20:00 AM tarihinde Fırat KÜÇÜK tarafından gönderildi.

simple calculator language design via using antlr v3

Ufak işler için ufak programlama dillerine ihtiyac duyabiliriz. Örneğin matematiksel ifadeleri resim'e dönüştüren bir dil olabilir ya da matris hesaplamaları yapan bir dil ya da belirlediğimiz özel bir metin biçeminde sorgu yapan bir dil ya da oyuncak arabamızı kontrol eden bir dil ya da siteniz için tema motoru.

Javy'de kullandığımız antlr çözümleyici oluşturucu (parser generator) ve jline konsol paketleri ile ufak bir hesap makinesi dili yapmaya çalışalım. Bu takdirde Javy'nin temel çalışma mantığı da göstermiş olacağım.

Bir betik veya uçbirim (konsol) girdisi, derleyici veya yorumlayıcı tarafında iki aşamada çözümlenir. Birinci aşama sözcük çözümleme (lexing) ikinci aşama ise sözdizim çözümlemedir (parsing). Bu çözümleme çeşitli kurallara göre yapılmalıdır. Ve bu kuralları oluşturacağımız da bir betimleme dili olmalıdır. İşte bu betimleme dili BNF'tir.

Oluşturacağımız lexer ve parser kural dosyası şu şekildedir.

grammar HesapMakinesi;

options {
    output       = AST;
    ASTLabelType = Tree;
}

// ### SOZ DIZIM COZUMLEYICI - AYRISTIRICI (PARSER) KURALLARI

basla             :    (islem (SATIR_SONU islem)* SATIR_SONU?)?           ;
// basla kurali icin gerekli olanlar
//   * Bos bir ifadeye imkan taninabilmeli. Hicbir veri girilmese de
//     soz dizim dogru olmalidir.
//   * Islem Betigin son islemi ise satir sonu gelmeyebilir. Bunun haricinde
//     her islemden sonra satir sonu gelmelidir.
//   * Iki bagimsiz islem arada islec (operator) olmadan asla arka arkaya
//     gelmemelidir.

islem             :    oncelikli_islem ((ARTI^ | EKSI^) oncelikli_islem)* ;
oncelikli_islem   :    SAYI ((CARPI^ | BOLU^) SAYI)*                      ;

// ### SOZCUK COZUMLEYICI (LEXER) KURALLARI

SATIR_SONU         :    ('\u000D' | '\u000A')                             ;
SAYI               :    RAKAM+                                            ;
ARTI               :    '+'                                               ;
EKSI               :    '-'                                               ;
CARPI              :    '*'                                               ;
BOLU               :    '/'                                               ;
BEYAZ_BOSLUKLAR    :    (' ' | '\t')+   {$channel = HIDDEN;}              ;

// ### SOZCUK COZUMLEYICI ALT PARCALARI

fragment
RAKAM              :    '0' .. '9'                                        ;

BNF ile DFA (Deterministic Finite Automata - Belirgin Sonlu Otomat) oluşturabiliriz. Önce sözcük çözümleyicisi (lexer) için BNF kuralları oluşturalım.

ARTI : '+';

ARTI adlı bir sözcüğümüz (token) oldu. Bu tarayıcı sözcüğüne (token) atanan karakter ise '+'dır. Benzer şekilde diğer işleçlerimizi (operatör) de yazalım.

EKSI : '-';
CARPI : '*';
BLU : '/';

Bunlara ilaveten satır sonlarını gösteren de bir kural eklememiz gerekir.

SATIR_SONU : ('\u000D' | '\u000A') ;

SATIR_SONU sözcüğü ya 0D ya da 0A onaltılık değerlere sahip olacaktır. BNF dilinde anlaşıldığı gibi '|' düşey boru işareti yada (OR) manasına gelir.

Yazdığımız bu dilde işe yaramayan boşluk karakterlerini ise aşağıdaki kural ile tanımlayalım.

BEYAZ_BOSLUKLAR : (' ' | '\t')+ ;

DFA'nın bir başka şekli olan Düzenli ifadeler (regular expressions) ile uğraşmış olan geliştiriciler bilirler. + işleci birden fazla tekrar manasına gelir * ise 0'dan fazla tekrar manasına gelir. Örnek kural üzerinde gösterelim.

SAAT : 's' 'a'+ 't'

Yukarıdaki kural 'sat', 'saat', 'saat', 'saaaaaaaaaat' ifadeleri ile eşleşecektir. Fakat 'st' ile eşleşmeyecektir. Çünkü + işleci 1 ve 1 den fazla tekrar manasına gelir. * işleci ise 0 veya daha fazla tekrar manasına geleceğinden 'st' ile de eşleşebilir.

SAAT : 's' 'a'* 't'

Yukarıdaki kural, 'st', 'sat', 'saat', 'saaaaaaat' gibi ifadeler ile eşleşir. Benzer şekilde BEYAZ_BOSLUKLAR sözcüğü (token) 'sekme', 'boşluk', 'sekme-boşluk', 'bosluk-sekme', 'sekme-sekme', 'sekme-sekme-sekme', 'boşluk-boşluk-sekme' gibi daha birçok eşleşmeye sahiptir. Yani betiğimizdeki tüm boşluk ifadelerini kapsar.

RAKAM : '0' .. '9';

RAKAM sözcüğü 0'dan 9'a kadar tüm rakamlar ile eşleşir. Yukarıda aralık belirten '..' işleci aslında birçok veya işleminin kısa yoludur. Yukarıdaki kural şu şekilde de yazılabilirdi.

RAKAM : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';

Tüm kurallara ilaveten son sözcük çözümleyici (lexer) kuralımız ise şu şekilde olmalıdır.

SAYI : RAKAM+;

SAYI sözcüğü (token), bir veya birden fazla rakamın birleşmesinden oluşacaktır. '1', '12', '1000', '0122' gibi bir çok eşleşme SAYI sözcüğü ile ifade edilebilir.

Örnek hesaplama ifademiz:

1 + 56 - 31 / 89 * 21

Bu ifadeyi söcük çözümleyici şu şekilde çözümler.

SAYI ARTI SAYI EKSI SAYI BOLU SAYI CARPI SAYI

Tabi şu anda yaptığımız ayrıştırıcı gerekli sözcükleri çözümlüyor. Fakat sözdizim konusunda sorunları bulunmakta örneğin:

++4423412+++++++++++3444++--++ 14324 142314

İfadesi de benzer şekilde ayrıştırılacaktır. Bu bağlamda söz dizim kurallarımız olması gerekir bu işlemi ise parser dediğimiz söz dizim çözümleyicisi yapacaktır. Bu kurallar sayesinde işleç öncelikleri de belirleyebiliriz.

Öncelikle bir kök kuralımız olması gerekmektedir. Bu kök kural şunları gözetmeli:

 * Boş bir ifadeye imkan tanınabilmeli. Hiçbir veri girilmese de söz dizim doğru olmalı
 * İşlem Betiğin son işlemi ise satır sonu gelmeyebilir. Bunun haricinde her işlemden sonra satır sonu gelmelidir.
 * İki bağımsız işlem arada operatör olmadan asla arka arkaya gelmemelidir.

Bu veriler ışığın da kuralımız şu şekilde olabilir.

basla : (islem (SATIR_SONU islem)* SATIR_SONU?)? ;

Kuralın DFA grafiği yukarıdaki şekilde olabilir. Kural direkt geçilebiliyor bu da demek oluyor ki boş bir ifade de kabul edilebilir. Ve bir işlem'den sonra SATIR_SONU gelebilir veya gelmeyebilir ama iki işlemden sonra arada mutlaka SATIR_SONU olmalıdır. Fakat son işlemden sonra her zaman SATIR_SONU ister konulabilir ister konulmayabilir.

Yukarıdaki kurallardan sonra işlem kuralını açalım. İşlem kuralında önce bir toplama - çıkarma işlemi yapıyoruz. Böylelikle işleç önceliğini çarpma ve bölmeye vermiş oluruz.

Kural şu şekilde olmalıdır.

islem : oncelikli_islem ((ARTI | EKSI) oncelikli_islem)*;

Bir öncelikli işlemden sonra başka bir veya birden fazla öncelikli işlem geliyor ise mutlaka ARTI veya EKSI ile birleştirilmelidir.

oncelikli_islem kuralımız ise buna benzer bir ifade olacaktır.

oncelikli_islem : SAYI ((CARPI | BOLU) SAYI)*;

Bir sayı tek veya birden çok çarpı/bölü işleçleri ile birleştirilmiş olmalıdır.

Şimdi bir örnek üzerinde kuralların kullanımınından bahsedelim.

1 + 15 / 5 + 1 * 1

Yukarıdaki ifadeyi sözdizimin kök kuralı olarak irdeleyelim öncelikle.

islem

İfade hiçbir SATIR_SONU ile ayrılmamış tek bir işlemdir. 'islem' kuralı nazarından bakarsak şöyle çözümlenecektir.

oncelikli_islem ARTI oncelikli_islem ARTI oncelikli_islem

Görüldüğü gibi islem ifadesi 3 farklı kola ayrıldı.

1. işlem : 1
2. işlem : 15 / 5
3. işlem : 1 * 1

Bu sözdizim kurallarının yanında bir de ağaç yapısı oluşturmuş olduk. Ağaç yapısı şu şekildedir.

Bir ağaç sözdizim ayrıştırıcı ile yapılan ifadeleri irdeleyebiliriz. Örnek işlemimizin LISP stili ağaç yapısı şu şekildedir.

(+ (+ 1 (/ 15 5)) (* 1 1))

Böyle bir ağaç yapısını ayrıştırmamız için gelen verileri analiz edecek bir de ağaç çözümleyiciye (tree parser) ihtiyacımız bulunmaktadır. Ağaç çözümleyicinin kuralları şu şekildedir.

tree grammar HesapMakinesiTreeParser;

options {
    tokenVocab   = HesapMakinesi;
    ASTLabelType = Tree;
}

// ### AGAC SOZ DIZIM COZUMLEYICI - AYRISTIRICI (PARSER) KURALLARI

basla
    :   a = ifade {System.out.println(a);}
    ;

ifade returns [int ifd]
    :   ^(ARTI  a = ifade b = ifade {ifd = a + b;})
    |   ^(EKSI  a = ifade b = ifade {ifd = a - b;})
    |   ^(CARPI a = ifade b = ifade {ifd = a * b;})
    |   ^(BOLU  a = ifade b = ifade {ifd = a / b;})
    |   s = SAYI {ifd = (int) new Integer(s.getText());}
    ;

Yukarıdaki antlr tarzı BNF imlasında (+ (+ 1 (/ 15 5)) (* 1 1)) ağaç yapısı yukarıdan aşağı doğru işletilir. Kıvrık parantezlerdeki Java kodları bir çok ifade oluşturup nihayet kök kural olan 'basla' biterken konsola bulunan ifade yazdırılır.

Bu kurallar ile antlr kullanılarak 3 adet dosya elde ederiz. HesapMakinesiLexer.java, HesapMakinesiParser.java, HesapMakinesiTreeParser.java bu 3 sınıfı çağıran ana programımız ise şu şekilde olabilir.

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import jline.*;

public class HesapMakinesi {

    public static void main(String[] args) throws Exception {

        ConsoleReader okuyucu = new ConsoleReader();
        String satir;

        while ((satir = okuyucu.readLine(">>> ")) != null) {
            CharStream                         karakter_katari      = new ANTLRStringStream(satir);
            HesapMakinesiLexer                 sozcuk_cozumleyici   = new HesapMakinesiLexer(karakter_katari);
            CommonTokenStream                  sozcukler            = new CommonTokenStream(sozcuk_cozumleyici);
            HesapMakinesiParser                sozdizim_cozumleyici = new HesapMakinesiParser(sozcukler);
            HesapMakinesiParser.basla_return   agac                 = sozdizim_cozumleyici.basla();

            // AGAC YAPISI LISP STILI GORUNTULEMEK ICIN ASAGIDAKI IFADE KULLANILABILIR
            System.out.println("agac yapısı : " + ((Tree)agac.tree).toStringTree());

            CommonTreeNodeStream               dugumler             = new CommonTreeNodeStream((Tree)agac.tree);
            HesapMakinesiTreeParser            agac_cozumleyici     = new HesapMakinesiTreeParser(dugumler);
            agac_cozumleyici.basla();

        }
    }
}
atölye, java, javykaynak | yorumlar [0]


kahve çekirdeğinin kabuğu yok mudur?

Saturday, March 3, 2007 10:09:00 AM tarihinde Fırat KÜÇÜK tarafından gönderildi.

Java Türkçe kitaplarında sıklıkla karşılaşılan bir hata vardır. Bean kelimesini fasulye diye tercüme eder çoğu kitap. Fakat bean aslında kahve çekirdeğidir. Hani öğütülünce güzel güzel kokan kahve çekirdeğidir "bean" diye bahsedilen. Bu çekirdekler gereğinde Enterprise Java Beans olur. Gereğinde Netbeans olur.

İşte birbirinden farklı kabuk şekilleri :

import java.io.*;

public class JavaKabuk {


   public static void main(String[] args) {

       System.out.println("Java Standart Kütüphaneleri ile Basit Kabuk Oluşturma");

       try {


           BufferedReader okuyucu = new BufferedReader(new InputStreamReader(System.in));
           String satir;


           while (true) {
               System.out.print("kabuk> ");


               // Mevcut satır okunuyor.
               satir = okuyucu.readLine().trim();

               // Okunan metin tekrar uçbirime yazılıyor.

               System.out.println(satir);

               // Kabuktan çıkmak için "cikis" ifadesini kullanabiliriz
               if (satir.equals("cikis")) break;
           }


       } catch (IOException ex) {
           System.out.println("okuma hatası!");
       } catch (NullPointerException ex) {

           // Ctrl + D gibi çıkış ifadelerini tutmak için
           // NullPointerException kullanıyoruz.
       }
   }
}

Yukarıdaki Standart Java Kütüphanelerini kullanan kabuk kodu, basit bir komut uçbirimi sağlayacaktır. Fakat klavyedeki yön tuşlarımız satır üzerinde imleç görevi görmeyecektir. Gıpta ettiğimiz bash tarzı kabuk yaklaşımı için standart Java kütüphanelerinden daha fazlasına ihtiyacımız bulunmakta. UNIX sistemlerde çalışan çoğu kabuk, GNU Readline kütüphanesini veya BSD EditLine kütüphanesini tercih etmekteler. Bu yoğun ilgiden dolayıdır ki bu kütüphaneler bir çok programlama diline tercüme edilmiştir. Java - Readline kütüphanesi ise Javanın bu hazineden nasiplenmesi olabilirdi..

Fakat işler sanıldığı gibi değildir. Java Readline kütüphanesi Java kadar platformlar arası geçiş esnekliğine sahip değil. Bunun en büyük nedeni ise Readline'ın JNI'den dolayı platform bağımlı kodlara sahip olmasıdır. JNI'i kullanıp ta Macintosh, Windows, Linux üçlemesini destekleyen bir çok Java projesi mevcutken Java - Readline, yalnızca Linux ve Macintosh'u desteklemektedir. Hal böyle olunca da Jython gibi büyük bir projede de kullanılan bu kütüphane, daha sonraları eklenti olarak kullanılmaya başlanmıştır. Java Readline, Groovy gibi yeni JVM betik dillerinde de kullanım bulamamıştır.

Şimdiler ise daha işlevsel bir yaklaşıma sahip Platform bağımlı (Native) öğeler içermeyen JLine daha geniş kullanım buluyor. JLine sayesinde daha esnek kabuklar yapabiliriz. Basit bir JLine Kabuğu şu şekilde olabilir:

import jline.*;
import java.io.*;

public class JLineKabuk {


   public static void main(String[] args) {

       System.out.println("basit kabuk kullanımı");
       try {
           ConsoleReader okuyucu = new ConsoleReader();


           String satir;
           while ((satir = okuyucu.readLine("kabuk> ")) != null) {

               System.out.println(satir);
           }

       } catch (IOException ex) {

           System.out.println("okuma hatası!");
       }
   }
}

JLine ile artık rahatlıkla girdi yaparken ok tuşlarını, Home, Edit gibi yardımcı tuşları kullanabiliriz. Dahası kod tamamlama bile yapabiliriz. Aşağıdaki kod sayesinde yazdığımız harflerin akabinde sekme (tab) tuşu kullandığımızda "ali", "ayşe", "veli" önermeleri yapılacaktır.

import jline.*;
import java.io.*;
import java.util.*;

public class JLineKabuk2 {

   public static void main(String[] args) {

       System.out.println("basit kabuk kullanımı");
       try {
           ConsoleReader okuyucu        = new ConsoleReader();
           List          tamamlayicilar = new LinkedList();


           tamamlayicilar.add(
               new SimpleCompletor(
                   new String[] {"ali", "veli", "ayse"}

               )
           );

           okuyucu.addCompletor(new ArgumentCompletor(tamamlayicilar));


           String satir;
           while ((satir = okuyucu.Readline("kabuk> ")) != null) {

               System.out.println(satir);
           }

       } catch (IOException ex) {

           System.out.println("okuma hatası!");
       }
   }
}

Varın siz karar verin: "kahve çekirdeğinin kabuğu oluyor mu?" diye.

atölye, groovy, java, javy, jythonkaynak | yorumlar [0]




1


pengulog

her hakkı erkektir © 2008 e-posta