2 puan yazan GN⁺ 2024-11-03 | 1 yorum | WhatsApp'ta paylaş
  • llamafile için yeni bir sözdizimi vurgulayıcı oluşturmak üzere 42 dili ele alınca, basit leksing işlemlerinde bile dillere özgü istisnalar ve eski gramerlerin sıkça gizlendiği ortaya çıktı
  • Gerçekleme, anahtar sözcük aramasını hızlandırmak için C++ ve GNU gperf kullanıyor; string, yorum ve anahtar sözcük odaklı işleme ise for döngüsü ve switch tabanlı sonlu durum makinesiyle çözülüyor
  • C'nin trigraph'ları, JavaScript'in u2028 ve u2029 satır sonlandırıcıları, Shell·Perl·Ruby'nin heredoc'u, Kotlin·Scala·TypeScript·Swift'in string interpolasyonu, yalnızca leksingle ele alınması zor örnekler olarak tekrar tekrar öne çıkıyor
  • Kod satırı sayısına göre FORTH 125 satır, Ruby ise 1042 satıra kadar çıkıyor; Ruby'de << operatörü, heredoc ve backquote çakıştığı için parse etmeden vurgulama özellikle zor
  • llamafile'ın yeni vurgulayıcısı Windows 10'da Meta LLaMA 3.2 3B Instruct ile gösterildi; macOS, Linux, FreeBSD ve NetBSD'de de çalışabiliyor, bu da onu sözdizimi vurgulaması olmayan ollama'dan ayırıyor

llamafile vurgulayıcısını yapma motivasyonu

  • llamafile için yeni bir sözdizimi vurgulayıcı oluşturmak amacıyla bir ay boyunca 42 programlama dili incelendi
  • Desteklenenler: Ada, Assembly, BASIC, C, C#, C++, COBOL, CSS, D, FORTH, FORTRAN, Go, Haskell, HTML, Java, JavaScript, Julia, JSON, Kotlin, ld, LISP, Lua, m4, Make, Markdown, MATLAB, Pascal, Perl, PHP, Python, R, Ruby, Rust, Scala, Shell, SQL, Swift, Tcl, TeX, TXT, TypeScript, Zig
  • Bu liste TIOBE Index'in büyük kısmını kapsıyor; ancak Scratch metin yerine blok kullandığı için vurgulama kapsamına alınmadı

Gerçekleme yöntemi: gperf ve sonlu durum makinesi

  • Temel bir sözdizimi vurgulayıcıda en büyük darboğaz, bir token'ın anahtar sözcük olup olmadığını belirlerken yinelenen string karşılaştırmalarının artması
  • C++ ve GNU gperf kullanılarak mükemmel bir hash tablosu üretildi
    • Örnek gperf girdisi, true, false, null gibi Java sabitlerini anahtar sözcük olarak tanımlıyor
    • gperf tarafından üretilen C dosyası, çakışmasız arama için yalnızca tek bir karakteri dikkate alan bir hash fonksiyonu oluşturabiliyor
  • C vurgulaması, yaklaşık 4.000 anahtar sözcük tanımlansa bile gperf sayesinde 35 MB/s hızında çalışabiliyor
  • Kalan işlemlerin çoğu için sonlu durum makinesi yeterli oluyor
    • flex, bison, ragel olmadan, yalnızca for döngüsü ve switch ile temel bir vurgulayıcı yapılabiliyor
    • String'lere, yorumlara ve anahtar sözcüklere odaklanınca işin çoğu leksing düzeyinde çözülebiliyor
    • C fonksiyon adları gibi öğeleri de vurgulamak istenirse gerçek parse gerekebilir
  • highlight_ada.cpp örnek gerçekleme olarak kullanılıyor

llamafile demosu ve çalışma ortamı

  • Yeni vurgulayıcı ve chatbot arayüzü, llamafile'ın kullanılabilirliğini artırdı; ollama'da olmayan sözdizimi vurgulaması ayırt edici bir özellik oldu
  • Demo, Windows 10'da Meta LLaMA 3.2 3B Instruct modeliyle çalıştırıldı
  • Bu llamafile, macOS, Linux, FreeBSD ve NetBSD'de de çalışabiliyor
  • gemma 27b it gibi açık ağırlıklı modellerin kalitesi arttıkça Claude kullanma motivasyonu azalıyor

