1

Zerosquare a souhaité poursuivre une discussion dérivée dans un nouveau sujet. La discussion initiale a eu lieu dans le sujet topics/164999-f-moved-mon-langage-est-mieux-que-le-tien/176#post-5270 tandis que la discussion dérivée continue dans le sujet topics/196681-c-zero-le-c-a-godzil où les messages en rapport ont été copiés.
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

2

Je présente C-Zero:

// ============================================================================= // AstroFury: A C-Zero Language Showcase // // This file demonstrates the features of the C-Zero language by implementing // a simple, hypothetical retro space shooter game. It is intended to be used // as a comprehensive test case for a C-Zero parser and compiler. // ============================================================================= // 1. Module System: Every file starts with a module declaration. // This replaces C's header files. module astrofury; // We can import other modules. The compiler/linker is responsible for finding them. // These are hypothetical modules representing a simple standard library. import system::video; import system::input; import system::rng; import system::math; // ============================================================================= // 2. Constants and Compile-Time Features // ============================================================================= // 'readonly' creates a true, type-safe constant, replacing `#define`. // The compiler can place this data in ROM. exported readonly uint16 SCREEN_WIDTH = 320; exported readonly uint16 SCREEN_HEIGHT = 200; // 'compile_if' provides structured, safe conditional compilation. // This replaces C's `#if`/`#endif`. internal readonly bool DEBUG_MODE = true; compile_if (DEBUG_MODE == true) { // This function will only exist in debug builds. external void log_message(string msg); } // ============================================================================= // 3. Data Structures: Enums, Structs, Bitfields, and Unions // ============================================================================= // 'enum' with an explicit underlying type for precise memory layout. exported enum GameState : uint8 { TITLE_SCREEN, PLAYING, GAME_OVER } // Another enum, this time for identifying entity types. enum EntityType : uint8 { PLAYER, ENEMY_FIGHTER, ENEMY_BOMBER, PLAYER_BULLET, ENEMY_BULLET } // 'struct' with public/private access modifiers and bitfields. // The 'bits' block has an explicit packing order defined by the platform config. struct StatusFlags { public: bits { uint8 active : 1; // Is this entity currently in use? uint8 shielded : 1; // Is it shielded from damage? uint8 exploding : 1; // Is it in an exploding state? uint8 _reserved : 5; // Reserved for future use. } } // A base struct for all game objects, demonstrating composition. struct EntityBase { public: EntityType type; StatusFlags flags; fixed x, y; // Position using platform-defined fixed-point math. fixed dx, dy; // Velocity. private: uint16 internal_id; // Private members are only accessible by methods of EntityBase. } // A struct for the player. It uses the 'base' keyword for composition. // This is a zero-cost abstraction for code reuse. struct Player { public: base EntityBase entity; // 'base' must be the first member. uint8 health; uint16 score; private: uint8 lives; } // A struct for enemies. struct Enemy { public: base EntityBase entity; uint8 hit_points; uint16 despawn_timer; } // A simple struct for bullets. struct Bullet { public: base EntityBase entity; uint8 time_to_live; } // 4. Tagged Unions: A core feature for safe polymorphism. // This union can hold any one of our game objects. The compiler tracks the // active variant, preventing unsafe access. union GameObject { PlayerObject { Player p; }; EnemyObject { Enemy e; }; BulletObject { Bullet b; }; Empty { }; // An empty variant for unused slots. } // ============================================================================= // 5. Globals and Hardware Access // ============================================================================= // 'internal' is the default visibility, replacing C's `static` at file scope. internal GameState current_state; // 'preserve' creates a local variable that retains its value across function calls, // replacing C's `static` inside a function. internal uint16 generate_id(void) { preserve uint16 next_id = 0; next_id = next_id + 1; return next_id; } // 'hardware' qualifier indicates a variable can change at any time, // replacing C's `volatile`. Accesses to it will not be optimized away. // Attributes are specified at the end of the declaration. hardware uint8 VSYNC_COUNTER @(address:0xD011); // Hypothetical VSync register. // 'fastmem' is a platform-agnostic way to request allocation in a special // high-speed memory region (e.g., ZP on 6502). fastmem GameObject game_objects[32]; // ============================================================================= // 6. Functions, Methods, and ABI Control // ============================================================================= // 'external' declares a function defined elsewhere (e.g., in ROM or assembly). // This showcases the powerful ABI control features of C-Zero. // This function uses a named ABI from the platform config file. external void clear_screen(uint8 color) @(abi:"fastcall"); // This function demonstrates per-parameter location specifiers. // This syntax remains correct according to the spec. external void draw_sprite(uint8(register:A) sprite_id, uint16(stack) x, uint16(stack) y); // Here we use the short aliases for location specifiers. // (A) is short for (register:A), (@0x8000) for (address:0x8000), etc. external void set_palette(uint8(A) index, ptr uint8(@0x8000) color_data); // An 'interrupt' function. The compiler will auto-generate code to save/restore // all registers and use a special "return from interrupt" instruction. interrupt void nmi_handler(void) { // Handle a non-maskable interrupt, e.g., for music playback. asm { // Inline assembly is possible for ultimate low-level control. // The exact syntax would be platform-specific. "PLAY_MUSIC_TICK" } } // A method "attached" to the Player struct using ':'. // The 'self' pointer is implicitly available. void Player:take_damage(uint8 amount) { if (self.entity.flags.shielded == 1) { return; } if (self.health > amount) { self.health = self.health - amount; } else { self.health = 0; self.lives = self.lives - 1; // ... trigger explosion ... } } // A 'readonly' method guarantees it will not modify 'self'. // The compiler enforces this. uint16 Player:get_score(void) readonly { return self.score; } // A function using a pass-by-reference parameter with the 'ref' keyword. // This function can modify the original variable passed to it. void increase_score(ref uint16 score, uint16 amount) { score = score + amount; // 'score' is used directly, no '*' needed. } // ============================================================================= // 7. Function Pointers // ============================================================================= // Forward declarations for AI functions. internal void ai_fighter(ref Enemy e); internal void ai_bomber(ref Enemy e); // A function pointer declared using the safe, list-based form. // The compiler knows all possible targets, enabling deep analysis. func_ptr(ai_fighter, ai_bomber) enemy_ai_behavior; // A function pointer using the signature-based form for more flexibility. // The compiler would warn that it cannot guarantee perfect safety here. func_ptr(void(ref Enemy)) generic_ai_ptr; // ============================================================================= // 8. Control Flow and Expressions // ============================================================================= internal void update_game(void) { // 'foreach' with 'ref' provides modifiable, zero-copy access to elements. foreach (ref GameObject go : game_objects) { // The vastly improved 'switch' statement is the primary way to handle // tagged unions. It's safe and requires no breaks by default. switch (go) { case PlayerObject as po: // Handle player input if (input::is_joy_down(0, input::UP)) { po.p.entity.y = po.p.entity.y - (1.5 as fixed); } increase_score(&po.p.score, 1); // Call site for 'ref' requires '&' break; // We still need breaks to exit the switch case EnemyObject as eo: // Assign and call a function pointer. if (eo.e.entity.type == ENEMY_FIGHTER) { enemy_ai_behavior = &ai_fighter; } else { enemy_ai_behavior = &ai_bomber; } enemy_ai_behavior(&eo.e); break; case BulletObject as bo: bo.b.entity.y = bo.b.entity.y - (4.0 as fixed); if (bo.b.time_to_live == 0) { bo.b.entity.flags.active = 0; } else { bo.b.time_to_live = bo.b.time_to_live - 1; } break; case Empty: // This case is for empty slots, do nothing. break; // Explicit break } } // 'switch' with ranges and explicit 'fallthrough'. uint8 score_category = get_player().get_score() / 1000; switch (score_category) { case 0: // ... break; case 1 .. 5: // "Rookie" rank log_message("Player is a Rookie"); fallthrough; // Explicit fallthrough to the next case case 6 .. 10: // "Veteran" rank log_message("Player is a Veteran"); break; default: log_message("Player is an Ace!"); break; } } // ============================================================================= // 9. Casting and Optimization Control // ============================================================================= internal void render_frame(void) { clear_screen(0x01); // Blue background // 9a. Casting // 'foreach' with 'readonly ref' provides guaranteed read-only, zero-copy access. // This is the most efficient and safe way to iterate when not modifying elements. foreach (readonly ref GameObject go : game_objects) { switch (go) { case PlayerObject as po: draw_sprite(0, po.p.entity.x as uint16, po.p.entity.y as uint16); break; case EnemyObject as eo: // Assuming enemies have a different sprite ID, e.g., 1 draw_sprite(1, eo.e.entity.x as uint16, eo.e.entity.y as uint16); break; case BulletObject as bo: // Assuming bullets have a different sprite ID, e.g., 2 draw_sprite(2, bo.b.entity.x as uint16, bo.b.entity.y as uint16); break; // No need for an 'Empty' case, as there's nothing to draw. } } // An example of a dangerous, but sometimes necessary, cast. // 'transmute' is used to reinterpret a raw integer address as a pointer. // The keyword is intentionally loud to signal risk. ptr Player p_hw = transmute(ptr Player, 0xC000); p_hw.score = 9999; // Directly manipulate memory at 0xC000. // 9b. Optimization Control // The 'unoptimized' block is critical for timing-sensitive operations. // The compiler is forbidden from reordering instructions inside this block. unoptimized { // Wait for the start of the vertical blank. uint8 start_vsync = VSYNC_COUNTER; while (start_vsync == VSYNC_COUNTER) { // This busy-wait loop will not be optimized away. } } // ... swap screen buffers ... } // ============================================================================= // 10. Main Application Entry Point // ============================================================================= exported int16 main(void) { current_state = GameState.TITLE_SCREEN; while (true) { switch (current_state) { case TITLE_SCREEN: // ... show title ... if (input::is_joy_pressed(0, input::FIRE)) { current_state = GameState.PLAYING; } break; case PLAYING: update_game(); render_frame(); break; case GAME_OVER: // ... show game over screen ... break; } } return 0; }
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

