3 puan yazan GN⁺ 2024-05-03 | 1 yorum | WhatsApp'ta paylaş

Cognition programlama dili tanıtımı

Sorun

  • Lisp programcıları, S-ifadeleri kodu ve fonksiyonel makro sistemiyle metaprogramlama ve genelleştirilmiş sistemler kurabileceklerini iddia eder
  • Ancak Lisp'in temel bir sorunu var
    • Metaprogramlama ile normal programlama aynı olmadığı için Lisp'te her zaman katı bir sözdizimi vardır (parantezler veya look-ahead için bazı karakterler)
    • Sol parantez, Lisp'e sağ parantezle karşılaşana kadar okumaya devam etmesi gerektiğini söyler
    • Bu, sol ve sağ parantezleri dil içinde değiştirilemez hale getirir (kuramsal olarak değil, bazı uygulamalarda olanaksızdır)
    • Daha da önemlisi, bu token'ların ayrıştırıldığı sırayı string işleme olmadan sonradan değiştirmek mümkün değildir
  • Diğer dillerde de belirli bir token'a bakarak ileri okunacak içeriği belirleyen farklı yollar vardır
    • Bu sürece sözdizimi (syntax) denir
  • Cognition ise tamamen postfix kullanan bir anti-syntax (antisyntax) ile ayrılır
    • Bu, concatenative programlama dillerine benzer, ancak bu dillerde de iki büyük sorun vardır
      1. Sol/sağ köşeli parantez karakterleri eklemek (aslında bu prefix sözdizimidir)
      2. Dize için tırnak karakterleri
    • Bu, sıradan bir dil için uygun değildir
    • Lisp'in C sözdizimi uygulamasında da aynı sorunlar görüldü (aşırı kaçış karakteri kullanımı, belirli bir token'ın başlangıç ve bitişini ayırmak için boşluk karakterine gerek duymak)
  • Racket makro sistemine sahip olsa da runtime'da dinamik değildir ve pre-processing kullanır

Cognition tanıtımı

  • Matthew Hinton ile birlikte aylarca üzerinde çalıştığımız bir proje
  • Tamamen postfix kullanarak bilinen en genel sözdizimi sistemlerinden biri oluşturmayı hedefliyor
  • Sözdizimi, tokenleştirme ve ayrıştırma hakkında geçmiş bilgi gerekebilir ama anlaşılır kılmaya çalışıyoruz
  • Repo: https://github.com/metacrank/cognition

Baremetal Cognition

  • Baremetal Cognition Brainfuck ile benzer ama ciddi metaprogramlama olanağı sunuyor
  • Bootstrap kod örneği:
ldfgldftgldf dfiff1 crank f
  • Boşluk ve yeni satır önemlidir
      1. satırda df sonrası bir boşluk bulunur
      1. satırda bir boşluk karakteri bulunur
  • Bu sayede delimiter (sınırlayıcı) ve ignore (yoksayma) adlı iki yeni kavram tanıtılabilir

Tokenization

  • Delimiter (Sınırlayıcı), tokenizer'ın token'ın başlangıcını ve sonunu ayırmasını sağlar
  • Tek karakterli tokenizer listesi açıktır; Cognition içinde okunup değiştirilebilir
  • Ignored character (Yoksayılan karakter), tüm read-eval-print döngüsünün ilk adımında tokenizer tarafından tamamen göz ardı edilir
    • Yani token toplama başlamadan önce ayarlanmış yoksayılmış karakter kümesi atlanır
  • Varsayılan olarak her karakter bir delimiter'dir ve yoksayılan karakter yoktur
  • Delimiter ve ignored listeleri için verilen karakterlerde blacklist/whitelist geçişi yapılabilir (hem kısa hem de pratik kullanım için)

