Gewissermaßen zehntausend Teile; alle schwarz.
Vor etwa vier Jahren habe ich angefangen, eine C-ähnliche Sprache zu bauen, weil ich eine Frage zu Programmiersprachen hatte, die keiner ohne dieses Experiment hätte beantworten können.
Tatsächlich hat man dabei relativ schnell gesehen, dass man, wenn man aus den Fehlern vergangener Jahrzehnte lernt, relativ einfach zu einer kompakten und spaßig zu benutzenden Sprache kommt.
Da ich wegen Corona gerade ohnehin nicht viele gute Hobbies habe und ich schon immer wissen wollte, ob ich eine effiziente Sprache ohne GC-Crap bauen kann, in der ein Array trotzdem eine Collection wie jede andere ist, habe ich das Ganze in den letzten Wochen soweit fertig gebaut, dass man es langsam benutzen kann.
Momentan implementierte Features sind:
- Typen sind Werte, wie alle anderen Werte (ob das geht war die initiale Frage)
- Statische Typ- und Namensbindung
- Statisches und dynamisches Typsystem, z.B. Vergleich von Typen
- Typen sind in der Sprache definiert, z.B. int oder ITable (ich hatte einfach keine Lust, das in C++ zu machen)
- Templates wie in C++, nur dass sie komplett übersetzt ins binary gehen und man sie von anderen Paketen aus instantiieren kann und es kein fucking ODR gibt (ODR wird vom compiler durchgesetzt, identische Instanzen werden automatisch zusammengefügt)
- Vererbung auf primitiven Typen und Structs (type), z.B. pointer
- Dualität zwischen virtuellen Funktionen und ihren Realisierungen (beispiel). Das ist etwas, das ich an so ziemlich jeder verbreiten Programmiersprache wirklich vermisse
- Method placement realizations. Weil es einfach nicht dasselbe ist, wenn man denselben Code kopiert und hofft, dass die Linkeroptimierung das zusammenfaltet.
- Forward-flow-single-level-return-type-Inferenz (so ziemlich jedes Beispiel bisher) Die ganzen vars und vals, aber nicht nur das
- Klassen und Interfaces (beispiel). Interfaces dürfen von Klassen erben und Felder definieren, d.h. deren Existenz versprechen. Und das ganze ohne die teils irren Konsequenzen von Mehrfachvererbung in C++
- Der VTable pointer wird gesetzt bevor der Konstruktor aufgerufen wird (ffs, warum ist das in C++ anders?)
- Elaboration order checks (beispiel) Im Prinzip kommen die aus Ada und dienen der Erkennung von zyklischen Abhängigkeiten bei Definitionen von quasi allem. Dadurch kann man sich auch die lästigen Vorwärtsdeklarationen sparen
- Es gibt nur Expressions; das Resultat eines Blocks ist der letzte Ausdruck (beispiel)
- In die Sprache eingebaute Tests, für die flexiblere Regeln gelten, als für Funktionen und die Sachen prüfen können, die man mit JUnit nicht prüfen kann (beispiel)
- Apply/Update z.B. Arrays. Ein bisschen wie der Call-Operator in C++; letztlich ist der Trick aus Scala
- Block Parameter, z.B. bool (und ja, da kommt exakt derselbe Code raus, wie wenn man das in C++ verwendet)
- Generalized Binder (beispiel) hier sind cond und die foralls Funktionen, die der Compiler auflöst und inlined, damit man ohne viel tippen trotzdem effizienten Code bekommt
- Operator overloading und overload resolution (beispiel)
- Eine Paketverwaltung
Wenn man das mal ausprobieren möchte:
Hier gibt's einen Installer, die Standardbibliothek und jede Menge Tests, die derzeit als Sprachspezifikation dienen.
Falls jemand Interesse hat, kann ich auch mal ein paar Tutorials schreiben.