
# Architektur

Phasis ist ein vierstufiger Tree-Walker mit einer opportunistischen Bytecode-VM. Der Datenfluss sieht so aus:

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

Jede Stufe ist eine PHP-Klasse mit einer kleinen Schnittstelle, die von der nächsten Stufe verwendet wird. Einen Link-Schritt gibt es nicht: Das Parsing erzeugt einen AST, den der Interpreter direkt durchläuft.

## Lexer

`src/Lexer/Lexer.php` wandelt Quelltext in einen Strom von `Token`-Instanzen um. Jedes Token ist ein schreibgeschützter Struct mit Typ (`TokenType`-Enum mit rund 80 Fällen), Wert, Zeile und Spalte.

Der Lexer verarbeitet:

- Zahlenliterale – dezimal, hexadezimal (`0x`), oktal (`0o`), binär (`0b`), exponentiell und mit numerischen Trennzeichen.
- String-Literale – einfache und doppelte Anführungszeichen, alle Escape-Sequenzen sowie Template-Literale mit verschachtelten Ausdrücken.
- Reguläre Ausdrucksliterale – vollständige `/pattern/flags`-Tokenisierung. Dabei wird berücksichtigt, ob ein `/` als regulärer Ausdruck oder als Division zu lesen ist.
- Bezeichner, Schlüsselwörter und Satzzeichen.
- Zeilen- und Blockkommentare, die als Trivia erhalten bleiben.
- Unicode-Quelltext – UTF-8-Bytes, die als JS-Quelltext in UTF-16 interpretiert werden.

Die Ausgabe ist ein Generator, der Tokens bei Bedarf liefert. Der Parser liest sie mit einem Token Vorausblick.

## Parser

`src/Parser/Parser.php` ist ein handgeschriebener Pratt-Parser. Die Operatorrangfolge von Ausdrücken ist in `src/Parser/Precedence.php` festgelegt. Anweisungen werden mit rekursivem Abstieg geparst.

Er erzeugt unveränderliche AST-Knoten aus `src/Ast/`. Jeder Knoten speichert seine Quellposition für Stack-Traces und Fehlermeldungen.

Beispiele für wichtige Strukturen:

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

Die automatische Semikolon-Einfügung wird gemäß Spezifikation im Parser (nicht im Lexer) umgesetzt.

## Interpreter

`src/Runtime/Interpreter.php` ist der AST-Walker. Er besucht jeden Knoten und gibt einen `JsValue` zurück (oder ein `Completion` bei Anweisungen, die den Kontrollfluss ändern).

Wichtige unterstützende Klassen:

- **`Environment`** (`src/Runtime/Environment.php`) — lexikalische Gültigkeitsbereichskette. Jeder Funktionsaufruf erzeugt eine neue Umgebung, die auf ihren lexikalischen Elternbereich verweist. `var` wird in den Funktionsbereich gehoben; `let` / `const` sind blockbezogen und unterliegen einer TDZ.
- **`CallStack`** (`src/Runtime/CallStack.php`) — expliziter Stack mit Tiefenlimit. Funktions- und Methodenaufrufe legen Frames ab; Ausnahmen bauen sie wieder ab.
- **`Completion`** (`src/Runtime/Completion.php`) — diskriminierte Union aus `normal`, `return`, `throw`, `break` und `continue`, ergänzt um einen optionalen Wert und ein Label.
- **`Reference`** (`src/Runtime/Reference.php`) — Referenztyp gemäß Spezifikation für die Auswertung der linken Seite bei Zuweisungen und `delete`.

## Werte

JavaScript-Werte sind PHP-Objekte unter `src/Value/`. Jedes implementiert die Schnittstelle `JsValue` und verfügt über eigene Typkonvertierungsmethoden (`toNumber()`, `toString()`, `toBoolean()`).

