
# Arsitektur

shikiphp mengubah kode sumber menjadi HTML yang disorot dengan tema menggunakan grammar TextMate dan tema VS Code, tanpa ekstensi PHP di luar `json`/`mbstring`, tanpa runtime Node, dan tanpa binding native Oniguruma. Halaman ini merangkum pipeline; kontrak normatif lengkapnya ada di `ARCHITECTURE.md` dalam repositori.

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

Semua beroperasi dalam ruang offset **unit kode UTF-16** — ruang yang sama dengan mesin regex JS dan tokenizer milik Shiki. Untuk ASCII, nilainya sama dengan offset byte.

## Runtime regex

Pada lapisan terbawah terdapat `Shikiphp\Regex`, mesin regex ECMAScript murni PHP lengkap yang **disertakan dari vendor [`inline0/phasis`](https://github.com/inline0/phasis)** (mesin PHP JavaScript murni). Mesin ini mengurai sumber RegExp JS beserta flag, lalu menjalankan pencarian maju dengan semantik Oniguruma `onig_search`.

## Konversi Oniguruma → JS

Grammar TextMate ditulis dalam Oniguruma. `Shikiphp\Oniguruma\PatternConverter` adalah port PHP dari [`oniguruma-to-es`](https://github.com/slevithan/oniguruma-to-es): ini mengubah pola Oniguruma menjadi string sumber dan flag yang kompatibel dengan RegExp JS dan diterima mesin regex. Ini adalah jalur Oniguruma → RegExp yang sama yang digunakan mesin regex JavaScript milik Shiki, sehingga shikiphp dapat mencocokkan output mesin tersebut dengan tepat pada setiap offset. Converter menangani konstruksi nyata yang digunakan grammar: kelas POSIX, kuantifier posesif dan grup atomik, flag inline, grup bernama dan backreference, properti Unicode, dan lainnya.

## Pemindai dan jalur cepat PCRE

`Shikiphp\Oniguruma\OnigScanner` meniru pemindai milik `vscode-oniguruma`: pemindai ini dibuat sekali untuk setiap aturan dari daftar pola dan menemukan kecocokan paling kiri pada atau setelah posisi awal.

Pencocok yang menelusuri pohon sudah benar, tetapi tidak cepat. Karena itu, pemindai memiliki **jalur cepat PCRE yang hanya digunakan jika ekuivalen** dengan dua tingkat. `PcreTranslator` mengubah sumber JS dari converter menjadi pola PCRE native hanya jika hasilnya dapat dibuktikan:

- **Cocok penuh.** Jika terjemahan berperilaku identik dengan pencocok, termasuk setiap tangkapan, pola dijalankan langsung oleh PCRE. Grup bernama, referensi balik, `\p{}`, `\b`, `\G`, emulasi atomik, dan tangkapan dengan semantik JavaScript yang berbeda dari PCRE (di dalam grup terkuantifikasi atau lookbehind) ditolak dari tingkat ini.
- **Posisi saja.** Jika hanya tangkapan yang mungkin berbeda, terjemahan kedua menerima konstruksi tersebut (`\b` dan `\B` sebagai lookaround ASCII yang persis, grup atomik sebagai `(?>…)` asli, `\p{…}` jika PCRE dapat mengompilasinya). PCRE kemudian hanya mencari posisi kecocokan berikutnya, lalu pencocok dijalankan ulang pada offset tersebut untuk menghasilkan tangkapan yang sebenarnya. Referensi balik dan `\G` tetap ditolak di sini.

Semua yang lain hanya dijalankan oleh pencocok. Untuk grammar bawaan pada Shiki 3.23.0, sekitar 39% dari 14,234 pola berbeda dijalankan sepenuhnya dengan PCRE, sekitar 57% menggunakan tingkat posisi, dan sekitar 4% tetap menggunakan pencocok. Klasifikasi ini diperiksa oleh alat pengembangan di repositori (`bin/.oracle-tools/pcre-equivalence.php`) yang membandingkan jalur cepat dengan pencocok — indeks, akhir, serta setiap rentang hasil tangkapan untuk tingkat pertama — pada input skenario dan serangkaian string sintetis, tanpa toleransi terhadap perbedaan.

## Grammar dan tema

- **Grammar** (`Shikiphp\Grammar`) adalah port setia dari `vscode-textmate`: `Registry` menyelesaikan grammar berdasarkan nama scope (termasuk referensi `include`, bahasa tertanam, dan injeksi), sedangkan `Tokenizer` mengelola tumpukan aturan dan scope baris demi baris.
- **Tema** (`Shikiphp\Theme`) mem-port pencocokan tema `vscode-textmate`: latar depan dan gaya font ditentukan berdasarkan kekhususan pemilih scope dengan pencocokan scope induk.

## Render

`Shikiphp\Render\HtmlRenderer` membangun pohon HAST `pre.shiki > code > span.line >
span` yang persis seperti yang dibangun Shiki, lalu menserialisasikannya. Mode tema tunggal menulis warna polos; mode tema ganda menulis variabel `--shiki-light` / `--shiki-dark` CSS.

## Divalidasi terhadap Shiki

Shiki sendiri adalah oracle, dipatok ke `shiki` 3.23.0 dengan mesin regex JavaScript-nya. Direktori `scenarios/` berisi **214** fixture, masing-masing berupa satu input dalam satu bahasa dan satu tema; secara keseluruhan, fixture ini mencakup 167 dari 253 grammar bawaan, `lang: 'ansi'`, dan seluruh 65 tema. `bin/test-regression` menyorot masing-masing dengan kedua mesin dan memastikan HTML identik setelah spasi awal dan akhir dihapus. Opsi dan transformer diperiksa melalui pengujian kesetaraan terhadap output yang diambil dari Shiki dan `@shikijs/transformers` 4.2.0 yang sama. Aturan yang tidak dapat ditawar adalah output penyorotan tidak boleh mengalami regresi terhadap Shiki.
