
# Архитектура

shikiphp преобразует исходный код в подсвеченный HTML с помощью грамматик TextMate и тем VS Code, без расширений PHP за исключением `json`/`mbstring`, без среды выполнения Node и без нативной привязки Oniguruma. На этой странице кратко описан конвейер; полный нормативный контракт находится в `ARCHITECTURE.md` репозитория.

## Конвейер

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

Все операции используют смещения в единицах кодов **UTF-16** — в том же пространстве, что и регулярный движок JS и токенизатор Shiki. Для ASCII это совпадает со смещениями в байтах.

## Среда выполнения регулярных выражений

В основе лежит `Shikiphp\Regex` — полноценный движок регулярных выражений ECMAScript, полностью реализованный на PHP и **поставляемый вендором из [`inline0/phasis`](https://github.com/inline0/phasis)** (движок PHP JavaScript). Он разбирает исходный код RegExp JS с флагами и выполняет поиск вперед с семантикой Oniguruma `onig_search`.

## Преобразование Oniguruma → JS

Грамматики TextMate написаны на Oniguruma. `Shikiphp\Oniguruma\PatternConverter` — это перенос PHP [`oniguruma-to-es`](https://github.com/slevithan/oniguruma-to-es): он преобразует шаблон Oniguruma в исходный код и строку флагов, совместимые с RegExp JS, которые принимает движок регулярных выражений. Это тот же путь Oniguruma → RegExp, который использует движок регулярных выражений Shiki JavaScript, что позволяет shikiphp сопоставлять его вывод со смещениями с точностью до позиции. Преобразователь обрабатывает конструкции, используемые в реальных грамматиках: классы POSIX, притязательные квантификаторы и атомарные группы, встроенные флаги, именованные группы и обратные ссылки, свойства Unicode и многое другое.

## Сканер и ускоренный путь PCRE

`Shikiphp\Oniguruma\OnigScanner` повторяет работу сканера `vscode-oniguruma`: он создается один раз для каждого правила из списка шаблонов и находит самое левое совпадение на заданной начальной позиции или после нее.

Обход дерева для сопоставления работает правильно, но медленно, поэтому в сканере есть **ускоренный путь PCRE с проверкой эквивалентности** из двух уровней. `PcreTranslator` преобразует исходный текст шаблона JS в шаблон PCRE для нативного движка, только если может доказать корректность результата:

- **Полное совпадение.** Если преобразование ведет себя так же, как средство сопоставления, включая все захваты, шаблон напрямую выполняется в PCRE. Именованные группы, обратные ссылки, `\p{}`, `\b`, `\G`, эмуляция атомарных групп и захваты с отличающейся от PCRE семантикой JavaScript (внутри квантифицированной группы или ретроспективного просмотра) на этот уровень не допускаются.
- **Только позиция.** Если могут различаться только захваты, второе преобразование разрешает такие конструкции (`\b` и `\B` как точные ASCII-условия просмотра, атомарные группы как нативные `(?>…)`, `\p{…}` при успешной компиляции в PCRE). Тогда PCRE лишь определяет позицию следующего совпадения, а средство сопоставления повторно запускается с этого смещения, чтобы получить реальные захваты. Обратные ссылки и `\G` здесь также не допускаются.

Все остальное обрабатывается только средством сопоставления. Для встроенных грамматик в Shiki 3.23.0 около 39% из 14,234 различных шаблонов полностью выполняются в PCRE, около 57% используют уровень определения позиции, а около 4% остаются за средством сопоставления. Классификация проверяется инструментом разработки в репозитории (`bin/.oracle-tools/pcre-equivalence.php`), который сравнивает ускоренный путь со средством сопоставления — индекс, конец и для первого уровня каждый диапазон захвата — на входных данных сценариев и наборе искусственных строк; расхождений не допускается.

## Грамматика и тема

- **Грамматика** (`Shikiphp\Grammar`) — точный перенос `vscode-textmate`: `Registry` определяет грамматики по имени области видимости (включая ссылки `include`, встроенные языки и внедрения), а `Tokenizer` поддерживает стек правил и областей видимости построчно.
- **Тема** (`Shikiphp\Theme`) переносит сопоставление тем из `vscode-textmate`: цвет переднего плана и стиль шрифта определяются специфичностью селектора области видимости с учетом родительских областей.

## Отрисовка

`Shikiphp\Render\HtmlRenderer` создает в точности то же дерево HAST, что и Shiki `pre.shiki > code > span.line >
span`, а затем сериализует его. В режиме одной темы записываются обычные цвета; в режиме двух тем — переменные `--shiki-light` / `--shiki-dark` CSS.

## Проверено на Shiki

Сам Shiki служит эталоном; он закреплен на версии `shiki` 3.23.0 со своим движком регулярных выражений JavaScript. В каталоге `scenarios/` находится **214** тестовых примеров, каждый из которых содержит один входной файл для одного языка и одной темы; вместе они охватывают 167 из 253 встроенных грамматик, `lang: 'ansi'` и все 65 тем. `bin/test-regression` выполняет подсветку каждого примера обоими движками и проверяет, что HTML совпадает после удаления пробелов в начале и конце. Параметры и преобразователи проверяются тестами на соответствие выводу, полученному из того же Shiki и `@shikijs/transformers` 4.2.0. Обязательное условие: результат подсветки не должен регрессировать относительно Shiki.
