
# Arsitektur

Phasis adalah tree walker empat tahap dengan VM bytecode oportunistis. Alur datanya:

```
JS text
  │
  ▼
┌──────────┐
│  Lexer   │  src/Lexer/
└────┬─────┘
     ▼ tokens
┌──────────┐
│  Parser  │  src/Parser/
└────┬─────┘
     ▼ AST nodes
┌──────────────┐
│ Interpreter  │  src/Runtime/
│  + Bytecode  │  src/Bytecode/
│      VM      │
└────┬─────────┘
     ▼ JsValue
PHP host
```

Setiap tahap adalah kelas PHP dengan antarmuka kecil yang digunakan tahap berikutnya. Tidak ada tahap penautan — parsing menghasilkan AST yang langsung ditelusuri interpreter.

## Lexer

`src/Lexer/Lexer.php` mengubah teks sumber menjadi aliran instans `Token`. Setiap token adalah struct readonly: tipe (enum `TokenType`, sekitar 80 kasus), nilai, baris, kolom.

Lexer menangani:

- Literal angka — desimal, heksadesimal (`0x`), oktal (`0o`), biner (`0b`), eksponensial, pemisah numerik.
- Literal string — tanda kutip tunggal dan ganda, semua urutan escape, literal template dengan ekspresi bersarang.
- Literal ekspresi reguler — tokenisasi `/pattern/flags` penuh dengan mengetahui kapan `/` harus dibaca sebagai regex atau pembagian.
- Identifier, kata kunci, tanda baca.
- Komentar (baris dan blok), dipertahankan sebagai trivia.
- Sumber Unicode — byte UTF-8 ditafsirkan sebagai sumber UTF-16 JS.

Output-nya berupa generator yang menghasilkan token secara malas. Parser mengonsumsinya dengan lookahead satu token.

## Parser

`src/Parser/Parser.php` adalah parser Pratt yang ditulis manual. Prioritas ekspresi berada di `src/Parser/Precedence.php`. Pernyataan menggunakan recursive descent tersendiri.

Parser menghasilkan node AST immutable dari `src/Ast/`. Setiap node menyimpan lokasi sumbernya untuk stack trace dan pelaporan error.

Bentuk penting:

- **Ekspresi**: `Literal`, `Identifier`, `BinaryExpression`, `UnaryExpression`, `AssignmentExpression`, `CallExpression`, `MemberExpression`, `ArrowFunction`, `ObjectExpression`, `ArrayExpression`, `ConditionalExpression`, `TemplateLiteral`, `SpreadElement`, `NewExpression`, `ThisExpression`, `SequenceExpression`, `TaggedTemplate`, `ClassExpression`, `YieldExpression`, `AwaitExpression`.
- **Pernyataan**: `BlockStatement`, `IfStatement`, `ForStatement`, `ForInStatement`, `ForOfStatement`, `WhileStatement`, `DoWhileStatement`, `SwitchStatement`, `ReturnStatement`, `ThrowStatement`, `TryStatement`, `BreakStatement`, `ContinueStatement`, `ExpressionStatement`.
- **Deklarasi**: `VariableDeclaration`, `FunctionDeclaration`, `ClassDeclaration`, `ImportDeclaration`.
- **Pola** (destructuring): `ArrayPattern`, `ObjectPattern`, `RestElement`, `AssignmentPattern`.

Penyisipan titik koma otomatis diterapkan di parser (bukan lexer) sesuai spesifikasi.

## Interpreter

`src/Runtime/Interpreter.php` adalah penelusur AST. Ia mengunjungi setiap node dan mengembalikan `JsValue` (atau `Completion` untuk pernyataan yang mengubah alur kontrol).

Kelas pendukung utama:

- **`Environment`** (`src/Runtime/Environment.php`) — rantai cakupan leksikal. Setiap pemanggilan fungsi membuat lingkungan baru yang menunjuk ke induk leksikalnya. `var` diangkat ke cakupan fungsi; `let` / `const` memiliki cakupan blok dengan TDZ.
- **`CallStack`** (`src/Runtime/CallStack.php`) — stack eksplisit dengan batas kedalaman. Pemanggilan fungsi dan metode menambahkan frame; exception mengurainya.
- **`Completion`** (`src/Runtime/Completion.php`) — union bertag dari `normal`, `return`, `throw`, `break`, `continue` beserta nilai dan label opsional.
- **`Reference`** (`src/Runtime/Reference.php`) — tipe Reference spesifikasi untuk evaluasi sisi kiri dalam assignment dan `delete`.

## Nilai

Nilai JavaScript adalah objek PHP di bawah `src/Value/`. Masing-masing menerapkan antarmuka `JsValue` dan memiliki metode konversi tipenya sendiri (`toNumber()`, `toString()`, `toBoolean()`).

