If two data structures are different, they should be written as different.
– Niklaus Wirth, inventeur du langage Pascal
Ce document est le manuel utilisateur de ProtoScript V2.
Il est descriptif. La spécification SPECIFICATION.md reste la source normative.
Philosophie directrice : La magie cache les coûts. ProtoScript les rend visibles.
Public cible : Ce manuel s'adresse à des développeurs ayant déjà une expérience de langages impératifs (C, JS, PHP, Java), et suppose une familiarité avec les notions de typage statique et de compilation.
ProtoScript V2 est un langage statiquement typé, déterministe, prototype-based, conçu pour une compilation bas niveau (notamment vers C) sans sémantique cachée.
- Spécification (
SPECIFICATION.md) : règles normatives (ce qui est autorisé/interdit). - Manuel (ce document) : guide pratique (comment écrire du code correct et lisible).
import Io;
function main() : void {
Io.printLine("Hello world");
Io.print("Hello world".concat(Io.EOL));
Io.print(["Hello", " ", "world", Io.EOL].concat());
}Ref: EX-001
function main() {
Io.printLine("Hello");
}Ce code est invalide : le type de retour est obligatoire.
Le langage préfère une surface explicite dès le premier exemple : type de retour visible, point d'entrée explicite, aucun comportement implicite.
ProtoScript V2 vise la transparence des coûts.
Au-delà des règles syntaxiques et sémantiques, ce manuel indique, à titre informatif, deux métriques complémentaires :
- Complexité algorithmique
- Impact énergétique estimé
Ces indicateurs sont pédagogiques.
Ils ne font pas partie de la spécification normative et peuvent évoluer selon l’implémentation du runtime.
Ils ont pour objectif de rendre visibles :
- la croissance asymptotique d’une opération
- son profil énergétique typique
Notes :
- Cette classification décrit un ordre de grandeur.
- Les constantes multiplicatives ne sont pas représentées.
- Elle ne reflète ni la localité mémoire ni les coûts de cache.
- Elle ne constitue pas une garantie contractuelle.
Notes :
- L’impact énergétique dépend du runtime et de la plateforme.
- Il peut différer de la complexité asymptotique.
- Il ne constitue pas une mesure électrique physique.
Une complexité algorithmique faible n’implique pas un impact énergétique minimal.
| Situation | Complexité | Impact énergétique |
|---|---|---|
| O(log n) avec accès mémoire dispersé | ||
| O(n) séquentiel | ||
| O(1) avec réallocation |
Les deux indicateurs sont complémentaires.
Un fichier contient des déclarations (imports, prototypes, fonctions, déclarations autorisées par la grammaire).
- Chaque instruction se termine par
;. - Les blocs utilisent
{ ... }. - La portée est lexicale.
Exemple :
import Io;
function main() : void {
int x = 1;
{
int y = 2;
Io.printLine(y.toString());
}
Io.printLine(x.toString());
}Ref: EX-002
// commentaire ligne
/* commentaire bloc */Ref: EX-003
- Pas de balises.
- Pas d'HTML embarqué (contrairement à l'usage historique de PHP).
Oublier ; en fin d'instruction. ProtoScript n'a pas d'insertion automatique de point-virgule.
Le typage est statique et explicite. Les types sont résolus à la compilation.
boolbyteintfloatglyphstring
Ces types sont immuables au niveau langage, manipulés par valeur, sans délégation (sans héritage) ni champs utilisateur.
Exemples :
bool ok = true;
byte b = 255;
int n = 42;
float f = 3.14;
glyph g = "A"[0];
string s = "abc";Ref: EX-004
ProtoScript2 distingue byte, int, float.
Les littéraux numériques sont non typés et leur type est déterminé par le contexte.
En l’absence de contexte, le type par défaut est int.
La conversion explicite se fait avec la forme :
(T)exproù T est un type numérique (byte, int, float).
Règles :
- aucune conversion implicite n’est autorisée entre types numériques ;
- une conversion explicite est autorisée uniquement si la valeur est exactement représentable dans
T; - toute conversion entraînant dépassement, troncature, wrap ou saturation est une erreur statique.
Exemples valides :
byte a = 255;
byte b = (byte)255;
int c = (int)3.0;
float d = (float)42;Exemples invalides :
byte a = 256;
byte b = (byte)256;
int c = (int)3.14;Il n'y a pas de nullité universelle.
Contre-exemple :
// invalide : `null` n'est pas une valeur du langage
string s = null; // Erreur : E2001 UNRESOLVED_NAMERef: EX-005
Cette approche rend l'absence explicite et statiquement typée, sans introduire de nullité implicite.
Quand un type "vide" est nécessaire, on utilise un prototype explicite avec un indicateur statique.
Exemple (chaîne nullable) :
import Io;
prototype NullableString {
bool is_null;
string value;
function isNull() : bool {
return self.is_null;
}
}
function main() : void {
NullableString a = NullableString.clone();
a.is_null = true;
NullableString b = NullableString.clone();
b.is_null = false;
b.value = "ok";
if (a.isNull()) {
Io.printLine("empty");
}
if (!b.isNull()) {
Io.printLine(b.value);
}
}Ref: EX-006
Le prototype standard JSONValue représente un JSON complet, y compris null.
On utilise les constructeurs explicites du module JSON.
Exemple :
import JSON;
import Io;
function main() : void {
JSONValue v = JSON.null();
if (v.isNull()) {
Io.printLine("json null");
}
JSONValue obj = JSON.object({
"a": JSON.null(),
"b": JSON.number(1)
});
string s = JSON.encode(obj);
Io.printLine(s);
}Ref: EX-007
Toute variable, champ ou valeur allouée est implicitement initialisée au moment de sa création. Il n’existe pas d’état non initialisé observable dans le langage.
Valeurs par défaut :
- bool → false
- byte → 0
- int → 0
- float → 0.0
- glyph → U+0000
- string → ""
Exemple :
function main() : void {
int x;
Io.printLine(x.toString()); // affiche "0"
}Ref: EX-008
int n = 12;
string s = n.toString();
float f = s.toFloat();Ref: EX-010
Supposer qu'un int se convertit implicitement en string dans un appel. Les conversions restent explicites.
L'absence de null et de conversions implicites réduit les branches cachées et rend les erreurs plus locales.
- Décimal, hexadécimal (
0x), binaire (0b), octal (0...). - Le signe
-est un opérateur unaire.
int a = 10;
int b = 0x2A;
int c = 0b1010;
int d = -5; // unaire '-' appliqué à 5Ref: EX-011
float f1 = 1.5;
float f2 = 1e-3;Ref: EX-012
string s = "Bonjour";Ref: EX-013
Échappements reconnus dans les littéraux de chaîne :
string q = "\""; // guillemet
string b = "\\"; // antislash
string n = "\n"; // LF
string t = "\t"; // TAB
string r = "\r"; // CR
string bs = "\b"; // BS
string ff = "\f"; // FF
string u = "\u263A"; // Unicode (☺)Ref: EX-014
list<int> xs = [1, 2, 3];
map<string, int> mm = {"a": 1, "b": 2};Ref: EX-015
list<int> xs = [];
map<string, int> mm = {};Ref: EX-016
Contre-exemple :
var x = []; // Erreur : E3006 MISSING_TYPE_CONTEXT
var m = {}; // Erreur : E3006 MISSING_TYPE_CONTEXTRef: EX-017
Confondre {} map vide avec un bloc vide. Dans une expression, {} est un littéral de map.
var n = 10;
int x = 20;Ref: EX-018
var déclenche une inférence locale du type à partir de l'initialiseur.
Le type reste statique et connu à la compilation.
Une déclaration var doit donc toujours avoir une valeur d'initialisation.
Exemple :
var s = "ok"; // s : string
var n = 12; // n : intRef: EX-019
Contre-exemple :
var x; // Erreur : E1001 PARSE_UNEXPECTED_TOKENRef: EX-020
const est réservé aux types scalaires fondamentaux et impose une initialisation immédiate.
La valeur est ensuite non réassignable (affectations simples, composées, ++/--).
Exemple :
const int Max = 10;
const string Name = "ps2";Ref: EX-103
Contre-exemples :
const int x; // Erreur : E3130 CONST_REASSIGNMENT
const int y = 1;
y = 2; // Erreur : E3130 CONST_REASSIGNMENTRef: EX-104
function main() : void {
int x = 1;
{
int x = 2; // shadowing local
Io.printLine(x.toString()); // 2
}
Io.printLine(x.toString()); // 1
}Ref: EX-021
Règle normative :
- Dans un même bloc de portée, un identifiant local ne peut être déclaré qu’une seule fois.
- Violation:
E3131 REDECLARATION. - Cette règle supprime les ambiguïtés d’initialisation et garantit le même comportement entre backends JS et C.
Exemple INVALID :
import Io;
function main(): void {
string t = Io.tempPath();
TextFile t = Io.openText(t, "w"); // E3131 REDECLARATION
}Exemple VALID (shadowing en bloc interne) :
import Io;
function main(): void {
string t = Io.tempPath();
{
TextFile t = Io.openText(t, "w");
}
}Dans ProtoScript2, une variable est toujours initialisée à une valeur par défaut déterministe au moment de sa création. Il n’existe pas d’état non initialisé observable.
Exemple :
function main() : void {
int x;
Io.printLine(x.toString()); // affiche "0"
}Ref: EX-022
Exemple (champ de prototype) :
prototype P { int n; }
function main() : void {
P p = P.clone();
Io.printLine(p.n.toString()); // affiche "0"
}Ref: EX-023
- Pas de variable dynamique nommée à l'exécution.
- Pas de superglobale (variable globale prédéfinie, accessible partout sans déclaration explicite).
En JS/PHP, des accès à des noms dynamiques peuvent exister. Ici, la résolution est réalisée à la compilation (compile-time).
Un group définit un ensemble de constantes nommées d’un type scalaire fondamental.
Il ne crée aucun type nominal. En revanche, la déclaration produit un descripteur nominal accessible par son nom (par exemple Color) et utilisable comme valeur (notamment pour Debug.dump).
Forme :
T group Nom {
Member = ExprConstante,
...
}Règles pratiques :
Test un type scalaire fondamental (bool,byte,glyph,int,float,string).- chaque membre est typé
T. - l’expression doit être constante (réduite par le folding existant).
- un membre de
groupn’est jamais assignable. - aucun modificateur n’est autorisé sur un
group. - le nom du
group(Color) désigne un descripteur du groupe ; ce descripteur n’est pas un type et n’a pas d’opérations.
Exemple :
int group Colors {
Red = 1,
Blue = Red + 3
}
function main() : void {
int x = Colors.Blue;
}Erreurs fréquentes :
- type non scalaire (
E3120) - membre non assignable à
Tou expression non constante (E3121) - tentative de mutation (
E3122)
Littéraux, identifiants, appels, accès indexés, accès membres, opérations unaires/binaires, ternaire.
L'évaluation est de gauche à droite.
&& et || court-circuitent.
function left() : bool { Io.printLine("L"); return false; }
function right() : bool { Io.printLine("R"); return true; }
function main() : void {
bool v = left() && right(); // affiche seulement L
}Ref: EX-024
int a = 1;
int b = 2;
int m = (a < b) ? a : b;Ref: EX-025
- L'affectation est une instruction.
- Elle n'a pas de valeur de retour.
- L'affectation chaînée est invalide.
Contre-exemple :
// invalide
// int x = (a = 1); // Erreur : E1001 PARSE_UNEXPECTED_TOKEN
// a = b = c;Ref: EX-026
Interdire l'affectation en expression supprime une source classique d'effets de bord implicites.
int a = 4;
int b = 2;
int c = a + b;
bool k = (a > b) && (b != 0);
int s = a << 1;Ref: EX-027
- Arithmétiques :
+ - * / % - Comparaison :
== != < <= > >= - Logiques :
&& || ! - Bitwise :
& | ^ ~ << >> - Affectation :
= += -= *= /= - Incrémentation / décrémentation :
++ --
| Exemple | Nom | Résultat |
|---|---|---|
a + b |
Addition | Somme de a et b. |
a - b |
Soustraction | Différence de a et b. |
a * b |
Multiplication | Produit de a et b. |
a / b |
Division | Quotient de a et b. |
a % b |
Modulo | Reste de a / b. |
Notes :
- opérateurs arithmétiques valides sur
int,float,byte(pas surstring). - division par zéro → exception runtime (
R1004/RUNTIME_DIVIDE_BY_ZERO). - overflow sur
int/byte→ exception runtime (R1001/RUNTIME_INT_OVERFLOW). - pas de promotion implicite :
int + floatest interdit → erreur statiqueE3001(TYPE_MISMATCH_ASSIGNMENT). Pour mélanger les types, il faut convertir explicitement l’un des deux (ex.x.toFloat()). a / bsurintest une division entière ; surfloat, division flottante.a % best défini pourint/byteuniquement.-xest un opérateur unaire ;-INT_MINdéclenche une exception runtime (R1001/RUNTIME_INT_OVERFLOW).- précédence (rappel) :
* / %avant+ -; les opérateurs unaires ont une précédence plus haute. La table complète est en annexe.
| Exemple | Nom | Résultat |
|---|---|---|
a == b |
Égal | true si a est égal à b (types compatibles, pas de conversion implicite). |
a != b |
Différent | true si a est différent de b (types compatibles, pas de conversion implicite). |
a < b |
Plus petit que | true si a est strictement plus petit que b. |
a > b |
Plus grand que | true si a est strictement plus grand que b. |
a <= b |
Inférieur ou égal | true si a est plus petit ou égal à b. |
a >= b |
Supérieur ou égal | true si a est plus grand ou égal à b. |
Notes :
- comparaisons autorisées uniquement entre types identiques.
- comparer des types différents est invalide → erreur statique
E3001(TYPE_MISMATCH_ASSIGNMENT). - pour comparer des types différents, il faut convertir explicitement l’un des deux.
- pour
string:==/!=comparent le contenu exact ;</<=/>/>=comparent lexicographiquement la séquence UTF‑8 (pas de locale, pas de normalisation). - pour les types structurés (objets/prototypes,
list,map,slice,view), la comparaison porte sur l’identité de valeur (pas de deep compare implicite).
| Exemple | Nom | Résultat |
|---|---|---|
!a |
Not (Non) | true si a n'est pas true. |
a && b |
And (Et) | true si a ET b sont true (court-circuit). |
| `a | b` |
Notes :
- opérandes obligatoirement de type
bool. - aucun transtypage implicite (
int,string, etc. sont interdits ici). - type invalide → erreur statique
E3001(TYPE_MISMATCH_ASSIGNMENT). - évaluation court‑circuitée pour
&&et||. - précédence (rappel) :
!avant&&, puis||.
| Exemple | Nom | Résultat |
|---|---|---|
a & b |
And (Et) | Bits à 1 dans a ET dans b restent à 1. |
| `a | b` | Or (Ou) |
a ^ b |
Xor (Ou exclusif) | Bits à 1 dans a OU dans b mais pas dans les deux. |
~a |
Not (Non) | Inversion bit à bit de a. |
a << b |
Décalage à gauche | Décale les bits de a de b positions vers la gauche. |
a >> b |
Décalage à droite | Décale les bits de a de b positions vers la droite. |
Notes :
- opérandes int/byte uniquement (pas de
float, pas destring). - type invalide → erreur statique
E3001(TYPE_MISMATCH_ASSIGNMENT). - décalage hors plage (
b < 0oub >= 64) poura << b/a >> b→ exception runtimeR1005(RUNTIME_SHIFT_RANGE). - précédence :
~><</>>>&>^>|.
| Exemple | Équivalent | Opération |
|---|---|---|
a = b |
a = b |
Affectation simple. |
a += b |
a = a + b |
Addition. |
a -= b |
a = a - b |
Soustraction. |
a *= b |
a = a * b |
Multiplication. |
a /= b |
a = a / b |
Division. |
Notes :
- opérandes numériques uniquement (
int,float,byte) selon l’opération sous‑jacente. - type invalide → erreur statique
E3001(TYPE_MISMATCH_ASSIGNMENT). - pas de conversion implicite (ex.
int += floatinterdit ; conversion explicite requise). - les mêmes erreurs runtime que pour les opérations arythmétiques s’appliquent (overflow, division par zéro).
| Exemple | Équivalent | Opération |
|---|---|---|
++a |
Pré-incrémente | Incrémente a de 1, puis retourne a. |
a++ |
Post-incrémente | Retourne a, puis incrémente a de 1. |
--a |
Pré-décrémente | Décrémente a de 1, puis retourne a. |
a-- |
Post-décrémente | Retourne a, puis décrémente a de 1. |
En contexte d'expression, la forme pré/post indique si la modification intervient avant ou après l'utilisation de la valeur.
Contre-exemple :
// invalide selon la spec
// string s = "a" + "b"; // Erreur : E3001 TYPE_MISMATCH_ASSIGNMENTRef: EX-028
Utiliser la concaténation explicite disponible par API/méthode.
Exemple correct de concaténation explicite :
string a = "Hello ";
string b = "world";
string c = a.concat(b);
// Astuce
string d = ["Hello", " ", "world"].concat();Ref: EX-029
Traiter + ou . comme concaténation universelle (réflexe JS/PHP). En ProtoScript V2, le code doit rester explicite.
if (x > 0) {
Io.printLine("pos");
} else {
Io.printLine("non-pos");
}Ref: EX-030
Le bloc est optionnel si la branche contient une seule instruction.
if (x > 0)
Io.printLine("pos");Ref: EX-031
Exemple avec else if :
if (x > 0) {
Io.printLine("pos");
} else if (x < 0) {
Io.printLine("neg");
} else {
Io.printLine("zero");
}Ref: EX-032
ProtoScript V2 propose des boucles classiques et des boucles d'itération.
while (cond) {
// ...
}Ref: EX-033
do {
// ...
} while (cond);Ref: EX-034
for (int i = 0; i < 10; i++) {
// ...
}Ref: EX-035
Exemples d'itération indexée :
list<int> xs = [10, 20, 30];
for (int i = 0; i < xs.length(); i = i + 1) {
Io.printLine(xs[i].toString());
}Ref: EX-036
string s = "abc";
for (int i = 0; i < s.length(); ++i) {
glyph g = s[i];
Io.printLine(g.toString());
}Ref: EX-037
Note :
map<K,V> ne s'itère pas par index. Utiliser for ... of (valeurs) ou for ... in (clés).
Alternative explicite : récupérer les clés puis itérer sur la liste de clés.
map<string, int> m = {"a": 1, "b": 2};
list<string> ks = m.keys();
for (int i = 0; i < ks.length(); i++) {
int v = m[ks[i]];
Io.printLine(v.toString());
}Ref: EX-038
for ... of itère sur les valeurs d'une structure itérable.
list<int> xs = [1, 2, 3];
for (int v of xs) {
Io.printLine(v.toString());
}Ref: EX-039
Sur string, for ... of itère sur les glyphes :
string s = "a😀b";
for (glyph g of s) {
Io.printLine(g.toString());
}Ref: EX-040
Sur map<K,V>, for ... of itère sur les valeurs V :
map<string, int> m = {"a": 1, "b": 2};
for (int v of m) {
Io.printLine(v.toString());
}Ref: EX-041
for ... in itère sur les clés d'une map (et uniquement une map).
map<string, int> m = {"a": 1, "b": 2};
for (string k in m) {
Io.printLine(k);
}Ref: EX-042
Contre-exemple :
list<int> xs = [1, 2, 3];
// invalide : `for ... in` ne s'applique pas à `list<T>`
// for (int v in xs) { ... } // Erreur : E2001 UNRESOLVED_NAME
string s = "abc";
// invalide : `for ... in` ne s'applique pas à `string`
// for (glyph g in s) { ... }Ref: EX-043
Erreur fréquente :
Confondre for ... of (valeurs) et for ... in (clés).
break et continue sont disponibles dans les boucles :
breaksort immédiatement de la boucle courante.continuepasse directement à l'itération suivante.
Exemple break :
list<int> xs = [1, 2, 3, 4];
for (int v of xs) {
if (v == 3) {
break;
}
Io.printLine(v.toString());
}Ref: EX-044
Exemple continue :
for (int i = 0; i < 5; i++) {
if (i == 2) {
continue;
}
Io.printLine(i.toString());
}Ref: EX-045
switch (x) {
case 1:
Io.printLine("one");
break;
default:
Io.printLine("other");
break;
}Ref: EX-046
Chaque case / default doit se terminer par une instruction de terminaison explicite :
break: quitte leswitchreturn: quitte la fonctionthrow: lève une exception
Contre-exemple :
switch (x) {
case 1:
Io.printLine("one"); // invalide sans terminaison explicite
default:
Io.printLine("other");
break;
} // Erreur : E2001 UNRESOLVED_NAMERef: EX-047
Contre-exemple (fallthrough implicite) :
switch (x) {
case 1:
case 2:
Io.printLine("one and two"); // invalide : fallthrough implicite
break;
default:
Io.printLine("other");
break;
} // Erreur : E2001 UNRESOLVED_NAMERef: EX-048
Reproduire un style C classique avec fallthrough implicite. ProtoScript V2 le refuse pour éviter les effets de bord implicites et imposer des branches de contrôle lisibles et explicitement terminées.
function add(int a, int b) : int {
return a + b;
}Ref: EX-049
- Paramètres explicitement typés.
- Type de retour explicite (même pour
void). - Pas de paramètres optionnels implicites.
Contre-exemple :
// invalide : paramètres par défaut non supportés
// function greet(string name = "world") : void { // Erreur : E1001 PARSE_UNEXPECTED_TOKEN
// Io.printLine(name);
// }Ref: EX-050
function sum(list<int> values...) : int {
int acc = 0;
for (int v of values) {
acc = acc + v;
}
return acc;
}Ref: EX-051
Appels valides :
int r = sum(1, 2, 3);
int r = sum(); // variadique vide => liste videRef: EX-052, EX-053
Note :
Un appel variadique peut être vide. Dans ce cas, la liste capturée est vide (view<T> de longueur 0).
Les méthodes variadiques utilisent exactement le même mécanisme que les fonctions variadiques :
- la syntaxe de déclaration reste
list<T> ... - le type canonique interne capturé est
view<T> - la vue capturée peut être vide
- aucune allocation implicite n’est introduite
Exemple :
import Io;
prototype P {
function count(list<int> xs...) : int {
return xs.length();
}
}
function main() : void {
P p = P.clone();
Io.printLine(p.count().toString()); // 0
Io.printLine(p.count(1, 2, 3).toString()); // 3
}- Pas de fonctions comme valeurs.
- Pas de génériques de fonctions.
Pas de closures/fonctions anonymes comme valeurs de premier ordre. Les appels sont résolus statiquement.
ProtoScript V2 adopte un modèle orienté objet prototype-based, et rejette explicitement le modèle class-based.
Il n’existe dans ProtoScript V2 :
-
ni classes,
-
ni instances de classes,
-
ni hiérarchie d’héritage dynamique,
-
ni mécanisme de construction implicite.
Le choix fondamental est celui du prototype concret.
Un prototype n’est pas une abstraction ni un type théorique :
c’est un objet gabarit explicite, entièrement défini à la compilation, servant de base à la création d’objets par clonage.
Le modèle objet de ProtoScript V2 repose sur les principes suivants :
-
un objet est créé par clonage d’un prototype explicite
-
la structure d’un prototype (champs, méthodes, relations) est figée à la compilation
-
la résolution des champs et des méthodes est statique
-
les relations entre prototypes sont déclaratives et non dynamiques
Ce modèle élimine toute forme de magie d’héritage ou de résolution tardive.
Il privilégie des structures lisibles, prédictibles et compilables efficacement.
Conceptuellement, ProtoScript V2 met en œuvre une délégation statique, et non un héritage dynamique.
Il n’existe pas de classes dans ProtoScript V2.
Les objets sont créés exclusivement par clonage de prototypes.
Exemple :
prototype Point {
int x;
int y;
}
function main() : void {
Point p = Point.clone();
p.x = 1;
p.y = 2;
}Ref: EX-054
Le prototype Point définit :
-
un layout mémoire,
-
un ensemble de champs,
-
un ensemble de méthodes.
Le clonage produit une instance conforme à cette définition, sans mécanisme implicite supplémentaire.
Un prototype peut définir des champs et des méthodes.
À l’intérieur d’une méthode, le mot-clé self désigne l’objet courant.
Règles :
selfne peut pas être retourné (return self;est invalide).- les méthodes mutantes doivent retourner
void. - aucun style fluent/chaîné basé sur mutation n’est supporté.
prototype Point {
int x;
int y;
function move(int dx, int dy) : void {
self.x = self.x + dx;
self.y = self.y + dy;
}
}Ref: EX-055
self est lexicalement et statiquement résolu.
Il n’existe aucune ambiguïté liée à un contexte d’appel dynamique.
Un prototype peut être défini comme une extension statique d’un autre prototype :
prototype ColoredPoint : Point {
int color;
}Ref: EX-056
Un ColoredPoint peut être utilisé là où un Point est attendu, selon les règles de substitution statiques définies par le langage.
Cette relation :
-
n’implique aucune chaîne dynamique de prototypes,
-
ne repose sur aucun mécanisme de lookup tardif,
-
garantit la compatibilité structurelle à la compilation.
sealed prototype interdit uniquement la délégation statique (héritage).
La création d’objets via Type.clone() reste possible (lookup de méthode normal).
Exemple valide :
sealed prototype Box {
int v;
}
function main() : void {
Box b = Box.clone();
b.v = 1;
}Contre-exemple :
sealed prototype Base {}
prototype Child : Base {} // Erreur : E3140 SEALED_INHERITANCEProtoScript V2 expose deux niveaux uniquement :
publicpar défaut (n'est pas un token autorisé)internal
internal est autorisé uniquement sur les membres de prototype (champs et méthodes).
Regles :
- accès autorisé uniquement depuis une méthode du prototype propriétaire
- accès autorisé uniquement depuis une méthode d'un prototype enfant
- accès interdit hors de cette frontière (
E3201 VISIBILITY_VIOLATION) internalest interdit hors membres de prototype (E3200 INVALID_VISIBILITY_LOCATION)
Exemple valide :
prototype Core {
internal int state;
internal function bump() : void {
self.state = self.state + 1;
}
function publicBump() : void {
self.bump();
}
}
prototype Child : Core {
function use() : void {
self.state = self.state + 1; // autorisé (enfant)
}
}
function main() : void {
Core c = Core.clone();
c.publicBump(); // autorisé (appel public)
// c.state = 1; // interdit
// Io.printLine(c.state); // interdit
}Une méthode peut être redéfinie dans un prototype descendant à condition de conserver une signature strictement compatible.
-
le nom doit être identique ;
-
la liste des paramètres doit être strictement identique (même nombre, même types, même ordre) ;
-
le type de retour doit être identique ;
-
aucune surcharge par nombre ou type de paramètres n’est autorisée ;
-
une méthode ne peut pas être remplacée par un champ.
En cas de violation :
-
E3001 TYPE_MISMATCH_ASSIGNMENTpour signature incompatible ; -
E3145 METHOD_OVERRIDE_INVALID(si vous voulez un code dédié, recommandé).
prototype Point {
function move(int dx, int dy) : void {
self.x = self.x + dx;
self.y = self.y + dy;
}
}
prototype ColoredPoint : Point {
function move(int dx, int dy) : void {
self.x = self.x + dx;
self.y = self.y + dy;
}
}Ref: EX-057
prototype Bad1 : Point {
function move(int dx) : void { }
}
prototype Bad2 : Point {
function move(int dx, int dy) : int { return 0; }
}Ref: EX-058
Erreur : E3001 TYPE_MISMATCH_ASSIGNMENT
Un champ peut être initialisé directement dans la déclaration :
prototype P {
int i = 5;
string s = "hello";
list<int> values = [0, 1, 2];
}-
l’initialiseur est vérifié statiquement ;
-
l’initialisation est exécutée à chaque
clone(); -
l’ordre est parent puis enfant ;
-
sans initialiseur, la valeur par défaut est utilisée ;
-
selfest interdit dans un initialiseur (E3150) ; -
un champ
constdoit avoir un initialiseur (E3151) ; -
un champ
constne peut pas être réassigné (E3130) ; -
un champ ne peut pas être redéfini avec un type différent (
E3001).
En ProtoScript2, clone() est une méthode normale.
Cela signifie :
-
elle suit les règles de délégation statique ;
-
elle peut être redéfinie,
-
elle est dispatchée dynamiquement,
-
la descendance hérite des redéfinitions.
La chaîne de délégation est figée à la compilation ; le dispatch reste dynamique sur cette chaîne immuable.
Il n’existe aucune primitive spéciale cachée pour instancier un prototype.
La création d’instance n’est pas un mécanisme distinct du modèle objet : elle est entièrement exprimée dans le langage.
Tous les prototypes héritent implicitement d’un prototype racine : Object
Ce prototype définit :
prototype Object {
function clone() : Object {}
}C’est l’implémentation par défaut.
Le comportement par défaut de clone() est :
-
Créer un nouvel objet.
-
Le rendre instance du prototype appelant.
-
Initialiser ses champs avec les valeurs par défaut.
Autrement dit :
A.clone()
crée un nouvel objet qui se comporte comme une instance de A.
Si un prototype redéfinit clone(), la descendance en hérite comme toute méthode.
prototype MyType {
function clone() : MyType {
Exception e = Exception.clone();
e.message = "forbidden";
throw e;
}
}
prototype MyNewType : MyType {
}Ici : MyNewType.clone() appelle la version définie dans MyType.
C’est un dispatch normal.
Note normative :
P.clone()(appel statique) suit exactement le même lookup de délégation (d'héritage) que toute méthode.- un nom interne éventuel de backend (ex.
__clone_static) n’est qu’un détail d’implémentation et ne doit jamais bypass la résolution normale declone().
-
la syntaxe méthode = sémantique méthode,
-
aucune règle cachée,
-
cohérence totale du modèle de délégation (d’héritage).
super permet d’appeler la version héritée d’une méthode.
Forme :
super.methodName(args...)
Règle :
-
la recherche commence au parent direct du prototype qui déclare la méthode courante,
-
le receveur (
self) reste le même.
C’est maintenant le pattern recommandé pour personnaliser l’instanciation.
prototype A {
int x = 1;
}
prototype B : A {
int y = 2;
function clone() : B {
B b = super.clone(); // allocation via parent
b.y = 99; // personnalisation
return b;
}
}super.clone() garantit :
-
allocation correcte
-
respect de la hiérarchie
-
extensibilité future
Règles pédagogiques à retenir :
- affectation
=d’instances : copie de référence (pas de copie d’objet) clone(): instanciation d’un nouvel objet du prototype dynamique du receveur- typage spécial : après résolution de
E.clone(), le type statique estTypeOf(E) - ce mécanisme est limité au nom exact
clone - aucun type
Selfn’existe dans ProtoScript2
Exemple mémoire (copie de référence) :
prototype P { int n; }
function main() : void {
P a = P.clone();
P b = a;
b.n = 7;
Debug.assert(a.n == 7); // vrai
}Exemple instanciation (clone() ne copie pas l’état dynamique) :
prototype P { int n = 1; }
function main() : void {
P p = P.clone();
p.n = 9;
P q = p.clone();
Debug.assert(q.n == 1); // réinitialisé
}Forme courte du typage spécial :
si méthode résolue nommée "clone" :
TypeOf(E.clone()) = TypeOf(E)
Exemple multi-niveaux :
prototype A {
function clone() : A { return super.clone(); }
}
prototype B : A { }
prototype C : B { }
function main() : void {
C c = C.clone(); // valide
}Important :
Bien que la signature héritée puisse indiquer un type parent,
l’expressionE.clone()est toujours typée comme le type du receveurE.
super est valide uniquement dans une méthode.
Erreurs normatives :
| Code | Situation |
|---|---|
| E3210 | super utilisé hors méthode |
| E3211 | prototype sans parent |
| E3212 | méthode inexistante dans la hiérarchie |
Exemple :
function main() : void { super.clone(); // E3210 }
-
clone()est une méthode héritée normale. -
Le dispatch est dynamique.
-
superpermet d’appeler la version parent. -
Le retour statique de
clone()est spécialisé au prototype courant. -
Aucun type
Selfn’existe. -
L’instanciation est cohérente avec le modèle de délégation (d’héritage).
Lors d’un override :
- les paramètres doivent rester identiques ;
- le type de retour doit être identique ou un sous-type du retour parent.
Règle :
Si le type de retour de le méthode override est différent du type de retour de la méthode parente, alors c'est une violation qui déclanche l'erreur : E3221 INVALID_OVERRIDE_RETURN_TYPE.
ProtoScript V2 exclut explicitement :
-
les classes
-
les interfaces
-
les traits ou mixins
-
les casts dynamiques
-
la RTTI utilisateur
-
la modification dynamique des prototypes
Ces exclusions sont des choix de conception, et non des limitations accidentelles.
Le modèle prototype-based de ProtoScript V2 est conçu pour être pleinement compilable.
Il permet :
-
un layout mémoire déterministe
-
une résolution des champs et méthodes à la compilation
-
l’absence de tables virtuelles dynamiques
-
une génération directe de structures C stables
Chaque prototype correspond à une structure concrète connue à la compilation.
La délégation est résolue statiquement, sans coût d’indirection dynamique.
Le modèle de ProtoScript V2 s’inscrit dans la lignée conceptuelle du langage Self: The Power of Simplicity (Ungar & Smith, 1987), qui a posé les bases du prototype-based programming :
-
objets sans classes
-
clonage explicite
-
délégation comme mécanisme fondamental
Cependant, ProtoScript V2 s’en distingue par un choix assumé :
la délégation est statique et non dynamique.
Contrairement à JavaScript (cf. ECMAScript Language Specification), ProtoScript V2 :
-
ne permet pas la mutation des chaînes de prototypes,
-
ne mélange pas prototypes et classes syntaxiques,
-
n’introduit aucune ambiguïté liée à
this.
Les confusions historiques mises en lumière par JavaScript: The Good Parts (Crockford, 2008) sont volontairement évitées.
ProtoScript V2 adopte ainsi un modèle que l’on peut qualifier de :
prototype-based statique à layout figé
Ce positionnement vise la clarté conceptuelle, la sûreté sémantique et l’efficacité de compilation.
Le langage Self (Ungar & Smith, 1987) a introduit le modèle prototype-based en supprimant toute notion de classe au profit d’objets clonés et de délégation. Cette approche a démontré qu’un modèle orienté objet pouvait être à la fois plus simple et plus expressif qu’un système class-based traditionnel. Le langage Io, conçu par Steve Dekorte, s’inscrit directement dans cette lignée en radicalisant la simplicité syntaxique et la réflexivité du modèle. Io adopte une délégation entièrement dynamique et une métaprogrammation étendue. ProtoScript V2 reprend le principe fondamental du prototype comme objet concret. Il s’en distingue par un choix volontairement opposé sur le plan technique. Les relations de prototypes y sont figées à la compilation. La résolution des champs et méthodes est strictement statique. Aucune mutation dynamique des prototypes n’est autorisée. Ce positionnement vise la clarté sémantique, la sûreté et l’efficacité de compilation.
(Self / Io / JavaScript / ProtoScript V2)
| Critère | Self | Io | JavaScript | ProtoScript V2 |
|---|---|---|---|---|
| Modèle objet | Prototype-based pur | Prototype-based pur | Prototype-based hybride | Prototype-based statique |
| Classes | Absentes | Absentes | Introduites syntaxiquement | Absentes |
| Création d’objets | Clonage | Clonage | Constructeurs / prototypes | Clonage explicite |
| Délégation | Dynamique | Dynamique | Dynamique | Statique |
| Chaîne de prototypes | Dynamique | Dynamique | Dynamique mutable | Figée à la compilation |
| Mutation des prototypes | Autorisée | Autorisée | Autorisée | Interdite |
| Lookup des méthodes | Tardif (runtime) | Tardif (runtime) | Tardif (runtime) | Compilation |
Résolution de self / this |
Dynamique | Dynamique | Dynamique et contextuelle | Statique (self) |
| Appel parent explicite | resend (dynamique) |
resend / forwarding (dynamique) |
super (runtime) |
super (résolution statique) |
| Override | Dynamique | Dynamique | Dynamique | Statique, signature stricte |
| Surcharge | Possible dynamiquement | Possible | Possible | Interdite |
| RTTI utilisateur | Présente | Présente | Présente | Absente |
| Métaprogrammation | Étendue | Très étendue | Étendue | Absente |
| Typage | Dynamique | Dynamique | Dynamique | Statique |
| Layout mémoire | Non garanti | Non garanti | Non garanti | Déterministe |
| Compilation native | Non prioritaire | Non prioritaire | Secondaire | Objectif central |
| Objectif principal | Recherche conceptuelle | Simplicité réflexive | Généraliste | Clarté, sûreté, performance |
- Mutable et possédante.
list[i] = xest une écriture stricte : l'index doit exister.- Pas de redimensionnement implicite via indexation.
Test un type explicite ; il peut aussi désigner un type prototype (objet), la substitution parent/enfant est validée statiquement.
Exemple :
list<int> xs = [10, 20];
xs[1] = 30; // mise à jour
xs.push(40);
int v = xs.pop();Ref: EX-059
Contre-exemple :
list<int> xs = [1];
// xs[3] = 10; // runtime OOB // Erreur : R1002 RUNTIME_INDEX_OOBRef: EX-060
Méthodes disponibles :
| Méthode | Description | Paramètres | Retour | Erreurs |
|---|---|---|---|---|
length() : int |
Lit la taille courante sans muter la liste. | — | Taille courante >= 0. |
— |
isEmpty() : bool |
Teste la vacuité sans effet de bord. | — | true si la taille vaut 0, sinon false. |
— |
push(T element) : int |
Ajoute un élément en fin de liste et mute la liste. | element: valeur de type T compatible statiquement avec la liste. |
Nouvelle taille après insertion. | Erreur statique si type incompatible. |
pop() : T |
Retire et retourne le dernier élément ; mutation en place. | — | Élément précédemment en fin de liste. | Erreur statique STATIC_EMPTY_POP si vacuité prouvée ; sinon exception runtime RUNTIME_EMPTY_POP si vide à l’exécution. |
contains(T element) : bool |
Recherche séquentielle d’un élément. | element: valeur de type T comparable avec les éléments de la liste. |
true si présent, sinon false. |
Erreur statique si type incompatible. |
sort() : int |
Trie la liste en place ; ordre stable et déterministe. | — | Taille de la liste après tri (égale à la taille avant tri). | Erreur statique si T non comparable ou si compareTo(T other) : int est absent/invalide. |
reverse() : int |
Inverse l’ordre des éléments en place. | — | Taille de la liste après inversion. | — |
join(string separator) : string |
Méthode conditionnelle (T == string) ; concatène les éléments avec séparateur. |
separator: chaîne insérée entre deux éléments voisins. |
Chaîne résultante ; peut être vide si liste vide. | Erreur statique si T != string. |
concat() : string |
Méthode conditionnelle (T == string) ; concatène sans séparateur. |
— | Chaîne résultante ; peut être vide si liste vide. | Erreur statique si T != string. |
toUtf8String() : string |
Méthode conditionnelle (T == byte) ; décode la liste comme UTF-8 strict. |
— | Chaîne décodée en UTF-8. | Erreur statique si T != byte ; exception runtime RUNTIME_INVALID_UTF8 si séquence invalide. |
Notes sur sort() :
- tri en place, stable et déterministe.
- aucune variante
sort(cmp)n’existe.
| Opération | Complexité | Impact énergétique |
|---|---|---|
| pop() | ||
| indexation | ||
| push(x) (amorti) | ||
| reverse() | ||
| contains(x) | ||
| concat() | ||
| join(sep) | ||
| sort() |
Exemple (types primitifs) :
list<int> xs = [3, 1, 2];
xs.sort();Ref: EX-064
Exemple (prototype avec compareTo) :
prototype Item {
int key;
int id;
function compareTo(Item other) : int {
if (self.key < other.key) return -1;
if (self.key > other.key) return 1;
return 0;
}
}
function main() : void {
Item a = Item.clone();
a.key = 2;
a.id = 10;
Item b = Item.clone();
b.key = 1;
b.id = 20;
list<Item> xs = [a, b];
xs.sort();
}Ref: EX-065
reverse() inverse l’ordre des éléments en place, sans créer de nouveaux éléments.
Exemple (liste d’entiers) :
list<int> xs = [1, 2, 3];
xs.reverse();Ref: EX-066
Exemple (prototype utilisateur) :
prototype Item {
int id;
}
function main() : void {
Item a = Item.clone();
a.id = 1;
Item b = Item.clone();
b.id = 2;
list<Item> xs = [a, b];
xs.reverse();
}Ref: EX-067
KetVsont des types explicites ; ils peuvent aussi désigner des types prototypes (objets), la substitution parent/enfant est validée statiquement.
map<string, int> m = {};
m["a"] = 1; // insertion (clé absente)
m["a"] = 2; // mise à jour (clé présente)
int x = m["a"]; // lecture valideRef: EX-061
Littéral direct :
map<string, int> m = {"a": 3, "b": 2, "c": 1};Ref: EX-062
Contre-exemple :
map<string, int> m = {};
int x = m["absent"]; // runtime missing key // Erreur : R1003 RUNTIME_MISSING_KEYRef: EX-063
| Méthode | Description | Paramètres | Retour | Erreurs |
|---|---|---|---|---|
length() : int |
Lit le nombre d’entrées sans muter la map. | — | Nombre d’entrées >= 0. |
— |
isEmpty() : bool |
Teste la vacuité. | — | true si aucune entrée, sinon false. |
— |
containsKey(K k) : bool |
Vérifie la présence d’une clé. | k: clé de type K. |
true si la clé existe. |
Erreur statique si type de clé incompatible. |
remove(K k) : bool |
Supprime l’entrée de clé k si elle existe. |
k: clé de type K. |
true si suppression effective, sinon false. |
Erreur statique si type de clé incompatible. |
keys() : list<K> |
Extrait les clés dans l’ordre d’insertion courant. | — | Liste de clés ; peut être vide. | — |
values() : list<V> |
Extrait les valeurs dans l’ordre d’insertion courant. | — | Liste de valeurs ; peut être vide. | — |
| Opération | Complexité | Impact énergétique |
|---|---|---|
| lecture map[k] (amorti) | ||
| écriture map[k] (amorti) | ||
| containsKey(k) | ||
| keys() | ||
| values() |
Une map conserve l’ordre d’insertion et ne définit aucune méthode sort().
Pour trier, il faut extraire une list (par exemple via keys()) puis utiliser list.sort().
Exemple (tri par clé) :
map<string, int> m = {"b": 2, "a": 1, "c": 3};
list<string> ks = m.keys();
ks.sort();
for (string k of ks) {
int v = m[k];
string line = k.concat(":").concat(v.toString());
Io.printLine(line);
}Ref: EX-097
Exemple (tri par valeur, valeurs uniques) :
map<string, int> m = {"a": 3, "b": 1, "c": 2};
list<int> vals = m.values();
vals.sort();
list<string> ks = m.keys();
for (int v of vals) {
for (string k of ks) {
if (m[k] == v) {
string line = k.concat(":").concat(v.toString());
Io.printLine(line);
break;
}
}
}Ref: EX-098
Supposer que map[k] en lecture crée automatiquement une entrée. Ce n'est vrai qu'en écriture (map[k] = v).
La distinction lecture stricte / écriture constructive rend les effets de bord visibles.
for (int v of xs) { ... }
for (string k in m) { ... } // clés
for (int v of m) { ... } // valeursRef: EX-064
Notes :
- L’ordre d’itération des maps est l’ordre d’insertion courant.
- Si une clé est supprimée puis ré‑insérée, elle apparaît en fin d’ordre.
Confondre les erreurs statiques et runtime :
list.pop(): erreur statique si la liste est prouvée vide, sinon exception runtime si elle est vide à l’exécution.
slice<T>: vue mutable, non possédante.view<T>: vue lecture seule, non possédante.
list<int> xs = [1, 2, 3, 4];
slice<int> s = xs.slice(1, 2);
view<int> v = xs.view(0, 3);Ref: EX-065
s[0] = 99; // autorisé
// v[0] = 99; // invalide (view en lecture seule)Ref: EX-066
| Méthode | Signature | Résultat | Erreurs |
|---|---|---|---|
length() |
() : int |
longueur | — |
isEmpty() |
() : bool |
vrai si vide | — |
slice(start, len) |
(int,int) : slice<T> |
sous‑vue mutable | exception runtime lorsque hors bornes |
view(start, len) |
(int,int) : view<T> |
sous‑vue readonly | exception runtime lorsque hors bornes |
string.view(start,len) retourne view<glyph> avec indexation glyphique.
| Opération | Complexité | Impact énergétique |
|---|---|---|
| création slice/view | ||
| accès indexé | ||
| mutation structurelle source |
Une vue ne doit pas survivre au stockage source. Les mutations structurelles du stockage source peuvent invalider des vues.
Concrètement :
slice/viewréférencent le même stockage que lalistsource.- une mutation structurelle (
push,pop, réallocation interne) peut déplacer le buffer. - toute vue créée avant cette mutation peut devenir invalide.
- une vue invalidée ne doit plus être utilisée : tout accès après invalidation est une erreur runtime.
Comment s’assurer qu’une vue reste valide :
- vous pouvez intercepter l’exception si un accès est invalide.
- en dehors de cela, il n’existe pas d’API de validation runtime.
- la règle est discipline de code : ne pas muter structurellement la source tant que la vue est utilisée.
- cela concerne toutes les vues :
view<T>etslice<T>.
En cas d’accès après invalidation, une exception runtime peut être levée et interceptée :
list<int> xs = [1, 2, 3];
view<int> v = xs.view(0, 2);
xs.push(4); // peut invalider v
try {
int a = v[0]; // accès potentiellement invalide
} catch (Exception e) {
Io.printLine("vue invalidée");
}Ref: EX-067
Exemple :
list<int> xs = [1, 2, 3];
view<int> v = xs.view(0, 2); // v -> [1,2]
xs.push(4); // mutation structurelle : v peut être invalidée
// l’accès ci‑dessous est invalide si le buffer a bougé
// int a = v[0];Ref: EX-068
Traiter view<T> comme un list<T> léger. view<T> n'est pas possédante et interdit l'écriture.
stringest immuable.- Sémantique en glyphes Unicode.
stringn'est pas unbyte[].
string s = "a😀b";
int n = s.length(); // 3 glyphes
glyph g = s[1]; // 😀Ref: EX-069
Index hors bornes :
// runtime OOB
// glyph g = s[99];Ref: EX-070
string suit les glyphes/scalaires définis par le langage, pas une indexation brute par octet.
string s = "abc";
// s[0] = "x"[0]; // invalideRef: EX-071
Exemple d'approche correcte (création d'une nouvelle chaîne) :
string s = "abc";
string t = s.concat("x"); // s reste inchangéeRef: EX-072
- JS/PHP/C confondent souvent octets, code units et caractères utilisateurs.
- ProtoScript V2 impose une sémantique glyphique explicite pour éviter ces ambiguïtés.
Supposer que string[i] modifie la chaîne. Toute mutation indexée de string est interdite.
| Méthode | Description | Paramètres | Retour | Erreurs |
|---|---|---|---|---|
length() : int |
Retourne le nombre de glyphes de la chaîne. | — | Entier >= 0 représentant la longueur logique. |
— |
isEmpty() : bool |
Teste si la chaîne est vide. | — | true si length() == 0. |
— |
toString() : string |
Retourne la chaîne elle-même (identité). | — | Même valeur texte. | — |
toInt() : int |
Parse la chaîne comme entier. | — | Valeur entière convertie. | Exception runtime lorsque format invalide ou hors bornes. |
toFloat() : float |
Parse la chaîne comme flottant. | — | Valeur flottante convertie. | Exception runtime lorsque format invalide. |
concat(string s) : string |
Concatène la chaîne courante avec s. |
s: suffixe de type string. |
Nouvelle chaîne concaténée. | Erreur statique si argument non string. |
subString(int start, int length) : string |
Extrait une sous-chaîne en unités glyphiques. | start: index glyphique de départ ; length: nombre de glyphes à extraire. |
Nouvelle chaîne (copie), sans vue partagée. | Exception runtime RUNTIME_INDEX_OOB si bornes invalides. |
indexOf(string needle) : int |
Cherche la première occurrence de needle. |
needle: sous-chaîne recherchée. |
Index glyphique de la première occurrence, -1 sinon. |
Erreur statique si argument non string. |
contains(string needle) : bool |
Teste la présence d’une sous-chaîne. | needle: sous-chaîne recherchée. |
true si occurrence trouvée. |
Erreur statique si argument non string. |
lastIndexOf(string needle) : int |
Cherche la dernière occurrence de needle. |
needle: sous-chaîne recherchée. |
Index glyphique de la dernière occurrence, -1 sinon. |
Erreur statique si argument non string. |
startsWith(string prefix) : bool |
Vérifie le préfixe. | prefix: préfixe attendu. |
true si la chaîne commence par prefix. |
Erreur statique si argument non string. |
endsWith(string suffix) : bool |
Vérifie le suffixe. | suffix: suffixe attendu. |
true si la chaîne finit par suffix. |
Erreur statique si argument non string. |
split(string sep) : list<string> |
Découpe la chaîne selon un séparateur littéral (pas regex). | sep: séparateur textuel. |
Liste ordonnée des segments. | Erreur statique si argument non string. |
trim() : string |
Retire les espaces ASCII en tête et en fin. | — | Nouvelle chaîne nettoyée. | — |
trimStart() : string |
Retire les espaces ASCII en tête. | — | Nouvelle chaîne nettoyée en tête. | — |
trimEnd() : string |
Retire les espaces ASCII en fin. | — | Nouvelle chaîne nettoyée en fin. | — |
replace(string old, string new) : string |
Remplace la première occurrence de old par new. |
old: motif littéral ; new: remplacement littéral. |
Nouvelle chaîne modifiée. | Erreur statique si argument non string. |
replaceAll(string old, string new) : string |
Remplace toutes les occurrences non chevauchantes de old. |
old: motif littéral ; new: remplacement littéral. |
Nouvelle chaîne modifiée. | Erreur statique si argument non string ; exception runtime RUNTIME_INVALID_ARGUMENT si old == "". |
glyphAt(int index) : glyph |
Retourne le glyphe à l’index donné. | index: position glyphique. |
Valeur glyph extraite. |
Exception runtime RUNTIME_INDEX_OOB si hors bornes. |
repeat(int count) : string |
Répète la chaîne count fois. |
count: nombre de répétitions. |
Nouvelle chaîne répétée (éventuellement vide). | Exception runtime RUNTIME_INVALID_ARGUMENT si count < 0. |
padStart(int targetLength, string pad) : string |
Complète à gauche jusqu’à targetLength. |
targetLength: longueur cible en glyphes ; pad: motif de remplissage. |
Nouvelle chaîne paddée à gauche. | Exception runtime RUNTIME_INVALID_ARGUMENT si padding requis et pad == "". |
padEnd(int targetLength, string pad) : string |
Complète à droite jusqu’à targetLength. |
targetLength: longueur cible en glyphes ; pad: motif de remplissage. |
Nouvelle chaîne paddée à droite. | Exception runtime RUNTIME_INVALID_ARGUMENT si padding requis et pad == "". |
toUpper() : string |
Convertit vers une forme majuscule. | — | Nouvelle chaîne en majuscules. | — |
toLower() : string |
Convertit vers une forme minuscule. | — | Nouvelle chaîne en minuscules. | — |
toUtf8Bytes() : list<byte> |
Encode la chaîne en UTF-8 strict. | — | Liste d’octets UTF-8, ordre conservé. | — |
Exemples :
string s = " abc ";
bool a = s.startsWith(" ");
bool b = s.endsWith(" ");
int p = s.indexOf("bc"); // index en glyphes
string t = s.trim(); // retire espaces ASCII en début/fin
string u = s.replace("a", "A"); // première occurrence
list<string> parts = "a,b,c".split(",");Ref: EX-073
Notes :
splitne fait aucun traitement regex- les indices de
indexOfsont exprimés en glyphes trim,trimStart,trimEndretirent uniquement' ','\t','\n','\r'
| Opération | Complexité | Impact énergétique |
|---|---|---|
| length() | ||
| indexation | ||
| subString() | ||
| concat() | ||
| replace() | ||
| split() |
Immutabilité + sémantique glyphique = comportement stable, coûts visibles, pas de magie cachant des copies.
Si vous devez manipuler des octets, utilisez une list<byte>.
La conversion est explicite et strictement validée.
string s = "Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en canoë au delà des îles, près du mälström où brûlent les novæ.";
list<byte> bytes = s.toUtf8Bytes();
string back = bytes.toUtf8String();Ref: EX-074
Si la liste de bytes n'est pas un UTF-8 valide, toUtf8String() lève une exception runtime.
subString(start, length) extrait une sous-chaîne en indices de glyphes.
Elle retourne une nouvelle chaîne et ne crée pas de vue partagée.
string s = "a😀b";
string t = s.subString(1, 1); // "😀"Ref: EX-075
Erreurs :
startoulengthhors bornes lève une exception runtime
Note :
Il n'existe pas d'API de slicing/view pour string. L'extraction est explicite et copie la sous-chaîne.
Deux formes existent :
- import de module avec alias (espace de noms)
- import explicite de symboles
- import par chemin (string literal)
import Io;
import Io as io;
import Math.{abs, sqrt as racine};
import JSON.{encode, decode};
import "./datastruct/Stack.pts";
import "/abs/path/collections/Stack.pts".{push, pop};Ref: EX-076
Mini‑grammaire (EBNF) :
ImportStmt = "import" ImportTarget ";" ;
ImportTarget =
ImportByName
| ImportByPath ;
ImportByName =
ModulePath [ "as" Identifier ]
| ModulePath "." "{" ImportItem { "," ImportItem } "}" ;
ImportByPath =
StringLiteral [ "as" Identifier ]
| StringLiteral "." "{" ImportItem { "," ImportItem } "}" ;
ModulePath = Identifier { "." Identifier } ;
ImportItem = Identifier [ "as" Identifier ] ;Exemples d'usage :
float x = abs(-9.0);
float y = racine(x);
string s = encode(decode("{\"value\":1}"));Ref: EX-077
Règles supplémentaires pour l’import par chemin :
- le chemin doit référencer un fichier
.pts - chemin relatif : résolu par rapport au fichier courant
- chemin absolu : utilisé tel quel
- aucune recherche via
search_pathsn’est effectuée
Erreurs statiques dédiées :
E2002:IMPORT_PATH_NOT_FOUNDE2003:IMPORT_PATH_BAD_EXTENSIONE2004:IMPORT_PATH_NO_ROOT_PROTO
- Import explicite des symboles.
- Aliases explicites.
- Pas de wildcard import.
Contre-exemple :
// invalide
// import std.io.*; // Erreur : E1001 PARSE_UNEXPECTED_TOKENRef: EX-078
Les symboles de module sont résolus à la compilation. Aucun chargement dynamique.
Les modules natifs étendent l'environnement de noms, pas la sémantique du langage.
Documentation officielle : docs/native-modules.md.
Note :
un prototype non exporté par un module natif est simplement inaccessible depuis l’extérieur.
Cela ne modifie pas le mécanisme d’instanciation par Type.clone().
- Les builtins sont décrits comme des prototypes ProtoScript.
- La surface observable (méthodes, erreurs, règles de dispatch) est normative.
- L’implémentation interne peut utiliser des structures runtime spécifiques, sans effet sémantique.
| Builtin prototype | sealed | error on extend | clonable | error on clone() |
|---|---|---|---|---|
| TextFile | yes | E3140 SEALED_INHERITANCE | no | R1013 RUNTIME_CLONE_NOT_SUPPORTED |
| BinaryFile | yes | E3140 SEALED_INHERITANCE | no | R1013 RUNTIME_CLONE_NOT_SUPPORTED |
| Dir | yes | E3140 SEALED_INHERITANCE | no | R1013 RUNTIME_CLONE_NOT_SUPPORTED |
| Walker | yes | E3140 SEALED_INHERITANCE | no | R1013 RUNTIME_CLONE_NOT_SUPPORTED |
| RegExp | yes | E3140 SEALED_INHERITANCE | no | R1013 RUNTIME_CLONE_NOT_SUPPORTED |
| PathInfo | yes | E3140 SEALED_INHERITANCE | no | R1013 RUNTIME_CLONE_NOT_SUPPORTED |
| PathEntry | yes | E3140 SEALED_INHERITANCE | no | R1013 RUNTIME_CLONE_NOT_SUPPORTED |
| RegExpMatch | yes | E3140 SEALED_INHERITANCE | no | R1013 RUNTIME_CLONE_NOT_SUPPORTED |
| ProcessEvent | yes | E3140 SEALED_INHERITANCE | no | R1013 RUNTIME_CLONE_NOT_SUPPORTED |
| ProcessResult | yes | E3140 SEALED_INHERITANCE | no | R1013 RUNTIME_CLONE_NOT_SUPPORTED |
| CivilDateTime | no | yes |
Le chargeur utilise un registry JSON pour résoudre import Io, import Math, import JSON.
- Surcharge possible via
PS_MODULE_REGISTRY(chemin absolu ou relatif). - Ordre de recherche par défaut :
PS_MODULE_REGISTRY,registry.jsonà côté du binaireps,./registry.json,/etc/ps/registry.json,/usr/local/etc/ps/registry.json,/opt/local/etc/ps/registry.json,./modules/registry.json.
Constantes
| Nom | Type | Description |
|---|---|---|
Io.EOL |
string |
fin de ligne ("\n") |
Io.stdin |
TextFile |
flux standard d’entrée (texte) |
Io.stdout |
TextFile |
flux standard de sortie (texte) |
Io.stderr |
TextFile |
flux standard d’erreur (texte) |
Fonctions globales
| Fonction | Description | Erreurs |
|---|---|---|
Io.openText(string path, string mode) : TextFile |
ouvre un fichier texte | InvalidModeException, InvalidPathException, FileNotFoundException, PermissionDeniedException, FileOpenException |
Io.openBinary(string path, string mode) : BinaryFile |
ouvre un fichier binaire | InvalidModeException, InvalidPathException, FileNotFoundException, PermissionDeniedException, FileOpenException |
Io.tempPath() : string |
chemin temporaire unique (non créé) | IOException |
Io.print(any value) : void |
écrit sans fin de ligne | InvalidArgumentException, WriteFailureException |
Io.printLine(any value) : void |
écrit + Io.EOL |
InvalidArgumentException, WriteFailureException |
Notes :
Io.openText(...)/Io.openBinary(...)lèvent une exception runtime si l’ouverture échoue.- en cas d’échec, aucun handle n’est retourné.
Io.tempPath()retourne un chemin inexistant et ne crée pas le fichier.Io.tempPath()utilise$TMPDIRsinon/tmpet ne protège pas contre une race condition externe.Io.print(...)/Io.printLine(...): sivaluen’est pas unestring,toString()est appelé et doit retourner unestring, sinonInvalidArgumentException.- Exceptions Io (toutes
RuntimeException) :InvalidModeException,FileOpenException,FileNotFoundException,PermissionDeniedException,InvalidPathException,FileClosedException,InvalidArgumentException,InvalidGlyphPositionException,ReadFailureException,WriteFailureException,Utf8DecodeException,StandardStreamCloseException,IOException.
Prototype TextFile
sealed prototype TextFile {
function read(int size): string{}
function write(string text): void {}
function tell(): int {}
function seek(int pos): void {}
function size(): int {}
function name(): string {}
function close(): void {}
}Méthodes sur TextFile
| Méthode | Description | Erreurs |
|---|---|---|
TextFile.read(int size) : string |
lit size glyphes |
InvalidArgumentException, FileClosedException, Utf8DecodeException, ReadFailureException |
TextFile.write(string text) : void |
écrit du texte | InvalidArgumentException, FileClosedException, WriteFailureException |
TextFile.tell() : int |
position en glyphes | FileClosedException, ReadFailureException |
TextFile.seek(int pos) : void |
positionne en glyphes | InvalidArgumentException, InvalidGlyphPositionException, FileClosedException, ReadFailureException |
TextFile.size() : int |
taille en glyphes | FileClosedException, ReadFailureException |
TextFile.name() : string |
nom/chemin | FileClosedException |
TextFile.close() : void |
ferme le fichier | StandardStreamCloseException si stdin/stdout/stderr |
Prototype BinaryFile
sealed prototype BinaryFile {
function read(int size): list<byte> {}
function write(list<byte> bytes): void {}
function tell(): int {}
function seek(int pos): void {}
function size(): int {}
function name(): string {}
function close(): void {}
}Méthodes sur BinaryFile
| Méthode | Description | Erreurs |
|---|---|---|
BinaryFile.read(int size) : list<byte> |
lit size octets |
InvalidArgumentException, FileClosedException, ReadFailureException |
BinaryFile.write(list<byte> bytes) : void |
écrit des octets | InvalidArgumentException, FileClosedException, WriteFailureException |
BinaryFile.tell() : int |
position en octets | FileClosedException, ReadFailureException |
BinaryFile.seek(int pos) : void |
positionne en octets | InvalidArgumentException, FileClosedException, ReadFailureException |
BinaryFile.size() : int |
taille en octets | FileClosedException, ReadFailureException |
BinaryFile.name() : string |
nom/chemin | FileClosedException |
BinaryFile.close() : void |
ferme le fichier | StandardStreamCloseException si stdin/stdout/stderr |
Notes :
- en texte,
read(size)retourne unestringdont la longueur est le nombre de glyphes lus. - en binaire,
read(size)retourne unlist<byte>. read(size)retourne une valeur de longueur nulle (length == 0) si EOF.- les écritures sont atomiques : aucune écriture partielle ne doit être observable et en cas d’échec la position du curseur est inchangée.
Exemple :
Écriture de texte :
TextFile f = Io.openText("out.txt", "w");
f.write("hello");
f.close();
Io.printLine("done");Ref: EX-079
Lecture de texte :
TextFile f = Io.openText("in.txt", "r");
int n = f.size();
string data = f.read(n);
f.close();Ref: EX-080
Lecture binaire et écriture binaire :
BinaryFile f = Io.openBinary("in.bin", "r");
list<byte> bytes = f.read(1024);
f.close();
BinaryFile g = Io.openBinary("out.bin", "w");
g.write(bytes);
g.close();Ref: EX-081
Chemin temporaire :
string p = Io.tempPath();
TextFile f = Io.openText(p, "w");
f.write("temp");
f.close();Ref: EX-081A
Écrire sur Io.stderr :
Io.stderr.write("error\n");Ref: EX-082
Constantes (toutes float)
| Nom | Valeur | Description |
|---|---|---|
Math.PI |
π | constante π |
Math.E |
e | constante e |
Math.LN2 |
ln(2) | logarithme naturel de 2 |
Math.LN10 |
ln(10) | logarithme naturel de 10 |
Math.LOG2E |
log2(e) | log base 2 de e |
Math.LOG10E |
log10(e) | log base 10 de e |
Math.SQRT1_2 |
√(1/2) | racine de 1/2 |
Math.SQRT2 |
√2 | racine de 2 |
Fonctions (toutes pures, retour float)
| Fonction | Unités / domaine | Résultat / notes |
|---|---|---|
Math.abs(float x) : float |
tout réel | |x| |
Math.min(float a, float b) : float |
tout réel | plus petit des deux |
Math.max(float a, float b) : float |
tout réel | plus grand des deux |
Math.floor(float x) : float |
tout réel | ⌊x⌋ |
Math.ceil(float x) : float |
tout réel | ⌈x⌉ |
Math.round(float x) : float |
tout réel | arrondi au plus proche |
Math.trunc(float x) : float |
tout réel | troncature vers 0 |
Math.sign(float x) : float |
tout réel (NaN inclus) | −1, +1, 0, −0, NaN (voir contrat) |
Math.fround(float x) : float |
tout réel | arrondi float (IEEE‑754) |
Math.sqrt(float x) : float |
x ≥ 0 | √x, sinon NaN |
Math.cbrt(float x) : float |
tout réel | ∛x |
Math.pow(float a, float b) : float |
tout réel | a^b (IEEE‑754) |
Math.sin(float x) : float |
radians | sin(x) |
Math.cos(float x) : float |
radians | cos(x) |
Math.tan(float x) : float |
radians | tan(x) |
Math.asin(float x) : float |
x ∈ [−1, 1] | arcsin(x) en radians, sinon NaN |
Math.acos(float x) : float |
x ∈ [−1, 1] | arccos(x) en radians, sinon NaN |
Math.atan(float x) : float |
tout réel | arctan(x) en radians |
Math.atan2(float y, float x) : float |
tout réel | arctan(y/x) en radians, quadrant correct |
Math.sinh(float x) : float |
tout réel | sinh(x) |
Math.cosh(float x) : float |
tout réel | cosh(x) |
Math.tanh(float x) : float |
tout réel | tanh(x) |
Math.asinh(float x) : float |
tout réel | asinh(x) |
Math.acosh(float x) : float |
x ≥ 1 | acosh(x), sinon NaN |
Math.atanh(float x) : float |
x ∈ (−1, 1) | atanh(x), sinon NaN |
Math.exp(float x) : float |
tout réel | e^x |
Math.expm1(float x) : float |
tout réel | e^x − 1 |
Math.log(float x) : float |
x > 0 | ln(x), sinon NaN |
Math.log1p(float x) : float |
x > −1 | ln(1 + x), sinon NaN |
Math.log2(float x) : float |
x > 0 | log2(x), sinon NaN |
Math.log10(float x) : float |
x > 0 | log10(x), sinon NaN |
Math.hypot(float a, float b) : float |
tout réel | √(a² + b²) |
Math.clz32(float x) : float |
entier 32 bits (float) | count leading zeros (JS‑like) |
Math.imul(float a, float b) : float |
entiers 32 bits (float) | multiplication 32 bits (JS‑like) |
Math.random() : float |
— | uniforme dans [0.0, 1.0) |
Paramètres :
- Les arguments
intsont acceptés et convertis implicitement enfloat. - Tout autre type doit provoquer une erreur runtime de type.
- Les fonctions trigonométriques (
sin,cos,tan,asin,acos,atan, etc.) utilisent des radians.
Les fonctions Math suivent IEEE‑754 :
- pour les cas hors domaine mathématique, le résultat est NaN (aucune exception implicite).
- pour les débordements, le résultat peut être +Infinity ou −Infinity.
-0est préservé si le résultat IEEE‑754 est-0.
Exemples typiques :
float a = Math.log(-1.0); // NaN
float b = Math.sqrt(-1.0); // NaN
float c = Math.exp(1000.0); // +Infinity (overflow)Ref: EX-083
- Sémantique IEEE‑754 (double précision).
NaN,+Infinity,−Infinitypeuvent être produits.- Aucune exception implicite pour valeurs hors domaine : retour
NaNou±Infinity. -0est préservé si le résultat IEEE‑754 est-0.- Comparaisons avec
NaN:NaN != NaNesttrue, comparaisons ordonnées avecNaNsontfalse.
Math.sign :
- retourne
NaNsi l’argument estNaN, - retourne
-0si l’argument est-0, - retourne
-1si l’argument est négatif, - retourne
1si l’argument est positif, - retourne
0si l’argument est+0.
- PRNG interne au runtime (pas de
rand()libc). - aucun seed exposé au langage.
- déterministe à état initial identique.
- retourne un
floatdans[0.0, 1.0). - pas de dépendance système.
Format : code + catégorie + message, ex. R1010 RUNTIME_TYPE_ERROR.
Erreurs typiques :
- type invalide →
R1010 RUNTIME_TYPE_ERROR
Exemples :
float a = Math.abs(-3.5);
float b = Math.sqrt(9.0); // 3.0
float c = Math.log(Math.E); // 1.0
float d = Math.pow(2.0, 3.0); // 8.0Ref: EX-084
Exemple trigonométrique (radians) :
float s = Math.sin(Math.PI / 2.0); // ~1.0Ref: EX-085
Fonctions
| Fonction | Description | Erreurs |
|---|---|---|
JSON.encode(any) : string |
sérialise | runtime si valeur non sérialisable |
JSON.decode(string) : JSONValue |
parse JSON | runtime si JSON invalide |
JSON.isValid(string) : bool |
valide sans exception | runtime si argument non string |
Notes :
encodeaccepte unJSONValueou des valeurs récursivement sérialisables :bool,int,float,string,list<T>,map<string,T>.NaN,+Infinity,-Infinitysont interdits à l’encode → exception runtime.-0est préservé.decodeparse du JSON UTF‑8 strict et retourne unJSONValue.
Type JSONValue (scellé)
JSONValue est un type somme standard scellé.
Il ne peut pas être étendu par l’utilisateur.
Sous‑types : JsonNull, JsonBool, JsonNumber, JsonString, JsonArray, JsonObject.
Constructeurs explicites :
| Fonction | Description |
|---|---|
JSON.null() : JSONValue |
null JSON |
JSON.bool(bool) : JSONValue |
bool JSON |
JSON.number(float) : JSONValue |
nombre JSON |
JSON.string(string) : JSONValue |
chaîne JSON |
JSON.array(list<JSONValue>) : JSONValue |
tableau JSON |
JSON.object(map<string, JSONValue>) : JSONValue |
objet JSON |
Règle snapshot (normative côté surface observable) :
JSON.arrayetJSON.objectprennent un snapshot de leur entrée.- Une mutation ultérieure du conteneur source n’altère pas la valeur JSON stockée.
asArray()/asObject()doivent préserver la stabilité observée de la valeur JSON.
sealed prototype JSONValue {
function isNull(): bool {}
function isBool(): bool {}
function isNumber(): bool {}
function isString(): bool {}
function isArray(): bool {}
function isObject(): bool {}
function asBool(): bool {}
function asNumber(): float {}
function asString(): string {}
function asArray(): list<JSONValue> {}
function asObject(): map<string, JSONValue> {}
}Méthodes :
| Méthode | Résultat | Erreurs |
|---|---|---|
JSONValue.isNull() |
bool |
— |
JSONValue.isBool() |
bool |
— |
JSONValue.isNumber() |
bool |
— |
JSONValue.isString() |
bool |
— |
JSONValue.isArray() |
bool |
— |
JSONValue.isObject() |
bool |
— |
JSONValue.asBool() |
type correspondant | runtime si mauvais type |
JSONValue.asNumber() |
type correspondant | runtime si mauvais type |
JSONValue.asString() |
type correspondant | runtime si mauvais type |
JSONValue.asArray() |
type correspondant | runtime si mauvais type |
JSONValue.asObject() |
type correspondant | runtime si mauvais type |
Exemple :
string s = JSON.encode({"a": 1, "b": [true, false]});
JSONValue v = JSON.decode(s);
bool ok = JSON.isValid("{\"x\":1}");Ref: EX-086
Fonctions
| Fonction | Description |
|---|---|
Time.nowEpochMillis() : int |
epoch UTC en millisecondes (non déterministe) |
Time.nowMonotonicNanos() : int |
horloge monotone en nanosecondes (non déterministe) |
Time.sleepMillis(int ms) : void |
suspend l’exécution au moins ms millisecondes |
Notes :
nowEpochMillisretourne un epoch UTC (millisecondes).nowMonotonicNanosest monotone, indépendante de l’horloge murale.sleepMillispeut dépasser la durée demandée.
Exemple :
import Io;
import Time;
function main() : void {
int start = Time.nowEpochMillis();
Time.sleepMillis(10);
int end = Time.nowEpochMillis();
Io.printLine((end >= start).toString());
}Ref: EX-087
Prototype standard : CivilDateTime
prototype CivilDateTime {
function year(): int {}
function month(): int {}
function day(): int {}
function hour(): int {}
function minute(): int {}
function second(): int {}
function millisecond(): int {}
function setYear(int y): void {}
function setMonth(int m): void {}
function setDay(int d): void {}
function setHour(int h): void {}
function setMinute(int m): void {}
function setSecond(int s): void {}
function setMillisecond(int ms): void {}
}Constantes DST
| Nom | Valeur | Usage |
|---|---|---|
TimeCivil.DST_EARLIER |
0 |
choisit l’occurrence la plus tôt |
TimeCivil.DST_LATER |
1 |
choisit l’occurrence la plus tard |
TimeCivil.DST_ERROR |
2 |
lève une exception en cas d’ambiguïté |
Fonctions
| Fonction | Description | Exceptions |
|---|---|---|
fromEpochUTC(int) : CivilDateTime |
epoch → civil UTC | InvalidDateException |
toEpochUTC(CivilDateTime) : int |
civil UTC → epoch | InvalidDateException |
fromEpoch(int, string tz) : CivilDateTime |
epoch → civil dans tz |
InvalidTimeZoneException, InvalidDateException |
toEpoch(CivilDateTime, string tz, int strategy) : int |
civil → epoch dans tz |
InvalidTimeZoneException, InvalidDateException, DSTNonExistentTimeException, DSTAmbiguousTimeException |
isDST(int, string tz) : bool |
vrai si offset ≠ standard | InvalidTimeZoneException, InvalidDateException |
offsetSeconds(int, string tz) : int |
offset UTC total | InvalidTimeZoneException, InvalidDateException |
standardOffsetSeconds(string tz) : int |
offset hors DST | InvalidTimeZoneException |
dayOfWeek(int, string tz) : int |
1=lundi … 7=dimanche | InvalidTimeZoneException, InvalidDateException |
dayOfYear(int, string tz) : int |
1–365/366 | InvalidTimeZoneException, InvalidDateException |
weekOfYearISO(int, string tz) : int |
semaine ISO 8601 | InvalidTimeZoneException, InvalidDateException |
weekYearISO(int, string tz) : int |
année ISO 8601 | InvalidTimeZoneException, InvalidDateException |
isLeapYear(int year) : bool |
année bissextile | — |
daysInMonth(int year, int month) : int |
nombre de jours | InvalidDateException |
parseISO8601(string s) : int |
parse ISO strict → epoch UTC | InvalidISOFormatException |
formatISO8601(int epoch) : string |
format UTC YYYY-MM-DDTHH:MM:SS.sssZ |
InvalidDateException |
Validation de la TimeZone
- c'est une chaîne de type
string - identifiant IANA strict, sensible à la casse ;
- aucun whitespace (leading/trailing ou interne) ;
- pas de normalisation de locale ;
- pas de fallback implicite vers UTC ;
- alias acceptés uniquement s’ils existent dans la base IANA du système ;
"UTC"accepté seulement si la base système le supporte.
Règles DST
- heure inexistante (spring forward) → toujours
DSTNonExistentTimeException - heure ambiguë (fall back) → stratégie obligatoire (
DST_EARLIER,DST_LATER,DST_ERROR)
ISO 8601
- parsing strict :
YYYY-MM-DD,YYYY-MM-DDTHH:MM:SS,YYYY-MM-DDTHH:MM:SS.sss, suffixeZou offset±HH:MM - sans suffixe
Z/offset, l’interprétation est UTC - pas d’autre format accepté
formatISO8601retourne toujours en UTC avecZ
Exemples :
import Io;
import TimeCivil;
function main() : void {
int epoch = 0;
CivilDateTime dt = TimeCivil.fromEpochUTC(epoch);
int round = TimeCivil.toEpochUTC(dt);
Io.printLine((round == 0).toString());
}Ref: EX-088
import Io;
import TimeCivil;
function main() : void {
int epoch = TimeCivil.parseISO8601("1970-01-01T00:00:00.000Z");
string s = TimeCivil.formatISO8601(epoch);
Io.printLine(s);
}Ref: EX-089
Le module Fs fournit des primitives synchrones pour le système de fichiers POSIX.
Exceptions Fs (toutes RuntimeException)
FileNotFoundExceptionNotADirectoryExceptionNotAFileExceptionPermissionDeniedExceptionDirectoryNotEmptyExceptionInvalidPathExceptionIOException
Fonctions
| Fonction | Description | Exceptions |
|---|---|---|
Fs.exists(string path) : bool |
vrai si le chemin existe | InvalidPathException, IOException |
Fs.isFile(string path) : bool |
vrai si fichier régulier | InvalidPathException, IOException |
Fs.isDir(string path) : bool |
vrai si répertoire | InvalidPathException, IOException |
Fs.isSymlink(string path) : bool |
vrai si lien symbolique | InvalidPathException, IOException |
Fs.isReadable(string path) : bool |
vérifie la lisibilité | InvalidPathException, IOException |
Fs.isWritable(string path) : bool |
vérifie l’écriture | InvalidPathException, IOException |
Fs.isExecutable(string path) : bool |
vérifie l’exécution | InvalidPathException, IOException |
Fs.size(string path) : int |
taille en octets d’un fichier | FileNotFoundException, NotAFileException, PermissionDeniedException, IOException |
Fs.mkdir(string path) : void |
crée un répertoire | FileNotFoundException, NotADirectoryException, PermissionDeniedException, InvalidPathException, IOException |
Fs.rmdir(string path) : void |
supprime un répertoire vide | FileNotFoundException, NotADirectoryException, DirectoryNotEmptyException, PermissionDeniedException, InvalidPathException, IOException |
Fs.rm(string path) : void |
supprime un fichier | FileNotFoundException, NotAFileException, PermissionDeniedException, InvalidPathException, IOException |
Fs.cp(string src, string dst) : void |
copie un fichier | FileNotFoundException, NotAFileException, PermissionDeniedException, InvalidPathException, IOException |
Fs.mv(string src, string dst) : void |
déplace un fichier | FileNotFoundException, PermissionDeniedException, InvalidPathException, IOException |
Fs.chmod(string path, int mode) : void |
change les permissions POSIX | FileNotFoundException, PermissionDeniedException, InvalidPathException, IOException |
Fs.cwd() : string |
répertoire courant | IOException |
Fs.cd(string path) : void |
change de répertoire | FileNotFoundException, NotADirectoryException, PermissionDeniedException, IOException |
Fs.pathInfo(string path) : PathInfo |
découpe un chemin sans normalisation | InvalidPathException, IOException |
Fs.openDir(string path) : Dir |
ouvre un itérateur de répertoire | FileNotFoundException, NotADirectoryException, PermissionDeniedException, IOException |
Fs.walk(string path, int maxDepth, bool followSymlinks) : Walker |
parcours récursif itératif | FileNotFoundException, NotADirectoryException, PermissionDeniedException, IOException |
Notes :
- Les requêtes de capacité (
isReadable,isWritable,isExecutable) renvoientfalsesi l’accès est refusé. - En cas d’erreur système inattendue, les requêtes lèvent
IOExceptionouInvalidPathException. - Les opérations mutantes sont atomiques : en cas d’exception, aucune modification partielle n’est visible.
- Les liens symboliques cassés :
existsretournetrue,isFileetisDirretournentfalse. - Le module Fs.walk fournit un itérateur récursif streaming de l’arborescence des fichiers. Comparé à une implémentation récursive côté utilisateur, il évite les débordements de pile et gère efficacement les arborescences profondes, tout en restant synchrone, déterministe et sans allocation massive.
Prototype PathInfo
sealed prototype PathInfo {
function dirname(): string {}
function basename(): string {}
function filename(): string {}
function extension(): string {}
}Prototype Dir
sealed prototype Dir {
function hasNext(): bool {}
function next(): string {}
function close(): void {}
function reset(): void {}
}Méthodes :
| Méthode | Description | Erreurs |
|---|---|---|
hasNext() : bool |
vrai si un next() est possible |
— |
next() : string |
retourne l’entrée suivante | IOException si fin |
close() : void |
ferme le handle | — |
reset() : void |
rembobine le flux | IOException en cas d’échec |
Les entrées . et .. sont filtrées.
Prototype Walker
sealed prototype Walker {
function hasNext() : bool {}
function next() : PathEntry {}
function close() : void {}
}Méthodes :
| Méthode | Description | Erreurs |
|---|---|---|
hasNext() : bool |
vrai si une entrée suivante existe | — |
next() : PathEntry |
entrée suivante | IOException si fin |
close() : void |
libère les ressources | — |
Prototype PathEntry
sealed prototype PathEntry {
function path(): string {}
function name(): string {}
function depth(): int {}
function isDir(): bool {}
function isFile(): bool {}
function isSymlink(): bool {}
}Exemple : listing simple
import Fs;
import Io;
function main() : void {
Dir d = Fs.openDir(".");
while (d.hasNext()) {
Io.printLine(d.next());
}
d.close();
}Ref: EX-099
Exemple : walk récursif
import Fs;
import Io;
function main() : void {
Walker w = Fs.walk(".", -1, false);
while (w.hasNext()) {
PathEntry e = w.next();
Io.printLine(e.path());
}
w.close();
}Ref: EX-100
Le module Sys expose un accès minimal en lecture seule à l'environnement du processus et une exécution contrôlée de processus.
Exceptions Sys (toutes RuntimeException)
InvalidArgumentExceptionEnvironmentAccessExceptionInvalidEnvironmentNameExceptionIOExceptionProcessCreationExceptionProcessExecutionExceptionProcessPermissionExceptionInvalidExecutableException
Fonctions
| Fonction | Signature | Description | Exceptions |
|---|---|---|---|
Sys.hasEnv(string name) : bool |
vrai si la variable d'environnement existe | InvalidEnvironmentNameException, EnvironmentAccessException, IOException |
|
Sys.env(string name) : string |
valeur de la variable d'environnement | InvalidEnvironmentNameException, EnvironmentAccessException, IOException |
|
Sys.execute(string program, list<string> args, list<byte> input, bool captureStdout, bool captureStderr) : ProcessResult |
exécution synchrone d'un programme POSIX | InvalidExecutableException, ProcessPermissionException, ProcessCreationException, ProcessExecutionException, InvalidArgumentException, IOException |
Notes :
- Accès lecture seule : aucune mutation ni énumération de l'environnement.
- Pas de cache : chaque appel reflète l'état courant du processus.
- Nom invalide si chaîne vide ou si le caractère
=est présent. - Les valeurs doivent être du UTF-8 valide ; sinon
EnvironmentAccessException. - Une variable existante peut avoir une valeur vide.
executen'invoque aucun shell ;programest exécuté tel quel etargssont passés verbatim.inputest écrit intégralement sur stdin puis stdin est fermé (EOF).- Si
captureStdout/captureStderrestfalse, le flux hérite du processus parent.
Prototype ProcessResult
sealed prototype ProcessResult {
function exitCode(): int {}
function events(): list<ProcessEvent> {}
}Prototype ProcessEvent
sealed prototype ProcessEvent {
function stream(): int {} // 1 = stdout, 2 = stderr
function data(): list<byte> {}
}Ordonnancement
eventsest chronologique : l'ordre correspond à l'ordre d'observation des lectures multiplexées stdout/stderr.- La taille des chunks est dépendante de l'implémentation.
- Si le processus est terminé par un signal,
exitCodeest mappé à128 + signal.
Exemple : environnement
import Sys;
import Io;
function main() : void {
if (Sys.hasEnv("HOME")) {
Io.printLine(Sys.env("HOME"));
}
}Ref: EX-101
Exemple : exécution
import Sys;
import Io;
function main() : void {
ProcessResult r = Sys.execute("/bin/echo", ["hello"], [], true, true);
for (ProcessEvent e in r.events()) {
if (e.stream() == 1) {
Io.printLine(e.data().toUtf8String());
}
}
}Ref: EX-102
Le module RegExp fournit un moteur regex natif pour la recherche, la capture, le remplacement et le split.
Objectifs d'usage :
- API typée (
RegExp,RegExpMatch) - indices en glyphes
- UTF-8 strict
- comportement déterministe
Restrictions V1 (importantes) :
- pas de backreferences dans le motif (
\\1, etc.) - pas de lookaround (
(?=...),(?!...),(?<=...),(?<!...))
Fonctions / méthodes principales :
RegExp.compile(pattern, flags) : RegExpr.test(input, start) : boolr.find(input, start) : RegExpMatchr.findAll(input, start, max) : list<RegExpMatch>r.replaceFirst(input, replacement, start) : stringr.replaceAll(input, replacement, start, max) : stringr.split(input, start, maxParts) : list<string>r.pattern() : stringr.flags() : string
Remarque : RegExp peut désigner le prototype ou le module. Ils portent le même nom. La fonction compile() du module RegExp permet de construire le prototype suivant :
sealed prototype RegExp {
function test(string input, int start): bool {}
function find(string input, int start): RegExpMatch {}
function findAll(string input, int start, int max): list<RegExpMatch> {}
function replaceFirst(string input, string replacement, int start): string {}
function replaceAll(string input, string replacement, int start, int max): string {}
function split(string input, int start, int maxParts): list<string> {}
function pattern(): string {}
function flags(): string {}
}| Opération | Complexité | Impact énergétique |
|---|---|---|
| split() | ||
| test() | ||
| findAll() | ||
| replaceAll() |
Note : la complexité dépend fortement du motif.
Conventions de limite (uniformes) :
findAll(..., max = -1): illimitéreplaceAll(..., max = -1): illimitésplit(..., maxParts = -1): illimité- seules les valeurs
< -1lèventRegExpRange
RegExpMatch expose :
sealed prototype RegExpMatch {
function ok(): bool {}
function start(): int {}
function end(): int {}
function groups(): list<string> {} // groups()[0] = match complet
}Remplacement (replaceFirst / replaceAll) :
$0= match complet$1..$99= groupes capturants$$=$littéral
Exemple :
import RegExp;
import Io;
function main() : void {
RegExp r = RegExp.compile("(\\w+)-(\\w+)", "");
Io.printLine(r.replaceAll("alpha-beta gamma-delta", "$2:$1", 0, -1));
// beta:alpha delta:gamma
}Erreurs runtime (catégories dans le message) :
RegExpSyntaxRegExpLimitRegExpRange
Si on veut une validation simple et raisonnable sans couvrir 100% du RFC 5322 (ce qui serait irréaliste).
import RegExp;
import Io; function main() : void {
RegExp email = RegExp.compile( "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$", "" );
Io.printLine(email.test("john.doe@example.com", 0)); // true
Io.printLine(email.test("bad@@example", 0)); // false
}Ref: EX-105
Version pratique, plus robuste, recommandée, qui couvre: local part classique, points internes (pas au début/fin), pas de double point, domaine structuré et TLD ≥ 2 lettres.
import RegExp;
import Io;
function main() : void {
RegExp email = RegExp.compile(
"^[A-Za-z0-9_%+-]+(\\.[A-Za-z0-9_%+-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)+$",
""
);
Io.printLine(email.test("john.doe@example.com", 0)); // true
Io.printLine(email.test("john..doe@example.com", 0)); // false
Io.printLine(email.test(".john@example.com", 0)); // false
}Ref: EX-106
Ce que ça accepte :
john.doe@example.com
user_123@test-domain.fr
first.last+tag@gmail.com
a@b.co
Ce que ça refuse (volontairement) :
.john@example.com
john.@example.com
john..doe@example.com
john@localhost
john@example
Si on veut aller encore un cran au-dessus, on peut empêcher les labels domaine qui commencent ou finissent par - :
RegExp email = RegExp.compile([
"^[A-Za-z0-9_%+-]+(\\.[A-Za-z0-9_%+-]+)*@",
"[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?",
"(\\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)+$"].concat(),
""
);Ref: EX-107
Format international simple.
RegExp phone = RegExp.compile("^\\+?[0-9]{8,15}$", "");
Io.printLine(phone.test("+33612345678", 0)); // true
Io.printLine(phone.test("0612345678", 0)); // true
Io.printLine(phone.test("12-34-56", 0)); // falseRef: EX-108
Format DD/MM/YYYY
RegExp date = RegExp.compile("(\\d{2})/(\\d{2})/(\\d{4})", "");
RegExpMatch m = date.find("Date: 31/12/2026", 0);
if (m.ok) {
Io.printLine(m.groups[1]); // 31
Io.printLine(m.groups[2]); // 12
Io.printLine(m.groups[3]); // 2026
}Ref: EX-109
Io.printLine(
date.replaceAll("31/12/2026", "$3-$2-$1", 0, -1)
);
// 2026-12-31Ref: EX-110
RegExp tags = RegExp.compile("<[^>]+>", "");
string cleaned = tags.replaceAll("<p>Hello <b>world</b></p>", "", 0, -1);
Io.printLine(cleaned); // Hello worldRef: EX-111
Au moins :
-
8 caractères
-
lettres et chiffres
RegExp password = RegExp.compile("^[A-Za-z0-9]{8,}$", "");
Io.printLine(password.test("abc12345", 0)); // true
Io.printLine(password.test("short1", 0)); // falseRef: EX-112
Note :
Sans lookahead, on ne peut pas forcer “au moins un chiffre et une lettre” en une seule regex.
C’est volontaire et cohérent avec le choix d'implémentation retenue par ProtoScript2.
RegExp numbers = RegExp.compile("\\d+", "");
list<RegExpMatch> matches = numbers.findAll("Prix: 10€, 2€ taxe, 12€", 0, -1);
for (int i = 0; i < matches.length(); i++)
Io.printLine(matches[i].groups[0]);Ref: EX-113
Sortie :
10 2 12
Structure qui ressamble à une adresse IPv4.
RegExp ipv4 = RegExp.compile( "^([0-9]{1,3}\\.){3}[0-9]{1,3}$", "" );
Io.printLine(ipv4.test("192.168.1.1", 0)); // true
Io.printLine(ipv4.test("999.999.999.999",0)); // true (structure OK)Ref: EX-114
Note pédagogique :
Ici la regex valide la structure, pas la validité numérique (0–255).
La validation complète doit être faite en code.
RegExp comma = RegExp.compile(",", "");
list<string> parts = comma.split("alpha,beta,gamma", 0, -1);
for (int i = 0; i < parts.length(); i++)
Io.printLine(parts[i]);Ref: EX-115
RegExp spaces = RegExp.compile("\\s+", "");
Io.printLine(
spaces.replaceAll("Hello world !", " ", 0, -1)
);
// Hello world !Ref: EX-116
RegExp identifier = RegExp.compile( "^[A-Za-z_][A-Za-z0-9_]*$", "" );
Io.printLine(identifier.test("valid_name_1", 0)); // true
Io.printLine(identifier.test("1invalid", 0)); // falseRef: EX-117
Les comportements complets sont normatifs et définis dans :
docs/module_io_specification.mddocs/module_math_specification.mddocs/module_json_specification.mddocs/module_fs_specification.mddocs/module_sys_specification.mddocs/module_sys_execute_specification.mddocs/module_regexp_specification.md
- introduire de nouveaux opérateurs
- changer les règles de typage
- activer de la RTTI/réflexion
- modifier la grammaire
L'extension est un mécanisme d'intégration, pas un mécanisme de mutation du langage.
Diagnostics avec code, catégorie, position file:line:column.
Les violations runtime normatives lèvent des exceptions catégorisées.
Toute exception dérive du prototype racine Exception.
Aucune autre valeur ne peut être levée avec throw.
Les exceptions runtime standard dérivent de RuntimeException.
Vous pouvez définir des prototypes dérivés de Exception.
Les exceptions s’instancient exclusivement via clone() ; Exception(...) et RuntimeException(...) sont interdits.
prototype Exception {
string file;
int line;
int column;
string message; /* optionnel */
Exception cause; /* optionnel, sert au chaînage d’exceptions (cause racine) */
}prototype RuntimeException : Exception {
string code; /* exemple : R1004 */
string category; /* exemple RUNTIME_DIVIDE_BY_ZERO */
}| Code | Catégorie | Exemple |
|---|---|---|
R1001 |
RUNTIME_INT_OVERFLOW |
overflow int |
R1002 |
RUNTIME_INDEX_OOB |
index hors bornes |
R1003 |
RUNTIME_MISSING_KEY |
map clé absente |
R1004 |
RUNTIME_DIVIDE_BY_ZERO |
division par zéro |
R1005 |
RUNTIME_SHIFT_RANGE |
décalage invalide |
R1006 |
RUNTIME_EMPTY_POP |
pop sur liste vide |
R1007 |
RUNTIME_UTF8_INVALID |
UTF‑8 invalide |
R1010 |
RUNTIME_TYPE_ERROR |
type runtime incompatible |
import Io;
function main() : void {
try {
Exception e = Exception.clone();
e.message = "Quelque chose s'est mal passe";
throw e;
} catch (Exception e) {
Io.printLine(e.message);
}
}Ref: EX-087A
import Io;
prototype MyError : Exception {
string details;
}
function main() : void {
try {
MyError e = MyError.clone();
e.message = "Erreur metier";
e.details = "code:42";
throw e;
} catch (MyError ex) {
Io.printLine(ex.details);
}
}Ref: EX-087B
Exception non catchée
function main() : void {
Exception e = Exception.clone();
e.message = "boom";
throw e;
}Sortie attendue :
script.pts:4:5 R1011 UNHANDLED_EXCEPTION: unhandled exception. got Exception("boom"); expected matching catch
Division par zéro
function main() : void {
int a = 1;
int b = 0;
int c = a / b;
}Sortie attendue :
script.pts:4:17 R1004 RUNTIME_DIVIDE_BY_ZERO: division by zero. got 0; expected non-zero divisor
Clé manquante dans un map
function main() : void {
map<string,int> m = {};
int v = m["absent"];
}Sortie attendue :
script.pts:3:13 R1003 RUNTIME_MISSING_KEY: missing key. got "absent"; expected present key
try {
risky();
} catch (Exception e) {
Io.printLine("handled");
} finally {
Io.printLine("cleanup");
}Ref: EX-087
Sémantique de filtrage catch
- les clauses
catchsont évaluées dans l’ordre d’écriture. - une clause
catch (T e)correspond si le type dynamique de l’exception estTou dérive deT. - la première clause qui correspond est exécutée ; les suivantes sont ignorées.
- si aucune clause ne correspond, l’exception est propagée après exécution du
finally(s’il existe). catch (Exception e)est un catch‑all.
// invalide : throw d'une valeur non Exception
// throw 42;Confondre absence de RTTI utilisateur et mécanisme catch par type : catch utilise une métadonnée interne d'exception, non exposable.
Exécution déterministe selon l'ordre d'évaluation défini.
Le CLI ps exécute un fichier ProtoScript V2, ou du code inline.
Exemples :
ps run fichier.pts
ps -e "Io.printLine(42);"
ps check fichier.pts
ps emit-c fichier.pts
ps testRef: EX-088
Options courantes :
--help
--version
--trace
--trace-ir
--time
Ref: EX-089
Position des options :
- les options peuvent apparaître avant ou après la commande.
- exemples équivalents :
ps --trace run fichier.ptsps run fichier.pts --trace
Détails des commandes :
ps run fichier.pts: exécute le programme (runtime C).ps -e "code": exécute un extrait inline (wrap dans unmainimplicite).ps check fichier.pts: parse + analyse statique uniquement (aucune exécution).ps ast fichier.pts: affiche l’AST (arbre de syntaxe) en JSON stable pour inspection.ps ir fichier.pts: affiche l’IR (intermédiaire) en JSON stable pour inspection.ps emit-c fichier.pts: génère du C via l’oracleprotoscriptc(Node).ps test: lance la suite de conformité (tests normatifs).
Règle normative CLI :
- La commande
runDOIT effectuer la validation statique avant toute exécution runtime. - Si des erreurs statiques sont présentes,
runDOIT s’arrêter immédiatement, afficher les diagnostics, et NE DOIT PAS invoquer le runtime. - Cette règle reste vraie avec
--traceet--trace-ir: aucun log runtime/IR ne doit apparaître quand des erreurs statiques existent.
Détails des options :
--trace: journalisation des étapes d’exécution (runtime). Sorties préfixées par[trace].--trace-ir: journalisation des instructions IR au moment de l’exécution. Sorties préfixées par[ir].--time: affiche le temps d’exécution total (ms).
ps test exécute la suite de conformité complète (tests normatifs).
- Pas de RTTI utilisateur.
- Pas de réflexion.
- Pas de comportement implicite dépendant de l'environnement runtime.
Signatures autorisées :
function main() : void { }
function main() : int { return 0; }
function main(list<string> args) : void { }
function main(list<string> args) : int { return 0; }Ref: EX-090
args reçoit tous les arguments tels que fournis par le système, sans filtrage, y compris le binaire et la sous‑commande.
Exemple avec le CLI :
./ps run fichier.pts a b
Ref: EX-091
args vaut :
["./ps", "run", "fichier.pts", "a", "b"]
Ref: EX-092
Codes de sortie par défaut :
0: succès2: erreur utilisateur (syntaxique, statique, runtime)1: erreur interne (assert/bug/OOM)
Si main retourne un int, cette valeur devient le code de sortie.
Quand vous compilez du ProtoScript V2 vers du C avec le compilateur protoscriptc (option --emit-c), le code C généré s’appuie sur le runtime C et sur les modules natifs nécessaires.
Cela signifie que :
import Math...,import Io...,import JSON...exigent que ces modules soient présents au link/chargement.- il n’y a aucun fallback implicite : si le module n’est pas fourni, l’exécution échoue.
Exemple minimal :
import Math.{sqrt};
function main() : void {
float x = sqrt(9.0);
Io.printLine(x);
}Ref: EX-093
Le binaire C généré doit être exécuté avec le runtime et les modules natifs disponibles.
Exemple concret (compilation + édition de liens) :
# Générer le C depuis un fichier ProtoScript
bin/protoscriptc --emit-c hello.pts > hello.c
# Compiler et lier contre le runtime C de ProtoScript
cc -std=c11 -O2 -I./include \
hello.c \
c/runtime/ps_api.c c/runtime/ps_errors.c c/runtime/ps_heap.c c/runtime/ps_value.c \
c/runtime/ps_string.c c/runtime/ps_list.c c/runtime/ps_object.c c/runtime/ps_map.c \
c/runtime/ps_dynlib_posix.c c/runtime/ps_json.c c/runtime/ps_modules.c c/runtime/ps_vm.c \
-ldl -o hello
# Exécuter (les modules natifs requis doivent être accessibles)
./helloRef: EX-094
pscc fournit un frontend C partiel et peut rediriger vers l’oracle Node pour certaines sorties.
Commandes principales :
./c/pscc --check file.pts
./c/pscc --check-c file.pts
./c/pscc --check-c-static file.pts
./c/pscc --ast-c file.pts
./c/pscc --emit-ir-c-json file.pts
./c/pscc --emit-ir file.pts # forward vers bin/protoscriptc
./c/pscc --emit-c file.pts # forward vers bin/protoscriptcRef: EX-095
Pas d'ajout dynamique de membres/fonctions à chaud. L'exécution suit un contrat statique.
Les coûts doivent rester visibles dans le code et prévisibles.
ProtoScript V2 privilégie la transparence des coûts.
| Principe | Effet |
|---|---|
| Copies explicites | Pas de coût caché |
| Vues non possédantes | Pas de duplication implicite |
| Checks runtime normatifs | Sécurité visible |
| Absence de magie | Pas d’explosion implicite |
La complexité algorithmique et l’impact énergétique doivent rester visibles dans le code.
Les checks normatifs font partie de l'exécution normale. Ils ne sont élidables que si leur inutilité est prouvée.
Le coût "zéro-cost" concerne le mécanisme d'unwind/dispatch quand aucune exception n'est levée. Il ne signifie pas "absence de checks runtime normatifs".
- Même sémantique observable.
- Différences autorisées : instrumentation et qualité des diagnostics.
Le langage privilégie des garanties défendables plutôt que des promesses de performance implicites.
Types (base) :
bool,byte,int,float,glyph,string- pas de
nulluniversel - conversions explicites seulement
Collections :
list<T>: mutable,list[i] = xstrict,push/popexplicitesmap<K,V>: lecture stricte (map[k]exige clé présente), écriture constructive (map[k] = vinsère/met à jour)slice<T>: vue mutable non possédanteview<T>: vue lecture seule non possédantestring: immuable, indexation glyphique
Erreurs fréquentes :
- oublier le type de retour d'une fonction
- tenter
a = b = c(affectation chaînée interdite) - supposer
sum()valide avec variadique (la séquence variadique doit être non vide) - écrire dans
string[i]ouview[i] - lire
map[k]sur une clé absente en pensant obtenir une valeur par défaut
Différences clés vs JS/PHP :
- pas de typage dynamique
- pas de fonctions comme valeurs
- pas de null universel
- pas de chargement dynamique des modules
- pas de concaténation implicite de chaînes
| Concept | Où lire |
|---|---|
| Unicode / glyphes | §13 |
| Exceptions | §15 |
| map lecture stricte / écriture constructive | §11.2 |
| Variadique | §9.3 |
| slice / view | §12 |
| Prototypes et substitution parent/enfant | §10 |
| Modules et imports | §14 |
| Ordre d'évaluation | §6.2 |
| switch sans fallthrough implicite | §8.4 |
| Absence de null | §3.3 |
| Famille | Opérateurs |
|---|---|
| Unaires | ! ~ - ++ -- |
| Multiplicatifs | * / % |
| Additifs | + - |
| Bitwise | `& |
| Shifts | << >> |
| Comparaison | == != < <= > >= |
| Logiques | `&& |
| Conditionnel (ternaire) | ?: |
| Affectation | = += -= *= /= |
function sum(list<int> values...) : int {
int acc = 0;
for (int v of values) {
acc = acc + v;
}
return acc;
}
function main() : void {
int r = sum(1, 2, 3);
Io.printLine(r.toString());
}Ref: EX-096
- Par rapport à JavaScript : pas de typage dynamique, pas de fonctions comme valeurs, pas de métaprogrammation runtime.
- Par rapport à PHP : pas d'HTML embarqué, pas de superglobales, pas de variables dynamiques.
- Par rapport à C : sémantique de sûreté normative (checks/diagnostics), tout en gardant un modèle de compilation bas niveau.
Ce manuel décrit l'usage quotidien.
La spécification SPECIFICATION.md définit la loi du langage.