Files
saselang-bible/chapters/021-strings-unicode-et-encodages.md
2026-09-13 00:12:41 +02:00

10 KiB

21. Strings, Unicode et encodages

21.1 String — V1 REQUIS — FIGÉ EN PRINCIPE

String est le type ordinaire de texte Unicode au niveau sémantique.

Sa valeur observable est une séquence de Unicode scalar values.

La représentation physique interne peut varier suivant le backend/target, sans devenir observable via l'API sémantique de String.

String est mutable par défaut lorsque son API expose des opérations mutantes contrôlées.

String text = "abc";
text::append("def"); // OK

Un accès const String interdit les mutations via cet accès selon les règles générales de const.

21.2 char — V1 REQUIS — FIGÉ

char représente exactement un Unicode scalar value, et non un octet, une unité UTF-8/UTF-16 ou un grapheme utilisateur complet.

Exemples valides :

'A'
'é'
'中'
'😀'
'\n'
'\u{1F600}'

Après traitement des escapes, un littéral char doit contenir exactement un scalar.

Saselang n'effectue aucune normalisation Unicode implicite.

21.3 Types encodés Core — V1 REQUIS — DIRECTION FIGÉE

Le Core fournit les types encodés explicites :

Utf8Char
Utf16Char
Utf32Char

Utf8String
Utf16String
Utf32String

Ils ne sont ni des alias ni des sous-classes de String/char.

Ils expriment explicitement l'encodage utilisé.

Utf8Char
    exactement un Unicode scalar encodé en UTF-8
    1 à 4 uint8

Utf16Char
    exactement un Unicode scalar encodé en UTF-16
    1 à 2 uint16

Utf32Char
    exactement un Unicode scalar encodé en UTF-32
    1 uint32
Utf8String
    séquence UTF-8 valide

Utf16String
    séquence UTF-16 valide

Utf32String
    séquence UTF-32 valide

Toute valeur de ces types maintient son invariant d'encodage.

Les code units brutes restent :

UTF-8  -> uint8
UTF-16 -> uint16
UTF-32 -> uint32

Aucun Utf8CodeUnit / Utf16CodeUnit / Utf32CodeUnit distinct n'est introduit tant qu'un besoin sémantique réel n'est pas démontré.

21.4 Conversions Unicode — V1 REQUIS — FIGÉ EN PRINCIPE

Aucun transcodage implicite n'existe.

Les conversions totales utilisent to....

Les constructions ou réductions pouvant échouer utilisent tryFrom... / tryTo....

Exemples :

char::toUtf8Char()
Utf8Char::toChar()
Utf8Char::toUtf16Char()

Utf8Char::tryFrom(uint8)
Utf16Char::tryFrom(uint16)
Utf32Char::tryFrom(uint32)

Utf8Char::tryToUint8()
Utf16Char::tryToUint16()
Utf32Char::toUint32()

La matrice normative détaillée est définie dans annexes/B-unicode-encoding-conversions.md.

21.5 Aucun mélange implicite de types — V1 REQUIS — FIGÉ

Une API textuelle n'effectue jamais un transcodage implicite de son argument.

Utf8String text = ...;
Utf16Char value = ...;

text::append(value);                // ERROR
text::append(value::toUtf8Char());  // OK

Même règle pour concaténation, insertion, remplacement, recherche typée et construction.

Les conversions sont effectuées explicitement avant l'opération métier.

21.6 Indexation — V1 REQUIS — FIGÉ

String n'implémente pas OpIndex.

String text = ...;
text[5]; // ERROR

Son unité logique est explicitement nommée :

text::scalarAt(index) -> char
text::scalarCount() -> uint64

Les strings encodées peuvent exposer [] en lecture pour leurs code units physiques :

Utf8String[index]  -> uint8
Utf16String[index] -> uint16
Utf32String[index] -> uint32

Elles n'exposent pas OpIndexMut, afin qu'une écriture brute ne puisse pas casser l'invariant d'encodage.

21.7 Accès scalar et caractère encodé — V1 REQUIS — FIGÉ EN PRINCIPE

Pour un UtfXString :

encodedCharAt(scalarIndex)
    retourne le UtfXChar correspondant au scalar ordinal demandé

scalarAt(scalarIndex)
    retourne le char correspondant

Exemples :

Utf8String::encodedCharAt(uint64)  -> Utf8Char
Utf16String::encodedCharAt(uint64) -> Utf16Char
Utf32String::encodedCharAt(uint64) -> Utf32Char

Utf8String::scalarAt(uint64)  -> char
Utf16String::scalarAt(uint64) -> char
Utf32String::scalarAt(uint64) -> char

Un index de code unit et un ordinal de scalar sont des concepts distincts.

Une API de décodage depuis un offset de code unit peut exister explicitement. En UTF-8/UTF-16, elle est faillible lorsqu'un offset peut pointer au milieu d'une séquence encodée.

Le nom exact de cette API reste à stabiliser avec les vues/slices.

21.8 Comptage — V1 REQUIS — FIGÉ

String expose :

scalarCount()

Les strings encodées distinguent :

codeUnitCount()
scalarCount()

Ces deux propriétés restent distinctes même lorsqu'elles coïncident systématiquement dans UTF-32, car elles décrivent deux unités sémantiques différentes.

