
# Architectuur

Phasis is een tree walker in vier fasen met een opportunistische bytecode-VM. Het gegevenspad is:

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

Elke fase is een klasse PHP met een beperkt oppervlak dat door de volgende fase wordt gebruikt. Er is geen linkstap: het parsen levert een AST op die de interpreter rechtstreeks doorloopt.

## Lexer

`src/Lexer/Lexer.php` zet brontekst om in een stroom instanties van `Token`. Elk token is een readonly-struct: type (enum `TokenType`, ongeveer 80 gevallen), waarde, regel en kolom.

De lexer verwerkt:

- Numerieke literals — decimaal, hexadecimaal (`0x`), octaal (`0o`), binair (`0b`), exponentieel en met numerieke scheidingstekens.
- Stringliterals — enkele en dubbele aanhalingstekens, alle escape-sequenties, template literals met geneste expressies.
- Reguliere-expressieliterals — volledige tokenisatie van `/pattern/flags`, waarbij rekening wordt gehouden met de vraag of een `/` een regex of deling moet zijn.
- Identifiers, keywords en interpunctie.
- Opmerkingen (regel- en blokopmerkingen), bewaard als trivia.
- Unicode-bron — UTF-8-bytes geïnterpreteerd als de JS UTF-16-bron.

De uitvoer is een generator die tokens lui oplevert. De parser verwerkt ze met één token vooruitkijken.

## Parser

`src/Parser/Parser.php` is een handgeschreven Pratt-parser. De prioriteit van expressies staat in `src/Parser/Precedence.php`. Statements gebruiken hun eigen recursieve descent.

Deze produceert onveranderlijke AST-nodes vanuit `src/Ast/`. Elke node bevat de bronlocatie voor stacktraces en foutmeldingen.

Opvallende structuren:

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

Automatische puntkommainvoeging is volgens de specificatie in de parser geïmplementeerd (niet in de lexer).

## Interpreter

`src/Runtime/Interpreter.php` doorloopt de AST. Deze bezoekt elke node en retourneert een `JsValue` (of een `Completion` voor statements die de control flow wijzigen).

Belangrijke ondersteunende klassen:

- **`Environment`** (`src/Runtime/Environment.php`) — lexicale scopeketen. Elke functieaanroep maakt een nieuwe omgeving aan die naar de lexicale ouder verwijst. `var` wordt naar de functiescope verplaatst; `let` / `const` hebben blokscope met een TDZ.
- **`CallStack`** (`src/Runtime/CallStack.php`) — expliciete stack met dieptelimiet. Functie- en methodeaanroepen voegen frames toe; uitzonderingen wikkelen deze af.
- **`Completion`** (`src/Runtime/Completion.php`) — tagged union van `normal`, `return`, `throw`, `break`, `continue`, plus een optionele waarde en label.
- **`Reference`** (`src/Runtime/Reference.php`) — het Reference-type uit de specificatie voor de evaluatie van de linkerzijde in toewijzingen en `delete`.

## Waarden

JavaScript-waarden zijn PHP-objecten onder `src/Value/`. Elk object implementeert de interface `JsValue` en heeft eigen methoden voor typeconversie (`toNumber()`, `toString()`, `toBoolean()`).