C: Basit bir dil izleniminden farklı leksing istisnaları

  • C, basit olduğu yönündeki izlenime rağmen leksik öğeler açısından oldukça sıra dışı
  • trigraph, #, [, \\, ^, {, |, }, ~ gibi karakterleri ??=, ??(, ??/, ??), ??', ??<, ??!, ??>, ??- ile değiştirebiliyor
    • C23 standardından çıkarıldı, ancak eski yazılımlar nedeniyle derleyiciler bunu desteklemeyi sürdürecek gibi görünüyor
    • İyi bir sözdizimi vurgulayıcı bu grameri de işlemeli
  • C'nin universal character yapısı, int \\uFEB2 = 1; örneğinde olduğu gibi ASCII kaynak kod içinde Unicode tanımlayıcılar yazılmasına izin veriyor
    • GCC, standart komitesinin izin verdiği belirli Unicode plane'ler dışında hata veriyor
  • C'de tek satırlık yorumlar, satır sonundaki ters eğik çizgi kullanılırsa birden fazla satıra yayılabiliyor
    • Perl, Ruby, Shell gibi kaynak kodda backslash escape destekleyen diller bile bu C tarzı davranışı desteklemiyor
    • Tcl ve GNU Make'in bu davranışı desteklediği anlaşılıyor
    • Emacs ve Pygments bunu zaman zaman yanlış işlerken, Vim'in backslash işlemesini sürekli doğru yaptığı görülüyor
  • C'de bir de null preprocessor directive var
    • İlk dönem v6 kaynak kodundaki .c dosyaları sık sık tek satırlık # ile başlıyor
    • Bu bugün de geçerli bir kod ve cc -C -E ile yorumların korunması istense bile önişlemcinin belirli yorumları kaldırmasını sağlamak için kullanılabiliyor

Yorum sözdizimi: Haskell ve D

  • C'de çok satırlı yorumların içine yeniden çok satırlı yorum yerleştirilemez
  • Haskell, {- ... {- ... -} ... -} biçiminde iç içe yorumları destekliyor
  • D, C'nin //, /* ... */ yorumlarını aynen alırken ek olarak /+ ... +/ biçiminde özyinelemeli yorum sözdizimi getiriyor
  • D'nin leksik gramer dokümanı biçimsel ve ayrıntılı; gerçekleme için gerekli bilgileri iyi sağlıyor
    • D lexical syntax document, hex string ve heredoc string gibi ayrıntıları ele alıyor
    • D'de "...", backtick string, r"...", q"...", x"..." gibi çeşitli string biçimleri var

Tcl ve JavaScript: tırnak işaretleri ve görünmez satır sonlandırıcılar

  • Tcl'de tanımlayıcıların içinde tırnak işareti bulunabiliyor
    • puts a"b, a"b çıktısını verir
    • Değişken adlarına da tırnak konabilir, ancak başvururken $a"b yerine ${a"b} yazımı kullanılmalı
  • JavaScript, regular expression için yerleşik bir leksik gramer içeriyor
    • /[/]/g örneğinde karakter kümesi köşeli parantezleri içindeki / için escape gerekmiyor
    • Sadece kapanış slash'ını tarayan yaklaşım, minified kodda yanlış sonuç verebilir
  • ECMAScript, u2028 LINE SEPARATOR ve u2029 PARAGRAPH SEPARATOR karakterlerini satır sonlandırıcı olarak tanımlıyor
    • Bu karakterler fiilen \n gibi davranıyor
    • Trojan Source karakterleri oldukları için Emacs'te sırasıyla , olarak görünecek şekilde ayarlanmış
    • Birçok yazılım bu karakterleri tanımıyor ve soru işareti olarak render edebiliyor
    • D dışında bunu yapan başka bir dil bilinmediği belirtiliyor
  • Bu özellik sayesinde SectorLISP içinde C ve JavaScript polyglot oluşturmak mümkün oldu
    • lisp.js tarayıcıda çalışıyor, GCC ile derlenip yerelde de çalıştırılabiliyor
    • llamafile bu tür kodları doğru vurguluyor; diğer vurgulayıcılarda ise aynı davranışın bulunamadığı söyleniyor