Falias

  • Falias, yığına konulduğunda çalışan kelimelerin listesidir
  • Tüm Falias'lar yığının üstündeki öğeyi çalıştırır (Stem'deki eval ile eşdeğerdir)
  • f varsayılan Falias'tır; yığına konulmaz, doğrudan yığının üstündeki d çalıştırılır
  • d, delimiter listesini kelimenin string değeriyle değiştirir (yani sadece l karakterini delimiter dışı bırakır)
  • Temel ortamda özel Falias'lar dışında hiçbir kelime çalıştırılmaz

Delimiter notları

  • Delimiter'larda ilginç bir kural var
    • Tokenleştirme döngüsünde bir karakter ignore edilmediyse, delimiter karakteri mevcut token'ın parçası olarak dahil edilir ve devam eder
    • Bu, singlet ile tersidir (singlet, token içinde kendisini dahil edip atlar; böylece token toplama sona erer)
  • Kara liste durumu da ayarlanabilir
    • Delimiter, singlet ve ignored listeleri blacklist/whitelist yapılabilir
    • Varsayılan olarak kara listeye alınmış delimiter ile beyaz listeye alınmış singlet veya ignored karakter yoktur
  • Diğer tüm karakterler, delimiter veya singlet kurallarıyla döngü durana kadar mevcut token'a eklenir

Bootstrap kodu devam ediyor

ldf
  • l delimiter olmayan karakter yapılır
