
# Arquitetura

shikiphp transforma código-fonte em HTML destacados com tema usando gramáticas TextMate e temas VS Code, sem extensões PHP além de `json`/`mbstring`, sem runtime Node e sem vinculação nativa de Oniguruma. Esta página resume o pipeline; o contrato normativo completo está em `ARCHITECTURE.md` no repositório.

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

Tudo opera no espaço de deslocamentos de **unidades de código UTF-16** — o mesmo do mecanismo de regex JS e do tokenizador de Shiki. Para ASCII, ele equivale aos deslocamentos em bytes.

## O runtime de regex

Na base está `Shikiphp\Regex`, um mecanismo completo de regex ECMAScript puro PHP, **distribuído a partir de [`inline0/phasis`](https://github.com/inline0/phasis)** (um mecanismo PHP JavaScript puro). Ele analisa uma origem de RegExp JS mais flags e executa buscas para a frente com a semântica de Oniguruma `onig_search`.

## Conversão de Oniguruma → JS

As gramáticas TextMate são escritas em Oniguruma. `Shikiphp\Oniguruma\PatternConverter` é uma adaptação PHP de [`oniguruma-to-es`](https://github.com/slevithan/oniguruma-to-es): converte um padrão Oniguruma em uma origem compatível com RegExp JS e em uma string de flags aceita pelo mecanismo de regex. Este é o mesmo caminho Oniguruma → RegExp usado pelo mecanismo de regex de Shiki JavaScript, o que permite a shikiphp corresponder à saída desse mecanismo deslocamento por deslocamento. O conversor lida com as construções usadas por gramáticas reais: classes POSIX, quantificadores possessivos e grupos atômicos, flags em linha, grupos nomeados e referências retroativas, propriedades Unicode e muito mais.

## O scanner e seu caminho rápido PCRE

`Shikiphp\Oniguruma\OnigScanner` espelha o scanner de `vscode-oniguruma`: é criado uma vez por regra a partir de uma lista de padrões e encontra a correspondência mais à esquerda na posição inicial ou depois dela.

O matcher que percorre a árvore está correto, mas é lento; por isso, o scanner tem um **caminho rápido PCRE condicionado à equivalência**, com duas camadas. Um `PcreTranslator` reescreve a origem JS do conversor como um padrão PCRE nativo somente quando pode provar o resultado:

- **Correspondência completa.** Quando a tradução se comporta de modo idêntico ao matcher, incluindo todas as capturas, o padrão é executado diretamente no PCRE. Grupos nomeados, referências retroativas, `\p{}`, `\b`, `\G`, emulação de grupos atômicos e capturas cujas semânticas de JavaScript diferem do PCRE (dentro de um grupo quantificado ou de um lookbehind) são rejeitados nesta camada.
- **Somente posição.** Quando apenas as capturas podem diferir, uma segunda tradução permite essas construções (`\b` e `\B` como lookarounds ASCII exatos, grupos atômicos como `(?>…)` nativo, `\p{…}` se o PCRE compilar). O PCRE então encontra somente a posição da próxima correspondência, e o matcher executa novamente nesse deslocamento para produzir as capturas reais. Referências retroativas e `\G` também são rejeitadas aqui.

Todo o restante é executado somente pelo matcher. Para as gramáticas incluídas no pacote em Shiki 3.23.0, cerca de 39% dos 14,234 padrões distintos são executados integralmente no PCRE, cerca de 57% usam a camada de posição e cerca de 4% ficam no matcher. Uma ferramenta de desenvolvimento no repositório (`bin/.oracle-tools/pcre-equivalence.php`) verifica a classificação comparando o caminho rápido com o matcher — índice, fim e, na primeira camada, cada intervalo de captura — nos cenários de entrada e em um conjunto de strings sintéticas, sem permitir divergências.

## Gramática e tema

- **Gramática** (`Shikiphp\Grammar`) é uma adaptação fiel de `vscode-textmate`: um `Registry` resolve as gramáticas pelo nome do escopo (incluindo referências `include`, idiomas incorporados e injeções), e um `Tokenizer` mantém as pilhas de regras e de escopos linha por linha.
- **Tema** (`Shikiphp\Theme`) adapta a correspondência de temas de `vscode-textmate`: as cores de primeiro plano e os estilos de fonte são resolvidos pela especificidade do seletor de escopo, com correspondência de escopos pai.

## Renderização

`Shikiphp\Render\HtmlRenderer` cria exatamente a árvore HAST `pre.shiki > code > span.line >
span` que Shiki cria e depois a serializa. No modo de tema único, grava cores simples; no modo de tema duplo, grava variáveis `--shiki-light` / `--shiki-dark` CSS.

## Validado em relação a Shiki

O próprio Shiki é a referência, fixada na versão `shiki` 3.23.0 com seu mecanismo de regex JavaScript. O diretório `scenarios/` contém **214** fixtures, cada uma com uma entrada em um idioma e tema; juntas, elas abrangem 167 das 253 gramáticas incluídas no pacote, `lang: 'ansi'` e os 65 temas. `bin/test-regression` destaca cada uma com ambos os mecanismos e verifica que a HTML é idêntica após remover espaços em branco no início e no fim. As opções e os transformadores são verificados por testes de paridade com saídas capturadas do mesmo Shiki e `@shikijs/transformers` 4.2.0. A regra inegociável é que a saída do destaque não pode regredir em relação a Shiki.