Shell ve heredoc'un köşe durumları

  • Shell'de heredoc, cat <<EOF ... EOF biçiminde çok satırlı string yazmak için kullanılıyor
  • Tırnaklı heredoc sözdizimi cat <<'END', değişken ikamesini devre dışı bırakıyor
  • heredoc işaretleyicisi boş string yapılırsa heredoc bir sonraki boş satırda biter
    • Örnek program, hello ve world çıktısını iki satır halinde veriyor
  • Shell, Ruby ve Perl gibi heredoc destekleyen dillerde aynı satırda birden fazla heredoc bulunabiliyor
  • Shell'de Tcl'deki gibi # her zaman yorum başlatmıyor
    • ${x#hi-} içinde #, öneki kaldırmak için kullanılıyor; örnek there çıktısını veriyor

String interpolasyonu: durum yığını gerektiren diller

  • Kotlin string'leri " ile başlasa bile string interpolasyonu nedeniyle { noktasında başka bir duruma geçebiliyor
  • TypeScript, Swift, Kotlin ve Scala; string içinde gerçek kod gömmeyi hedefleyen güçlü string interpolasyonu desteği sunuyor
  • Kotlin, Scala ve TypeScript'i vurgulamak için süslü parantezlerin sayılması ve bir parser durum yığını tutulması gerekiyor
    • TypeScript nispeten daha basit; sonlu durum makinesine birkaç ek durum eklemek yetiyor
    • Kotlin ve Scala, hem çift tırnak hem de üçlü tırnak sözdiziminde interpolasyonu desteklediğinden yalnızca string leksingi için yaklaşık 13 bağımsız durum gerekti
    • Swift, "\\(var)" interpolasyon sözdizimini ve üçlü tırnağı destekliyor; gerçekleme ise 10 durum gerektirdi

Swift, C#, FORTH: string'i çevreleme biçimlerindeki farklar

  • Swift, "...", """...""", /regex/ string'lerini istenen sayıda # ile sarmalama sözdizimini destekliyor
    • İki taraftaki # sayısı eşleşmeli
    • Bu yöntem, string içinde tırnak ya da regex sınırı bulundurma sorununu çözüyor
  • C#, Python'un üçlü tırnaklı çok satırlı string'ine benziyor, ancak başlangıç ve bitişte daha fazla sayıda tırnağın eşleştirilmesine izin veriyor
    • Soldaki tırnak sayısı sağdaki kapanış koşulunu belirliyor
    • Rastgele sayıda tırnak kabul etmenin, klasik Python üçlü tırnağına göre geçerlilik kurallarını azaltıp sonlu durum makinesiyle daha basit çözümleme sağladığı düşünülüyor
  • FORTH, her şeyi boşluk sınırlarına göre token'laştırıyor
    • String başlatma sözdizimi c" de tek bir token
    • c" hello world", diğer dillerdeki "hello world" ile aynı anlama geliyor

FORTRAN ve COBOL: sabit sütun kuralları

  • llamafile, FORTRAN ve COBOL programcıları emekli olduktan sonra bile banka sistemlerinin bakımına yardımcı olabilecek bir kullanım senaryosu olarak sunuluyor
  • Air-gapped ortamlarda kontrol edilebilir bir yapay zeka olan Gemma 27b'den COBOL ve FORTRAN kodu istenebileceği belirtiliyor
  • FORTRAN'ın sabit sütun kuralları şöyle
      1. sütunda *, c, C varsa satır yorum sayılıyor
      1. sütunda boşluk dışı bir karakter varsa 80 karakteri aşan satır devam ettirilebiliyor
    • 1–5. sütunlarda sayı varsa bu bir etiket oluyor
  • COBOL kuralları ise şöyle
      1. sütunda * varsa yorum
      1. sütunda - varsa 80 karakteri aşan satırın devamı
    • 1–6. sütunlar satır numarasını içeriyor

Zig ve Lua: çok satırlı string'e farklı çözümler

  • Zig, iki ters eğik çizgiyle başlayan bir çok satırlı string sözdizimine sahip
    • Bu sözdizimi, Python üçlü tırnaklı string'lerde textwrap.dedent() çağırma ihtiyacını ortadan kaldırıyor
    • Dezavantaj olarak noktalı virgülün hoş görünmediği düşünülüyor
    • Go, Scala, Python gibi noktalı virgül gerektirmeyen diller için değerlendirilebilecek bir string sözdizimi önerisi olarak sunuluyor
  • Lua'nın çok satırlı string'i [[...]] tabanlı ve köşeli parantezler arasına istenen sayıda = konabiliyor
    • [==[ ... ]==] biçiminde başlangıç ve bitişteki = sayısı eşleştiriliyor
    • Aynı yöntem yorumlar için de kullanılabiliyor
    • Hem --[[ ... ]] hem de --[==[ ... ]==] biçimleri mümkün

Assembly: lehçeler ve önişlemcilerin birleşimi

  • Assembly, çok sayıda lehçeye ayrıldığı için vurgulaması zor dillerden biri
  • llamafile, AT&T, nasm ve benzeri çeşitli sözdizimlerini makul biçimde işlemeyi hedefliyor
  • Anahtar sözcüklerin, satırdaki ilk tanımlayıcı olup iki nokta üst üste sonrasında gelmeyen öğeler olarak ele alınması çoğu assembly kodunu yeterince düzgün gösteriyor
  • Yorum sözdizimi de basit değil
    • Orijinal UNIX yorumları için yalnızca tek bir / yeterliydi
    • GNU as bunu hâlâ yalnızca satır başı konumunda destekliyor
    • Clang fixed comment'i desteklemediği için açık kaynak kodda pratik kullanımı zor deniyor
  • Orijinal UNIX assembler, karakter sabitlerinde kapanış tırnağı kullanmıyordu
    • 'x, x karakterinin 0x78 değerini ifade ediyor
    • GNU as bunu hâlâ destekliyor, LLVM ise desteklemiyor
    • Eski kodlarda bu sözdizimi bulunduğundan iyi bir vurgulayıcı bunu da desteklemeli
  • GNU assembler, alıntılanmış tanımlayıcılara izin verdiği için sembollerde neredeyse her karakter kullanılabiliyor
  • Assembly sıklıkla C önişlemcisi ya da m4 ile birlikte kullanılıyor
    • dnl, m4_dnl, C ile başlayan satırların da yorum olarak ele alınması gerekiyor

Ada ve BASIC: küçük gramer farklarının leksingi sarsması

  • Ada'nın leksingi oldukça basit, ancak tek tırnak kullanımı sıra dışı
    • C'deki gibi 'x' karakter sabitleri bulunabiliyor
    • Foo'Size örneğinde olduğu gibi özellik başvurularında da tek tırnak kullanılıyor
    • Character'(')')'Image, bir karakter tanımlayıp ardından Image fonksiyonuyla string gösterimine dönüştürüyor
  • Commodore BASIC örneği, sözdizimi vurgulamasına dair birçok varsayımı bozuyor
    • String'lerde satır sonunda kapanış tırnağı atlanabiliyor
    • Değişken adlarında $ gibi sigil'ler bulunuyor
    • goto gibi anahtar sözcükler tanımlayıcıların içinde bile agresif biçimde leksing ediliyor
  • Visual BASIC'te #1/1/2024# gibi bir tarih sabiti sözdizimi var
  • Visual BASIC'te ayrıca #If DEBUG Then, #Else, #End If gibi önişlemci yönergeleri de bulunduğundan leksing zorlaşıyor