3

intéressant sur les switch, que réfère le "as" ?

preserve et hardware c'est de la sémantique

bits est sympa, change t'il l'ordre seul suivant l'architecture ?

pourquoi pas mettre des soupçons de classes dans les structures, mais alors les fonctions sont externes à la déclaration ?
et la le mec il le pécho par le bras et il lui dit '

4

C'est un langage que tu as fait toi même ? Si oui, c’est avancé a quel point ? Je ne vois aucune référence à C-Zero sur Google à part un projet différent (un sous-ensemble de C). Ça pourrait te poser un soucis si C-Zero est le nom définitif. Globalement j’ai l’impression que tous les nom qui tournent autour de C sont déjà pris(C0, C1,C2,C3,C++,C+,C#,C--,...)

Je ferai un post plus long sur ce que j'en pense quand j'aurais le temps car il y a beaucoup de chose a en dire, mais a première vue, je trouve que c'est probablement la meilleure proposition que j'ai vu dans le domaine du langage de type C en plus propre. Ça m'a fait penser à C2 dans l'idée de base mais en mieux pensé.

Je suis pas sur que je l'utiliserai, le Rust couvrant plus ou moins toutes les fonctionnalités (à part l'ABI custom qui est un besoin que je n'ai pas) et en offrant d'autres en plus. De plus il commence a avoir un écosystème conséquent ce qui est plus rassurant pour une utilisation pro. Mais je comprend tout a fait l'envie de rester sur un scope de fonctionnalité plus limité que Rust et avec moins de contraintes.
avatar

