
# Arkitektur

Phasis er en trævandrer med fire trin og en opportunistisk bytecode-VM. Datavejen er:

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

Hvert trin er en klasse af typen PHP med en lille grænseflade, som det næste trin bruger. Der er intet linktrin — parsing producerer et AST, som fortolkeren gennemløber direkte.

## Lexer

`src/Lexer/Lexer.php` konverterer kildetekst til en strøm af forekomster af `Token`. Hvert token er en skrivebeskyttet struct: type (`TokenType`-enum, ~80 varianter), værdi, linje, kolonne.

Lexeren håndterer:

- Tal-litteraler — decimal, hex (`0x`), oktal (`0o`), binær (`0b`), eksponentialform og numeriske separatorer.
- Strenglitteraler — enkelt- og dobbeltanførselstegn, alle escape-sekvenser samt skabelonlitteraler med indlejrede udtryk.
- Regulære udtrykslitteraler — fuld `/pattern/flags`-tokenisering med kendskab til, hvornår et `/` skal fortolkes som regex eller division.
- Identifikatorer, nøgleord, tegnsætning.
- Kommentarer (linje- og blokkommentarer), bevaret som trivia.
- Unicode-kilde — UTF-8-bytes fortolket som den samme UTF-16-kilde som JS.

Outputtet er en generator, der udleverer tokens efter behov. Parseren bruger dem med ét tokens lookahead.

## Parser

`src/Parser/Parser.php` er en håndskrevet Pratt-parser. Udtryksprioriteten findes i `src/Parser/Precedence.php`. Sætninger håndteres med deres egen rekursive nedstigning.

Den producerer uforanderlige AST-noder fra `src/Ast/`. Hver node indeholder sin kildeplacering til stack traces og fejlrapportering.

Bemærkelsesværdige former:

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

Automatisk indsættelse af semikolon implementeres i parseren (ikke lexeren) i henhold til specifikationen.

## Fortolker

`src/Runtime/Interpreter.php` gennemløber AST'et. Den besøger hver node og returnerer en `JsValue` (eller en `Completion` for sætninger, der ændrer kontrolflowet).

Vigtige støtteklasser:

- **`Environment`** (`src/Runtime/Environment.php`) — leksikal scope-kæde. Hvert funktionskald opretter et nyt miljø, der peger på sin leksikale forælder. `var` hoistes til funktionsscopet; `let` / `const` har blokscope med en TDZ.
- **`CallStack`** (`src/Runtime/CallStack.php`) — eksplicit stack med dybdegrænse. Funktions- og metodekald skubber frames på stacken; undtagelser ruller dem tilbage.
- **`Completion`** (`src/Runtime/Completion.php`) — diskrimineret union af `normal`, `return`, `throw`, `break`, `continue` samt en valgfri værdi og et label.
- **`Reference`** (`src/Runtime/Reference.php`) — specifikationens Reference-type til evaluering af venstre side i tildelinger og `delete`.

## Værdier

JavaScript-værdier er PHP-objekter under `src/Value/`. Hver implementerer grænsefladen `JsValue` og har sine egne typekonverteringsmetoder (`toNumber()`, `toString()`, `toBoolean()`).

