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

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, яким користується рушій регулярних виразів JavaScript у Shiki, що дає змогу shikiphp зіставляти його вихід із точністю до зміщення. Конвертер обробляє конструкції, які використовуються в реальних граматиках: класи POSIX, квантифікатори з присвоюванням і атомарні групи, вбудовані прапорці, іменовані групи та зворотні посилання, властивості Unicode та інше.

## Сканер і швидкий шлях PCRE

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

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

- **Повний збіг.** Якщо перетворення поводиться так само, як засіб зіставлення, включно з усіма захопленнями, шаблон виконується безпосередньо в PCRE. Іменовані групи, зворотні посилання, `\p{}`, `\b`, `\G`, емуляція атомарних груп і захоплення, семантика JavaScript яких відрізняється від PCRE (усередині групи з квантифікатором або перевірки назад), не допускаються на цьому рівні.
- **Лише позиція.** Якщо можуть відрізнятися лише захоплення, друге перетворення допускає ці конструкції (`\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
`pre.shiki > code > span.line >
span`, як і Shiki, а потім серіалізує його. Режим однієї теми записує звичайні
кольори; режим двох тем записує змінні CSS `--shiki-light` / `--shiki-dark`.

## Перевірено на відповідність 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.