Perl: Shell ile programlama dili arasındaki karmaşıklık

  • Perl, Shell ile programlama dili arasında konumlandığı için iki tarafın karmaşıklığını da miras alıyor
  • Regular expression'ı dilin birinci sınıf unsuru haline getirdi ve bunun etkisi Python gibi diğer dillere de yayıldı
  • Perl'in değiştirme sözdizimi s/hello/Perl/i, sed'e benziyor
    • / yerine herhangi bir noktalama işareti ayraç olarak kullanılabiliyor
    • s!hello!Perl!i, slash regex içinde yer aldığında kullanışlı
    • s{hello}{Perl}i gibi eşleşen karakterler kullanıldığında ek karakter gerekebiliyor
  • Perl'de string gibi vurgulanması gereken çok sayıda sihirli önek bulunuyor
    • m, s, y, qr, qw, qq, qx çeşitli ayraçlarla birlikte kullanılıyor
  • y/x/y/ ifadesini yanlışlıkla bölme işlemi saymamak için bağlamı görmek gerekiyor
    • Perl değişkenleri skaler $, dizi @, hash % gibi sigil'ler taşıdığı için tüm gramer parse edilmeden de ayrım yapmaya yardımcı oluyor
  • Perl'de kaynak kod içine man page için POD belgeleri gömme geleneği var
    • Satır başındaki =word POD belgesini başlatıyor, =cut ise bitiriyor