| Klasse | JS type |
|---|---|
| `JsUndefined` | `undefined` (singleton) |
| `JsNull` | `null` (singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | IEEE 754 double; bevarer NaN, ±Infinity, ±0 |
| `JsString` | UTF-16-strengsemantik |
| `JsObject` | basisobjekt med egenskabskort og prototypekæde |
| `JsArray` | udvider `JsObject` med længdesporing |
| `JsFunction` | closure med indfanget `Environment` |
| `JsSymbol` | Symbol-primitiv |
| `JsBigInt` | vilkårlig præcision via bcmath |
| `JsProxy` | alle 13 traps, kan tilbagekaldes |
| `JsPromise` | synkron executor + microtask-kø |
| `JsGenerator` | dobbelt tilstand: framesnapshot (bytecodekompilerede bodies; se [Bytecode VM § Generators med framesnapshot](https://phasis.inline0.dev/da/docs/advanced/bytecode-vm)) med PHP Fiber-fallback til `yield*`, try-with-yield og async generators |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | samlinger |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | binære data |

Talhåndtering er den største historiske kilde til fejl: `NaN !== NaN` skal være sand, `-0 === 0` skal være sand, og kanttilfælde for ToNumber kræver præcision på bitniveau. Klassen `Spec/TypeConversion` implementerer specifikationsalgoritmerne ordret og er udtømmende testet.

## Indbygget bibliotek

`src/BuiltIn/` leverer alle standardbiblioteksobjekter: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console` samt Web Platform-/Fetch-pakkerne (`URL`, `Headers`, `Request`, `Response`, `Blob`, `FormData`, fulde Streams, `EventTarget`, `AbortController`/`AbortSignal`, `DOMException`, `structuredClone`, `performance`, `TextEncoder`/`TextDecoder`, `atob`/`btoa`), `crypto` + `SubtleCrypto`, `WebSocket`, `XMLHttpRequest` og event loop (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Hver indbygget funktion er en statisk `install()`-metode på en klasse. `Engine` kalder dem i afhængighedsrækkefølge under oprettelsen. Hver enkelt definerer sin prototypekæde, installerer metoder som `JsFunction`-instanser pakket omkring PHP-closures og eksponerer konstruktøren som en global.

## Bro mellem PHP↔JS

Værdikonvertering på tværs af grænsen foregår to steder:

- **`Phasis\Interop\PhpToJs::convert()`** — PHP-værdi til `JsValue`, brugt af `setGlobal()`, `call()`-argumenter og returværdier fra værtsfunktioner. En kaldbar PHP bliver til en `JsFunction`, hvis body kalder den.
- **`PhpToJs::toPhp()`** — `JsValue` til PHP for argumenterne til en værtsfunktion.
- **`Engine::toPhp()`** (privat) — `JsValue` til PHP for resultaterne af `eval()` og `call()`.

Der findes ingen objektbro: PHP-objekter, der ikke kan kaldes, konverteres til `undefined` ([issue #18](https://github.com/inline0/phasis/issues/18)). Klassen `JsToPhp` i `src/Bytecode/` er ikke relateret til interoperabilitet; det er transpilereren, der beskrives nedenfor. Konverteringsreglerne er dokumenteret under [Værdikonvertering](https://phasis.inline0.dev/da/docs/interop/values).

## Fejlhåndtering

En ikke-fanget JS `throw` bliver til en PHP `Phasis\Exceptions\JsThrowable`; fejl, som motoren selv frembringer, vises som `Phasis\Exceptions\TypeError`, `RangeError` eller `ReferenceError`, alle underklasser af `Phasis\Exceptions\RuntimeError`. Inde i motoren fanger en JS `try` / `catch` `JsThrowable`, enhver `RuntimeError` og `Phasis\Exceptions\SyntaxError`. Andre PHP-undtagelser, herunder dem, der udløses af værtsfunktioner, konverteres ikke og går direkte videre til PHP-kalderen ([issue #20](https://github.com/inline0/phasis/issues/20)).

Der findes to grænser: grænsen for kaldestakken (`CallStack::maxDepth`, 4,096 frames samt en vagt på 8,192 frames for transpilerede closures) og `maxLoopIterations`, der indstilles via `Engine::setLimit()`, og som kun håndhæves af træfortolkeren ([issue #19](https://github.com/inline0/phasis/issues/19)). Begge udløser `Phasis\Exceptions\InternalError`. Der er ingen grænser for strenglængde, outputstørrelse eller køretid.

## Bytecode-VM

Varme funktioner sænkes på to måder. `JsToPhp` (i `src/Bytecode/`) udsender en PHP-closure for bodies med mange tal eller lokale variabler, som PHPs tracing-JIT kan kompilere til native kode; pipelinen sænker læsning af `new Ctor(args)`, `obj.method(args)` og `this.member`, hvilket er nok til, at det typiske mønster med en klassemetode i en løkke helt omgår VM'en. Alt andet kompileres til flad bytecode, som `VM::execute` bruger, med en brugerdefineret kaldestak, der holder alle JS-kald inden for én enkelt PHP-frame og en inline-cache pr. PC til læsning af prototypemetoder. Enkle generator-bodies kompileres også til bytecode, hvor `Op::YIELD` indfanger et framesnapshot på heap'en i stedet for at passere gennem PHP Fiber.

Alle former, som compileren ikke sænker (e.g. `with`, `try` med en catch med ny binding, `yield*`, async generators), falder tilbage til træfortolkeren. Se [Bytecode VM](https://phasis.inline0.dev/da/docs/advanced/bytecode-vm) for hele mekanismen.
