3 Punkte von GN⁺ 2024-10-25 | 1 Kommentare | Auf WhatsApp teilen
  • Pretty C ist eine neue Scripting-Sprache, die mit C kompatibel ist und C-Programmen Komfortfunktionen wie dynamische Typisierung, generische Iteration und Ressourcenverfolgung hinzufügt
  • Verwendet wird es als Header-only-Bibliothek, indem lediglich pretty.h eingebunden wird; es kann zusammen mit bestehenden C-Dateien und C-Bibliotheken genutzt werden
  • Zu den bereitgestellten Funktionen gehören Aliasse wie string, any und nil, Typinferenz mit var/let, generische Ausgabe mit print/println, generischer Vergleich mit equal sowie besser lesbare ternäre Makros
  • Um Wiederholungen und Speicherarbeit zu reduzieren, bietet es eine makrobasierte Syntax wie foreach, forthese, fortimes, forrange, new, vector, delete, with, lambda und try/catch
  • Einige Funktionen setzen GCC, Clang, C23+ oder C11+ voraus; zudem sind mit with gebundene Variablen vom Typ void *, sodass vor der Nutzung möglicherweise eine Typumwandlung nötig ist

Ziele und Nutzung von Pretty C

  • Pretty C ist eine mit C kompatible Scripting-Sprache, die C-Programmen dynamische Typisierung, generische Iteration, Ressourcenverfolgung und verschiedene Syntax-Erleichterungen hinzufügt
  • Die Abwärtskompatibilität zu bestehendem C und C-Bibliotheken bleibt erhalten
  • Inspiriert von Lua, Python, JavaScript und Lisp
  • Zur Installation kann das Repository geklont oder einfach nur die Datei pretty.h kopiert und verwendet werden
    • Einbindung in beliebige C-Dateien mit #include "pretty.h"
    • Alternativ kann der Pretty-C-Pfad auch im Include-Pfad angegeben werden

Standardmäßig eingebundene Header und einfache Makros

  • Pretty C bindet Standard-Header ein, damit häufig verwendete Typen und alternative Operatordarstellungen sofort verfügbar sind
    • stdbool.h: true, false, bool
    • stdint.h: Ganzzahltypen mit fester Breite wie uint64_t
    • iso646.h: alternative Operatoren wie and statt && oder or statt ||
  • Außerdem werden häufig definierte einfache Makros bereitgestellt
    • max, min: Maximum und Minimum zweier Zahlen
    • len: Array-Länge
    • default: stellt einen Fallback-Wert bereit
    • limit: begrenzt einen Wert auf einen Bereich
    • between: prüft, ob eine Zahl in einem Bereich liegt
    • divisible: prüft, ob sie ohne Rest durch eine andere Zahl teilbar ist

Typen und Aliasse

  • Pretty C bietet kurze Typ-Aliasse für C-Typen
    • string ist char*
    • byte ist char
    • bytes ist char*
    • any ist void*
    • Als Kurzformen für Ganzzahlen gibt es uchar, ushort, uint, ulong
  • Enthalten sind auch von Lua und Lisp beeinflusste Syntax-Aliasse
    • eq, is: ==
    • bitnot, bitxor: Aliasse für Bit-Operationen
    • success, fail, failure: Aliasse für das Muster success == 0
    • below, above, upto, downto: Vergleichsoperatoren
    • even, odd, positive, negative, zero, empty: Prädikate für Zahlen und Daten
    • nil: NULL
    • until: negiertes while
    • elif: else if
    • ifnt, elifnt: Ausdrücke der Form if(!...)
    • repeat: Lua-artiger Alias für do
    • done, finish, pass: Aliasse für break, continue
    • always, forever, loop, indefinitely: Ausdrücke für Endlosschleifen
    • never, comment: Ausdrücke, mit denen Code nicht ausgeführt wird, aber weiterhin vom Compiler analysiert werden kann

