
# Architecture

shikiphp transforme le code source en HTML mis en évidence et stylisé à l’aide des grammaires TextMate et des thèmes VS Code, sans extension PHP autre que `json`/`mbstring`, sans environnement Node ni liaison native Oniguruma. Cette page résume le pipeline ; le contrat normatif complet figure dans `ARCHITECTURE.md` du dépôt.

## Le pipeline

```
source code
  └─ Highlighter::codeToTokens()
       ├─ Grammar\Registry        loads the grammar (+ its includes / embedded langs)
       ├─ Grammar\Tokenizer       line by line, driving an OnigScanner per rule state
       │     └─ Oniguruma\OnigScanner.findNextMatch()
       │           └─ Oniguruma\PatternConverter   Oniguruma pattern → JS regex
       │                 └─ Shikiphp\Regex (Parser + Matcher)   JS RegExp runtime
       ├─ Theme\Theme.match(scopeStack) → StyleAttributes   per token
       └─ Render\ThemedToken[]   (text + style) per line
  └─ Render\HtmlRenderer  → <pre class="shiki">…</pre>
```

Tout fonctionne avec des décalages en **unités de code UTF-16**, comme le moteur d’expressions régulières JS et le tokenizer de Shiki. Pour l’ASCII, cela correspond aux décalages en octets.

## L’environnement d’exécution des expressions régulières

À la base se trouve `Shikiphp\Regex`, un moteur complet d’expressions régulières ECMAScript pur-PHP, **intégré au projet depuis [`inline0/phasis`](https://github.com/inline0/phasis)** (un moteur JavaScript pur-PHP). Il analyse une source RegExp JS avec ses indicateurs et effectue des recherches vers l’avant avec la sémantique `onig_search` Oniguruma.

## Conversion de Oniguruma vers JS

Les grammaires TextMate sont écrites en Oniguruma. `Shikiphp\Oniguruma\PatternConverter` est un portage PHP de [`oniguruma-to-es`](https://github.com/slevithan/oniguruma-to-es) : il convertit un motif Oniguruma en une source et une chaîne d’indicateurs compatibles avec RegExp JS, acceptées par le moteur d’expressions régulières. C’est le même chemin Oniguruma → RegExp qu’emprunte le moteur d’expressions régulières JavaScript de Shiki, ce qui permet à shikiphp de correspondre à sa sortie décalage par décalage. Le convertisseur gère les constructions courantes des grammaires : classes POSIX, quantificateurs possessifs et groupes atomiques, indicateurs intégrés, groupes nommés et références arrière, propriétés Unicode, et bien d’autres.

## Le scanner et son chemin rapide PCRE

`Shikiphp\Oniguruma\OnigScanner` reprend le scanner de `vscode-oniguruma` : il est créé une fois par règle à partir d’une liste de motifs et trouve la correspondance la plus à gauche à partir d’une position donnée.

Le moteur de correspondance qui parcourt l’arbre est correct, mais lent. Le scanner dispose donc d’un **chemin rapide PCRE soumis à une vérification d’équivalence**, avec deux niveaux. Un `PcreTranslator` réécrit la source JS du convertisseur en motif PCRE natif uniquement lorsqu’il peut en prouver le résultat :

- **Correspondance complète.** Lorsque la traduction se comporte à l’identique du moteur de correspondance, y compris pour chaque capture, le motif est exécuté directement par PCRE. Les groupes nommés, références arrière, `\p{}`, `\b`, `\G`, l’émulation des groupes atomiques et les captures dont la sémantique JavaScript diffère de celle de PCRE (dans un groupe quantifié ou une assertion arrière) sont exclus de ce niveau.
- **Position uniquement.** Lorsque seules les captures peuvent différer, une seconde traduction accepte ces constructions (`\b` et `\B` comme assertions exactes en ASCII, groupes atomiques sous forme de `(?>…)` natifs, `\p{…}` si PCRE le compile). PCRE ne fait alors que trouver la position de la prochaine correspondance, puis le moteur de correspondance relance la recherche à cet emplacement pour produire les vraies captures. Les références arrière et `\G` restent également exclues ici.

Tout le reste est traité uniquement par le moteur de correspondance. Pour les grammaires fournies dans le bundle à Shiki 3.23.0, environ 39 % des 14,234 motifs distincts s’exécutent entièrement dans PCRE, environ 57 % utilisent le niveau position et environ 4 % restent sur le moteur de correspondance. La classification est vérifiée par un outil de développement du dépôt (`bin/.oracle-tools/pcre-equivalence.php`), qui compare le chemin rapide au moteur de correspondance — index, fin et, pour le premier niveau, chaque intervalle de capture — sur les entrées des scénarios et un ensemble de chaînes synthétiques, sans aucune divergence autorisée.

## Grammaire et thème

- **Grammaire** (`Shikiphp\Grammar`) : portage fidèle de `vscode-textmate`. Un `Registry` résout les grammaires par nom de portée (y compris les références `include`, les langages intégrés et les injections), et un `Tokenizer` gère les piles de règles et de portées ligne par ligne.
- **Thème** (`Shikiphp\Theme`) : portage de la correspondance des thèmes de `vscode-textmate`. La couleur de premier plan et le style de police sont déterminés selon la spécificité du sélecteur de portée et la correspondance des portées parentes.

## Rendu

`Shikiphp\Render\HtmlRenderer` construit l’arbre HAST `pre.shiki > code > span.line >
span` exact que construit Shiki, puis le sérialise. Le mode à thème unique écrit des couleurs simples ; le mode à deux thèmes écrit les variables `--shiki-light` / `--shiki-dark` CSS.

## Validé par rapport à Shiki

Shiki est lui-même la référence, épinglé à `shiki` 3.23.0 avec son moteur d’expressions régulières JavaScript. Le répertoire `scenarios/` contient **214** fixtures, chacune correspondant à une entrée dans un langage et un thème ; ensemble, elles couvrent 167 des 253 grammaires du bundle, `lang: 'ansi'` et les 65 thèmes. `bin/test-regression` applique les deux moteurs à chacune et vérifie que la chaîne HTML est identique après suppression des espaces de début et de fin. Les options et les transformateurs sont vérifiés par des tests de parité avec la sortie capturée des mêmes versions de Shiki et `@shikijs/transformers` 4.2.0. La règle impérative est que la sortie de la coloration syntaxique ne doit pas régresser par rapport à Shiki.
