2 puan yazan GN⁺ 2024-11-09 | 1 yorum | WhatsApp'ta paylaş
  • sqleibniz, SQLite lehçesindeki SQL’in sözdizimini, tablo/sütun/fonksiyon varlığını ve çalışma zamanı koşullarını kontrol etmeyi amaçlayan bir statik analiz aracıdır; bu yüzden tokenleştirme ve parsing temel adımlar hâline gelir
  • Rust’ın macro_rules! yapısı, AST düğümü struct’larını, Node trait implementasyonlarını ve Go tarzı tablo tabanlı testleri tekrar olmadan oluşturmaya yardımcı olur
  • matches! ve match desenleri kullanıldığında SQLite sayı literalleri, tanımlayıcılar, semboller ve EXPLAIN QUERY PLAN gibi gramer dallanmaları koda yakın biçimde aktarılabilir
  • Option’ın is_some_and, map, map_or metodları ve ? operatörü, girdi ve token akışı işlemede değer varlığını, dönüşümü, varsayılan değerleri ve hata yayılımını kısa ve net hâle getirir
  • Rust iterator’ları sayı literallerinden _ kaldırma, blob içindeki onaltılık karakterleri doğrulama ve hata konumu hesaplama için kullanılır; bu da tokenleştirme ve parsing kodunu daha okunabilir kılar

sqleibnizin analiz akışı

  • sqleibniz, SQLite lehçesini hedefleyerek geliştirilmekte olan bir SQL analiz aracıdır
  • SQL girdisi üzerinde sözdizimi denetimi, tablo/sütun/fonksiyon varlığı kontrolü ve yerleşik SQLite çalışma zamanı ile birlikte koşul doğrulaması yapmayı amaçlar
  • Hata mesajlarının bağlam ve açıklama sunması, ayrıca belirli tanıların yok sayılabilmesine olanak vermesi hedeflenir
  • Analiz akışı sözcüksel analiz/tokenleştirme ile başlar; SQLite belgelerine göre SQL parsing’i ve sonuç yapısının analiziyle devam eder
  • Statik analiz kısmı tamamlandıktan sonra SQL için bir LSP sunucusu yazılması da planlanmaktadır

Makrolarla AST düğümlerindeki tekrarı kaldırma

  • sqleibnizin AST düğümleri Token içeren struct’lardır ve tüm düğümlerin Node trait’ini implemente etmesi gerekir
  • Node trait’i std::fmt::Debug’ı süper trait olarak kullanır; böylece yalnızca Debug’ı sağlayan tipler Node’u implemente edebilir
  • Her düğüm için struct tanımını ve fn token(&self) -> &Token implementasyonunu tekrar etmemek adına node! makrosu bunları üretir
    • Düğüm adı ident metavariable olarak alınır
    • Belge dizgesi literal metavariable olarak alınır
    • Ek alanlar $($field_name:ident:$field_type:ty),* biçiminde tekrar edilerek işlenir
  • Literal düğümü yalnızca token alanına sahiptir; Explain düğümü ise ek olarak child: Option<Box<dyn Node>> alanını taşır
  • Dokümantasyon yorumları, /// yerine #[doc = $documentation] biçiminde makro argümanı olarak derleyiciye aktarılır

