378 lines
11 KiB
Markdown
378 lines
11 KiB
Markdown
# 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.
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
'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 :
|
|
|
|
```text
|
|
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é.
|
|
|
|
```text
|
|
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
|
|
```
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 directes retournent la destination attendue. Lorsqu'une validation runtime peut échouer, la callable peut déclarer `faults UnicodeEncodingFault` au lieu de forcer un `ResultError` ou une variante `try...` parallèle.
|
|
|
|
Exemples :
|
|
|
|
```text
|
|
char::toUtf8Char() -> Utf8Char
|
|
Utf8Char::toChar() -> char
|
|
Utf8Char::toUtf16Char() -> Utf16Char
|
|
|
|
Utf8Char::from(uint8) -> Utf8Char
|
|
faults UnicodeEncodingFault
|
|
|
|
Utf16Char::from(uint16) -> Utf16Char
|
|
faults UnicodeEncodingFault
|
|
|
|
Utf32Char::from(uint32) -> Utf32Char
|
|
faults UnicodeEncodingFault
|
|
|
|
Utf8Char::toUint8() -> uint8
|
|
faults UnicodeEncodingFault
|
|
|
|
Utf16Char::toUint16() -> uint16
|
|
faults UnicodeEncodingFault
|
|
|
|
Utf32Char::toUint32() -> uint32
|
|
```
|
|
|
|
`UnicodeEncodingFault extends Fault` est le nom de travail du fault Core associé aux données encodées invalides ou aux réductions impossibles.
|
|
|
|
Le Core n'expose pas simultanément `from(...)` et `tryFrom(...)` lorsque les deux opérations auraient exactement la même sémantique et ne différeraient que par le canal d'échec.
|
|
|
|
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.
|
|
|
|
```text
|
|
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`.
|
|
|
|
```text
|
|
String text = ...;
|
|
text[5]; // ERROR
|
|
```
|
|
|
|
Son unité logique est explicitement nommée :
|
|
|
|
```text
|
|
text::scalarAt(index) -> char
|
|
text::scalarCount() -> uint64
|
|
```
|
|
|
|
Les strings encodées peuvent exposer `[]` en lecture pour leurs code units physiques :
|
|
|
|
```text
|
|
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` :
|
|
|
|
```text
|
|
encodedCharAt(scalarIndex)
|
|
retourne le UtfXChar correspondant au scalar ordinal demandé
|
|
|
|
scalarAt(scalarIndex)
|
|
retourne le char correspondant
|
|
```
|
|
|
|
Exemples :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
scalarCount()
|
|
```
|
|
|
|
Les strings encodées distinguent :
|
|
|
|
```text
|
|
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ë :
|
|
|
|
```text
|
|
String implements Iterable<char>
|
|
```
|
|
|
|
Pour `Utf8String`, `Utf16String` et `Utf32String`, plusieurs parcours utiles existent :
|
|
|
|
```text
|
|
code units
|
|
encoded chars
|
|
Unicode scalars
|
|
```
|
|
|
|
Aucun `Iterable<T>` direct unique n'est imposé aux strings encodées en V1.
|
|
|
|
Les parcours sont explicites :
|
|
|
|
```text
|
|
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.
|
|
|
|
```text
|
|
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.
|
|
|
|
```text
|
|
Utf8String text = ...;
|
|
Utf8Char value = ...;
|
|
|
|
text::append(value); // OK
|
|
```
|
|
|
|
Une mutation brute de code unit reste interdite :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
"..." 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 :
|
|
|
|
```text
|
|
\\
|
|
\"
|
|
\'
|
|
\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.
|
|
|
|
```text
|
|
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.
|
|
|
|
---
|