Discussion:
Katılımcılar olsa, lisp programları yazsak.
Aycan İrican
2007-12-14 15:34:04 UTC
Permalink
Selam,

Nasıl anlatabilirim bilmiyorum ama, bu listede common lisp programlama
yapmak isteyen var mı acaba? Bir grup kütüphane ve buna bağlı bir web
sunucu yazdık biz.

Bunlara ayrıştırıcıların kolay birleştirilebilmesini sağlamak (parser
combinators diye geçiyor sanırım literatürde, bunu volkan bir projesinde
kullanmıştı, bkz. "aliw"), akışlar ile programlama (stream processing
olarak geçiyor ancak bizim şimdilik yaptığımız genel bir akış modeli
ortaya koymak oldu, kontrol noktaları belirleyebiliyor ve hata durumunda
bu noktalara geri sarabiliyoruz akışı, ve tabi türlü veri tiplerini
akışa çevirebiliyoruz), iş parçacıkları ile programlama (threaded
programming diye geçiyor olmalı) ile birden fazla işlemci çekirdeğini
kullanabiliyoruz. Bunları birleştirince fena kodlar çıkmadı.

Yani karakter dizisinden, veri tipine, veri tipinden ise karakter
dizisine dönüşümleri sağlayan "parser" ve "render" yazabiliyoruz
bunlarla. Örnek vermek gerekirse (zom: zero-or-more, :fixnum? ise diğer
bir ayrıştırıcı v.b.):

(defrule version? (version d)
(:fixnum? d) (:do (push d version))
(:zom #\. (:fixnum? d) (:do (push d version)))
(:return (nreverse version)))

(defrule http-protocol? (version)
(:seq "HTTP/") (:version? version)
(:return (list 'HTTP version)))


Http başlığındaki sürüm bilgisini bu iki (ve ilkel diğer birleştireçler)
ayrıştırıcının birleşmesi sonucu ayıklıyoruz akış içerisinden.

SERVER> (with-core-stream (s "HTTP/1.1")
(http-protocol? s))
(HTTP (1 1))

Ayrıştırıcılar lisp dünyasının temel veri tipi olan anlamlı listeler
dönüyor gördüğünüz gibi. Bunun tersi olan işlem ise liste tiplerini
karakter dizisi olarak ifade edebilmeyi sağlayan betimleyiciler. Bizim
rfc2616 gerçekleştirimimizin testlerinden buna bir örnek vermek gerekirse:

(deftest http-location!
(with-core-stream (s "")
(http-location! s (make-uri :scheme "http"
:username "john"
:password "foo"
:server "127.0.0.1"
:port 8080
:paths '(("test") ("me") ("up.html"))))
(equal (return-stream s)
"http://john:***@127.0.0.1:8080/test/me/up.html"))
t)

Burada betimleyicinin karmaşık bir liste alması yerine daha basit bir
veri yapısı kullanıldığını görüyorsunuz (uri sınıfı). Bu veri yapısını
akışa yazan bir test, ve akıştaki veriyi alıp olması gereken karakter
dizisi ile karşılaştırıyoruz.

Bu akışlar ve bunların üzerinde çalışan ayrıştırıcılar oldukça faydalı.
HTTP belirtimini okuyup sırasıyla bütün BNF'yi gerçekleştirebiliyoruz
(bunun benzeri ve hatta daha kuvvetli olanı haskell'de parsec, ve
BNF'den doğrudan ayrıştırıcı yaratan happy var).

Bahsedebileceğim diğer bir özellik ise iş parçacıklarını kolay
kullanabilmemizi sağlayan kodlar. Örneğin sunucumuz için gerekli olan
basit kayıt defteri aşağıdaki gibi programlanabiliyor.

(defclass logger-server (local-unit)
((log-stream :accessor log-stream :initarg :log-stream :initform nil)
(log-path :accessor log-path :initarg :log-path
:initform (default-log-path))))

(defmethod/unit log-me-raw :async-no-return ((self logger-server) message)
(string! (log-stream self) message)
(char! (log-stream self) #\Newline))

İşin güzel yanı burada iş parçacığına özel bir programlama
görmüyorsunuz. Ancak logger-server sınıfından bir örnek yaratıp
çalıştırdığınızda (bunun bir start metodu var) diğer iş parçacıkları
log-me-raw adlı metodu eş zamanlı olarak çağırabiliyor ve sihirli bir
şekilde bu çağırımlar girişmeden, sıralı olarak işliyor.

SERVER> (defparameter *logger1* (make-instance 'logger-server
:log-stream *standard-output*))
*LOGGER1*
SERVER> (start *logger1*)
NIL
SERVER> (log-me-raw *logger1* "core.gen.tr iz c00l")
core.gen.tr iz c00l

Bunu 40 iş parçacığıyla test edince çıktının tertemiz olduğunu
görebiliyoruz:

(defparameter *logger* (make-instance 'logger-server))

(defclass my-worker (local-unit)
((id :accessor id :initarg :id)))

(defmethod/unit logmeup :async-no-return ((self my-worker))
(log-me *logger* (format nil "I'm here as number ~D" (id self))))

(defparameter *workerz* (loop
for i from 1 to 40
collect (make-instance 'my-worker :id i)))

(defun setup-loggerz ()
(start *logger*)
(mapcar #'start *workerz*)
(mapcar #'logmeup *workerz*)
(mapcar #'stop *workerz*)
(sleep 5)
(stop *logger*))

Bunların yanında bir de sürdürmeleri kullanan web sunucumuz var artık.
Eskiden ucw kullanıyorduk ancak küfür ede ede onu bıraktık şimdi daha
işlevsel olmasını istediğimiz yeni bir sunucu bileşeni yazıyoruz. Sunucu
doğal olarak ucw+ gibi ajax desteklesin istedik, ve şu an yavaş yavaş
yeni sunucu altyapısı oluşuyor.

Sonuç olarak bu kodlara göz atmak isteyenler aşağıdaki adresi
kullanabilirler:

http://www.core.gen.tr/projects/core-server/

Sunucuyu kurmak isteyenler aşağıdaki bağlantıdaki kurulum paketini
kullanabilirler. Kurulum paketi bütün bağımlılıkları internet üzerinden
indirip derleyen bir paket. Bu pakette de betiklerde çoğunlukla
karşımıza çıkan "command pattern" için bir çözüm bulmanız mümkün.

http://www.core.gen.tr/projects/core-server-installer-latest.tar.gz

Tekrar söylemekte fayda görüyorum, bize common lisp ile çalışabilecek
deneyim sahibi katılımcılar lazım. Şu an çalışmanın büyük bir bölümünü
Evrim ULU yürütüyor. Zaten çoğu gerçekleştirim de ona ait. Çevrenizde
common lisp programlama yapmak isteyen birileri varsa ve bu anlattığım
konularla ilgilenmişse lütfen bize ulaşmalarını sağlayın. Katılmak
isteyenler doğrudan erişmek için IRC sunucumuza da gelebilirler.

irc.core.gen.tr:7000

Lütfen gelin, nolur gelin, geldirin, ettirin, katkı sağlayın.

Sevgiler...
--
aycan

Loading...