Ruby: en zor leksing hedefi

  • Ruby, önceki dillerin birleşimi gibi görünüyor ve gramerinin biçimsel olarak yeterince belgelenmediği düşünülüyor
  • Ruby kılavuzundaki syntax document, ayrıntı bakımından hafif bulunuyor
  • Ruby, backquote sözdizimini desteklerken aynı zamanda backquote'u yöntem adı gibi kullanabildiğinden, vurgulayıcının bunun string olup olmadığını ayırt etmesi zorlaşıyor
  • Ruby'de hem << operatörü hem de heredoc var
    • Gerçek kodda options[:includes] <<arg; true gibi biçimler bulunuyor
    • Bu, heredoc gibi görünebilir ve Emacs de bunu yanlış işliyor
  • puts "This is #{<<HERE.strip} evil" gibi string interpolasyonu içinde heredoc barındıran kodlar da geçerli
  • 42 dil arasında Ruby en büyük şok olarak anılıyor; Ruby'nin parse etmeden leksinginin mümkün olmayabileceği, parse edilse bile yorumlanabilirliğinden emin olunamayacağı düşünülüyor

Gerçekleme kod satırı sayısına göre karmaşıklık

  • Her dil vurgulayıcısının gerçekleme kod satırı sayısı baz alındığında en basit dil FORTH, en karmaşığı Ruby
  • En kısa gerçekleme highlight_forth.cpp ile 125 satır
  • Görece kısa gerçeklemeler arasında m4 132 satır, Ada 149 satır, LISP 160 satır, MATLAB 166 satır, COBOL 186 satır, BASIC 199 satır, FORTRAN 200 satır yer alıyor
  • Orta ölçekli gerçeklemeler arasında JavaScript 337 satır, TypeScript 371 satır, Kotlin 387 satır, Scala 387 satır, Assembly 447 satır, C 449 satır, Swift 455 satır, D 521 satır bulunuyor
  • En uzun gerçeklemeler Shell 570 satır, Perl 583 satır ve highlight_ruby.cpp 1042 satır

