
# Architektura

Phasis to czteroetapowy walker drzewa z doraźną maszyną wirtualną kodu bajtowego. Przepływ danych wygląda tak:

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

Każdy etap to klasa PHP o niewielkim interfejsie, z którego korzysta kolejny etap. Nie ma etapu linkowania — parsowanie tworzy AST, który interpreter przechodzi bezpośrednio.

## Lekser

`src/Lexer/Lexer.php` przekształca tekst źródłowy w strumień instancji `Token`. Każdy token jest strukturą tylko do odczytu: typ (enum `TokenType`, ok. 80 wariantów), wartość, wiersz i kolumna.

Lekser obsługuje:

- Literały liczbowe — dziesiętne, szesnastkowe (`0x`), ósemkowe (`0o`), binarne (`0b`), wykładnicze oraz separatory liczbowe.
- Literały tekstowe — pojedyncze i podwójne cudzysłowy, wszystkie sekwencje ucieczki, literały szablonów z zagnieżdżonymi wyrażeniami.
- Literały wyrażeń regularnych — pełna tokenizacja `/pattern/flags` z uwzględnieniem tego, czy `/` oznacza wyrażenie regularne, czy dzielenie.
- Identyfikatory, słowa kluczowe i znaki interpunkcyjne.
- Komentarze (wierszowe i blokowe), zachowywane jako elementy pomocnicze.
- Źródło Unicode — bajty UTF-8 interpretowane jako źródło UTF-16 JS.

Wynikiem jest generator, który leniwie zwraca tokeny. Parser odczytuje je z wyprzedzeniem o jeden token.

## Parser

`src/Parser/Parser.php` to napisany ręcznie parser Pratt. Priorytety wyrażeń są zdefiniowane w `src/Parser/Precedence.php`. Instrukcje obsługuje własny parser rekurencyjny typu descent.

Tworzy niezmienne węzły AST z `src/Ast/`. Każdy węzeł zawiera lokalizację w źródle, używaną w śladach stosu i raportach błędów.

Charakterystyczne struktury:

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

Automatyczne wstawianie średników jest zaimplementowane w parserze (nie w lekserze), zgodnie ze specyfikacją.

## Interpreter

`src/Runtime/Interpreter.php` przechodzi AST. Odwiedza każdy węzeł i zwraca `JsValue` (lub `Completion` dla instrukcji zmieniających przepływ sterowania).

Najważniejsze klasy pomocnicze:

- **`Environment`** (`src/Runtime/Environment.php`) — łańcuch zakresów leksykalnych. Każde wywołanie funkcji tworzy nowe środowisko wskazujące na nadrzędny zakres leksykalny. `var` są wynoszone do zakresu funkcji; `let` / `const` mają zakres blokowy i podlegają TDZ.
- **`CallStack`** (`src/Runtime/CallStack.php`) — jawny stos z limitem głębokości. Wywołania funkcji i metod odkładają ramki; wyjątki je zdejmują.
- **`Completion`** (`src/Runtime/Completion.php`) — unia rozłączna obejmująca `normal`, `return`, `throw`, `break`, `continue` oraz opcjonalną wartość i etykietę.
- **`Reference`** (`src/Runtime/Reference.php`) — typ Reference ze specyfikacji, służący do ewaluacji lewej strony przypisań i `delete`.

## Wartości

Wartości JavaScript to obiekty PHP w ramach `src/Value/`. Każdy z nich implementuje interfejs `JsValue` i ma własne metody konwersji typów (`toNumber()`, `toString()`, `toBoolean()`).

