
# Arquitectura

shikiphp convierte código fuente en HTML resaltado con temas, mediante gramáticas TextMate y temas VS Code, sin extensiones de PHP aparte de `json`/`mbstring`, sin entorno de ejecución de Node ni enlace nativo de Oniguruma. Esta página resume el proceso; el contrato normativo completo está en `ARCHITECTURE.md` del repositorio.

## El proceso

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

Todo funciona en el espacio de desplazamientos de **unidades de código UTF-16**, el mismo que el del motor de expresiones regulares JS y el tokenizador de Shiki. Para ASCII, equivale a los desplazamientos en bytes.

## El entorno de ejecución de expresiones regulares

En la base está `Shikiphp\Regex`, un motor completo de expresiones regulares ECMAScript puro-PHP **incluido desde [`inline0/phasis`](https://github.com/inline0/phasis)** (un motor JavaScript puro-PHP). Analiza un patrón de RegExp de JS con sus indicadores y ejecuta búsquedas hacia delante con la semántica de Oniguruma `onig_search`.

## Conversión de Oniguruma → JS

Las gramáticas TextMate están escritas en Oniguruma. `Shikiphp\Oniguruma\PatternConverter`
es una adaptación de PHP de
[`oniguruma-to-es`](https://github.com/slevithan/oniguruma-to-es): convierte un patrón Oniguruma en una cadena de origen y de indicadores compatible con RegExp de JS que acepta el motor de expresiones regulares. Esta es la misma ruta Oniguruma → RegExp que utiliza el motor de expresiones regulares de Shiki JavaScript, lo que permite que shikiphp coincida con el resultado de ese motor desplazamiento por desplazamiento. El conversor admite las construcciones habituales de las gramáticas: clases POSIX, cuantificadores posesivos y grupos atómicos, indicadores en línea, grupos con nombre y referencias inversas, propiedades Unicode, entre otras.

## El analizador y su ruta rápida de PCRE

`Shikiphp\Oniguruma\OnigScanner` refleja el analizador de `vscode-oniguruma`: se crea
una vez por regla a partir de una lista de patrones y busca la coincidencia más a la izquierda a partir de una posición inicial.

El comparador que recorre el árbol es correcto, pero lento, así que el analizador tiene una **ruta rápida de PCRE condicionada a la equivalencia** con dos niveles. Un `PcreTranslator` reescribe
el patrón de origen JS del conversor como un patrón PCRE nativo solo cuando puede demostrar que el resultado:

- **Coincidencia completa.** Cuando la traducción se comporta igual que el comparador,
  incluidas todas las capturas, el patrón se ejecuta directamente en PCRE. Se excluyen de este nivel los grupos con nombre,
  las referencias inversas, `\p{}`, `\b`, `\G`, la emulación de grupos atómicos y las capturas cuya
  semántica de JavaScript difiere de la de PCRE (dentro de un grupo cuantificado o una
  aserción hacia atrás).
- **Solo posición.** Cuando solo podrían diferir las capturas, una segunda traducción
  admite esas construcciones (`\b` y `\B` como aserciones de anticipación y retroceso ASCII exactas, grupos atómicos como `(?>…)` nativo, `\p{…}` si PCRE lo compila). PCRE solo encuentra dónde está la siguiente coincidencia; el comparador vuelve a ejecutarse en ese desplazamiento para
  generar las capturas reales. Las referencias inversas y `\G` también se excluyen aquí.

Todo lo demás se ejecuta únicamente en el comparador. Para las gramáticas incluidas en Shiki 3.23.0, alrededor del 39 % de los patrones distintos de 14,234 se ejecuta completamente en PCRE, cerca del 57 % usa el nivel de posición y alrededor del 4 % permanece en el comparador. Una herramienta de desarrollo del repositorio (`bin/.oracle-tools/pcre-equivalence.php`) verifica esta clasificación comparando la ruta rápida con el comparador —índice, final y, para el primer nivel, cada intervalo de captura— en las entradas de los escenarios y en un conjunto de cadenas sintéticas, sin permitir divergencias.

## Gramática y tema

- **Gramática** (`Shikiphp\Grammar`) es una adaptación fiel de `vscode-textmate`: un
  `Registry` resuelve gramáticas por nombre de ámbito (incluidas referencias a `include`,
  lenguajes incrustados e inyecciones), y un `Tokenizer` mantiene las pilas de reglas y
  ámbitos línea por línea.
- **Tema** (`Shikiphp\Theme`) adapta la correspondencia de temas de `vscode-textmate`:
  los colores de primer plano y los estilos de fuente se resuelven según la especificidad del selector de ámbito y la correspondencia con los ámbitos superiores.

## Renderizado

`Shikiphp\Render\HtmlRenderer` construye el árbol HAST exacto que genera `pre.shiki > code > span.line >
span` Shiki y luego lo serializa. El modo de tema único escribe colores sencillos; el modo de dos temas escribe variables CSS `--shiki-light`/`--shiki-dark`.

## Validado frente a Shiki

Shiki es la referencia y está fijado a `shiki` 3.23.0 con su motor de expresiones regulares JavaScript. El directorio `scenarios/` contiene **214** casos de prueba, cada uno con una entrada, un idioma y un tema; en conjunto, abarcan 167 de las 253 gramáticas incluidas, `lang: 'ansi'` y los 65 temas. `bin/test-regression` resalta cada caso con ambos motores y comprueba que la cadena HTML sea idéntica tras quitar los espacios en blanco iniciales y finales. Las opciones y los transformadores se comprueban con pruebas de paridad frente a resultados capturados de las mismas versiones de Shiki y `@shikijs/transformers` 4.2.0.
La regla innegociable es que el resultado del resaltado no debe empeorar respecto a Shiki.