Go tarzı tablo tabanlı testleri Rust makrolarıyla uygulama

  • Go’daki tablo tabanlı testlerde olduğu gibi, girdi case’leri dizisi üzerinde dönüp her case’in bağımsız test olarak çalıştırıldığı yapı Rust makrolarıyla yeniden oluşturulur
  • Lexer testleri test_group_pass_assert! ve test_group_fail! makrolarını kullanır
    • Başarılı testlerde girdi Lexer’a verilir ve Lexer.run() sonucundaki token tipi listesi beklenen değerle karşılaştırılır
    • Başarısız testlerde sonuç token vektörünün boş olduğu ve Lexer.errors içinde en az bir hata bulunduğu kontrol edilir
    • cargo test çalıştırıldığında her case ayrı bir test fonksiyonu gibi ok veya fail geri bildirimi üretir
  • Parser testleri de aynı yapıyı izler; ancak lexer çalıştıktan sonra Parser başlatılır ve parse() sonucu denetlenir
    • EXPLAIN VACUUM; ve EXPLAIN QUERY PLAN VACUUM; başarılı case’lerdir
    • EXPLAIN; ve EXPLAIN QUERY PLAN; başarısız case’lerdir
    • Başarısız case’ler, SQLite sql-stmt gramerine göre EXPLAIN sonrasında bir ifade gerektiği koşulunu doğrular

macro_rules!ın rahatsız edici yanları

  • macro_rules! içinde rust-analyzer desteği sınırlıdır
    • Gerçek IntelliSense yoktur
    • Tanıma gitme yoktur
    • Literaller ve dil yapısı imzaları için hover yoktur
  • cargo fmt, macro_rules! içini ve makro çağrı yerlerini biçimlendirmez veya girintilemez
  • treesitter ve chroma, macro_rules! sözdizimi renklendirmesinde zaman zaman zorlanır
  • Makro dokümantasyonu da görece yetersizdir

Karakter eşleştirmede öne çıkan matches! ve match

  • Lexer’daki karakter karşılaştırmaları diğer işlemlerin temelini oluşturur; Rust’ın matches! makrosu ve match desenleri bu bölümü kısa ve anlaşılır kılar
  • SQLite sayı tespiti matches! ile yazılır
    • +, -
    • _
    • .
    • a..=f, A..=F
    • 0..=9
  • Tanımlayıcı tespiti de matches!(c, 'a'..='z' | 'A'..='Z' | '_' | '0'..='9') biçiminde ifade edilir
  • Lexer’ın ana döngüsü mevcut karakteri match ile dallandırır
    • Boşluk karakterleri atlanır
    • *, ;, ,, % gibi karakterler karşılık gelen token’ı üretir
    • Bilinmeyen sembol işleme örneği atlanmış ve panic!("whoops") ile gösterilmiştir

Token eşleştirme ile SQL gramer yapısını işleme

  • Lexer, karakter akışını konum bilgisi ve tip bilgisi taşıyan Token struct akışına dönüştürür; parser ise bunu tüketerek AST oluşturur
  • Type enum’u Keyword, Ident, Number, String, Blob, Boolean, ParamName, Param, Dot, Asteriks, Semicolon, Percent, Comma, Eof gibi değerleri içerir
  • sql_stmt_prefix, SQLite belgelerindeki EXPLAIN ifadesini işleyen parser fonksiyonudur
    • Mevcut token Type::Keyword(Keyword::EXPLAIN) ise bir Explain düğümü oluşturur ve EXPLAIN token’ını tüketir
    • Sonraki token QUERY ise QUERY ve PLAN art arda tüketilir
    • Ardından gerçek SQL ifadesi child olarak parse edilir
    • EXPLAIN değilse normal sql_stmt işleme çağrılır
  • literal_value, string, sayı, blob ve boolean ile birlikte NULL, CURRENT_TIME, CURRENT_DATE, CURRENT_TIMESTAMP gibi anahtar sözcük literallerini Literal düğümüne dönüştürür

Hata gösterimi ve Option kullanımı

  • Lexer ve parser, SQL ifadesi sonundaki noktalı virgül eksikliği gibi durumları kullanıcıya hata olarak gösterir
  • Rust’ın ? operatörü hata işleme ve yayma için kullanılır
  • Option::is_some_and, sonraki karakterin veya mevcut karakterin var olup koşulu sağladığını kontrol ederken kullanılır
    • self.source.get(self.pos + 1).is_some_and(...)
    • self.source.get(self.pos).is_some_and(...)
  • Option::map, Vec<u8> girdisinde sonraki baytı char’a çevirmek için kullanılır
  • Option::map_or, mevcut token veya sonraki token varsa yalnızca o durumda tip karşılaştırması yapıp yoksa false döndürecek şekilde kullanılır

