
# Arkitektur

Phasis är en fyrastegs trädvandring med en opportunistisk bytekod-VM. Dataflödet är:

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

Varje steg är en klass i PHP med ett litet gränssnitt som nästa steg använder. Det finns inget länkningssteg – parsningen skapar en AST som interpreteraren går igenom direkt.

## Lexer

`src/Lexer/Lexer.php` omvandlar källtext till en ström av `Token`-instanser. Varje token är en skrivskyddad struct: typ (`TokenType`-enum, ~80 fall), värde, rad, kolumn.

Lexern hanterar:

- Numeriska literaler – decimala, hexadecimala (`0x`), oktala (`0o`), binära (`0b`), exponentiella samt tal med sifferavgränsare.
- Strängliteraler – enkla och dubbla citattecken, alla escape-sekvenser och malliteraler med nästlade uttryck.
- Reguljära uttryck som literaler – fullständig tokenisering av `/pattern/flags` med hänsyn till om en `/` ska tolkas som regex eller division.
- Identifierare, nyckelord, interpunktion.
- Kommentarer (rad- och blockkommentarer), bevarade som trivia.
- Källkod i Unicode – UTF-8-byte tolkade som samma JS-källa.

Utdata är en generator som ger token efter behov. Parsern förbrukar dem med en tokens framförhållning.

## Parsern

`src/Parser/Parser.php` är en handskriven Pratt-parser. Prioriteten för uttryck anges i `src/Parser/Precedence.php`. Satser använder egen rekursiv nedstigning.

Den skapar oföränderliga AST-noder från `src/Ast/`. Varje nod innehåller sin källposition för stackspårningar och felrapportering.

Noterbara strukturer:

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

Automatisk infogning av semikolon implementeras i parsern (inte lexern) enligt specifikationen.

## Interpreterare

`src/Runtime/Interpreter.php` är AST-vandraren. Den besöker varje nod och returnerar ett `JsValue` (eller ett `Completion` för satser som ändrar kontrollflödet).

Viktiga stödklasser:

- **`Environment`** (`src/Runtime/Environment.php`) – lexikal scope-kedja. Varje funktionsanrop skapar en ny miljö som pekar på sin lexikala förälder. `var` hissas upp till funktionsscopet; `let` / `const` är blockscope-begränsade med en TDZ.
- **`CallStack`** (`src/Runtime/CallStack.php`) – explicit stack med djupgräns. Funktions- och metodanrop lägger till stackramar; undantag avvecklar dem.
- **`Completion`** (`src/Runtime/Completion.php`) – diskriminerad union av `normal`, `return`, `throw`, `break`, `continue` samt ett valfritt värde och en etikett.
- **`Reference`** (`src/Runtime/Reference.php`) – specifikationens Reference-typ för utvärdering av vänsterled vid tilldelningar och `delete`.

## Värden

JavaScript-värden är PHP-objekt under `src/Value/`. Varje värde implementerar gränssnittet `JsValue` och har egna typkonverteringsmetoder (`toNumber()`, `toString()`, `toBoolean()`).

