
# Архитектура

Phasis е четиристепенен обход на дървото с VM за байткод, използвана при възможност. Потокът от данни е:

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

Всеки етап е клас PHP с малък интерфейс, който използва следващият етап. Няма стъпка за свързване — парсирането създава AST, който интерпретаторът обхожда директно.

## Лексикален анализатор

`src/Lexer/Lexer.php` преобразува изходния текст в поток от екземпляри `Token`. Всеки токен е структура само за четене: тип (перечисление `TokenType` с около 80 стойности), стойност, ред, колона.

Лексикалният анализатор обработва:

- Числови литерали — десетични, шестнадесетични (`0x`), осмични (`0o`), двоични (`0b`), експоненциални и с разделители на цифрите.
- Низови литерали — единични и двойни кавички, всички escape последователности, шаблонни литерали с вложени изрази.
- Литерали на регулярни изрази — пълна токенизация на `/pattern/flags`, която определя кога `/` трябва да се интерпретира като регулярен израз или деление.
- Идентификатори, ключови думи, пунктуация.
- Коментари (редови и блокови), запазени като незначими токени.
- Изходен код Unicode — UTF-8 байтовете се интерпретират като източника JS UTF-16.

Изходът е генератор, който подава токени при поискване. Парсерът ги обработва с един токен за предварителен преглед.

## Парсер

`src/Parser/Parser.php` е ръчно написан Pratt парсер. Приоритетът на изразите е зададен в `src/Parser/Precedence.php`. Инструкциите се обработват със собствен рекурсивен спускащ се парсер.

Той създава неизменяеми AST възли от `src/Ast/`. Всеки възел съдържа местоположението си в изходния код за проследяване на стека и докладване на грешки.

Характерни структури:

- **Израз**: `Literal`, `Identifier`, `BinaryExpression`, `UnaryExpression`, `AssignmentExpression`, `CallExpression`, `MemberExpression`, `ArrowFunction`, `ObjectExpression`, `ArrayExpression`, `ConditionalExpression`, `TemplateLiteral`, `SpreadElement`, `NewExpression`, `ThisExpression`, `SequenceExpression`, `TaggedTemplate`, `ClassExpression`, `YieldExpression`, `AwaitExpression`.
- **Инструкция**: `BlockStatement`, `IfStatement`, `ForStatement`, `ForInStatement`, `ForOfStatement`, `WhileStatement`, `DoWhileStatement`, `SwitchStatement`, `ReturnStatement`, `ThrowStatement`, `TryStatement`, `BreakStatement`, `ContinueStatement`, `ExpressionStatement`.
- **Декларация**: `VariableDeclaration`, `FunctionDeclaration`, `ClassDeclaration`, `ImportDeclaration`.
- **Шаблон** (деструктуриране): `ArrayPattern`, `ObjectPattern`, `RestElement`, `AssignmentPattern`.

Автоматичното вмъкване на точка и запетая е реализирано в парсера (не в лексикалния анализатор) според спецификацията.

## Интерпретатор

`src/Runtime/Interpreter.php` обхожда AST. Посещава всеки възел и връща `JsValue` (или `Completion` за инструкции, които променят потока на управление).

Основни помощни класове:

- **`Environment`** (`src/Runtime/Environment.php`) — верига от лексикални обхвати. Всяко извикване на функция създава нова среда, сочеща към лексикалния си родител. `var` се издига до обхвата на функцията; `let` / `const` са ограничени до блока и имат TDZ.
- **`CallStack`** (`src/Runtime/CallStack.php`) — изричен стек с ограничение за дълбочината. Извикванията на функции и методи добавят рамки; изключенията ги премахват.
- **`Completion`** (`src/Runtime/Completion.php`) — разграничено обединение на `normal`, `return`, `throw`, `break`, `continue`, както и незадължителни стойност и етикет.
- **`Reference`** (`src/Runtime/Reference.php`) — типът Reference от спецификацията за оценяване на лявата страна при присвоявания и `delete`.

## Стойности

Стойностите JavaScript са обекти PHP под `src/Value/`. Всяка реализира интерфейса `JsValue` и разполага със собствени методи за преобразуване на типове (`toNumber()`, `toString()`, `toBoolean()`).