gldftgldfdtgldf  dfiff1 crank f
  • d bir delimiter olduğu için gl yığına konur, Falias f çağrılarak gl delimiter olmayan karakter yapılır
  • tgl yığına konur ve df ile delimiter olmayan karakter olur
  • dtgl yığına konur ve \ndf ile yeni satır (\n) tek delimiter olmayan karakter olur (yeni satır gerçek kodda yer alır)
  • Delimiter kuralı gereği boşluk karakteri ve \n yığına konur (3. satırda boşluk bulunur)
  • Başka bir \ \n kelimesi tokenleştirilir
  • Mevcut yığın şu şekildedir (alttan üste): 3. dtgl 2. [boşluk karakteri]\n
    1. [boşluk karakteri]\n
  • df, \ \ni delimiter olmayan karakter olarak ayarlar
  • if, \ \ni ignored karakter olarak ayarlar (tokenleme başlangıcında ignore edilir)
  • f, dtglyi çalıştırarak dflagi toggle eder; bu, delimiter blacklist/whitelist ayrımını saklar
  • Şimdi delimiter olmayan her şey delimiter olurken, delimiter'ların tümü delimiter olmayan karakter olur
  • Son olarak, boşluk ve yeni satır token delimiter'ı olur ve token başlangıcında ignore edilir
  • Sonra 1 tokenleştirilip yığına konur; ardından crank kelimesi tokenleştirilir ve f tarafından çalıştırılır (1 tokeni burada sayı olarak ele alınsa da Cognition'da her şey bir kelimedir)
  • Bootstrap dizisi tamam! crankin ne yaptığı bir sonraki bölümde açıklanıyor

Bootstrap özeti

  • Cognition ile tokenize etme yaklaşımını programatik olarak, dinamik biçimde değiştirmek mümkün
    • Başka dillerde mümkün değildir
    • Farklı bir dil için tokenizer'ı Cognition içinde programlayıp istediğiniz gibi tokenize edebilirsiniz
  • Postfix kullanıldığı ve look-ahead yapılmadığı için mümkün olur
    • Bir ifadeyi değerlendirmeden önce bir veya daha fazla token ayrıştırılması gerekmez
  • Falias sayesinde prefix kelimeler veya temel kelime çalıştırmadan da kelime çalıştırılabilir

Crank

  • metacrank sistemi, yığında tokenleri çalıştırmanın varsayılan yöntemini ayarlamaya izin verir
  • crank kelimesi bir sayı argümanı alır; stack'e n adet kelime konduğunda üst öğeyi çalıştırır
  • Örnek kod (crank 1 ayarlanmış halde):
5 crank 2
crank 2 crank 
1 crank unglue swap quote prepose def
  • crank 1 ortamında token değerlendirmesi sırasında f kullanımı devre dışı bırakılabilir
    • Tokenleştirilmiş 1 token başına 1 token evaluate edilir
    • Sonuç olarak, satır sonu ve boşlukla ayrılmış sözdizimi programlandığı için kodu sezgisel okuyabilirsiniz
  • Kod, değerlendirme için 5 den başlar (builtin olmadığı için kendisiyle değerlendirilir)
  • crank, 5 token yığına her konduğunda çalıştırılacak biçimde ayarlanır
  • 2crank, 2, crank ve 1 yığına konulur (crank 5 ile ayarlandığı için crank bir builtin olsa da execute edilmez): 4. 2crank 3. 2 2. crank
    1. 1
  • crank beşinci kelime olduğu için execute edilir (crank 1 ayarıyla)
  • unglue, builtin olup yığının en üstündeki kelimenin değerini alır (1 tarafından kullanılana göre)
    • Yani crank builtiniyle bağlantılı function pointer alınır
  • Yığın şu şekildedir: 3. 2crank 2. 2
    1. [CLIB]
    • CLIB, crank builtiniyle bağlantılı bir function pointer'dır
  • swap çalıştırılır: 3. 2crank 2. [CLIB]
    1. 2
  • quote (yığının üstündeki öğeyi alıntılayan builtin) çalıştırılır: 3. 2crank 2. [CLIB]
    1. [2]
  • prepose (stem'deki composea benzer, ancak VMACRO denilen yere öne yerleştirilir) çalıştırılır: 2. 2crank
    1. ([2] [CLIB])
  • def çağrılır
    • 2 yığını ekleyip crank builtini'ne işaret eden fonksiyon pointerını çağıran 2crank kelime tanımı yapılır
  • VMACRO'nun ne olduğu, Cognition yığını ile Stem yığını arasındaki farkın açıklanması gerekir

Stem ile farklar

  • Stem yığınında kelimeler doğrudan yığına konulabilir
  • Cognition'da kelime evaluate edilmeden bir container içine konur ve oradan yığına atılır
    • Stem'de compose gibi bir kelime, bir kelimeyle (veya tek kelime içeren bir container ile) farklı bir container içinde çalışır
    • Bu durum Cognition API'sını daha tutarlı hale getirir
  • cd gibi kelimeler de bu kavramı kullanır

Makro

  • Stem quote ile Cognition container arasındaki bir başka fark
  • Makro değerlendirmesinde, makronun içindeki her şey evaluate edilir ve crank ignore edilir
  • Kelimeye bağlandığında, o kelime değerlendirilirken

1 yorum

 
GN⁺ 2024-05-03
Hacker News yorumları

Birkaç temel görüşü özetlemek gerekirse:

  • Belgenin giriş kısmında Cognition projesiyle ilgili açıklama çok geç geliyor. Okuyucunun zamanını verimli kullanmak için en kritik bilgileri önce vermek daha iyi olur.
  • Racket’in okuyucu (reader) katmanı ayarı gibi, sözdizimini genişletirken bile birlikte çalışabilirliği koruyan başka yaklaşımlar zaten mevcut. Cognition’ın yaklaşımının temelde gerçekten "daha iyi" olup olmadığı net değil.
  • Common Lisp’te de reader macro, macro ve compiler macro ile sözdizimi tamamen değiştirilebilir. Metaprogramlama, sözdizimden çok anlambilimi ele almayı hedeflemeli.
  • Cognition’ın çalışma zamanında sözdizimsel yapıları tanımlayıp yeniden tanımlayarak gerektiğinde bunlara girip çıkabilmesi çok güçlü ve ilginç; gerçek anlamda "düşünen" bir makine yaratma olasılığını açıyor.
  • Sözdizimi bir yapı sağladığı için, sözdiziminin kendisini ortadan kaldırmak paradoksal olur. Fazla minimal bir sözdizim tersine okunabilirliği ve anlaşılabilirliği azaltabilir.
  • Belgenin anlatım üslubu biraz uzun ve alaycı olduğu için okumak zor olabiliyor. Ancak konu derinliğine sahip.