Typinferenz, Ausgabe, Vergleich und ternäre Ausdrücke

  • Typinferenz ist mit GCC, Clang oder C23+ verfügbar
    • Beispielhaft werden Formen wie var t = time(0);, let lt = localtime(&t);, local at = asctime(lt); bereitgestellt
  • Generische Ausgabe ist ab C11+ verfügbar
    • print gibt den übergebenen Wert aus
    • println fügt nach der Ausgabe einen Zeilenumbruch an
  • Generischer Gleichheitsvergleich wird ebenfalls über C11+-Funktionen bereitgestellt
    • Zeichenkettenvergleich wie equal("NA", line)
    • Enthalten ist auch ein Beispiel wie equal(0.3, 0.2 + 0.1)
  • Der ternäre Operator kann mit textbasierten Makros lesbarer gemacht werden
    • when expandiert zu einem leeren String zur besseren Lesbarkeit
    • unless expandiert zu not und fungiert als verneinte Form von when
    • then ist ?
    • other und otherwise sind :
    • only wird verwendet, wenn kein otherwise-Zweig nötig ist
    • otherwhen wird verwendet, um die nächste Bedingung anzuhängen

Iterationsmakros

  • Pretty C abstrahiert häufig verwendete for-Schleifenmuster über Makros
  • foreach (var, type, length, ...)
    • Iteriert über ein Array oder einen Speicherbereich
    • In jeder Iteration ist var ein Zeiger auf das jeweilige Array-Element
    • Da es ein Zeiger ist, kann das Element bei Bedarf direkt an Ort und Stelle verändert werden
  • forthese (var, type, ...)
    • Iteriert über die übergebenen variadischen Argumente und bindet jeden Wert an var vom Typ type
  • fortimes (var, times)
    • Reduziert das übliche Muster einer Wiederholung von 0 bis zu einer positiven Anzahl
  • forrange (var, init, target)
    • Iteriert über einen Zahlenbereich von init bis target
    • init und target können beliebige signed oder unsigned Integer sein
    • Ist init größer als target, wird die Schrittweite negativ
  • forrangeby (var, type, init, target, by)
    • Iteriert mit var des angegebenen type von init bis target in Schritten von by

Allokation und Block-Hilfsfunktionen

  • Allokationsmakros vereinfachen, in von C++ beeinflusster Form, typische Muster der Speicherallokation
    • new (type, ...): erzeugt Strukturen usw. in einer Form ähnlich zu C++ new
    • vector (length, type, ...): vereinfacht das Muster, ein Array mit angegebener Länge und Typ zu allokieren und mit Anfangsinhalten zu versehen
    • delete (...): entspricht free, kann aber verwendet werden, wenn ein C++-artiger Name bevorzugt wird
  • Block-Hilfsfunktionen bieten Makros für lokale Bindungen, verzögerte Ausführung und blockweise Operationen
    • lambda (ret, name, ...): ermöglicht unter GCC, Clang oder C++ verschachtelte Funktionen, Lambdas oder Closures in C
    • with (var, close, ...): stellt die Freigabefunktion close vorab bereit und ist nützlich für dynamisch allokierte Objekte oder Dateihandles
      • Nachteilig ist, dass das gebundene var vom Typ void * ist und vor der Nutzung eventuell in den gewünschten Typ umgewandelt werden muss
    • defer: wurde entfernt; stattdessen wird auf ein neu verfügbares defer verwiesen
    • try und catch: ermöglichen eine auffälligere Schreibweise für Fehlerbehandlung in C
    • NOERR, NOERROR: werden als Komfortfunktionen für Fehler-switch-case bereitgestellt

Beitragen