| Клас | JS тип |
|---|---|
| `JsUndefined` | `undefined` (сингълтон) |
| `JsNull` | `null` (сингълтон) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | IEEE 754 double; запазва NaN, ±Infinity, ±0 |
| `JsString` | семантика на низовете UTF-16 |
| `JsObject` | базов обект с карта на свойствата и верига на прототипите |
| `JsArray` | разширява `JsObject` с проследяване на дължината |
| `JsFunction` | затваряне със захванат `Environment` |
| `JsSymbol` | примитив Symbol |
| `JsBigInt` | произволна точност чрез bcmath |
| `JsProxy` | всичките 13 капана, с възможност за отмяна |
| `JsPromise` | синхронен изпълнител + опашка за микрозадачи |
| `JsGenerator` | двоен режим: генератори със снимка на рамката (компилирани до байткод тела; вижте [Bytecode VM § Генератори със снимка на рамката](https://phasis.inline0.dev/bg/docs/advanced/bytecode-vm)) с резервен вариант чрез PHP Fiber за `yield*`, try-with-yield и асинхронни генератори |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | колекции |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | двоични данни |

Обработката на числа е най-големият източник на исторически грешки: `NaN !== NaN` трябва да е true, `-0 === 0` трябва да е true, а граничните случаи на ToNumber изискват точност до битово ниво. Класът `Spec/TypeConversion` реализира алгоритмите от спецификацията дословно и е подложен на изчерпателни тестове.

## Вградена библиотека

`src/BuiltIn/` предоставя всички обекти от стандартната библиотека: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console`, както и пакетите Web Platform / Fetch (`URL`, `Headers`, `Request`, `Response`, `Blob`, `FormData`, пълни потоци, `EventTarget`, `AbortController`/`AbortSignal`, `DOMException`, `structuredClone`, `performance`, `TextEncoder`/`TextDecoder`, `atob`/`btoa`), `crypto` + `SubtleCrypto`, `WebSocket`, `XMLHttpRequest` и цикъла на събитията (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Всеки вграден обект е статичен метод `install()` на клас. `Engine` ги извиква в реда на зависимостите при създаването. Всеки от тях определя веригата си на прототипите, инсталира методите като екземпляри `JsFunction`, обвити около затваряния PHP, и предоставя конструктора като глобален обект.

## Мост PHP↔JS

Преобразуването на стойности през границата се извършва на две места:

- **`Phasis\Interop\PhpToJs::convert()`** — стойност PHP към `JsValue`; използва се за `setGlobal()`, аргументи `call()` и върнати стойности от хост функции. Извикваем обект PHP става `JsFunction`, чието тяло го извиква.
- **`PhpToJs::toPhp()`** — `JsValue` към PHP за аргументите на хост функция.
- **`Engine::toPhp()`** (частен) — `JsValue` към PHP за резултатите от `eval()` и `call()`.

Няма мост за обекти: обектите PHP, които не са извикваеми, се преобразуват в `undefined` ([проблем № 18](https://github.com/inline0/phasis/issues/18)). Класът `JsToPhp` в `src/Bytecode/` не е свързан с взаимодействието; това е транспилаторът, описан по-долу. Правилата за преобразуване са описани в [Преобразуване на стойности](https://phasis.inline0.dev/bg/docs/interop/values).

## Обработка на грешки

Неуловен JS `throw` става PHP `Phasis\Exceptions\JsThrowable`; грешките, генерирани от самия енджин, се проявяват като `Phasis\Exceptions\TypeError`, `RangeError` или `ReferenceError`, всички подкласове на `Phasis\Exceptions\RuntimeError`. В енджина обработчик JS `try` / `catch` улавя `JsThrowable`, всяко `RuntimeError` и `Phasis\Exceptions\SyntaxError`. Други изключения PHP, включително хвърлените от хост функции, не се преобразуват и се предават директно на извикващата страна PHP ([проблем № 20](https://github.com/inline0/phasis/issues/20)).

Има два лимита: таванът на стека за извиквания (`CallStack::maxDepth`, 4,096 рамки, плюс предпазител от 8,192 рамки за транспилирани затваряния) и `maxLoopIterations`, задаван чрез `Engine::setLimit()`, който се прилага само от обхождащото дърво ([проблем № 19](https://github.com/inline0/phasis/issues/19)). И двата генерират `Phasis\Exceptions\InternalError`. Няма лимити за дължина на низовете, размер на изхода или време за изпълнение.

## Виртуална машина за байткод

Горещите функции се преобразуват по два начина. `JsToPhp` (в `src/Bytecode/`) генерира затваряне PHP за тела с много числови операции или локални променливи, които JIT компилаторът с проследяване на PHP може да компилира до машинен код; конвейерът преобразува четенията на `new Ctor(args)`, `obj.method(args)` и `this.member`, което е достатъчно, за да заобиколи напълно VM при типичния шаблон с метод на клас в цикъл. Всичко останало се компилира до плосък байткод, изпълняван от `VM::execute`, с персонализиран стек за извиквания, който държи всяко извикване на JS в една рамка PHP и кеш за вграждане на четенията на методи от прототипа за всяка PC позиция. Телата на простите генератори също се компилират до байткод, като `Op::YIELD` улавя снимка на рамка в купа памет вместо да преминава през PHP Fiber.

Всеки шаблон, който компилаторът не може да преобразува (e.g. `with`, `try` с повторно обвързване на catch, `yield*`, асинхронни генератори), преминава към обхождащото дърво. Вижте [Виртуална машина за байткод](https://phasis.inline0.dev/bg/docs/advanced/bytecode-vm) за пълното описание на механизма.
