MetaWare High C derleyicisinin kaybolan dil uzantıları (2023)
(duriansoftware.com)- 1989’da FM TOWNS için çıkan High C Compiler, DOS ortamını desteklemenin ötesinde, dönemin C derleyicileri için nadir sayılacak birçok kullanıcı odaklı dil özelliği içeriyordu
- Phar Lap’in DOS extender’ı ile birleşerek, 16 bit MS-DOS ortamında 32 bit 80386’dan yararlanan geliştirme akışı içinde FM TOWNS’un 1st-party C derleyicisi oldu
- Sayısal literal alt çizgileri, etiketli argümanlar, case aralıkları, iç içe fonksiyonlar, generator’lar gibi özellikler C/C++ standartlarına çok daha sonra girdi ya da hâlâ standartta yok
- İç içe fonksiyonlar, fonksiyon işaretçisi ile bağlam işaretçisini birlikte aktaran dışarı kaçmayan closure biçiminde “full function value” sağlayarak sıradan C fonksiyon işaretçilerinden daha ifade gücü yüksek bir yapı sunuyordu
- Generator’lar, iç içe fonksiyonların üzerinde sözdizimsel şeker olarak uygulanmıştı; çağıranın
fordöngüsü gövdesini iç içe bir fonksiyona çeviripyieldargümanı olarak geçirmek gibi basit bir yapıyla çalışıyordu
FM TOWNS ve High C’nin konumu
- FM TOWNS ile ilgili kitap yığınından çıkan 1980’ler C derleyici kılavuzu, beklenenden daha zengin dil uzantıları içeriyordu
- C ve onun soyundan gelen dilleri gerçek ortamlarda kullanmak için uzun süre üretici uzantılarına ihtiyaç vardı
- Günümüzde GCC, Clang, MSVC merkezli ortamlarda uzantılar daha çok platforma özgü işlemler veya düşük seviyeli ayrıntı kontrolü etrafında yoğunlaşma eğiliminde
- 1980’lerde ise daha küçük ve daha çok şirket benimsenme yarışı verdiğinden uzantılar da daha çeşitliydi
- Phar Lap, 16 bit MS-DOS ortamında 32 bit 80386 işlemcisinden yararlanmayı sağlayan erken dönem DOS extender’lardan birini yaptı
- MetaWare, Phar Lap’in isteğiyle High C Compiler’ı Phar Lap’in DOS extender SDK’sına port etti
- Fujitsu, 803386 tabanlı FM TOWNS platform işletim sistemine Phar Lap’in DOS extender’ını entegre etti ve High C bu platformun 1st-party C derleyicisi oldu
- FM TOWNS, ilk ANSI C standardı olan C89’un onaylanmasından hemen önce, 1989’da piyasaya çıktı
Standardın önüne geçen küçük kolaylıklar
-
Sayısal literal’larda alt çizgi ayırıcıları
- Uzun sayısal literal’ları okunabilir kılmak için sayıların içine alt çizgi ayırıcıları konabiliyordu
- C++, C++14’te
1'000'000gibi kesme işareti ayırıcılarını getirdi - C ise benzer bir özelliği ancak C23’te getirdi
-
Etiketli argümanlar
- Çok parametreli ya da
boolgibi çağrı noktasında anlamı kolay anlaşılmayan türleri sık kullanan fonksiyonlarda argüman adları belirtilebiliyordu - High C’nin etiketli argümanları Python’ın popüler özelliğine benzer çalışıyordu
- Argüman etiketi isteğe bağlıydı
- Etiket varsa
argumentName => valuesözdizimiyle argümanlar istenen sırada belirtilebiliyordu - Etiketsiz ve etiketli argümanlar karıştırılabiliyordu, ancak fonksiyonun tüm parametreleri için karşılık gelen bir argüman bulunmalıydı
- Standart C ve C++’ta bu özellik hâlâ yok
- Çok parametreli ya da
-
case aralıkları
- Pascal’daki
case low..highgibi, bir değer aralığını tek seferde eşleme özelliği sunuyordu - Standart C ve C++ bu özelliği benimsemedi
- Pascal’daki
İç içe fonksiyonlar ve full function value
- High C, Pascal gibi bir fonksiyonun içinde iç içe fonksiyonlar bildirmenize izin veriyordu
- Uygulama yöntemi, standart Pascal’dan ya da GCC’nin iç içe fonksiyon uzantısından daha eksiksiz bir biçime yakındı
- High C, iç içe fonksiyon bildiriminin yanı sıra full function value türleri de bildirebiliyordu
- Geleneksel C fonksiyon işaretçilerinden farklı olarak, fonksiyon işaretçisiyle birlikte bir bağlam işaretçisi de taşıyordu
- İç içe fonksiyonun yakaladığı bağlamın yeniden bulunmasını sağlıyordu
- Dış fonksiyon döndükten sonra ömrü devam etmeyen, dışarı kaçmayan bir closure’dı
- GCC’nin iç içe fonksiyon uzantısı, iç içe fonksiyonu normal bir fonksiyon işaretçisiyle referans edebilmek için çağrı yığınına çalıştırılabilir kod yazarak bağlam işaretçisini thunk ediyordu
- Bu yöntem ciddi güvenlik risklerine yol açtı ve birçok platformun bu özelliği tamamen devre dışı bırakmasına neden oldu
- High C’nin yerel fonksiyon referansları birinci sınıf değerler gibi kullanılabiliyordu, ancak ömürleri dış fonksiyonun dönüşünden sonrasına uzamıyordu
- İç içe fonksiyonlar ebeveyn fonksiyona
gotoda yapabiliyordu- Smalltalk blokları gibi, iç içe fonksiyonun dışına çıkmayı sağlayan yerel olmayan çıkış mümkündü
- Bununla kontrol akışı gibi davranan fonksiyonlar oluşturulabiliyordu
- Objective-C, 2009’da escaping closure olarak kullanılabilen blocks özelliğini aldı; C++ ise 2011’de lambdas’ı getirdi
- Bu iki özellikte de yerel olmayan çıkış yeteneği yoktu
- Standart C’de hâlâ resmi bir iç içe fonksiyon özelliği yok
Generator coroutine’leri
- MetaWare, bir bölümün tamamını ayıracak kadar generator özelliğini öne çıkarıyordu
- High C, 1989’da plain C içinde Python tarzı generator coroutine’lerini destekliyordu
- Generator fonksiyonu
void foo(Arg arguments) -> (Yield yields)sözdizimiyle bildiriliyordu- Fonksiyon içinde sihirli
yield(values...)fonksiyonu birden çok kez çağrılarak bir değer dizisi üretilebiliyordu - Çağıran taraf, üretilen değerleri
for variable... <- foo(arguments...) do { ... }biçimindeki yenifordöngüsü sözdizimiyle sırayla dolaşıyordu
- Fonksiyon içinde sihirli
- Bu uygulama iç içe fonksiyonlarla karmaşık biçimde birleştirilebiliyordu
- Generator içindeki iç içe fonksiyon, dış generator’ın
yielddavranışını yakalayabiliyordu - İç içe fonksiyon kendisini özyinelemeli çağırıp ağaçları veya özyinelemeli veri yapılarını dolaşırken her adımda
yieldedebiliyordu
- Generator içindeki iç içe fonksiyon, dış generator’ın
- Bu biçim, Python’da veya birçok ana akım generator coroutine dilinde uygulanması zor görünen bir yöntem gibi duruyor
Generator uygulama yöntemi ve standart dillerle farkı
- High C’nin generator’ları gelişmiş bir runtime olmadan iç içe fonksiyonlar üzerinde sözdizimsel şeker olarak çalışıyordu
void foo(Arg arguments) -> (Yield yields)biçimindeki generator bildirimi, sıradanvoid foo(void yield(Yield yields)!, Arg arguments)fonksiyon bildirimiyle eşdeğerdiyield, “full function value” türünde örtük bir parametreydi- Generator gövdesindeki
yield(values)çağrısı, bu örtük fonksiyon parametresini çağıran sıradan bir fonksiyon çağrısıydı
- Çağıran taraftaki
fordöngüsü gövdesi iç içe bir fonksiyona dönüştürülüyordu- Bu iç içe fonksiyon generator’ın
yieldargümanı olarak geçiriliyordu - Yapı basitti ama etkiliydi
- Bu iç içe fonksiyon generator’ın
- İç içe fonksiyonlar yerel olmayan çıkışı desteklediğinden,
fordöngüsü gövdesinin dışına çıkanbreak,continue,gotoda uygun döngü dışı konumagotoedecek şekilde çalışıyordu - Standart C’nin böyle bir özelliği entegre etmeye çalışması pek olası değil
- C++20, derleme zamanı coroutine dönüşümüne dayanan çok esnek ve karmaşık bir coroutine özelliği sunuyor
- Bununla generator uygulamak mümkün görünüyor
- Ancak ortaya çıkan sonucun yerel fonksiyonlarla bu kadar sezgisel biçimde birleşmesi pek olası görünmüyor
1 yorum
Hacker News yorumları
Neyse ki elimde İngilizce High C/C++ Language Reference'ın bir kopyası var
http://jdebp.uk./FGA/metaware-iterator-driven-for.html
http://jdebp.uk./Proposals/metaware-iterator-driven-for.html
breakveyareturnifadesinin nasıl derlendiğini merak ediyorum.yieldfonksiyonunun durum kodu döndürecek şekilde dönüştürülüp çağrı noktasında kontrol edildiği bir yöntem miydi?casearalığı, adlandırılmış argümanlar, iç içe fonksiyonlar, statik iç içe fonksiyonlar, jeneratöre benzer özelliklerÖrneğin
int a = 1_234_567;,case 5 .. case 6:,test(b:3, a:4);gibi biçimler mümkünStatik iç içe fonksiyonlar dış fonksiyonun frame değişkenlerine erişemediği için
Error: static function test.foo.plus cannot access variable i in frame of function test.foogibi bir hata veriyorJeneratöre benzer özellikler https://dlang.org/spec/statement.html#foreach_over_struct_an... adresinde var
Örneğin bellek içi bir önbellek servisi yapıyorsanız, önbellek öğelerinin kendisinin çöp toplayıcısı tarafından izlenmemesi daha iyi olabilir. Çöp toplayıcısı gerçek erişim desenlerini çoğu zaman bilmediği için engel olabilir. Ama o servisin diğer bileşenlerinin büyük kısmı için çöp toplayıcısı olması daha uygundur
Adlandırılmış argümanlar neden
test(a:4, b:3)biçiminde detest(.a=4, b.=3);biçiminde değil?C'de birinci sınıf türleri nasıl ele alabileceklerini de merak ediyorum
lcc-winC derleyicisi operatör aşırı yükleme, varsayılan fonksiyon argümanları ve fonksiyon aşırı yükleme ekledi. Belgelerde “generic functions” bölümüne bakılabilir [1]Plan 9 C derleyicisi de çeşitli dil uzantıları getirdi; bunlardan anonim struct/union gibi bazıları daha sonra C standardına girdi. Günümüzde GCC,
-fplan9-extensionsbayrağını kabul ediyor [2] ve fonksiyon çağrıları ile atamalarda struct pointer'ları anonim alanlara otomatik dönüştürme gibi oldukça yararlı özellikleri açabiliyor[1] https://lcc-win32.services.net/C-Tutorial.pdf
[2] https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html
Bunların daha geniş yayılıp dil standartlarını etkileyememiş olması üzücü. Bu kadar uzun zaman önce böyle özelliklerin var olması şaşırtıcı
Daha önce Hacker News'te de gündeme gelmişti: https://news.ycombinator.com/item?id=38938402
Bir yerde PDF kopyası var mıdır?
yieldiçeren for döngülerine sahipti [0]. Benzer dönemin Icon dili de benzer jeneratör özelliklerine sahipti [1] veyieldyerinesuspendkullanıyordu. Ada'da da (1983) buna benzer bir özellik olduğunu biliyorumYani bu tür dil özellikleri tamamen bilinmiyor değildi
[0] https://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TR-2...
[1] https://dl.acm.org/doi/pdf/10.1145/800055.802034
Sayıların içinde alt çizgi,
casearalıkları, adlandırılmış parametreler, iç içe fonksiyonlar, hatta tam teşekküllü fonksiyon değişkenlerini açıklıyorhttps://bitsavers.org/pdf/metaware/…
Dosyanın sonundan yaklaşık 50 sayfa gerideki Appendix A'ya bakın
Eskiden kod öğrenip kullandığım zamanlarda onları biraz şüpheli siteler üzerinden tanımıştım
Sistem programlama dillerinin de zengin bir tarihini keşfedersiniz. C ile Go'nun tasarım açısından, başka ekosistemlerde olup bitenleri ve geçmiş deneyimleri göz ardı etmeleri bakımından ne kadar benzer olduğunu da görürsünüz
Derleyici kılavuzu bağlantısı burada: https://winworldpc.com/product/metaware-high-c-cpp/33x
C kılavuzu PDF'inde 2007 telif hakkı ibaresi var
https://f.duriansoftware.com/@joe/113195961485703110
\nyerine¥nile bittiğini merak ediyorsanız, bu kod örnekleri muhtemelen Shift-JIS ile yazılmış. Shift-JIS'te ASCII'deki\konumunda¥bulunur[0] https://en.wikipedia.org/wiki/JIS_X_0201
Bu kullanım için EUC-JP daha iyidir. Çünkü bu sorun yoktur. Pascal'da
{ }yorumları yerine(* *)yorumları kullanılırsa Shift-JIS'te bu sorun yaşanmazBunun yerine, Shift-JIS'in temelini oluşturan JIS X 0201'in (https://en.m.wikipedia.org.org/wiki/JIS_X_0201) kullanılmış olması daha olası
C:\değilC:¥idiCall (Param_A => 1, Param_B => "Foo");biçiminde etiketler, rastgele tabanlı sayılarda alt çizgi kullanımı (X : Integer := 1_000;), iç içe alt programlar ve aralık tabanlı denetimler varO dönemde C'nin birçok başka dille karşılaştırıldığında inanılmaz derecede ilkel olduğunu sık sık unuttuğumuzu düşünüyorum
Japon yazım sistemi ya da kerning kuralları hakkında yeterince bilgim yok ama hem kanji hem Latin harfleri içeren orantılı bir yazı tipini alıp sabit genişlikli hücrelere zorla sığdırmışlar gibi görünüyor
Her hâlükârda kod örneklerinin, bende bulunan birçok kitaptaki gibi 8 punto olmaması güzel
try_fold()aklıma geldi (https://scribe.rip/@veedrac/rust-is-slow-and-i-am-the-cure-3...)Ama tam da bu yüzden bu uzantılar nispeten az bilindi ve onlarca yıl sonra modern C/C++ içinde yeniden keşfedilip yeniden icat edilmeleri gerekti