| Kelas | Tipe JS |
|---|---|
| `JsUndefined` | `undefined` (singleton) |
| `JsNull` | `null` (singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | IEEE 754 double; mempertahankan NaN, ±Infinity, ±0 |
| `JsString` | semantik string UTF-16 |
| `JsObject` | objek dasar dengan peta properti dan rantai prototipe |
| `JsArray` | memperluas `JsObject` dengan pelacakan panjang |
| `JsFunction` | closure dengan `Environment` yang tertangkap |
| `JsSymbol` | primitif Symbol |
| `JsBigInt` | presisi arbitrer melalui bcmath |
| `JsProxy` | semua 13 trap, dapat dicabut |
| `JsPromise` | executor sinkron + antrean microtask |
| `JsGenerator` | dua mode: frame-snapshot (badan terkompilasi bytecode; lihat [VM Bytecode § Generator frame-snapshot](https://phasis.inline0.dev/id/docs/advanced/bytecode-vm)) dengan fallback Fiber PHP untuk `yield*`, try-with-yield, dan generator asinkron |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | koleksi |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | data biner |

Penanganan angka adalah sumber terbesar bug historis: `NaN !== NaN` harus bernilai true, `-0 === 0` harus bernilai true, dan kasus khusus ToNumber memerlukan ketelitian hingga tingkat bit. Kelas `Spec/TypeConversion` menerapkan algoritme spesifikasi secara verbatim dan telah diuji secara menyeluruh.

## Pustaka bawaan

`src/BuiltIn/` menyediakan semua objek pustaka standar: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console`, serta paket Web Platform / Fetch (`URL`, `Headers`, `Request`, `Response`, `Blob`, `FormData`, Streams lengkap, `EventTarget`, `AbortController`/`AbortSignal`, `DOMException`, `structuredClone`, `performance`, `TextEncoder`/`TextDecoder`, `atob`/`btoa`), `crypto` + `SubtleCrypto`, `WebSocket`, `XMLHttpRequest`, dan event loop (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Setiap bawaan adalah metode statis `install()` pada sebuah kelas. `Engine` memanggil semuanya sesuai urutan dependensi saat konstruksi. Masing-masing mendefinisikan rantai prototipe, memasang metode sebagai instans `JsFunction` yang dibungkus dalam closure PHP, dan mengekspos konstruktor sebagai global.

## Jembatan PHP↔JS

Konversi nilai lintas batas berlangsung di dua tempat:

- **`Phasis\Interop\PhpToJs::convert()`** — nilai PHP menjadi `JsValue`, digunakan untuk argumen `setGlobal()`, `call()`, dan nilai kembalian fungsi host. Callable PHP menjadi `JsFunction` yang isi fungsinya memanggil callable tersebut.
- **`PhpToJs::toPhp()`** — `JsValue` menjadi PHP untuk argumen fungsi host.
- **`Engine::toPhp()`** (privat) — `JsValue` menjadi PHP untuk hasil `eval()` dan `call()`.

Tidak ada jembatan objek: objek PHP yang bukan callable dikonversi menjadi `undefined` ([masalah #18](https://github.com/inline0/phasis/issues/18)). Kelas `JsToPhp` di `src/Bytecode/` tidak terkait dengan interoperabilitas; kelas itu adalah transpiler yang dijelaskan di bawah. Aturan konversi didokumentasikan di [Konversi nilai](https://phasis.inline0.dev/id/docs/interop/values).

## Penanganan error

JS `throw` yang tidak tertangkap menjadi PHP `Phasis\Exceptions\JsThrowable`; error yang dimunculkan engine sendiri muncul sebagai `Phasis\Exceptions\TypeError`, `RangeError`, atau `ReferenceError`, yang semuanya merupakan subclass dari `Phasis\Exceptions\RuntimeError`. Di dalam engine, JS `try` / `catch` menangkap `JsThrowable`, setiap `RuntimeError`, dan `Phasis\Exceptions\SyntaxError`. Exception PHP lainnya, termasuk yang dilempar oleh fungsi host, tidak dikonversi dan langsung diteruskan ke pemanggil PHP ([masalah #20](https://github.com/inline0/phasis/issues/20)).

Ada dua batas: batas maksimum call stack (`CallStack::maxDepth`, 4,096 frame, ditambah penjaga 8,192 frame untuk closure yang ditranspilasi) dan `maxLoopIterations`, yang ditetapkan melalui `Engine::setLimit()` dan hanya diberlakukan oleh tree-walker ([masalah #19](https://github.com/inline0/phasis/issues/19)). Keduanya memunculkan `Phasis\Exceptions\InternalError`. Tidak ada batas panjang string, ukuran output, atau waktu eksekusi.

## VM Bytecode

Fungsi yang sering dipanggil diturunkan dengan dua cara. `JsToPhp` (di `src/Bytecode/`) menghasilkan closure PHP untuk isi fungsi yang berat pada numerik / variabel lokal dan dapat dikompilasi menjadi native oleh tracing JIT milik PHP; pipeline ini menurunkan pembacaan `new Ctor(args)`, `obj.method(args)`, dan `this.member`, sehingga pola kanonis metode kelas di dalam loop dapat sepenuhnya melewati VM. Sisanya dikompilasi menjadi bytecode datar yang dijalankan oleh `VM::execute`, dengan call stack khusus yang menjaga setiap pemanggilan JS di dalam satu frame PHP serta cache inline per-PC untuk pembacaan metode prototipe. Isi generator sederhana juga dikompilasi menjadi bytecode, dengan `Op::YIELD` yang menangkap snapshot frame di heap alih-alih melintasi Fiber PHP.

Bentuk apa pun yang tidak diturunkan oleh kompiler (e.g. `with`, `try` dengan catch yang mengikat ulang, `yield*`, generator asinkron) kembali menggunakan tree-walker. Lihat [VM Bytecode](https://phasis.inline0.dev/id/docs/advanced/bytecode-vm) untuk mekanisme selengkapnya.