21.9 Itération — V1 REQUIS — FIGÉ EN PRINCIPE

String possède une unité sémantique non ambiguë :

String implements Iterable<char>

Pour Utf8String, Utf16String et Utf32String, plusieurs parcours utiles existent :

code units
encoded chars
Unicode scalars

Aucun Iterable<T> direct unique n'est imposé aux strings encodées en V1.

Les parcours sont explicites :

codeUnits()
encodedChars()
scalars()

Le type concret de vue/itérateur retourné sera fixé avec les collections et slices.

21.10 Mutation contrôlée — V1 REQUIS — FIGÉ

String et UtfXString ne sont pas intrinsèquement immutables.

Ils peuvent exposer des méthodes mutantes contrôlées.

String text = "abc";
text::append("def");

Après l'appel, text contient la valeur modifiée selon le contrat de append.

Pour les strings encodées, toute méthode mutante doit préserver l'invariant d'encodage.

Utf8String text = ...;
Utf8Char value = ...;

text::append(value); // OK

Une mutation brute de code unit reste interdite :

text[index] = 0xFF; // ERROR

Un accès const désactive les opérations mutantes via cet accès, sans rendre globalement immutable l'objet partagé.

21.11 Comparaison et normalisation — V1 REQUIS — FIGÉ EN PRINCIPE

Aucune normalisation Unicode, case folding ou collation linguistique n'est implicite.

Deux String sont égales selon leur séquence sémantique exacte de Unicode scalars.

Des textes visuellement équivalents mais composés de séquences différentes peuvent donc être différents.

Les normalisations NFC/NFD, case folding, grapheme segmentation et collations localisées relèvent d'API explicites Core/SDK à détailler.

21.12 Familles de littéraux texte — V1 REQUIS / RÉSERVÉ

Les familles suivantes sont reconnues ou réservées parce qu'elles représentent des sémantiques distinctes :

"..."                 String normale, escapes actifs
"""..."""             String normale multiligne
r"..."                String brute
r"""..."""            String brute multiligne
i"..."                String interpolée
i"""..."""            String interpolée multiligne
ir"..."               interpolation + contenu brut, réservé
b"..."                bytes, réservé
br"..."               bytes bruts, réservé
u8"..."               texte encodé UTF-8 explicitement, réservé
u16"..."              texte encodé UTF-16 explicitement, réservé
u32"..."              texte encodé UTF-32 explicitement, réservé
c"..."                chaîne compatible C, réservé
cr"..."               chaîne C brute, réservé
t"..."                template structuré, réservé

V1 doit au minimum couvrir les formes normales, multilignes, raw et interpolées.

Le délimiteur interne de l'interpolation reste à finaliser entre les formes déjà inventoriées.

21.13 Chaînes multilignes — V1 REQUIS — FIGÉ

Les triples quotes fournissent la forme multiligne.

Le délimiteur de fermeture détermine l'indentation structurelle à retirer de chaque ligne. Une indentation supplémentaire volontaire dans le contenu est conservée.

Lorsque le délimiteur d'ouverture multiligne est immédiatement suivi d'un retour à la ligne, ce premier retour structurel n'appartient pas à la valeur. Lorsque le délimiteur de fermeture se trouve seul après l'indentation structurelle d'une nouvelle ligne, le dernier retour structurel est également supprimé.

Le formatter peut réindenter la structure source uniquement en préservant exactement la valeur sémantique du littéral.

21.14 Raw strings et escapes — V1 REQUIS — FIGÉ

Les raw strings utilisent r contigu et peuvent employer zéro ou plusieurs # dans leur délimiteur.

Aucun escape n'est interprété dans le contenu raw.

Dans les String normales et les char, l'ensemble canonique reste :

\\
\"
\'
\n
\r
\t
\0
\u{...}

\u{...} contient de 1 à 6 chiffres hexadécimaux, n'accepte pas _, doit être inférieur ou égal à 0x10FFFF et ne peut pas désigner la plage surrogate UTF-16.

\xNN reste réservé aux littéraux orientés octets.

21.15 Source Unicode et représentation — V1 REQUIS — FIGÉ EN PRINCIPE

Le fichier source UTF-8 peut contenir directement des scalars Unicode valides dans String, char, commentaires et Saseldoc.

Une séquence UTF-8 invalide est une erreur de source.

La représentation physique interne de String reste indépendante de cette syntaxe source et peut varier suivant backend/target.

21.16 Sous-chaînes et représentation interne — V1 REQUIS — FIGÉ EN PRINCIPE

La représentation interne de String n'est pas observable.

Une implémentation peut notamment employer un stockage contigu, partagé, segmenté, copy-on-write, rope, small-string optimization ou une combinaison de ces techniques.

substring(...) retourne une vraie String sémantiquement indépendante.

String a = "abcdef";
String b = a::substring(1..<4);

b représente indépendamment "bcd".

Une mutation ultérieure de a ne peut pas modifier la valeur observable de b, et inversement.

Cette indépendance sémantique n'impose aucune copie physique immédiate : le runtime peut partager des segments/backing storage tant que le contrat reste respecté.

Aucun type StringView n'est introduit ni réservé à ce stade. Il ne sera étudié que si un besoin concret apparaît.