1 Kommentare

 
GN⁺ 2024-10-25
Meinungen auf Hacker News
  • Die Tradition, in C domänenspezifische Sprachen zu bauen, ist alt.
    Stephen Bourne wollte die Shell in ALGOL schreiben, also bearbeitete er den C-Präprozessor hartnäckig so lange, bis sie wie seine bevorzugte Sprache aussah.
    https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh...

  • Ich frage mich, ob jemand erklären kann, ob das ein Witz ist oder ob der Autor wirklich verwirrt ist.
    Nichts davon ergibt Sinn. Es ist kein „Scripting“, es behauptet, „starke Typisierung“ einzuführen, tut aber nichts mit Typen, und die an Lua und Lisp angelehnten Operator-Aliase gibt es in keiner der beiden Sprachen. Gleichzeitig wirkt es aber auch nicht wie eine offensichtliche Parodie, was es wirklich verwirrend macht.

    • Es scheint ernst gemeint zu sein. Immerhin heißt es: „Schafft Lua, Python, JavaScript, Ruby und Dutzende anderer Sprachen ab. Pretty C ist die ultimative Scripting-Sprache, blitzschnell und sogar stark typisiert!!“
    • Ich glaube überhaupt nicht, dass der Autor verwirrt ist. Verwirrung an sich ist natürlich in Ordnung.
  • Es behauptet, eine Scripting-Sprache zu sein, aber man muss das Programm weiterhin kompilieren. Buhrufe dafür.
    Wenn man CINT (https://root.cern.ch/root/html534/guides/users-guide/CINT.ht...) einbindet, bekommt man sofortige Ausführung und auch eine REPL.

    • Wenn der Zweck dieses Repositories darin besteht, Schmerzen zu verursachen, könnte man doch einfach eine Shebang in die Datei einfügen und sie ausführbar machen [0].
      Ich habe früher einmal einen Blogbeitrag gesehen, der ausführlich behandelte, wie ./foo funktioniert und wie ELF-Dateien ausgeführt werden. Auf dieselbe Weise könnte man wohl auch .c-Programme registrieren, sodass sie kompiliert und anschließend ausgeführt werden.
      [0] https://gist.github.com/jdarpinian/1952a58b823222627cc1a8b83...
    • Besser wäre wohl, einfach tcc zu verwenden [0]. Das ist viel leichter als dieses Monster und außerdem C, nicht C++.
      [0] https://bellard.org/tcc/tcc-doc.html
    • CERN verwendet inzwischen cling (https://github.com/root-project/cling)
    • Wer hat denn festgelegt, dass eine Scripting-Sprache nicht kompiliert werden darf? Und Clang-REPL ist ebenfalls ein weiterer Weg, sie REPL-freundlich zu machen.
  • „Es bietet genug syntaktischen Zucker, um C-Entwicklern einen diabetischen Herzinfarkt zu verpassen.“
    Diese Formulierung ist wirklich gut.

  • Ich sehe ein paar Dinge, die zu CNoEvil[0] und ogw[1] hinzugefügt werden sollten. Jedes Mal, wenn dieses Projekt alle paar Monate wieder auftaucht, scheint es mehr davon zu geben.
    [0] https://git.sr.ht/~shakna/cnoevil3/
    [1] https://git.sr.ht/~shakna/ogw

    • „Es nimmt zahllose schlechte Ideen und presst sie zu einem abscheulichen Monster zusammen.“
      Ein Satz, den man wirklich von Herzen lieben muss.
  • Das macht denselben Fehler wie Python 2. Strings und Bytes sind nicht derselbe Typ und sollten auch nicht so behandelt werden.

    • Als welchen Typ sollte man Shell-Text betrachten, also Werte in argv oder die Ausgabe von Unterprozessen?
      Darin kann beliebiger Müll stehen, also ist es kein gültiger UTF-8-String, aber Tools wie awk und grep werden überall verwendet.
      Man kann Strings und Bytes als denselben allgemeinen Typ ansehen, wobei es manchmal nützlich ist, gültigen UTF-8-Bytes intern einen anderen Typ zu geben. Rust löst das mit OsString und String größtenteils gut.
    • Da dieses Projekt pretty.c heißt und „abwärtskompatibel mit C und allen Bibliotheken“ sein soll: Welche Wahl ergäbe mehr Sinn, als die Multibyte-Strings von C beizubehalten?
      https://en.cppreference.com/w/c/string/multibyte
    • Dem stimme ich nicht zu. Dieses Prinzip setzt Folgendes alles voraus: String-Daten sind korrekt kodiert; es gibt nur eine String-Kodierung, meist UTF-8; String-Daten müssen bei ihrer Erzeugung validiert werden; das Abschneiden logischer Codepoints ist niemals erlaubt; auf „ungültige“ Bytes sollten keine String-Operationen angewendet werden; korrekte Kodierung ist Anfang und Ende der Validierung.
      Nichts davon ist immer wahr. Es ist nützlich, eine validierte Byte-Sequenz in einen Typ zu verpacken, der nur durch Validierung erzeugt werden kann, aber wenn man das tut, gehören Utf8String und EmailAddress im Grunde zur selben Kategorie. Es gibt keinen Grund, im Typsystem nur die Kodierung besonders zu behandeln.
    • Es hängt davon ab, wie man „String“ definiert.
      Wenn damit „für Menschen lesbarer Text“ gemeint ist, stimme ich zu, dass ein String nicht dasselbe ist wie ein beliebiges Byte-Array. Viele Sprachen erzwingen diese Definition aber nicht.
  • Erinnert mich an eine C++-Codebase, die ich früher einmal überprüfen musste. Das Ganze war so, als wäre es in Java geschrieben: CamelCase für alles, Getter/Setter für alle Klassenvariablen und überall Interfaces

    • Du hast noch nicht das Richtige gesehen. Schau dir den Bourne-Shell-Quellcode der Unix Seventh Edition an
      https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd...
      Es ist schwer zu glauben, dass das nicht ALGOL ist
    • CamelCase, Getter/Setter und Interfaces unterscheiden sich in vielen Fällen nicht groß von Richtlinien. Windows-Code ist zum Beispiel so, natürlich nicht bis hin zu jeder einzelnen Variablen. Auch vieles am Java-Design wurde aus C++ kopiert
    • Ich habe schon ähnliche Codebases gesehen, geschrieben von Leuten, die zu lange Java benutzt hatten. Es gab sogar eine eigene String-Klasse, die im Grunde nur std::string umhüllte, aber mit Java-artigen Methoden versehen war
    • Da war es wohl ein Glück, dass sie MSVC nicht benutzt haben
      https://learn.microsoft.com/en-us/cpp/cpp/property-cpp?view=...
    • Ich habe so etwas auch erlebt, nur dass Strings zusätzlich wie in Java als f(String *) übergeben wurden, sodass der C++-Code dann etwa f(new String("Hello") war
  • Wenn dich das interessiert, könnte dir auch libcello.h gefallen
    https://www.libcello.org

    • Genau, das war eine der Inspirationsquellen
  • Die Typnamen sind gut, und auch die Auswahl der Built-in-Funktionsmakros wie min ist perfekt. Len gefällt mir ebenfalls
    Benannte boolesche Operatoren sind vielleicht etwas übertrieben, aber einen Versuch wert; der ternäre Operator ist ohnehin schwer lesbar, da gibt es also viel Verbesserungspotenzial. Von allen Schleifendefinitionen bin ich nicht ganz überzeugt, aber einige ergeben Sinn. Das Ressourcen-Tracking ist beeindruckend, und die for...-Form sieht etwas hässlich aus, da könnte man wohl einen anderen Namen finden
    Insgesamt ein ziemlich solider Versuch. Für das Design würde ich etwa 8/10 geben. Die Art, wie es mit C-Makros skizziert ist, ist wirklich elegant und sieht tatsächlich nach gutem Code aus. Ob ich es selbst verwenden würde, weiß ich nicht. Es ist eine neue Sprache, und ich mag C ohnehin schon. Trotzdem ist die Implementierung sehr klar, sodass sie Leuten helfen kann, C zu lernen und über Sprachdesign nachzudenken

    • Man muss nicht alles davon verwenden. Meine Projekte nutzen meistens nur Booleans, len(), max/min und ein paar Operator-Makros. Die anderen netten Features habe ich bisher nicht wirklich gebraucht. Also probier ruhig schon ein paar der Operator-Makros aus
  • Wenn ifnt für if(!...) gedacht ist, wirkt unless lesbarer als ifnt

    • Ein weiterer Namensstreitpunkt wäre, eine endlose for(;;)-Schleife always zu nennen
      In anderen Sprachen habe ich loop gesehen. Qt nennt es aber forever, und das ist wirklich hübsch. Auch sehr Qt-typisch
    • Andererseits macht es Spaß, ifnt laut auszusprechen
    • Stimmt. Aber unless habe ich für ternäre Bedingungen reserviert. Dort ist es schließlich nützlicher
      Ach, Moment, das ist ja John Tromp. Der Erfinder von BLC! Ich bin Fan