Iterator’larla sayı ve blob işleme

  • SQLite sayı parsing’i _ kullanımına izin verir; ancak Rust sayı parsing’i _ kabul etmediği için lexer, _ karakterlerini de tüketir ve parsing öncesinde kaldırır
  • Bu işlem bir iterator zinciriyle yazılır
    • Bayt dilimi alınır
    • Her bayt char’a dönüştürülür
    • _ olmayan karakterler filtrelenir
    • String olarak toplanır
  • Bu durumda unwrap_or_default() kullanılır; ancak boş string sayı olarak geçerli olmadığından parser zaten başarısız olur
  • Go’da aynı işlem için karakter listesini dolaşmak, strings.Builder’a bayt yazmak ve ardından yeniden string oluşturmak gerekir
  • SQLite blob, x'<hex>' biçiminde onaltılık veriye izin verdiğinden, string’deki her karakter chars().enumerate() ile dolaşılarak is_ascii_hexdigit() olup olmadığı kontrol edilir
    • enumerate, hatalı karakterin konum bilgisini hata gösterimi için elde etmekte kullanılır
    • Geçersiz onaltılık karakterle karşılaşılırsa hata oluşturulur ve işlem durdurulur

1 yorum

 
GN⁺ 2024-11-09
Hacker News yorumları
  • İki ay önceye kadar ben de yazarla aynı düşünüyordum ama Rust’ın katı sınırı olan borrow checker ile sürekli çarpıştım
    Cebirsel veri türleri, örneğin enum ve pattern matching, gerçekten harikaydı ama borrow checker ve düşük seviyeli bellek kaygıları yüzünden projenin asıl konusu olan programlama dili sorunlarından çok borrow checker ile boğuşmaya zaman harcadım
    Bu yüzden tokenization ve parsing idare ederdi ama interpreter ve type checking acı verici hale geldi; daha uygun bir dil ararken F#, Zig/C ve Go’yu değerlendirdikten sonra OCaml’i keşfettim
    Sözdizimi daha dostça bir Haskell gibi ve lifetimesız Rust gibi göründüğü için ikna oldum; ilk Rust derleyicisi de OCaml ile yazılmıştı ve programlama dilleri alanında iyi biliniyor
    Hâlâ öğreniyorum, bu yüzden adil bir değerlendirme yapmak zor ama şu ana kadar tam olarak aradığım şeye en yakın olan bu

    • Go konusu açılınca nedense hep sinirleniyorum
      Pratik ve hızlı, gerçekten düşük seviye değil, derlemesi de hızlı ve hepsinden önemlisi çok popüler olduğu için tüm kütüphaneler var; o yüzden kullanmam gerekiyormuş gibi geliyor
      Ama dilin kendisinden neredeyse irrasyonel biçimde nefret ediyorum ve her yönüyle çirkin geliyor
      2009’da C tarafındaki insanlar tarafından yapılmış bir dil; o tarihin ölçütlerine göre bile son 20 yılda programlama dili tasarımında ilginç olan şeylerden habersizmiş gibi duruyor
      2009’daki PHP bile Go’dan daha modern ve daha iyi tasarlanmış bir dildi ve Go’nun o zamandan beri de kayda değer biçimde ilerlemediği hissinden kurtulamıyorum
    • Rust’ta abstract syntax tree ile uğraşırken kilit noktanın string gibi şeyleri ağaçta saklamamak olduğunu düşünüyorum
      Bunun yerine cheap clone ve interning yapabilen, örneğin bir static string kütüphanesi gibi yapılar kullanmak; metin konumları için de yalnızca indeks tutmak daha iyi
      Mümkünse referans saklamaktan kesinlikle kaçınmak gerekir
      Cheap/free clone yapılabilen şeylerden daha fazlasını tuttukça borrow checker ile daha az kavga edersiniz ve gerekirse clone’a geçebilirsiniz
      Asıl interpreter tarafında ise arena benzeri yaklaşımlarla bellek yönetimine yardımcı olan kütüphaneler epey faydalı
      Çok uzmanlaşmış bir alan ama hem performans hem kullanılabilirlik sağlıyor ve Ruffle gibi projeler de bu deseni sık kullanıyor
      Yine de OCaml ve Haskell, yerleşik reference counting ve garbage collection sayesinde bunları “bedavaya” sağlıyor; buna rağmen Rust ile çok hızlı gitme fikri yine de hoşuma gidiyor
    • Son bir yılda Go’yu çok kullandım ama parser yazımı için kullanacağımı sanmıyorum
      Go, modernleştirilmiş C’ye daha yakın ve sunduğu model çok basit
      C#’tan gelince bu sadelik yüzünden öğrenmesi tersine daha zordu; kavramsal yükünün düşük olması avantajı ve ayrıntıcılık ödününü kabul edebileceğiniz küçük, odaklı uygulamalara iyi uyuyor
      Tavsiye edecek olsam F# derdim; modern C# da bence fena değil
      Microsoft işin içinde diye tamamen şeytani büyük şirketlerin yaptığı hiçbir şeyi kullanmayacağınız bir dünyada yaşamak isterseniz işiniz zorlaşır
      Java, Go, Python, TypeScript/JavaScript ve Swift de buna giriyor; o zaman da neredeyse hiç seçenek kalmıyor
      Yaklaşık bir yıl OCaml kullandıktan sonra ne düşündüğünü merak ediyorum
      Haskell türevi diller ilginç ama Haskell’in kendisinde öğrenme eğrisi karşılığında aldığım fayda benim için iyi değildi; Rust da benzer
      C#’ta tür sistemini derinlemesine kurcalayıp ustalaştım ama Rust’ta o kadar derine inmeye vaktim yok
    • Go istemci tarafında da çok kullanılıyor ve mobilde de go-mobile sayesinde desteği epey iyi
      Elbette ikili dosya boyutuna ve bellek kullanımına 10–20MB kadar ekliyor ama günümüz ölçülerinde bu neredeyse hiçbir şey
      Örneğin Tailscale, mobil ve masaüstü uygulamalarında çapraz platform WireGuard katmanı olarak Go kullanıyor gibi görünüyor ve iyi çalışıyor gibi
      Go ile native UI yazmazdım ama düşük seviyeli işler için mükemmel
      TinyGo, mikrodenetleyiciler veya WebAssembly için Go yazmayı da mümkün kılıyor; desteklenmeyen çok şey var ama standart kütüphanenin önemli bir kısmı kullanılabiliyor
    • Go’ya server-side language demezdim
      Örneğin Go derleyicisinin kendisi de Go ile yazılmış
      Cross-compilation ve görece küçük binary’ler sayesinde dağıtım çok kolay
      Ama sözdizimsel şekerin az olduğu doğru ve işlevsel tarzdaki pattern matching için pek uygun değil
  • Parsing’e yaklaşım olarak biraz tuhaf görünüyor ve yazarın Rust’a ve onun temelindeki programlama dili kavramlarına çok da aşina olmadığı izlenimini veriyor
    Birkaç noktaya bakarsak, AST cebirsel veri türleriyle tanımlansa çok daha basit olurdu gibi geliyor
    sqlite sözdiziminin aniden yeni düğümler ekleyerek karmaşık bir encoding gerektirecek şekilde genişlemesi pek olası görünmüyor
    Mevcut encoding, nesne yönelimliye alışkın ama cebirsel veri türlerine alışkın olmayan birinin aklına gelecek türden görünüyor
    “Makrolar çoğu dilde farklı çalışır ama ana sebep kod tekrarını azaltmak ve yinelemeyi düşürmektir” sözü, fonksiyonlar gibi tüm soyutlama mekanizmaları için de söylenebilir
    Makroları tanımlayan ayırt edici özellik, compile time’da çalışmalarıdır
    Parser’ı temiz biçimde yapılandırmanın yollarını görmek için parser combinator araştırmaları iyi bir başlangıç noktası olabilir

    • Yazar hiçbir zaman deneyimli bir programcı olduğunu iddia etmedi
      Blog başlığı da “Why I love ...”; işaret ettiğiniz noktalar geçerli görünse de deneyim eksikliğini özellikle vurgulamak çok gerekli görünmüyor
      Birinin programlamayı sevmesi iyi bir şey ve deneyim zamanla gelecektir
    • Blog yazısının bağlamında yapmak istediği şey struct tanımları üretmek
      Bunu fonksiyonlarla yapamazsınız
  • Forsyth-Edwards satranç notasyonu için küçük bir ayrıştırıcı [0] yazmış biri olarak, sadelik ve okunabilirlik açısından Haskell'in ezici biçimde üstün olduğunu düşünüyorum
    Neredeyse BNF gibi okunuyor ve teknik törensellik neredeyse hiç yok; bu yüzden gerçekten ayrıştırmak istediğiniz dilbilgisine odaklanabiliyorsunuz
    [0] https://github.com/ryandv/chesskell/blob/master/src/Chess/Fa...
    [1] https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notati...

    • Haskell, parser combinator kullanımı açısından kesinlikle en iyisi, ama ortaya çıkan sonucu işlemek için yine de Haskell ile yaşamaya devam etmeniz gerekiyor
    • Bu, sadece saf Haskell kullanmak değil de bir parser combinator kütüphanesi kullanmak değil mi?
      Rust'ta benzer bir yaklaşımın mümkün olmaması için açık bir neden olup olmadığını merak ediyorum
      Örneğin winnow [1] yeterince bildirime dayalı bir stil sunuyor gibi görünüyor ve Rust'ta başka birçok parser combinator kütüphanesi de var
      [1]: https://docs.rs/winnow/latest/winnow/
    • FEN'i harika bir ayrıştırma örneği olarak görmüyorum
      Çünkü tek döngülü basit bir işleve indirgenebiliyor
      Birkaç gün önce deneysel bir quad-bitboard uygulaması için bir FEN “parser”ı yazdım ve neredeyse kendi kendine yazıldı
      Bu arada Hackage'daki chessIO'nun yazarıyım
  • Rust ile bir eBPF disassembler ve yarım kalmış bir emülatör yazdım; ayrıştırma türü işler yapmak için Rust oldukça keyifli bir dildi
    Ama yazarın vaka çalışmasının altıda birine bile gelmeden makrolara ihtiyaç duymaya başlaması, sanki kendi argümanını zayıflatıyor gibi
    Makrolar tam anlamıyla kod üretimi değil, ama dil içinde işi deyimsel biçimde yaptığınız hissini de çok güçlü vermiyor
    Bunu kötülemek için söylemiyorum; Rust'ın burada gerçekten oldukça güçlü olduğunu düşünüyorum

    • Rust'ta sonsuz dilbilgileri nasıl tanımlanabiliyor?
      Örneğin bağlamdan bağımsız kural S ::= abc|aabbcc|aaabbbccc|..., a^Nb^Nc^N ifadesini etkin biçimde ayrıştırabiliyor ve bu da bağlama duyarlı dilbilgisine bir örnek
      Bu basit bir örnek ama pratikte de benzer şeyler görülebiliyor; bir dilin operatör tanımlarına izin vermesi buna bir örnek
      Rust bunu nasıl ele alıyor?
    • Mümkünse eBPF disassembler bağlantısını paylaşmanız harika olurdu
      Kulağa hoş geliyor
  • Ragel ile parser ve lexer yazmış, ayrıca Go, Java, C++, C kullanmış biri olarak, belli bir düzeyde boilerplate üretici elinizde varsa saf C bile yazarın anlattığı Rust kodu kadar iyi olabiliyor
    Sadelik yüzünden hatta daha iyi bile olabilir
    Örneğin bir JSON parser için gereken kodun büyük kısmı aşağı yukarı şu kadar
    https://github.com/gritzko/librdx/blob/master/JSON.lex
    Aslında o eBNF sadece lexer'ı üretiyor ve parser kısmı da pek etkileyici değil; 120 satır ve epey tekrarlı
    https://github.com/gritzko/librdx/blob/master/JSON.c
    Sonuçta parser altyapısı, yalnızca eBNF ile parser üretebildiğiniz noktaya kadar evriliyor ve bence doyum noktası da bu

    • O tekrarcılığı avantaj değil dezavantaj olarak görmek mümkün
      Rust'ın cebirsel veri tipleri, üretilen sözdizimi ağaçlarını ele almayı çok daha kolay hale getiriyor diye düşünüyorum
      Yine de biraz kod üretimi ya da makro sihriyle C'nin oldukça kullanışlı hale gelebileceğine katılıyorum
    • Ragel'i gerçekten seviyorum
      Ama burada kod
      https://github.com/gritzko/librdx/blob/master/JSON.lex
      [ karakterini geçerli JSON olarak kabul etmiyor mu?
      delimiter = OpenObject | CloseObject | OpenArray | CloseArray | Comma | Colon;
      primitive = Number | String | Literal;
      JSON = ws* ( primitive? ( ws* delimiter ws* primitive? )* ) ws*;
      Root = JSON;
      JSON'da tek bir delimiter seçip geri kalan her şeyi 0 kez seçmek mümkün gibi görünüyor
      Ben genelde RFC ile başlarım
      https://datatracker.ietf.org/doc/html/rfc4627#autoid-3
      JSON'un Ragel ile uygulanıp uygulanamayacağından da emin değilim
      Bildiğim kadarıyla Ragel yalnızca düzenli dilleri işleyebiliyor, JSON ise bağlamdan bağımsız bir dil
    • Cloudbleed'in nedeni bir C/Ragel hatasıydı ve Cloudflare'in Rust'a geçme sebeplerinden biri de buydu
      https://en.wikipedia.org/wiki/Cloudbleed
  • Bununla ilgili olarak Rob Pike’ın Go üzerine yaptığı leksik tarama konuşmasını seviyorum
    eğitici ve zarif bir yaklaşım
    https://www.youtube.com/watch?v=HxaD_trXwRE

    • O konuşma harika, ama sonrasında Go’nun aslında bu tekniği kullanmadığına dair bir tartışma gördüğümü hatırlıyorum
      nedeni goroutine zamanlama ek yükü ya da verimsiz bellek ayırma kalıpları gibi bir şeydi sanırım
      bulabildiğim en iyi tartışma [1]
      verimli lexer ve parser yazımı üzerine bir diğer harika konuşma da Andrew Kelley’nin “Practical Data Oriented Design” [2] sunumu
      özetle, programın bellek kullanımını azaltırken onu önbellek dostu hale getirip iş hacmini artıran çeşitli stratejileri anlatıyor
      1: https://news.ycombinator.com/item?id=31649617
      2: https://www.youtube.com/watch?v=IroPQ150F6c
    • O konuşma, leksingin kendisinden çok, eşzamanlılığın doğal olarak akla geldiği bir problemde eşzamanlılığın nasıl ifade edildiğiyle ilgili gibi görünüyor
  • Benim için şaşırtıcı bir deneyim vardı
    üst düzey bir derleyici parser’ında kullandığım parser combinator kütüphanesini aynen no-std ortamında kullanabildim, mikrodenetleyici için derledim ve gömülü ortamda yüksek performanslı bir protokol parser’ı olarak dağıtabildim
    aynı kütüphaneyi olduğu gibi kullandım
    fark, String kullanımını azaltıp daha fazla &'static str kullanmak kadardı
    bu yüzden derleyicilerle uğraşmak, gömülü protokol parser’ları yazma becerisine oldukça iyi aktarılıyor

  • Rust ile tüm AST parser’ını yazarken zorlandığım nokta, somut AST tiplerinin hiyerarşisini upcasting ve downcasting dahil ifade etmekti
    bir yolunu buldum ama PhantomData gibi tuhaf tip oyunları ve makrolar gerekti
    burada da epey ağır makro kullanımı gerekmiş gibi görünüyor
    bu konuda önceki çalışmaların nasıl göründüğünü merak ediyorum

    • Rust’ın cebirsel veri tipleri ve pattern matching sözdizimi iyi görünüyor
      tabii upcasting/downcasting noktasına gelene kadar
      Rust deneyimim yeterli değil, bunun için iyi bir yaklaşım var mı bilmiyorum
      belki dynamic trait’ler işe yarıyordur
    • Eğer açık kaynaksa, herkese açık bir depo var mı diye merak ediyorum
  • Bu tür makro kodları nasıl debug ediyorsunuz ya da kod tabanına yeni gelen biri bunu nasıl anlıyor
    node! makrosunun kullanım yerlerine ve makro tanımına baksam bile gerçekte hangi kodun üretildiğini anlamanın zor olacağını düşünüyorum
    örnekleri çalıştırıp hangi type hint’lerin çıktığına bakmak mı gerekiyor, IDE’de üstüne gelince genişletilmiş sürümü görüyor musunuz, yoksa emin olmak için derlenmiş koda mı bakmak gerekiyor merak ediyorum
    Sadece JS/TS ile çalıştığım için makrolarla uğraşmıyorum; o yüzden bu iş akışını merak ediyorum

    • $ cargo expand çalıştırırsanız ortaya çıkan kodu görebilirsiniz
      Rust aslında birkaç dile daha yakın; “vanilla” Rust, declarative macro’lar ve procedural macro’ların her birinin biraz farklı yetenekleri ve lehçeleri var
      zamanla her biriyle çalışmaya alışıyorsunuz
      unit test’ler de makro değişikliklerinin etkisini anlamak için iyi bir deney alanı oluyor
    • VSCode vb. içinde kullanılan Rust LSP’si rust-analyzer, declarative macro’ları ve procedural macro’ları özyinelemeli olarak genişletebiliyor
      çok kötü değil, ama bir kod tabanında procedural macro ne kadar azsa o kadar iyi
      declarative macro’lar biraz daha anlaşılır oluyor, bakım ve test de çok daha kolay
      diğer dillerdeki opak kod üretimi için de benzer hissediyorum
  • sqlite sözdizimini parse etmek için şimdiden bol şans
    birkaç yıl önce iş için sqlite’ın oldukça küçük bir alt kümesinin parser’ını yazmam gerekmişti
    sqlite’ı gerçekten çok seviyorum ve her zaman ilham kaynağı olmuştur
    demiryolu diyagramları inanılmaz faydalı
    https://www.sqlite.org/syntaxdiagrams.html
    lemon parser generator’ın yeterince takdir edilmediğini düşünüyorum
    https://sqlite.org/src/doc/trunk/doc/lemon.html
    dil seçimi açısından, cebirsel veri tipleri olan herhangi bir dil bunun için iyi iş çıkarır
    TypeScript bile bu amaç için harika olabilir
    bir zamanlar Rust ile elle parser yazmaya giriş niteliğinde küçük bir yazı da yazmıştım
    https://www.nhatcher.com/post/a-rustic-invitation-to-parsing...