| Klasa | Typ JS |
|---|---|
| `JsUndefined` | `undefined` (singleton) |
| `JsNull` | `null` (singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | liczba zmiennoprzecinkowa IEEE 754; zachowuje NaN, ±Infinity, ±0 |
| `JsString` | semantyka ciągów UTF-16 |
| `JsObject` | obiekt bazowy z mapą właściwości i łańcuchem prototypów |
| `JsArray` | rozszerza `JsObject` o śledzenie długości |
| `JsFunction` | domknięcie z przechwyconym `Environment` |
| `JsSymbol` | typ prymitywny Symbol |
| `JsBigInt` | dowolna precyzja dzięki bcmath |
| `JsProxy` | wszystkie 13 pułapek, z możliwością odwołania |
| `JsPromise` | synchroniczny executor + kolejka zadań mikroprocesowych |
| `JsGenerator` | dwa tryby: migawka ramki (ciała skompilowane do kodu bajtowego; zob. [Generatory z migawką ramki maszyny wirtualnej kodu bajtowego](https://phasis.inline0.dev/pl/docs/advanced/bytecode-vm)) z awaryjnym użyciem PHP Fiber dla `yield*`, try-with-yield i asynchronicznych generatorów |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | kolekcje |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | dane binarne |

Obsługa liczb jest głównym historycznym źródłem błędów: `NaN !== NaN` musi być prawdziwe, `-0 === 0` musi być prawdziwe, a przypadki brzegowe ToNumber wymagają dokładności na poziomie bitów. Klasa `Spec/TypeConversion` implementuje algorytmy ze specyfikacji dosłownie i została gruntownie przetestowana.

## Wbudowana biblioteka

`src/BuiltIn/` udostępnia wszystkie obiekty biblioteki standardowej: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console`, a także pakiety Web Platform / Fetch (`URL`, `Headers`, `Request`, `Response`, `Blob`, `FormData`, pełny Streams, `EventTarget`, `AbortController`/`AbortSignal`, `DOMException`, `structuredClone`, `performance`, `TextEncoder`/`TextDecoder`, `atob`/`btoa`), `crypto` + `SubtleCrypto`, `WebSocket`, `XMLHttpRequest` oraz pętlę zdarzeń (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Każda funkcja wbudowana jest statyczną metodą `install()` klasy. Podczas tworzenia `Engine` wywołuje je w kolejności zależności. Każda definiuje łańcuch prototypów, instaluje metody jako instancje `JsFunction` opakowane wokół domknięć PHP i udostępnia konstruktor jako zmienną globalną.

## Most PHP↔JS

Konwersja wartości na granicy odbywa się w dwóch miejscach:

- **`Phasis\Interop\PhpToJs::convert()`** — wartość PHP na `JsValue`; używane dla argumentów `setGlobal()`, `call()` i wartości zwracanych przez funkcje hosta. Wywoływalny obiekt PHP staje się `JsFunction`, którego ciało go wywołuje.
- **`PhpToJs::toPhp()`** — `JsValue` na PHP dla argumentów funkcji hosta.
- **`Engine::toPhp()`** (prywatne) — `JsValue` na PHP dla wyników `eval()` i `call()`.

Nie ma mostu dla obiektów: obiekty PHP, które nie są wywoływalne, są konwertowane na `undefined` ([zgłoszenie #18](https://github.com/inline0/phasis/issues/18)). Klasa `JsToPhp` w `src/Bytecode/` nie ma związku z interoperacyjnością; to transpiler opisany poniżej. Zasady konwersji opisano w sekcji [Konwersja wartości](https://phasis.inline0.dev/pl/docs/interop/values).

## Obsługa błędów

Nieprzechwycony JS `throw` staje się PHP `Phasis\Exceptions\JsThrowable`; błędy zgłaszane przez sam silnik są prezentowane jako `Phasis\Exceptions\TypeError`, `RangeError` lub `ReferenceError`, wszystkie będące podklasami `Phasis\Exceptions\RuntimeError`. Wewnątrz silnika JS `try` / `catch` przechwytuje `JsThrowable`, dowolne `RuntimeError` i `Phasis\Exceptions\SyntaxError`. Inne wyjątki PHP, w tym rzucane przez funkcje hosta, nie są konwertowane i propagują się bezpośrednio do wywołującego PHP ([zgłoszenie #20](https://github.com/inline0/phasis/issues/20)).

Istnieją dwa limity: maksymalna głębokość stosu wywołań (`CallStack::maxDepth`, 4,096 ramek oraz ograniczenie do 8,192 ramek dla transpile'owanych domknięć) i `maxLoopIterations`, ustawiany przez `Engine::setLimit()` i egzekwowany tylko przez walker drzewa ([zgłoszenie #19](https://github.com/inline0/phasis/issues/19)). Przekroczenie obu limitów powoduje rzucenie `Phasis\Exceptions\InternalError`. Nie ma limitów długości ciągu, rozmiaru danych wyjściowych ani czasu wykonania.

## Maszyna wirtualna kodu bajtowego

Często wykonywane funkcje są obniżane na dwa sposoby. `JsToPhp` (w `src/Bytecode/`) emituje domknięcie PHP dla ciał intensywnie korzystających z liczb lub zmiennych lokalnych, które śledzący JIT PHP może skompilować do kodu natywnego; potok obniża odczyty `new Ctor(args)`, `obj.method(args)` i `this.member`, co wystarcza, by typowy wzorzec metody klasy w pętli całkowicie ominął maszynę wirtualną. Pozostałe przypadki są kompilowane do płaskiego kodu bajtowego, przetwarzanego przez `VM::execute`, z własnym stosem wywołań, który utrzymuje każde wywołanie JS w pojedynczej ramce PHP, oraz pamięcią podręczną inline dla odczytów metod prototypu, indeksowaną według PC. Proste ciała generatorów również są kompilowane do kodu bajtowego; `Op::YIELD` przechwytuje migawkę ramki na stercie zamiast przechodzić przez Fiber PHP.

Każda postać, której kompilator nie obniża (e.g. `with`, `try` z ponownym przypisaniem w catch, `yield*`, generatory asynchroniczne), wraca do walkera drzewa. Pełny opis mechanizmu znajdziesz w sekcji [Maszyna wirtualna kodu bajtowego](https://phasis.inline0.dev/pl/docs/advanced/bytecode-vm).