| Klasse | JS-Typ |
|---|---|
| `JsUndefined` | `undefined` (Singleton) |
| `JsNull` | `null` (Singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | IEEE-754-Double; erhält NaN, ±Infinity, ±0 |
| `JsString` | UTF-16-Zeichenfolgensemantik |
| `JsObject` | Basisobjekt mit Eigenschaftszuordnung und Prototypenkette |
| `JsArray` | erweitert `JsObject` um Längenverwaltung |
| `JsFunction` | Closure mit erfasstem `Environment` |
| `JsSymbol` | Symbol-Primitive |
| `JsBigInt` | beliebige Genauigkeit über bcmath |
| `JsProxy` | alle 13 Traps, widerrufbar |
| `JsPromise` | synchroner Executor + Microtask-Warteschlange |
| `JsGenerator` | Dual-Modus: Frame-Snapshot (Bytecode-kompilierte Funktionskörper; siehe [Bytecode-VM § Frame-Snapshot-Generatoren](https://phasis.inline0.dev/de/docs/advanced/bytecode-vm)) mit PHP-Fiber-Fallback für `yield*`, try-with-yield und asynchrone Generatoren |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | Sammlungen |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | Binärdaten |

Die Zahlenverarbeitung ist die häufigste Ursache für historische Fehler: `NaN !== NaN` muss wahr sein, `-0 === 0` muss wahr sein, und ToNumber-Grenzfälle erfordern genaue Sorgfalt auf Bitebene. Die Klasse `Spec/TypeConversion` implementiert die Spezifikationsalgorithmen wortgetreu und wurde umfassend getestet.

## Integrierte Bibliothek

`src/BuiltIn/` stellt alle Standardbibliotheksobjekte bereit: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console` sowie die Web-Platform- und Fetch-Pakete (`URL`, `Headers`, `Request`, `Response`, `Blob`, `FormData`, vollständige Streams, `EventTarget`, `AbortController`/`AbortSignal`, `DOMException`, `structuredClone`, `performance`, `TextEncoder`/`TextDecoder`, `atob`/`btoa`), `crypto` + `SubtleCrypto`, `WebSocket`, `XMLHttpRequest` und die Ereignisschleife (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Jedes integrierte Objekt ist eine statische Methode `install()` einer Klasse. Der `Engine` ruft sie beim Erstellen in Abhängigkeitsreihenfolge auf. Jedes definiert seine Prototypenkette, installiert Methoden als `JsFunction`-Instanzen, die in PHP-Closures verpackt sind, und stellt den Konstruktor global bereit.

## PHP↔JS-Brücke

Die Wertkonvertierung über die Grenze hinweg findet an zwei Stellen statt:

- **`Phasis\Interop\PhpToJs::convert()`** — Wert von PHP zu `JsValue`, verwendet für `setGlobal()`, `call()`-Argumente und Rückgabewerte von Host-Funktionen. Ein aufrufbares PHP wird zu einem `JsFunction`, dessen Rumpf es aufruft.
- **`PhpToJs::toPhp()`** — von `JsValue` zu PHP für die Argumente einer Host-Funktion.
- **`Engine::toPhp()`** (privat) — von `JsValue` zu PHP für die Ergebnisse von `eval()` und `call()`.

Es gibt keine Objektbrücke: PHP-Objekte, die nicht aufrufbar sind, werden zu `undefined` ([Issue #18](https://github.com/inline0/phasis/issues/18)). Die Klasse `JsToPhp` in `src/Bytecode/` hat nichts mit Interoperabilität zu tun; sie ist der weiter unten beschriebene Transpiler. Die Konvertierungsregeln sind unter [Wertkonvertierung](https://phasis.inline0.dev/de/docs/interop/values) dokumentiert.

## Fehlerbehandlung

Ein nicht abgefangener JS-`throw` wird zu einer PHP `Phasis\Exceptions\JsThrowable`; vom Engine selbst ausgelöste Fehler werden als `Phasis\Exceptions\TypeError`, `RangeError` oder `ReferenceError` ausgegeben, allesamt Unterklassen von `Phasis\Exceptions\RuntimeError`. Innerhalb der Engine fängt ein JS-`try` / `catch` `JsThrowable`, jedes `RuntimeError` und `Phasis\Exceptions\SyntaxError` ab. Andere PHP-Ausnahmen, auch solche aus Host-Funktionen, werden nicht konvertiert und direkt an den PHP-Aufrufer weitergegeben ([Issue #20](https://github.com/inline0/phasis/issues/20)).

Es gibt zwei Limits: die Obergrenze des Aufrufstacks (`CallStack::maxDepth`, 4,096 Frames plus eine 8,192-Frame-Schranke für transpilierte Closures) und `maxLoopIterations`, festgelegt über `Engine::setLimit()`, die nur der Tree-Walker durchsetzt ([Issue #19](https://github.com/inline0/phasis/issues/19)). Beide lösen `Phasis\Exceptions\InternalError` aus. Es gibt keine Limits für Zeichenfolgenlänge, Ausgabegröße oder Ausführungszeit.

## Bytecode-VM

Häufig aufgerufene Funktionen werden auf zwei Arten umgewandelt. `JsToPhp` (in `src/Bytecode/`) erzeugt eine PHP-Closure für Funktionskörper mit vielen Zahlen oder lokalen Variablen, die der Tracing-JIT von PHP nativ kompilieren kann. Die Pipeline wandelt `new Ctor(args)`-, `obj.method(args)`- und `this.member`-Zugriffe um, sodass das typische Muster aus Klassenmethode in Schleife die VM vollständig umgehen kann. Alles andere wird zu flachem Bytecode kompiliert, den `VM::execute` verarbeitet. Dabei hält ein benutzerdefinierter Aufrufstack jeden JS-Aufruf in einem einzigen PHP-Frame, und ein Inline-Cache pro PC beschleunigt Zugriffe auf Prototypmethoden. Einfache Generator-Funktionskörper werden ebenfalls zu Bytecode kompiliert; dabei erfasst `Op::YIELD` einen Frame-Snapshot im Heap, statt eine PHP-Fiber-Grenze zu überqueren.

Alle Formen, die der Compiler nicht umwandelt (e.g, `with`, `try` mit neu gebundenem Catch, `yield*`, asynchrone Generatoren), fallen auf den Tree-Walker zurück. Den vollständigen Ablauf findest du unter [Bytecode-VM](https://phasis.inline0.dev/de/docs/advanced/bytecode-vm).