1 yorum

 
GN⁺ 2024-11-03
Hacker News görüşleri
  • C üçlü karakter dizileri (trigraph) içinde en sevdiğim şey, do_action() ??!??! handle_error() gibi bir biçim
    Özel bir hata işleme söz dizimi gibi görünüyor ama aslında ??!??!, || mantıksal OR'a dönüşüyor ve kısa devre değerlendirme kuralı sayesinde do_action() sıfır olmayan bir değer döndürdüğünde handle_error() çalıştırılıyor; bu da hoşuma gidiyor

  • Keyifle okudum ama bu beni daha çok Lisp bakış açısına yaklaştırdı
    Anladığım kadarıyla burada söylenen, söz diziminin bir dilde o kadar da önemli olmadığı, yardımcı olmaktan çok engel olmaya yatkın olduğu ve bu yüzden başka şeylere odaklanabilmek için olabildiğince basit ve tekdüze olması gerektiği
    Yine de Lisp'te yapısal düzenlemeyi öğrenmek şu ana kadar yardım olmaktan çok engel gibi geldi; belki bir gün karşılığını verir

    • Basit söz dizimi bilgisayar için uygun olabilir ama söz dizimi öncelikle insanlar okuyup yazsın diye tasarlanır
      Lisp'teki gibi fazla basitleştirince söz dizimi tartışması sadece anlambilim meselesine ötelenmiş oluyor; yani sadece katman değişiyor
      Karmaşık söz diziminin, karmaşık anlambilime sahip basit söz diziminden çok daha okunur ve yazılır olduğunu düşünüyorum. Söz dizimi hataları hızlı geri bildirim verirken anlambilim hataları çalıştırma anına kadar gizli kalabiliyor
    • Lisp'te lexer'ı yeniden programlamayı sağlayan reader macro'lar var ve görünen yapıyı sözdizim ağacına dönüştürme süreci de makrolarla ele alınabiliyor
      Mesela https://pyret.org/ gibi örnekler var. Bu yüzden Lisp'in gerçekten basit ya da zorunlu olarak tekdüze olduğunu söylemek zor
    • Yapısal düzenlemenin engel gibi gelmesine biraz şaşırdım; tek tavsiyem parinfer kullanmak ve sadece slurp, barf, raise adlı üç komutun tuş kısayollarını öğrenmek
      Yalnızca bu dördüyle bile paredit'in avantajlarının yaklaşık %95'inden, karmaşıklık olmadan yararlanabilirsiniz; geri kalan incelikleri ise alıştıktan sonra öğrenebilirsiniz
  • Yazıda “kim C'yi saniyede 35MB hızla sözdizimi vurgulamak ister bilmiyorum ama artık mümkün” deniyordu; hızlı olduğu doğru ama tcc, çok eski bilgisayarlarda bile C'yi saniyede 29MB ikili koda derliyor: https://bellard.org/tcc/#speed
    Daha da hızlı yapılabilir belki ama muhtemelen gerek yok

  • Yazarın gözden kaçırdığı birkaç nokta var
    Yalnızca TypeScript, Swift, Kotlin ve Scala, string interpolation içine gerçek kod koyma uç noktasına gitmiş değil; C#, Python, JavaScript, Ruby, Shell ve Make de benzer destek sunuyor
    Tcl'de kod ile veri arasındaki ayrım belirsiz olduğu için { } aslında alışılmadık bir string sınırlayıcısı gibi davranıyor ve xyzzy {#hello world} içindeki #hello world ifadesinin yorum mu yoksa string mi olduğu, değerlendirme anından önce bilinemez
    PostgreSQL'de kullanışlı bir dollar-quoted string yapısı var; https://www.postgresql.org/docs/current/sql-syntax-lexical.h... örneğinde olduğu gibi 'Dianne''s horse', $$Dianne's horse$$, $SomeTag$Dianne's horse$SomeTag$ aynı anlama geliyor

    • Perl'de de mümkün
      "I have $foo $bar's: @{[$bar x $foo]}" gibi bir string, I have 5 x's: xxxxx çıktısını üretebilir
      @{[...]} söz dizimi, Perl'in yalnızca skaler değil dizi interpolasyonu da yapabilmesinden yararlanıyor; içteki [...] bir dizi referansı oluşturuyor, dıştaki @{...} ise bunun referansını çözüyor. Perl yorumlayıcısı, iç taraftaki ifadede keyfi koda izin veriyor
    • Buna “string içine kod giriyor” demektense bunu özel bir string birleştirme söz dizimi olarak görmek daha doğru olabilir
      Böyle düşününce "foo { toUpper("bar { x + y } bar") } foo" gibi iç içe kullanım mümkün oluyor ve + string birleştirme ise bu, özünde "foo " + toUpper("bar " + (x + y) + " bar") + " foo" ile aynı şey oluyor
      Gerçekte böyle çalışan bir dil var mı bilmiyorum
    • Ruby bunu iyice uç noktaya taşıyor
      Ruby'yi seviyorum ama string interpolation ile heredoc'u karıştıran puts "This is #{< gibi geçerli Ruby kodlarını savunmak zor
      Ayrıca Ruby'de boşluk da bir alıntılama karakteri olabilir. Sol işlenenin olmadığı bağlamlarda % bir quoted string başlatır ve sonraki karakter alıntılama türünü belirler; bu yüzden % hello , hello içeren bir string olur. %(this is a string) ya da %{this is a string} güzel ama boşluk kullanımıyla vahşi doğada hiç karşılaşmadım, irb bile bunu düzgün işleyemiyor, kaldırılsa iyi olurdu
    • Scala'nın string interpolation özelliği, pattern matching hedefi olarak da kullanılabiliyor
      val s"${a} + ${b}" = "1 + 2" yazarsanız a, 1; b ise 2 olur
    • C# interpolated string'lerinin güzel yanı, tembel değerlendirilmesi
      Eskiden sıcak döngüler içinde log.trace($"Entering iteration {i} for customer {c.ID} [{c.ShortName}]"); gibi kodlar, logger gerçekten çıkış vermese bile her seferinde string.Concat çağırdığı için pek çok logger kendi interpolation mekanizmasını uyguluyordu
      C#'ta DefaultInterpolatedStringHandler ya da özel handler desenini alan overload'lar tanımlanabiliyor; bu overload'lar öncelikli seçiliyor, böylece gerçekten log yazılması gerekip gerekmediği önce kontrol edilip string oluşturma geciktirilebiliyor
  • Burada değinilmeyen, çoğu sözdizimi vurgulayıcıyı bozan bir başka dilbilgisi tuhaflığı daha var
    Java'da Unicode escape dizileri yalnızca string içinde değil, her yerde gelebilir
    Örneğin class Foo\u007b} geçerli bir sınıftır ve // yorumunun içindeki \u000A gerçek bir satır sonu olarak işlenebildiği için assert örneği de beklendiği gibi çalışmaz

    • Bunun düzgün biçimde sözdizimi vurgulanamamasını bir güvenlik sorunu olarak da görmek mümkün
      Unicode escape kullanarak blok yorumu kapatabildiğiniz için, Java kaynak dosyasındaki yorumların içine kötü amaçlı kod saklamak istiyorsanız yorum içinde Unicode escape bloğu bulunması için sadece makul bir bahane üretmeniz yeterli olur
      Bu özelliği bilmeyen geliştiriciler bunun yorum satırı olduğunu düşünüp kolayca gözden kaçırabilir
    • Java'da buna hiç rastlamadım; bunun gerçekten faydalı bir kullanım alanı var mı?
  • “Ruby, sözdizimini anlama girişimlerinin hepsinden kaçan bir dil” deniyor ama rastgele yeniden programlanabilen bir lexere sahip TeX açısından bakınca bu sevimli kalıyor

    • Lisp reader macro'ları ile de lexer programlanabilir
  • “Her C programcısı çok satırlı yorumların içine çok satırlı yorum konulamayacağını bilir” deniyor ama Standard ML programcıları için bu beklenmedik bir kısıt olabilir
    (* (* Nested (**) *) comment *) gibi iç içe yorumlar geçerlidir ve ardından gelen val _ = print "hello, world\n" da normal şekilde çalışır
    C'nin ortaya çıktığında ifade gücü yüksek bir dil sayıldığını düşününce, iç içe yorumların dile dahil edilmemiş olması ilginç

    • Bu sözde komik olan üç şey var
      ML'de tek satırlık yorum da yoktu; bu da benzer ölçüde şaşırtıcı bir kısıt. C'yi “ifade gücü yüksek” diye anmaksa ilk kez duyduğum bir şey ama 1972'de assembly ile karşılaştırılınca öyle görünmüş olabilir
      Bir de yorum sözdiziminin dilin ifade gücüyle ne ilgisi var bilmiyorum; tanım gereği hiçbir ilgisi olmadığını düşünürüm
    • İç içe yorumları lex etmek için bir stack ya da en azından iç içelik seviyesi sayacı gerekir
      Geleneksel olarak sözcüksel analiz, regex benzeri sonlu durum otomatlarının kullanıldığı bir alan sayıldığı için, böyle bir özellik lexing kapsamının dışında görülüyordu
    • C'de de #if 0 kullanarak yorumları iç içe geçirmenin bir yolu var
      #if 0 ... #if 0 ... #endif ... #endif şeklinde sarabilirsiniz
    • Pascal da örnekteki gibi iç içe yorum sözdizimini her zaman destekledi
    • Bu yalnızca Standard ML için değil, genel olarak ML için de geçerli
  • joe'nun sözdizimi vurgulama uygulamasını yeniden kullanılabilir bir biçimde görmek isterdim
    https://joe-editor.sf.net/ biçimi, Python f-string'lerini düzgün vurgulayabilecek kadar güçlü
    İlgili belgeler ve örnekler https://github.com/cmur2/joe-syntax/blob/joe-4.4/misc/HowItW..., https://gist.github.com/irdc/6188f11b1e699d615ce2520f03f1d0d... adreslerinde bulunuyor

    • joe'nun DFA tabanlı parsing yaklaşımını temel alarak birkaç lexer ve parser yazdım
      Durumlar ve geçiş dilbilgisi, standart araçlara göre çok daha anlaşılırdı
      Dezavantajı kural kümesinin uzayıp gitmesi ve ideal biçimde yapılandırmanın biraz zor olması; ama koddan tüm üretim kurallarını çıkarsamayı kolaylaştırdığı için bunu bir avantaj olarak da görüyorum
    • İlginç olan şu ki Python f-string'lerinin 3.12'de dilbilgisi değişti, bu yüzden vurgulamanın da sürüme göre değişmesi gerekiyor
  • Yazar belli ki TeX sözdizimi vurgulamayı denememiş
    Bu ruh sağlığı açısından iyi bir şey olabilir, çünkü TeX'te yorumlamadan tam sözdizimi vurgulama yapmak genelde imkansızdır
    Sadece parse etmek de yetmez; çünkü her karakterin ne işe yaradığını yeniden tanımlayabilirsiniz, hatta “artık K {, C ise }” gibi şeyler yapmak bile mümkündür
    Gerçekten de arXiv makaleleri arasında bu lanetli özelliği kullananlar var

    • https://github.com/Mozilla-Ocho/llamafile/blob/main/llamafil... dosyasını yazdım ve sabit diskte bulduğum .tex dosyalarında bozulmadan oldukça iyi vurgulama yapıyor
      Hedef, gerçek dünya kullanımının %99,9 kadarını kapsamak. Bu durumda bir LLM'in üretebileceği şeylerin de büyük kısmını kapsaması muhtemel
      Acayip sözdizimleri de string ya da yorumların dosya sonuna kadar sürüp kaynak kodun geri kalanını kaplamadığı sürece genelde büyük sorun çıkarmıyor
    • \makeatletter ifadesinin “bir şeyi at işaretine dönüştürmek” değil, parse sırasında @ karakterini harf gibi ele almak anlamına geldiğini öğrenince inanmak zor olmuştu
    • Common Lisp'te de benzer biçimde readtable yeniden tanımlanabilir. Ama bunun arXiv'de daha az suistimal edildiği düşünülebilir
  • Vim gibi iyi bir sözdizimi renklendirme motoru yazmanın kolay olmadığını düşünüyorum
    Sözdizimi renklendirme, belirli biçimlerde iç içe geçmiş içeriklere farklı kurallar uygulayan bir bağlam işleme gerektirir
    Vim sözdizimi vurgulayıcısı, match ve region olmak üzere iki tür öğe tanımlamaya izin verir; match basit bir sözcüksel kuraldır, region ise başlangıç, bitiş ve ara kısımları ayrı ayrı eşleyen ifadelere sahiptir
    Öğeler, yalnızca belirli bir alanın içinde olduklarında etkin olacak şekilde tanımlanabilir ve kapsama ilişkileri de birbirleri arasında belirtilebilir
    Bu temel anlamsal yapının üzerine, çeşitli özel durumlar için ek özellikler bindirilmiştir. Justine için bile bunu mülakatta anında yapmak zor olurdu; en azından bir gecelik ödevlik iş gibi görünüyor

    • Zor ele alınan örneklerden biri, TXR dilinin içine gömülü TXR Lisp
      man sayfasının HTML dönüşüm çıktısını alıp bunu TXR kılavuzunun HTML'ine dönüştüren genman betiği burada: https://www.kylheku.com/cgit/txr/tree/genman.txr
      Beyaz görünen kısımlar literal şablonlardır; Lisp kodu ise @(do ...) gibi yönergelerin içine girer. TXR anahtar sözcükleri mor, TXR Lisp anahtar sözcükleri yeşil görünür; aynı sözcük bile bağlama göre değişir
      Quasi-string'ler de iç içe geçmiş dilbilgileri barındırabilir; bunların içinde yeniden gömülü kod ya da quasi-string'ler bulunabilir. txr.vim ve tl.vim sözdizimi tanım dosyalarının ikisi de https://www.kylheku.com/cgit/txr/tree/genvim.txr üzerinden üretilir
    • Naif bir bakışla, sözdizimi vurgulayıcıyı düzgün yapmak için önce AST olarak parse edip ardından token'ları dolaşarak düğüm tipine göre rengi güncellemek gerektiğini düşünmüştüm
      Eğer böyle yapılmıyorsa muhtemelen verimlilik nedeniyledir. Örneğin, ekranda görünen kısmı vurgulamak yerine tüm dosyayı parse etmek gerekebilir ve bu da maliyeti artırabilir
    • Justine'in bunu mülakatta yapamayacağına dair iddiaya girmezdim ;)