5

Oui c'est joli. Comment se débrouille le langage pour trouver les méthodes d'objets qui sont déclarés dans d'autres fichiers ? Il y a d'abord une passe "globale" ?
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

6

(je paris mon c15 que c'est généré par ia

donne nous le prompt avec ton cahier des charges smile

> Uther : c'est surtout que ça pourrait être inclus dans le prochain C (bon sans preserve et hardware on ne va pas toucher static et volatile) en tous cas le flip automatique des bitfilelds serait pertinent .. un un concept évitant un pointeur vers la structure serait pertinent, après de la à en faire un langage neuf, éventuellement un transpileur
et la le mec il le pécho par le bras et il lui dit '

7

À part peut-être le bloc unoptimised, ça devrait pouvoir se transpiler en C oui smile
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

8

je ne l'ai pas vu passer, mais il est dur de définir la portée réelle d'un bloc "non optimisé" quant l'étape suivante est un changement vener de langage (#pragma max02 ? ça voudrait dire un volatile global au bloc ? inutile à mon sens
et la le mec il le pécho par le bras et il lui dit '

9

Dans son exemple c'est inutile on dirait oui, vu que le VSYNC_COUNTER est hardware/volatile🦜.

Et pour le nom : C80 alors. C15 ça fait un peu trop « voiture qui va finir dans un arbre », et C4 ça fait un peu trop « ça va te péter à la gueule ». Mais C80 en jeu de mots avec le hardware des années 80, et le Z80, et ça sonne bien.

La seule chose qui m'inquièterait c'est comment avoir un code optimisé au même point que le C classique ? (Si on passe par LLVM par exemple)
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

10

c'est exactement ça le C, liberté et contrôle, fonce dans l'arbre si tu veux :- D
et la le mec il le pécho par le bras et il lui dit '

11

Liberté oui, contrôle pas vraiment. Une particularité du C, c'est justement qu'on ne contrôle plus rien quand on déclenche un comportement indéfini. Une voiture qui fonce dans l'arbre, on sait comment ça va se finir. Un comportement indéfini peut aussi bien être sans conséquence visible et passer inaperçu que potentiellement catastrophique.
avatar

12

donc vous ne faites aucune confiance aux devs

avec la culture du moindre risque on va donc sur le même principe interdire par exemple toutes les voitures non autonomes ?
et la le mec il le pécho par le bras et il lui dit '

13

C'est pas ne pas faire confiance aux dev que d'être conscient de leur limites et de celles des technologies qu'ils emploient.
avatar

14

certes ça peut arriver à tout le monde mais taper une case trop loin sur un pointeur c'est généralement un truc de stagiaire ...

avoir un langage plus blindé accrédite encore plus le fait de coder en permanence les choses pour avant hier au lieu de prendre le temps nécessaire
et la le mec il le pécho par le bras et il lui dit '

15

Hum viens dire ça quand tu reçois un report de SEGA que ton jeu crash sans vraiment savoir où / pourquoi et après combien de temps, et que t'es 1-2 mois avant la release commerciale. Ptet qu'il y avait un stagiaire dans l'équipe (ça ne devrait pas exister dans cette industrie ?) ou un Yuji Naka qui a fini un red bull* de trop avant de s'affaler sur le sofa de l'entrée du bureau.

*Enfin un リボビタンD.
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

16

robinHood (./5244) :
intéressant sur les switch, que réfère le "as" ?
Cast "safe"

uint16 foo = bar as uint16

robinHood (./5244) :
preserve et hardware c'est de la sémantique

Comme a peu pret tous les mots clefs

robinHood (./5244) :
bits est sympa, change t'il l'ordre seul suivant l'architecture ?

L'ordre est defini suivant l'architecture, mais peux aussi etre forcé a la declaration de la structure (comme l'endianess), utile pour les format binaires et protocoles.

robinHood (./5244) :
pourquoi pas mettre des soupçons de classes dans les structures, mais alors les fonctions sont externes à la déclaration ?

C'est le cas

struct foo { private: int8 bar; } int8 foo:get() { return self->bar; } void foo:set(int8 v) { self->bar = v; } [...] foo a; a:set(42);

Uther (./5245) :
C'est un langage que tu as fait toi même ?
Oui, j'ai defini le language, je suis en train de travailler sur le parser pour l'instant.
Quand au nom j'ai aussi trouvé le meme que toi, et j'ai du changer pour eviter une confusion.

Caudex est le nouveau nom.

Uther (./5245) :
Je suis pas sur que je l'utiliserai, le Rust couvrant plus ou moins toutes les fonctionnalités (à part l'ABI custom qui est un besoin que je n'ai pas) et en offrant d'autres en plus. De plus il commence a avoir un écosystème conséquent ce qui est plus rassurant pour une utilisation pro. Mais je comprend tout a fait l'envie de rester sur un scope de fonctionnalité plus limité que Rust et avec moins de contraintes.
Apres je ne cherche pas a forcer qui que ce soit a l'utiliser, chacun ses preferences, perso le coté "zero overhead" et ABI sont les parties les plus importantes, ce language en theorie peux etre utilisé avec le 6502 car il peux cibler une machine avec aucune pile (ou presque) et quand c'est utilisé ainsi il y a bien sur des restrictions, il peux aussi en theorie supporter les pointeurs type far/near qu'on trouve avec le x86, mais tout ca sera lié a l'implementation du backend.

Sinon "transpiler" (je trouve ce terme est vraiment stupide et il n'aurais jamais du sortir hors du javascript) c'est vrai pour n'importe quel language. Tu peux convertir du java en C++, et meme tiens le C++ a la base c'est juste une surcouche du C.

Quand a l'optimisation c'est 100% dependant du compilateur pas du language.

Sinon un backend LLVM est prevu des que le frontend et le backend 6502 sont fait (le 6502 est la cible de base) car j'espere pouvoir arriver au moins au self-host.

Une des idée est de limiter voir supprimer toutes les UB du C/C++. Apporter du confort d'utilisation au C en lui meme, ce n'est PAS un language objet, meme si certains choses peuvent etre faites, on peux attacher des methodes a une structure, on peux avoir une forme de polymorphisme, et le language laisse acces a tout ce qui est "unsafe" mais tu doit simplement l'indiquer clairement, genre

uninitialized int8 array[128];
Indique au compilateur qu'on sait que ce tableau n'est pas initialisé par default, et que c'est le comportement voulu. Si le mot clef uninitialized n'est pas mis et que des acces avant initialisation sont fait le compilateur produira une erreur.

La majorite des choses que propose ce language sont "zero cost" dans le sens ou c'est lors de la compilation pas lors de l'execution. Ca limite certain points de securité mais ca permet un minimum.
Un des autres points est cette horreur du C sur les mots clefs pour la definition des variables. Je n'ai pas gardé un seul des mots clefs du C sur ce domaine pour que ca soit clair.

Un autre point est sur les types, pas d'horreur tel que char, int, long, long long long, unsigned, signed, ....

char existe, mais il est reservé au characters, tu ne peux pas ecrire

char c = 42;
les types sont aussi explicite, int8, uint32, etc..

bool existe par default et aussi un type "string", oui en interne ce n'est qu'un tableau (avec en bonus alapascal, la taille de la chaine)

Idem les {} sont obligatoire, pas de
if (foo) bar();
pour eviter certaines classes de bugs a la con comme on a pu voir il y a pas si longtemps.

Idem pour switch(), break est en theorie optionel, mais pas le fallthrough. Contrairement a ce que fait le C.

C'est l'idee de ce language: avoir quelque chose qui est portable, assez bas niveau pour faire du system, portable sur des cibles limité (coucou 6502 et grace a zero-cost), et qui cherche a supprimer toutes les sources d'ambiguité du C classique, et un confort qu'on peux trouver avec certains language de plus haut niveau (genre les methodes associé a une structure), casting plus clair et les casting "dangeureux" utilisent un mot clef particulier etc..


Exemple du "zero-cost"

l'exemple plus haut de la structure foo,

Si on devait convertir en C, la fonction

int8 foo:get() { return self->bar; }
reviendrait a ecrire en C:

int8 foo_get(foo *self) { return self->bar; }
et l'appel de fonction:

a:set(42); -> foo_set(&a, 42);
Et pour finir, si personne ne l'utilise a vrai dire je m'en fout, j'ai 3 raisons pour ce language
1 - J'ai besoin d'un language qui soit utilisable avec le 6502
2 - Idem mais avec le 80(1)86, le C est plus utilisable sur cette cible que le 6502, mais le support des pointeurs far/near est inexistant, et c'est un problem enrome.
3 - Ca m'amuse et j'ai un projet de compilateur depuis des lustres donc c'est une bonne occasion.

Il est aussi possible que j'etende le support du language au banking memoire.
Oh et j'oubliait, il est de base capable de supporter au besoin suivant les cibles les flotant a virgule fixe, les flottant classiques (float/double), juste un des deux, les deux ou aucun.


Edit dernier: Sinon oui le code d'exemple a ete genere par Gemini parceque c'est un bon moyen de savoir si il y a des choses qui clochent ou pas.
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

17

prendre en exemple une période ou tout était écrit en ASM pour défendre Rust est improbable :- D

je dis juste que c'est un classique, devant donc être l'objet d'une attention particulière d'un dev aguerri, on connaît les pièges ..

est ce que ça mérite une obligation de changer de langage car "ça peut arriver" ? pas à mes yeux.
et la le mec il le pécho par le bras et il lui dit '

18

Je ne sais pas ce que tu code, mais certains UB du C/C++ sont facile a declancher et ce n'est pas qu'une question d'experience.

Le C est vraiment traitre sur le sujet.

Et c'est aussi tres vite fait d'avoir du code qui marche marche marche jusqua ce qu'un bug traitre apparaisse et quand tu as des millions de lignes a regarder, c'est pas forcement si simple.
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

19

Perso, même si comme tous les développeurs je fais du code parfait et sans le moindre bug, je ne crache jamais sur les aides automatiques de ce type happy
avatar
<<< Kernel Extremis©®™ >>> et Inventeur de la différence administratif/judiciaire ! (©Yoshi Noir)

<Vertyos> un poil plus mais elle suce bien quand même la mienne ^^
<Sabrina`> tinkiete flan c juste qu'ils sont jaloux que je te trouve aussi appétissant

20

Oui, parce qu'on n'est jamais à l'abri de devoir une fois bosser avec des gens moins bons que soi embarrassed
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

21

Voila ma petite revue rapide de ce que tu nous a présenté :

Globalement la syntaxe est claire. On ressent bien l'envie de faire un langage proche du C dans l'idée mais sans les lourdeurs liées à l'historique et avec des fonctionnalités très bas niveau qui ne paraissent pas bricolées.

Ce qui est vraiment cool et qui manque au C :
- Modules : bien sûr c'est un point indispensable a corriger en C
- les tagged union : depuis que j'ai découvert Rust c'est la fonctionnalité qui me manque le plus dans d'autre langages
- types de base de taille clairement définie
- visibilité
- méthodes associées
- foreach
- switch avancé

Ce dont je suis pas fan :
- forward declaration : plus aucun langage moderne ne fonctionne comme ça.
- compile_if : c'est perturbant de déclarer dans ce qui ressemble à un bloc classique des éléments qui seront visibles en dehors
- la syntaxe de déclaration des tableau à la C, avec les crochets coté variable.

Ce qui a l'air intéressant mais dont je crains que ça pose beaucoup de problème à mettre en oeuvre :
- ABI custom
- L'assembleur inline nécessite généralement pas mal d'info supplémentaires pour bien s'intégrer au code

Ce que j'ai pas parfaitement compris:
- l'intéret du mot clé "base" ne me parait pas clairement expliqué. Est-ce ça signifie que l'on pourra accéder directement aux champs de l'objet contenu ?
- pour le switch, tu dis "It's safe and requires no breaks by default." mais tu utilises des break partout.

Remarques diverses en vrac :
- readonly similaire à constexpr qui est enfin arrivé dans C23
- y a il une raison de ne pas avoir fusionné completement les concepts de enum / tagged union comme en Rust ?
- on dirait que tu utilise toujours des bloc même si il n'y a qu'une instruction. C'est forcé par le langage ? Si oui c'est très bien, mais du coup, pas besoin de garder les parentèses autour des conditions des if, while, switch ,...
avatar

22

robinHood (./5255) :
certes ça peut arriver à tout le monde mais taper une case trop loin sur un pointeur c'est généralement un truc de stagiaire ...
avoir un langage plus blindé accrédite encore plus le fait de coder en permanence les choses pour avant hier au lieu de prendre le temps nécessaire
Il n'y a malheureusement pas que les stagiaires qui déclenchent des UB. Ca arrive tous les jours à des gens très compétents.
Il ne faut pas croire que le développeur C est une espèce mythique qui a toujours le temps de faire bien les choses alors que les autres non. Les contraintes de temps alloué à la qualité dépendent du besoin perçu sur le sujet par les responsables, pas du langage utilisé par le développeur. Alors si le langage peut l'aider au passage, c'est toujours bon a prendre
Pour rester dans le domaine automobile, on sait bien que mettre ta ceinture de sécurité ne fait pas de toi un plus mauvais conducteur, parce que tu te sentirais mieux protégé, au contraire.

robinHood (./5258) :
je dis juste que c'est un classique, devant donc être l'objet d'une attention particulière d'un dev aguerri, on connaît les pièges ..
On peut connaître le piège et quand même tomber dedans, s'il est particulièrement bien caché ou si on manque exceptionnellement d'attention, ce qui peut arriver à tout le monde même aux meilleurs. Le nombre de faille de sécurité qui sont découvertes tous les jours, y compris dans des logiciels sérieux niveau sécurité, en est la preuve éclatante.

robinHood (./5258) :
est ce que ça mérite une obligation de changer de langage car "ça peut arriver" ? pas à mes yeux.
Personne ne dit qu'il faut absolument réécrire tout le C existant en urgence.
Juste qu'il faut bien être conscient des risques et surtout ne pas dire : "Nous on est bons, ça ne nous concerne pas."
avatar

23

> Il n'y a malheureusement pas que les stagiaires qui déclenchent des UB. Ca arrive tous les jours à des gens très compétents.

mais c'est rare, non ce n'est pas tous les jours, "généralement" ton programme plante direct, et tu dois tester ton code
si c'est ultra planqué c'est aussi peut être que la structure n'est pas assez simple ou lisible

> Il ne faut pas croire que le développeur C est une espèce mythique qui a toujours le temps de faire bien les choses alors que les autres non. Les contraintes de temps alloué à la qualité dépendent du besoin perçu sur le sujet par les responsables, pas du langage utilisé par le développeur. Alors si le langage peut l'aider au passage, c'est toujours bon a prendre

non c'est l'inverse, ce n'est pas le C qui à plus de temps c'est l'autre qui en aura moins "le langage est safe, donc bat lec code deux fois plus vite"

> On peut connaître le piège et quand même tomber dedans, s'il est particulièrement bien caché ou si on manque exceptionnellement d'attention, ce qui peut arriver à tout le monde même aux meilleurs. Le nombre de faille de sécurité qui sont découvertes tous les jours, y compris dans des logiciels sérieux niveau sécurité, en est la preuve éclatante.

oui, "c'est possible, ça arrive" et donc ?

la trisomie arrive => go le 100% éprouvette
planter ton c15 au bout de 8 ans arrive => go 100% voiture autonome
les dépassement de tampon arrivent => go interdire le C et tout réécrire

il n'y a jamais eu de fin du monde jusqu'ici, mais pour le coup ça a permis de hacker la plupart des consoles, téléphones, ou votre Ti

au contraire de ce que transpire vos réponses, je ne me prétend pas meilleur, loin de la
c'est juste qu'en réalité en voulant interdire les pointeurs vous attaquez frontalement ma méthode personnelle de dev (et vous enlevez le fun absolu)
généralement je n'utilise pas de for ni de variable intermédiaire, je calcule les adresses de fin et fais des while, partout, bref calculer la fin d'un buffer c'est mon quotidien
(tester mon code derrière est obligatoire et en profondeur (sans jamais avoir écrit un seul test unitaire, tout au printf, flagellez moi)) alors bien sur je dois en laisser passer, mais c'est rare, dois je alors donner la main à autre chose qui va wrapper mon code ? non.

on dirait que le langage est le graal et va vous éviter tous les bugs du monde, vous faites la révolution et foutez à la poubelle la référence absolue depuis 60 ans juste pour un buffer overflow de temps en temps
et la le mec il le pécho par le bras et il lui dit '

24

Il y en a aussi qui cherchent à rendre le C plus sûr, mais ça a un coût en performance que du Rust bien écrit - dont la spécification est plus explicite - n'a pas:
GitHub - pizlonator/llvm-project-deluge: Fil-CGitHubFil-C. Contribute to pizlonator/llvm-project-deluge development by creating an account on GitHub.
.
Filip Jerzy Pizło est très bon dans son domaine.
avatar
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

25

robinHood (./5264) :
mais c'est rare, non ce n'est pas tous les jours, "généralement" ton programme plante direct, et tu dois tester ton code
si c'est ultra planqué c'est aussi peut être que la structure n'est pas assez simple ou lisible
Je ne voulais pas dire que ça arrive à tout le monde plusieurs fois par jours, mais que des failles de sécurité liées à ça sont découvertes tous les jours dans les logiciels les plus sérieux sur la sécurité, donc ça n'est clairement pas qu'une affaire de débutant.
Tu as peut être la chance de travailler sur du code simple, mais ça n'est malheureusement pas toujours le cas. On ne maîtrise pas toujours tout le code sur lequel on travaille, et le langage C ne rend pas forcément la base de code que tu hérites meilleure, loin de là.

robinHood (./5264) :
non c'est l'inverse, ce n'est pas le C qui à plus de temps c'est l'autre qui en aura moins "le langage est safe, donc bat lec code deux fois plus vite"
Ca dépend peut-être des sociétés, mais je peux te dire que c'est pas du tout ce que je constate. J'ai vu beaucoup plus de projets Java avec des politiques de qualité sérieuse que des project C ou C++.
Le sérieux dépend vraiment des moyens qu'acceptent d'y mettre les responsables. Et souvent quand on choisit un langage pour des raisons de sécurité, c'est justement parce qu'on s'en soucie. Quand on s'en moque, on se fiche pas mal du langage tant qu'on produit vite.

robinHood (./5264) :
la trisomie arrive => go le 100% éprouvette
planter ton c15 au bout de 8 ans arrive => go 100% voiture autonome
les dépassement de tampon arrivent => go interdire le C et tout réécrire
Au risque de me répéter : Personne ne dit qu'il faut absolument réécrire tout le C existant en urgence. Juste qu'il faut bien être conscient des risques et surtout ne pas dire : "Nous on est bons, ça ne nous concerne pas."
Toute technologie comportes des risque sur différent points à différents niveaux : il faut juste bien les évaluer et prendre les décisions en conséquence. On ne va bien sur pas récrire sans raison un code de qualité, utilisé dans un domaine pas particulièrement risqué.
Par contre quand on construit un nouveau langage, ça serait dommage de ne pas prendre en compte la sécurité, autant que possible au vu des objectifs.

robinHood (./5264) :
c'est juste qu'en réalité en voulant interdire les pointeurs vous attaquez frontalement ma méthode personnelle de dev (et vous enlevez le fun absolu)
généralement je n'utilise pas de for ni de variable intermédiaire, je calcule les adresses de fin et fais des while, partout, bref calculer la fin d'un buffer c'est mon quotidien
Encore une fois on n'impose rien a personne, on dit juste qu'il faut être conscient des problèmes potentiels.
Oui le C pose des risque supplémentaires comparé à d'autres langages, et si tu peux faire avec tant mieux pour toi, utilise le. Par contre, quelqu'un qui dit que le C ne pose pas de risque voire permet de le réduire m’inquiète.

robinHood (./5264) :
on dirait que le langage est le graal et va vous éviter tous les bugs du monde, vous faites la révolution et foutez à la poubelle la référence absolue depuis 60 ans juste pour un buffer overflow de temps en temps
On parle du langage parce qu'on est sur le sujet des langages, mais bien sur que ce n'est qu'un élément parmi d'autres. Et encore une fois on ne dit pas de mettre tout le C existant à la poubelle.
avatar

26

Heureusement que les commandes de vol des avions ne sont pas écrits en C... heu si en fait. (Ok je sors).

27

Certes, mais en général ce genre de logiciel est fait avec des niveaux de vérifications qu'on applique pas sur la très grande majorité des logiciels C.
avatar

28

Certain points on probablement deja ete repondu plus tot, mais je vais le faire ici quand meme, et merci pour la revue rapide.


Uther (./5262) :
Voila ma petite revue rapide de ce que tu nous a présenté :

Globalement la syntaxe est claire. On ressent bien l'envie de faire un langage proche du C dans l'idée mais sans les lourdeurs liées à l'historique et avec des fonctionnalités très bas niveau qui ne paraissent pas bricolées.

Ce qui est vraiment cool et qui manque au C :
- Modules : bien sûr c'est un point indispensable a corriger en C
- les tagged union : depuis que j'ai découvert Rust c'est la fonctionnalité qui me manque le plus dans d'autre langages
- types de base de taille clairement définie
- visibilité
- méthodes associées
- foreach
- switch avancé

Ce dont je suis pas fan :
- forward declaration : plus aucun langage moderne ne fonctionne comme ça.
A vrai dire c'est une erreur dans le code presenté ici. Tu peux le faire, le language ne l'interdit pas, mais ce n'est pas utile non plus. Le seul cas ou c'est indispensable c'est pour des API externes.

Uther (./5262) :

- compile_if : c'est perturbant de déclarer dans ce qui ressemble à un bloc classique des des éléments qui seront visibles en dehors
Il n'y a pas de preprocesseur, c'est un mot clef comme un autre.

C'est un equivalent de #ifdef X / #else / #endif et je ne suis pas vraiment sur de comment faire autrement, mais si tu as des idees je prends. Je ne suis pas fan de la compilation conditionelle, mais il y a des cas ou ca reste utile.

Uther (./5262) :

- la syntaxe de déclaration des tableau à la C, avec les crochets coté variable.
?

int8 tableau[42] ?

Ou l'acces a la variable

tableau[x] = 0 ?

Je ne vois pas trop comemnt faire, perso "tableau(42)" est une appel de fonction, pas la definition d'un tableau, et ca reste inspire du C, perso cette syntaxe ne me derange pas du tout. Mais c'est une question de gout plus qu'autre chose.

Uther (./5262) :

Ce qui a l'air intéressant mais dont je crains que ça pose beaucoup de problème à mettre en oeuvre :
- ABI custom
C'est optionel, et c'est surtout pour pouvoir s'interfacer avec de l'existant, par exemple sur une machine pouvoir taper dans la ROM, ou, et surtout ou, le code original est en assembleur, l'ABI est souvent tordue.

Uther (./5262) :

- L'assembleur inline nécessite généralement pas mal d'info supplémentaires pour bien s'intégrer au code
A quoi penses tu? Si tu pense a la syntax de GCC, c'est GCC qui est tordu. Avoir une visibilite sur les variables et autre fonction est deja pas mal je pense.

Uther (./5262) :

Ce que j'ai pas parfaitement compris:
- l'intéret du mot clé "base" ne me parait pas clairement expliqué. Est-ce ça signifie que l'on pourra accéder directement aux champs de l'objet contenu ?

C'est pour pouvoir faire une forme de polymorphisme. La structure, ou element marque "base" est une structure qui est "safe" pour caster la structure en question. Ce n'est pas represente ici, mais le language supporte d'ajouter des fonction pour faire un cast safe vers un autre type genre

struct Entity { [...] }; struct Player { base Entity entity; [...] }; struct Cat { base Entity entity; [...] }; struct Dog { base Entity entity; [...] }; Player Cat:as_Player() readonly { // retourner un "Player" valid avec les donnée de la structure Cat courante } // Valide: Entity e = Player as Entity; // Valide avec la methode "as_Player": Player p = Cat as Player; // Mais Dog n'a pas de as_Player donc ceci est invalide: Player p2 = Dog as Player;
Uther (./5262) :

- pour le switch, tu dis "It's safe and requires no breaks by default." mais tu utilises des break partout.
A vrai dire c'est plus un moins un erreur sur le commentaire.

Break est implicite, mais ca reste une bonne chose que de l'ecrire.

Uther (./5262) :

Remarques diverses en vrac :
- readonly similaire à constexpr qui est enfin arrivé dans C23
- y a il une raison de ne pas avoir fusionné completement les concepts de enum / tagged union comme en Rust ?
Pour moi ce sont deux choses differentes, un est juste une enumeration de valeurs avec un nom associe, l'autre est contiens de maniere normal des donnée.

Uther (./5262) :

- on dirait que tu utilise toujours des bloc même si il n'y a qu'une instruction. C'est forcé par le langage ? Si oui c'est très bien, mais du coup, pas besoin de garder les parentèses autour des conditions des if, while, switch ,...
Les {} sont forcé par le language en effet.

Quand aux (), c'est un des points de python que je n'aime pas, et encore une fois c'est inspiré du C, et c'est la logique de l'utiliser en C, ca ne *me* pose pas de probleme, c'est une question de gout comme souvent j'imagine, mais perso ca permet de marquer explicitement la valeur a tester
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

29

Les tableaux ont la taille associée, comme en Ada ? On dirait que tu fais ça avec les strings en tous cas, mais quid des tableaux ? C'est pas mal ça justement pour éviter les bugs les plus courants.


Comment tu vas gérer le polymorphisme si les objets sont dans des unités de compilation différentes ? (Fichiers .c/.h différents)

Si tu respectes en général le format de C, alors je ne rendrais pas break; optionnel, ça peut être confusant (que break soit le défaut et fallthrough optionnel) pour quelqu'un de non initié.
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

30

Godzil (./28) :
C'est un equivalent de #ifdef X / #else / #endif et je ne suis pas vraiment sur de comment faire autrement, mais si tu as des idees je prends. Je ne suis pas fan de la compilation conditionelle, mais il y a des cas ou ca reste utile.
Je pense qu'une syntaxe de type attribut ferait le boulot. Quelque chose du genre : conditional(DEBUG_MODE) void log_message(string msg)
Godzil (./28) :
int8 tableau[42] ? Ou l'acces a la variable tableau[x] = 0
Je n'ai pas de soucis avec la syntaxe d'indexation, je parlais de la déclaration. La fait d'etre un tableau est une propriété du type pas de la variable elle même.
La plupart des langage qui ont repris la syntaxe du C++ ont quand même corrigé ce point.

- En Java ou C#, on à : byte[] tableau = new byte[42];
- En Rust où on a des tableaux statiques on a : let tableau : [u8; 42];

Godzil (./28) :
A quoi penses tu? Si tu pense a la syntax de GCC, c'est GCC qui est tordu. Avoir une visibilite sur les variables et autre fonction est deja pas mal je pense.
Je suis pas un expert de l'ASM et encore moins de son intégration avec d'autre langage. Mais si GCC s'est embeté avec un syntaxe si lourde, c'est pas par plaisir. Les compilateur avancés ont besoin d'avoir des informations sur les effets de bord. J'ai suivi de loin l'introduction de la fonctionnalité en Rust et ça a été compliqué. Peut être que ton langage est suffisamment simple pour que ça ne pose pas de soucis, mais i tu veux utiliser LLVM, je pense que tu vas avoir besoin de ce genre d'info.

Godzil (./28) :A vrai dire c'est plus un moins un erreur sur le commentaire.
Break est implicite, mais ca reste une bonne chose que de l’écrire.
Tout l’intérêt du break dans le switch et d'éviter le passage d'une case à l'autre. Si ça n'est pas possible, c'est juste trompeur. Si tu tiens a avoir une démarcation claire du début et de la fin d'un case, des crochets me paraissent plus adaptés que ":" et "break" qui sont un héritage du goto.

Godzil (./28) :
Quand aux (), c'est un des points de python que je n'aime pas, et encore une fois c'est inspiré du C, et c'est la logique de l'utiliser en C, ca ne *me* pose pas de probleme, c'est une question de gout comme souvent j'imagine, mais perso ca permet de marquer explicitement la valeur a tester
Je remontais juste ça, car je sais qu'en Rust il les avaient laissé au début quand ils ont rendu les blocs obligatoires, avant de se rendre compte qu'il ne servaient plus a rien et de les enlever sans regret.
Mais si tu préfère comme ça, pas de soucis. Il y a beaucoup d'autre chose que je verrais différemment par préférence personnelle et que je n'ai pas remonté car je sais que c'est juste des problèmes de ressenti.
avatar