| Klass | Typen JS |
|---|---|
| `JsUndefined` | `undefined` (singleton) |
| `JsNull` | `null` (singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | IEEE 754-double; bevarar NaN, ±Infinity, ±0 |
| `JsString` | UTF-16-strängsemantik |
| `JsObject` | basobjekt med egenskapskarta och prototypkedja |
| `JsArray` | utökar `JsObject` med längdspårning |
| `JsFunction` | closure med infångad `Environment` |
| `JsSymbol` | Symbol-primitiv |
| `JsBigInt` | godtycklig precision via bcmath |
| `JsProxy` | alla 13 fällor, kan återkallas |
| `JsPromise` | synkron exekverare + mikrouppgiftskö |
| `JsGenerator` | två lägen: ramögonblicksbild (bytekodkompilerade kroppar; se [Bytekod-VM § Generatorer med ramögonblicksbild](https://phasis.inline0.dev/sv/docs/advanced/bytecode-vm)) med PHP Fiber-reservlösning för `yield*`, try-with-yield och asynkrona generatorer |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | samlingar |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | binärdata |

Talhantering är den enskilt största källan till historiska buggar: `NaN !== NaN` måste vara sant, `-0 === 0` måste vara sant, och gränsfall för ToNumber kräver exakt hantering på bitnivå. Klassen `Spec/TypeConversion` implementerar specifikationens algoritmer ordagrant och är uttömmande testad.

## Inbyggt bibliotek

`src/BuiltIn/` tillhandahåller alla standardbiblioteksobjekt: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console`, plus webbplattformens och Fetch-paketen (`URL`, `Headers`, `Request`, `Response`, `Blob`, `FormData`, fullständiga Streams, `EventTarget`, `AbortController`/`AbortSignal`, `DOMException`, `structuredClone`, `performance`, `TextEncoder`/`TextDecoder`, `atob`/`btoa`), `crypto` + `SubtleCrypto`, `WebSocket`, `XMLHttpRequest` och händelseslingan (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Varje inbyggd funktion är en statisk metod `install()` i en klass. `Engine` anropar dem i beroendeordning vid konstruktionen. Varje funktion definierar sin prototypkedja, installerar metoder som `JsFunction`-instanser omslutna runt PHP-closures och exponerar konstruktorn som global.

## Brygga mellan PHP↔JS

Värdekonvertering över gränsen sker på två ställen:

- **`Phasis\Interop\PhpToJs::convert()`** – värde från PHP till `JsValue`, används för `setGlobal()`, `call()`-argument och returvärden från värdfunktioner. Ett anropbart PHP blir ett `JsFunction` vars kropp anropar det.
- **`PhpToJs::toPhp()`** – från `JsValue` till PHP för argumenten till en värdfunktion.
- **`Engine::toPhp()`** (privat) – från `JsValue` till PHP för resultaten från `eval()` och `call()`.

Det finns ingen objektbrygga: objekt från PHP som inte är anropbara konverteras till `undefined` ([problem nr 18](https://github.com/inline0/phasis/issues/18)). Klassen `JsToPhp` i `src/Bytecode/` har inget med interoperabilitet att göra; det är transpilern som beskrivs nedan. Konverteringsreglerna finns i [Värdekonvertering](https://phasis.inline0.dev/sv/docs/interop/values).

## Felhantering

Ett ouppfångat JS `throw` blir ett PHP `Phasis\Exceptions\JsThrowable`; fel som motorn själv ger upphov till visas som `Phasis\Exceptions\TypeError`, `RangeError` eller `ReferenceError`, som alla är underklasser till `Phasis\Exceptions\RuntimeError`. Inne i motorn fångar en JS `try` / `catch` `JsThrowable`, alla `RuntimeError` och `Phasis\Exceptions\SyntaxError`. Andra undantag från PHP, inklusive sådana som kastas av värdfunktioner, konverteras inte utan skickas direkt vidare till anroparen PHP ([problem nr 20](https://github.com/inline0/phasis/issues/20)).

Det finns två gränser: taket för anropsstacken (`CallStack::maxDepth`, 4,096 ramar, plus en skyddsgräns på 8,192 ramar för transpilerade closures) och `maxLoopIterations`, som ställs in via `Engine::setLimit()` och bara upprätthålls av trädvandraren ([problem nr 19](https://github.com/inline0/phasis/issues/19)). Båda kastar `Phasis\Exceptions\InternalError`. Det finns inga gränser för stränglängd, utdatas storlek eller exekveringstid.

## Bytekod-VM

Hetta funktioner sänks på två sätt. `JsToPhp` (i `src/Bytecode/`) genererar en PHP-closure för kroppar med mycket numerik eller många lokala variabler, som PHPs spårnings-JIT kan kompilera till maskinkod; pipelinen sänker `new Ctor(args)`, `obj.method(args)` och `this.member`-läsningar, vilket räcker för att det typiska mönstret med en klassmetod i en loop ska kringgå VM:n helt. Allt annat kompileras till platt bytekod som `VM::execute` kör, med en anpassad anropsstack som håller varje JS-anrop inom en enda PHP-ram och en cache per PC för läsningar av prototypmetoder. Enkla generatorkroppar kompileras också till bytekod, där `Op::YIELD` fångar en ögonblicksbild av en ram på heapen i stället för att gå via PHP Fiber.

Former som kompilatorn inte sänker (e.g. `with`, `try` med en catch som binder om, `yield*`, asynkrona generatorer) faller tillbaka till trädvandraren. Se [Bytekod-VM](https://phasis.inline0.dev/sv/docs/advanced/bytecode-vm) för hela mekanismen.