| Klasse | Type JS |
|---|---|
| `JsUndefined` | `undefined` (singleton) |
| `JsNull` | `null` (singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | IEEE 754 double; behoudt NaN, ±Infinity, ±0 |
| `JsString` | UTF-16-stringsemantiek |
| `JsObject` | basisobject met propertymap en prototypeketen |
| `JsArray` | breidt `JsObject` uit met lengtetracking |
| `JsFunction` | closure met vastgelegde `Environment` |
| `JsSymbol` | Symbol-primitief |
| `JsBigInt` | willekeurige precisie via bcmath |
| `JsProxy` | alle 13 traps, herroepbaar |
| `JsPromise` | synchrone uitvoerder + microtaskwachtrij |
| `JsGenerator` | twee modi: frame-snapshot (bytecode-gecompileerde bodies; zie [Bytecode-VM § generators met frame-snapshots](https://phasis.inline0.dev/nl/docs/advanced/bytecode-vm)) met PHP Fiber als terugvaloptie voor `yield*`, try-with-yield en async generators |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | collecties |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | binaire gegevens |

Getalverwerking is historisch gezien de grootste bron van bugs: `NaN !== NaN` moet waar zijn, `-0 === 0` moet waar zijn; randgevallen van ToNumber vereisen exacte zorg op bitniveau. De klasse `Spec/TypeConversion` implementeert de specificatiealgoritmen letterlijk en is uitgebreid getest.

## Ingebouwde bibliotheek

`src/BuiltIn/` biedt elk object uit de standaardbibliotheek: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console`, plus de Web Platform-/Fetch-pakketten (`URL`, `Headers`, `Request`, `Response`, `Blob`, `FormData`, volledige Streams, `EventTarget`, `AbortController`/`AbortSignal`, `DOMException`, `structuredClone`, `performance`, `TextEncoder`/`TextDecoder`, `atob`/`btoa`), `crypto` + `SubtleCrypto`, `WebSocket`, `XMLHttpRequest` en de eventloop (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Elke ingebouwde functie is een statische methode `install()` van een klasse. `Engine` roept ze bij de constructie aan in afhankelijkheidsvolgorde. Elke functie definieert de prototypeketen, installeert methoden als `JsFunction`-instanties die zijn verpakt rond PHP-closures, en stelt de constructor beschikbaar als globale variabele.

## Bridge tussen PHP en JS

Waardeconversie over de grens vindt op twee plekken plaats:

- **`Phasis\Interop\PhpToJs::convert()`** — waarde van PHP naar `JsValue`, gebruikt voor `setGlobal()`, `call()`-argumenten en retourwaarden van hostfuncties. Een aanroepbare PHP wordt een `JsFunction` waarvan de body deze aanroept.
- **`PhpToJs::toPhp()`** — van `JsValue` naar PHP voor de argumenten van een hostfunctie.
- **`Engine::toPhp()`** (privé) — van `JsValue` naar PHP voor de resultaten van `eval()` en `call()`.

Er is geen objectbridge: PHP-objecten die niet aanroepbaar zijn, worden omgezet naar `undefined` ([probleem #18](https://github.com/inline0/phasis/issues/18)). De klasse `JsToPhp` in `src/Bytecode/` houdt geen verband met interoperabiliteit; het is de transpiler die hieronder wordt beschreven. Conversieregels staan beschreven in [Waardeconversie](https://phasis.inline0.dev/nl/docs/interop/values).

## Foutafhandeling

Een niet-afgevangen JS-`throw` wordt een PHP-`Phasis\Exceptions\JsThrowable`; fouten die de engine zelf veroorzaakt, worden weergegeven als `Phasis\Exceptions\TypeError`, `RangeError` of `ReferenceError`, allemaal subklassen van `Phasis\Exceptions\RuntimeError`. Binnen de engine vangt een JS-`try` / `catch` `JsThrowable`, elk `RuntimeError` en `Phasis\Exceptions\SyntaxError` op. Andere PHP-uitzonderingen, ook die van hostfuncties, worden niet omgezet en gaan rechtstreeks door naar de PHP-aanroeper ([probleem #20](https://github.com/inline0/phasis/issues/20)).

Er zijn twee limieten: de limiet voor de aanroepstack (`CallStack::maxDepth`, 4,096 frames, plus een bewaking van 8,192 frames voor getranspilede closures) en `maxLoopIterations`, ingesteld via `Engine::setLimit()`, die alleen de tree-walker afdwingt ([probleem #19](https://github.com/inline0/phasis/issues/19)). Beide veroorzaken `Phasis\Exceptions\InternalError`. Er zijn geen limieten voor tekenreekslengte, uitvoergrootte of uitvoeringstijd.

## Bytecode-VM

Veelgebruikte functies worden op twee manieren omgezet. `JsToPhp` (in `src/Bytecode/`) genereert een PHP-closure voor bodies met veel numerieke bewerkingen of lokale variabelen, die de tracing-JIT van PHP naar native code kan compileren; de pipeline zet `new Ctor(args)`-, `obj.method(args)`- en `this.member`-lezingen om, genoeg om het canonieke patroon van een klassmethode in een lus de VM volledig te laten omzeilen. Al het overige wordt gecompileerd naar vlakke bytecode die wordt verwerkt door `VM::execute`, met een aangepaste aanroepstack die elke JS-aanroep binnen één PHP-frame houdt en een inlinecache per PC voor het lezen van prototypemethoden. Eenvoudige generatorbodies worden ook naar bytecode gecompileerd; `Op::YIELD` legt daarbij een momentopname van een heapframe vast in plaats van over te gaan naar PHP Fiber.

Vormen die de compiler niet omzet (e.g. `with`, `try` met opnieuw gebonden catch, `yield*`, async-generators) vallen terug op de tree-walker. Zie [Bytecode-VM](https://phasis.inline0.dev/nl/docs/advanced/bytecode-vm) voor de volledige werking.
