fatiherikli

Öncelikle başlık hakkında bir açıklama yapayım. Umduğunuz gibi bir blog yazısı olmayabilir. Başlığın hikayesi bir Hüseyin Albayrak esprisine dayanıyor.

Ezoterik diller hep ilgimi çekmiştir. Bunlardan en hoşuma gideni Brainfuck. Ezoterik diller için deneysel ya da şaka amaçlı yapılmış programlama dilleri tanımı yapabiliriz.

Brainfuck bir tape (kaset), bir pointer (işaretçi) ve 8 komuttan oluşan bir programlama dilidir. Turing tarpit bir dildir; bu terim teorik olarak bir dil üzerinde aklınıza gelen herhangi bir problemi çözebileceğinizi ifade eder.

Dilin nasıl çalıştığını anlayabilmek aşağıdaki gibi bir kaset ve işaretçi hayal edebiliriz.

Dilin temel yapısı komutlar yardımıyla kasetin üzerindeki işaretçiyi sağa ya da sola hareket ettirmek ve bulunduğu hücre üzerinde giriş-çıkış birimleriyle etkileşime dayanıyor.

İşte komutlarımız:

>    İşaretçiyi bulunulan hücrenin sağına kaydır
<    İşaretçiyi bulunulan hücrenin soluna kaydır
+    Bulunulan hücreyi 1 arttır
-    Bulunulan hüçreyi 1 eksilt
.    Bulunulan hücrenin karakter değerini ekrana yazdır
,    Kullanıcıdan bir karakter al ve bunu hücreye yazdır
[    Eğer hücre sıfır ise bir sonraki "]" komutuna atla
]    Eğer hücre sıfır değil ise önceki "[" komutuna atla

Dilde bu karakterler dışında hiçbir şey önemsenmez. Bu sayede yorum satırları için ekstra bir karaktere ihtiyaç duymazsınız.

Örneklere geçmeden önce brainfuck scriptlerinizi çalıştırmak içın aşağıdaki linki inceleyebilirsiniz. http://fatiherikli.github.io/brainfuck-visualizer/

Selam Dünya

Klasik hello world örneğize geçelim. Ekrana selam yazdıralım. Bunu yapabilmek için yazdıracağımız karakterlerin sayısal değerlerini hücrelerimize yazdırmamız gerekiyor. Örnek olarak ilk hücrenin değeri 83, yani S harfinin sayısal değeri olması gerekiyor.

Bunun için 83 defa + komutu yazabiliriz. Ancak olmaz, çirkin :) Bunu döngülerle yapmalıyız.

Yorum satırlarında açıklamaya çalıştım.

+++++ +++++           ilk hücreyi 10 arttır
[
   > +++++ +++        sağa kay ve 8 arttır
   > +++++ ++         sağa kay ve 7 arttır
   > +++++ ++         sağa kay ve 7 arttır
   > +++++ +          sağa kay ve 6 arttır
   > +++++ ++         sağa kay ve 7 arttır
   <<<<< -            ilk hücreye git ve bir eksilt
]                     sıfır değil ise başa atla

> +++ .               S yazdır
> - .                 E yazdır
> +++++ + .           L yazdır
> +++++ .             A yazdır
> +++++ ++ .          M yazdır

Brainfuck'ta genelde programlar hücrelerdeki değerlerin komşularına taşınması, kopyalanması şeklinde ilerliyor. Bunlardan bahsetmek istiyorum.

Değer Taşımak

İlk hücremizdeki değeri ikinci hücreye taşıyalım.

+++++ ++      ilk hücreye 7 atadık
[             döngüyü başlat
   > +        sağ hücreye kay ve bir arttır
   < -        sol hücreye geri dön ve kay bir azalt
]             ilk hücre sıfır olana kadar tekrarla

Programı çalıştırdığınızda ilk hücredeki değer sıfırlanana kadar ikinci hücredeki değeri bir arttığını göreceksiniz. Taşıma işlemi bu şekilde gerçekleşiyor.

Peki bu ne anlama geliyor? Çarpma işlemini örnek olarak verebilirim. Taşıma işlemi yaparken > + yerine > ++ derseniz ikinci hücre ilk hücrenin değerinin iki katı olarak taşınmış olur. Bu da çarpma işlemini bu şekilde yapabileceğiniz anlamına geliyor.

Değer Kopyalamak

Kopyalama işlemi için bir adet geçici hücreye ihtiyacımız var. Taşıma işlemi ile aynı, sadece iki hücreye birden taşıma yapıyor. Geçici olan hücre döngüyü sağlamak için gerekli.

+++++ ++      ilk hücreyi 7 arttır
[             döngüyü başlat
   >> +       üçüncü hücreyi 1 arttır
   << -       ilk hücreyi 1 azalt
]             ilk hücre sıfır olana kadar tekrarla

İlk hücreyi geçici olan hücremize taşıdık. Şimdi ilk iki hücremizi bu değer ile dolduralım.

