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

Phasis — це чотириетапний обхід дерева з віртуальною машиною байткоду, яка за нагоди застосовується. Потік даних:

```
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`), експоненційні, з роздільниками чисел.
- Рядкові літерали — одинарні й подвійні лапки, усі керівні послідовності, шаблонні рядки з вкладеними виразами.
- Літерали регулярних виразів — повна токенізація `/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` | два режими: знімок кадру (тіла, скомпільовані в байткод; див. [Віртуальна машина байткоду § Генератори зі знімками кадрів](https://phasis.inline0.dev/uk/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`, повний Streams, `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` ([issue #18](https://github.com/inline0/phasis/issues/18)). Клас `JsToPhp` у `src/Bytecode/` не пов’язаний із взаємодією систем; це транспілятор, описаний нижче. Правила перетворення наведено в розділі [Перетворення значень](https://phasis.inline0.dev/uk/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 ([issue #20](https://github.com/inline0/phasis/issues/20)).

Є два обмеження: межа глибини стека викликів (`CallStack::maxDepth`, 4,096 кадрів, плюс захист на 8,192 кадрів для транспільованих замикань) і `maxLoopIterations`, що задається через `Engine::setLimit()` і контролюється лише інтерпретатором дерева ([issue #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::execute` із власним стеком викликів: кожен виклик JS залишається в межах одного кадру PHP, а для читання методів прототипу використовується кеш для кожного PC. Прості тіла генераторів також компілюються в байткод; `Op::YIELD` зберігає знімок кадру в купі замість переходу до Fiber PHP.

Будь-яка форма, яку компілятор не оптимізує (e.g. `with`, `try` із повторним прив’язуванням у catch, `yield*`, асинхронні генератори), обробляється інтерпретатором дерева. Повний опис механізму див. у розділі [Віртуальна машина байткоду](https://phasis.inline0.dev/uk/docs/advanced/bytecode-vm).
