Pretty.c — du scripting au-dessus de C
(github.com/aartaka)- Pretty C est un nouveau langage de scripting compatible avec C, qui ajoute aux programmes C des commodités comme le typage dynamique, l’itération générique et le suivi des ressources
- Il s’utilise comme une bibliothèque header-only en incluant un seul fichier
pretty.h, et peut être employé avec des fichiers C et des bibliothèques C existants - Les fonctionnalités proposées incluent des alias comme
string,anyetnil, l’inférence de typevar/let, la sortie génériqueprint/println, la comparaison génériqueequalet des macros de ternaire plus lisibles - Pour réduire le code lié aux boucles et à la mémoire, il fournit une syntaxe basée sur des macros comme
foreach,forthese,fortimes,forrange,new,vector,delete,with,lambdaettry/catch - Certaines fonctionnalités ont des prérequis comme GCC, Clang, C23+ ou C11+, et les variables encapsulées par
withétant de typevoid *, une conversion de type peut être nécessaire avant utilisation
Objectif et mode d’utilisation de Pretty C
- Pretty C est un langage de scripting compatible avec C, qui ajoute aux programmes C le typage dynamique, l’itération générique, le suivi des ressources et plusieurs commodités syntaxiques
- Il conserve la rétrocompatibilité avec le C existant et les bibliothèques C
- Il s’inspire de Lua, Python, JavaScript et Lisp
- Pour l’installer, il suffit de cloner le dépôt ou de copier uniquement le fichier
pretty.h- Il peut être inclus dans n’importe quel fichier C avec
#include "pretty.h" - Il peut aussi être utilisé en ajoutant le chemin de Pretty C au chemin d’inclusion
- Il peut être inclus dans n’importe quel fichier C avec
En-têtes inclus par défaut et macros simples
- Pretty C inclut des en-têtes standard afin de rendre immédiatement disponibles des types courants et des expressions alternatives pour les opérateurs
- Il fournit aussi des macros simples fréquemment définies
max,min: valeur maximale et minimale de deux nombreslen: longueur d’un tableaudefault: fournit une valeur de fallbacklimit: limite une valeur à une plagebetween: vérifie si un nombre se trouve dans une plagedivisible: vérifie si un nombre est divisible par un autre
Types et alias
- Pretty C fournit de courts alias de types pour les types C
stringcorrespond àchar*bytecorrespond àcharbytescorrespond àchar*anycorrespond àvoid*- Il fournit aussi des raccourcis d’entiers :
uchar,ushort,uint,ulong
- Il inclut aussi des alias syntaxiques influencés par Lua et Lisp
eq,is:==bitnot,bitxor: alias pour les opérations bit à bitsuccess,fail,failure: alias pour le motifsuccess == 0below,above,upto,downto: opérateurs de comparaisoneven,odd,positive,negative,zero,empty: prédicats sur les nombres et les donnéesnil:NULLuntil:whilenégatifelif:else ififnt,elifnt: expressions de typeif(!...)repeat: alias dedoà la manière de Luadone,finish,pass: alias debreaketcontinuealways,forever,loop,indefinitely: expressions de boucle infinienever,comment: expressions permettant au compilateur d’analyser du code sans l’exécuter
Inférence de type, sortie, comparaison et ternaire
- L’inférence de type est disponible avec GCC, Clang ou C23+
- Les exemples fournis incluent des formes comme
var t = time(0);,let lt = localtime(&t);,local at = asctime(lt);
- Les exemples fournis incluent des formes comme
- La sortie générique est disponible à partir de C11
printaffiche la valeur transmiseprintlnajoute un saut de ligne après l’affichage
- La comparaison générique d’égalité est aussi fournie comme fonctionnalité C11+
- Comparaison de chaînes comme
equal("NA", line) - Exemples de comparaison comme
equal(0.3, 0.2 + 0.1)
- Comparaison de chaînes comme
- L’opérateur ternaire peut être rendu plus lisible grâce à des macros textuelles
whense développe en chaîne vide pour améliorer la lisibilitéunlessse développe ennotet fonctionne comme la forme négative dewhenthencorrespond à?otheretotherwisecorrespondent à:onlys’utilise lorsqu’une clauseotherwisen’est pas nécessaireotherwhens’utilise pour chaîner la condition suivante
Macros d’itération
- Pretty C abstrait les motifs courants de boucles
forsous forme de macros foreach (var, type, length, ...)- Parcourt un tableau ou une zone mémoire
- À chaque itération,
vardevient un pointeur vers l’élément correspondant du tableau - Comme il s’agit d’un pointeur, l’élément peut être modifié sur place si nécessaire
forthese (var, type, ...)- Parcourt les arguments variadiques transmis et associe chaque valeur à
varde typetype
- Parcourt les arguments variadiques transmis et associe chaque valeur à
fortimes (var, times)- Réduit un motif courant qui itère de 0 jusqu’à un nombre positif de répétitions
forrange (var, init, target)- Parcourt une plage numérique de
initàtarget initettargetpeuvent être des entiers signed ou unsigned quelconques- Si
initest supérieur àtarget, le pas d’itération décroît
- Parcourt une plage numérique de
forrangeby (var, type, init, target, by)- Parcourt
var, de typetype, deinitàtargetavec un pas deby
- Parcourt
Allocation et utilitaires de bloc
- Les macros d’allocation, inspirées de C++, simplifient les motifs courants d’allocation mémoire
new (type, ...): crée une structure ou autre dans une forme similaire aunewde C++vector (length, type, ...): simplifie l’allocation d’un tableau d’une longueur et d’un type donnés, avec un contenu initialdelete (...): identique àfree, mais utilisable si l’on préfère un nom à la C++
- Les utilitaires de bloc fournissent des macros pour les liaisons locales, l’exécution différée et les opérations au niveau d’un bloc
lambda (ret, name, ...): permet d’utiliser en C des fonctions imbriquées, des lambdas ou des closures avec GCC, Clang ou C++with (var, close, ...): utile pour les objets alloués dynamiquement ou les handles de fichiers, en fournissant à l’avance une fonction de libération des ressourcesclose- Inconvénient : la variable liée
varest de typevoid *, ce qui peut nécessiter une conversion vers le type souhaité avant utilisation
- Inconvénient : la variable liée
defer: a été supprimé, avec une indication d’utiliser le nouveaudeferdisponibletryetcatch: permettent d’écrire la gestion d’erreurs en C sous une forme plus expressiveNOERR,NOERROR: fournis pour faciliter lesswitch-cased’erreurs
Mode de contribution
- Lors de l’ajout de fonctions, il est demandé de suivre le Linux Kernel Style Guide
- Il est recommandé d’exécuter
make indentavant de committer
- Il est recommandé d’exécuter
- Il est préférable, mais pas obligatoire, de suivre A’s Commit Message Guide
1 commentaires
Commentaires sur Hacker News
La tradition de créer des langages spécifiques à un domaine dans C est ancienne
Stephen Bourne voulait écrire le shell en ALGOL, alors il a martelé le préprocesseur C avec acharnement jusqu’à le faire ressembler au langage qu’il préférait
https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh...
https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh...
https://www.ioccc.org/
Je me demande si quelqu’un peut expliquer s’il s’agit d’une blague, ou si l’auteur est réellement confus
Rien n’a de sens. Ce n’est pas du « scripting », il prétend introduire un « typage fort » mais ne fait absolument rien avec les types, et les alias d’opérateurs censés s’inspirer de Lua et Lisp n’existent en fait dans aucun de ces deux langages. Pourtant, ça ne ressemble pas clairement à une parodie, donc c’est vraiment déroutant
Ça se présente comme un langage de scripting, mais il faut quand même compiler le programme. Huées
En y ajoutant CINT(https://root.cern.ch/root/html534/guides/users-guide/CINT.ht...), on obtient une exécution immédiate et même un REPL
J’avais lu autrefois un billet de blog qui détaillait le fonctionnement de
./fooet la manière dont les fichiers ELF sont exécutés. On pourrait sans doute enregistrer les programmes.cde la même façon pour les compiler puis les exécuter[0] https://gist.github.com/jdarpinian/1952a58b823222627cc1a8b83...
[0] https://bellard.org/tcc/tcc-doc.html
« Fournit assez de sucre syntaxique pour provoquer une crise cardiaque diabétique chez les développeurs C. »
J’adore vraiment cette formule
Je vois plusieurs choses à ajouter à CNoEvil[0] et ogw[1]. On dirait qu’il y en a toujours plus chaque fois que ce projet refait surface tous les quelques mois
[0] https://git.sr.ht/~shakna/cnoevil3/
[1] https://git.sr.ht/~shakna/ogw
Une phrase qu’on finit sincèrement par aimer au plus profond de soi
Ce projet refait la même erreur que Python 2. Chaînes de caractères et octets ne sont pas le même type, et ne doivent pas être traités comme tels
argvou à la sortie d’un sous-processusÇa peut contenir n’importe quelle donnée arbitraire, donc pas forcément une chaîne UTF-8 valide, mais des outils comme awk et grep sont utilisés partout
On peut considérer que chaînes et octets relèvent d’un même type général, tout en trouvant utile de donner en interne un type distinct aux octets UTF-8 valides. Rust gère cela plutôt bien avec OsString et String
https://en.cppreference.com/w/c/string/multibyte
Rien de tout cela n’est toujours vrai. Il est utile d’envelopper une séquence d’octets validée dans un type qui ne peut être créé qu’au terme d’une validation, mais dans ce cas
Utf8StringetEmailAddressrelèvent en pratique de la même catégorie. Il n’y a aucune raison de traiter spécialement l’encodage dans le système de typesSi l’on entend par là un « texte lisible par un humain », alors oui, je suis d’accord pour dire qu’une chaîne n’est pas la même chose qu’un tableau d’octets arbitraires. Mais beaucoup de langages n’imposent pas cette définition
Cela me rappelle une base de code C++ que j’avais dû auditer il y a longtemps. Tout était écrit comme si c’était du Java : camel case partout, des getters/setters pour toutes les variables de classe, et des interfaces disséminées un peu partout
https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd...
Difficile de croire que ce n’est pas de l’ALGOL
std::stringavec des méthodes à la Javahttps://learn.microsoft.com/en-us/cpp/cpp/property-cpp?view=...
f(String *), si bien que le code C++ ressemblait àf(new String("Hello")Si ça vous intéresse, vous aimerez peut-être aussi libcello.h
https://www.libcello.org
Les noms de types sont bien trouvés, et le choix d’un macro de fonction intégrée comme
minest parfait. J’aime bien aussiLenLes opérateurs booléens nommés sont peut-être un peu excessifs, mais ça vaut le coup d’essayer, et l’opérateur ternaire est de toute façon difficile à lire, donc il y a de la marge pour l’améliorer. Je ne suis pas totalement convaincu par toutes les définitions de boucles, mais certaines ont du sens. Le suivi des ressources est impressionnant, et la forme
for...est un peu laide, donc on pourrait sans doute lui donner un autre nomDans l’ensemble, c’est une tentative assez solide. Je donnerais autour de 8/10 au design. La manière dont c’est esquissé avec des macros C est vraiment élégante et donne l’impression d’un bon vrai code. Je ne sais pas si je l’utiliserais moi-même. C’est un nouveau langage, et j’aime déjà le C. Mais la façon dont c’est implémenté est très claire, donc ça peut aider les gens à apprendre le C et à réfléchir à la conception des langages
len(),max/minet quelques macros d’opérateurs, simplement parce que je n’ai pas encore eu besoin des autres fonctionnalités appétissantes. Donc ce serait bien d’essayer ne serait-ce que quelques macros d’opérateursPour
ifntcomme équivalent deif(!...), unless me paraît plus lisible queifntfor(;;)paralwaysDans d’autres langages, j’ai vu
loop. Mais Qt l’appelleforever, et c’est vraiment joli. Très Qt, aussiifntest amusant à prononcer à voix hauteunlesspour la condition ternaire. C’est plus utile là de toute façonAh, attends, c’est John Tromp. Le créateur de BLC ! Je suis fan