
# Architettura

shikiphp trasforma il codice sorgente in HTML evidenziato con temi, usando grammatiche TextMate e temi VS Code, senza estensioni PHP oltre a `json`/`mbstring`, senza runtime Node e senza binding nativo Oniguruma. Questa pagina riassume la pipeline; il contratto normativo completo è in `ARCHITECTURE.md` nel repository.

## La 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>
```

Tutte le operazioni usano offset in unità di codice **UTF-16** — lo stesso spazio del motore regex JS e del tokenizer di Shiki. Per ASCII equivale agli offset in byte.

## Il runtime regex

Alla base c’è `Shikiphp\Regex`, un motore regex ECMAScript completo e puro-PHP, **integrato da [`inline0/phasis`](https://github.com/inline0/phasis)** (un motore PHP JavaScript). Analizza una sorgente RegExp JS con i flag ed esegue ricerche in avanti con la semantica Oniguruma `onig_search`.

## Conversione da Oniguruma a JS

Le grammatiche TextMate sono scritte in Oniguruma. `Shikiphp\Oniguruma\PatternConverter` è un porting PHP di [`oniguruma-to-es`](https://github.com/slevithan/oniguruma-to-es): converte un pattern Oniguruma in una sorgente e una stringa di flag compatibili con JS-RegExp, accettate dal motore regex. È lo stesso percorso Oniguruma → RegExp usato dal motore regex di ShikiJavaScript, che consente a shikiphp di corrispondere al suo output offset per offset. Il convertitore gestisce i costrutti usati dalle grammatiche nel mondo reale: classi POSIX, quantificatori possessivi e gruppi atomici, flag inline, gruppi con nome e riferimenti all’indietro, proprietà Unicode e altro.

## Lo scanner e il suo percorso rapido PCRE

`Shikiphp\Oniguruma\OnigScanner` rispecchia lo scanner di `vscode-oniguruma`: viene costruito una volta per regola a partire da un elenco di pattern e trova la corrispondenza più a sinistra in corrispondenza o dopo una posizione iniziale.

Il matcher che percorre l’albero è corretto, ma lento; perciò lo scanner dispone di un **percorso rapido PCRE con equivalenza verificata**, articolato in due livelli. Un `PcreTranslator` riscrive la sorgente JS del convertitore come pattern PCRE nativo solo quando può dimostrare che il risultato:

- **Corrispondenza completa.** Se la traduzione si comporta in modo identico al matcher, incluse tutte le catture, il pattern viene eseguito direttamente in PCRE. Gruppi con nome, riferimenti all’indietro, `\p{}`, `\b`, `\G`, emulazione dei gruppi atomici e catture la cui semantica JavaScript differisce da PCRE (all’interno di un gruppo quantificato o di un lookbehind) vengono esclusi da questo livello.
- **Solo posizione.** Se possono differire solo le catture, una seconda traduzione ammette questi costrutti (`\b` e `\B` come lookaround ASCII esatti, gruppi atomici come `(?>…)` nativi, `\p{…}` se PCRE riesce a compilarlo). PCRE trova solo la posizione della corrispondenza successiva; il matcher viene poi rieseguito a quell’offset per produrre le catture effettive. Anche i riferimenti all’indietro e `\G` restano esclusi.

Tutto il resto viene eseguito solo dal matcher. Nelle grammatiche incluse a Shiki 3.23.0, circa il 39% dei 14,234 pattern distinti viene eseguito interamente in PCRE, circa il 57% usa il livello di sola posizione e circa il 4% resta al matcher. La classificazione viene verificata da uno strumento di sviluppo nel repository (`bin/.oracle-tools/pcre-equivalence.php`), che confronta il percorso rapido con il matcher — indice, fine e, per il primo livello, intervallo di ogni cattura — sugli input degli scenari e su un insieme di stringhe sintetiche, senza ammettere alcuna divergenza.

## Grammatica e tema

- **Grammatica** (`Shikiphp\Grammar`) è un port fedele di `vscode-textmate`: un `Registry` risolve le grammatiche in base al nome dello scope (incluse le referenze `include`, i linguaggi incorporati e le iniezioni), mentre un `Tokenizer` mantiene gli stack delle regole e degli scope riga per riga.
- **Tema** (`Shikiphp\Theme`) porta il matching dei temi di `vscode-textmate`: il colore di primo piano e lo stile del font vengono risolti in base alla specificità del selettore di scope e alla corrispondenza con gli scope genitori.

## Rendering

`Shikiphp\Render\HtmlRenderer` costruisce esattamente lo stesso albero HAST `pre.shiki > code > span.line >
span` di Shiki, quindi lo serializza. La modalità con un solo tema scrive colori semplici; quella con due temi scrive le variabili CSS `--shiki-light` / `--shiki-dark`.

## Convalidato rispetto a Shiki

Shiki stesso è il riferimento oracle, fissato a `shiki` 3.23.0 con il suo motore regex JavaScript. La directory `scenarios/` contiene **214** fixture, ciascuna con un input, un linguaggio e un tema; nel complesso coprono 167 delle 253 grammatiche incluse, `lang: 'ansi'` e tutti i 65 temi. `bin/test-regression` esegue l’evidenziazione di ciascuna con entrambi i motori e verifica che HTML sia identico dopo aver rimosso gli spazi iniziali e finali. Opzioni e transformer vengono verificati con test di parità rispetto all’output acquisito dallo stesso Shiki e `@shikijs/transformers` 4.2.0. Il requisito inderogabile è che l’output dell’evidenziazione non peggiori rispetto a Shiki.