>>            üçüncü hücreye atla
[             döngüyü başlat
   <+         sola kay ve bir arttır
   <+         bir sola daha kay ve arttır
   >> -       üçüncüya atla ve bir azalt
]             üçüncü hücre sıfır olana kadar tekrarla

Programı çalıştırdığınızda üçüncü hücre sıfırlanırken ilk hücrenin ikinci hücreye kopyalanmış olduğunu göreceksiniz.

Kolları sıvayalım

Bir örnek yapalım. Kullanıcıdan iki adet sayı alıp toplamını ekrana yazdıralım.

,                   ilk hücre için bir sayı al
>                   sağ hücreye kay
,                   bir sayı daha al

İlk iki hücreyi kullanıcıdan aldığımız değerlerle doldurduk. Brainfuck'ta input ile alınan değerin ascii değeri hücreye yazdırılmaktadır. Bu ascii değeri ondalık değere çevirmemiz gerek. Bunun için de her iki değerden 48 eksiltebiliriz.

> +++++ +           sağa kay ve 6 arttır
[                   döngüyü başlat
   -                bir azalt
   < ----- ---      sola kay 8 azalt
   < ----- ---      tekrar sola kay ve 8 azalt
   >>               geçici değere atla
]                   eğer sıfır değilse başa atla

Yukarıdaki döngü 6 kez çalışacak ve her hücreden 8 azalacak. Nihayetinde iki hücreden de 48 değer eksilmiş olacak.

Şimdi toplama işlemine başlayabiliriz.

<                  sola kay
[                  döngüyü başlat
 -                 bir eksilt
 < +               bir sola daha kay ve bir arttır
 >                 sağa kay
]                  sıfırlanana kadar devam et

Gördüğünüz üzere toplama işlemi de hücreleri kaydırmaktan ibaret. Şimdi elde ettiğimiz ondalık sayıyı ekrana yazdırmak için 48 arttıralım.

++++++             altı değer arttır
[                  döngüyü başlat
   -               bir değer eksilt
   < +++++ +++     sola kay ve sekiz ekle
   >               sağa  kay
]                  sıfırlanana kadar devam et

<                  ilk hücreye dön
.                  ekrana yazdır

Brainfuck ile uygulama geliştirmek bu şekilde. Adının neden brainfuck olduğunu apaçık ortada :) Buna karşın sadece 8 komut seti ile neler yapılabildiğini görmek açısından gayet eğlenceli.

Peki bu dilin interpreter'ını nasıl yazabiliriz? Açıkçası dil üzerinde bir şeyler yapmaktan çok çok daha basit. Bu dilin en sevdiğim özelliklerinden bir tanesi de programların çok basit bir şekilde başka bir dil üzerinde yorumlatılabilmesi.

Brainfuck visualizer'dan örnek vermek istiyorum. Uygulamanın interpreter kısmı aşağıdaki sınıftan ibaret.

var Interpreter = function (source, tape, pointer, out, 
                            awaitInput, instruction) {
    /*
    * Brainfuck Interpreter Class
    * @source: Brainfuck script
    * @tape: Tape model
    * @pointer: Pointer model
    * @out: Output callback
    * @awaitInput: Input callback
    *
    * Usage:
    *    var interpreter = new Interpreter(">", tape, pointer);
    *    interpreter.next()
    *    pointer.get("index") // 1
    * */
    var tokens = "<>+-.,[]";
    var jumps = [], action = 0;
    this.next = function () {
        if (action > source.length) throw {
            "name": "End",
            "message": "End of brainfuck script."
        };
        // Skip non-code characters, a.k.a tokenization
        if (tokens.indexOf(source[action]) === -1) {
            action++;
            return this.next();
        }
        instruction(action);
        var token = source[action];
        var cell = tape.models[pointer.get("index")];
        switch (token) {
        case "<":
            pointer.left();
            break;

        case ">":
            pointer.right();
            break;

        case "-":
            cell.dec();
            break;

        case "+":
            cell.inc();
            break;

        case ",":
            awaitInput(cell);
            break;

        case ".":
            out(cell);
            break;

        case "[":
            if (cell.get("value") != 0) {
                jumps.push(action);
            } else {
                var loops = 1;
                while (loops > 0) {
                    action++;

                    if (source[action] === "]") {
                        loops--;
                    } else if (source[action] === "[") {
                        loops++;
                    }
                }
            }
            break;

        case "]":
            if (cell.get("value") != 0) {
                action = jumps[jumps.length - 1];
            } else {
                jumps.pop();
            }
        }
        return action++;
    }
};

Brainfuck visualizer repo'suna ulaşmak için: https://github.com/fatiherikli/brainfuck-visualizer

Python üzerinde brainfuck implementasyonu için: https://github.com/fatiherikli/bfuck

Umarım ilginizi çekmiştir. Vaktinizi ayırdığınız için teşekkürler :)

comments powered by Disqus