
# Architektúra

A Phasis négylépcsős fa-bejáró, amely lehetőség szerint bájtkódos virtuális gépet használ. Az adatfolyam:

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

Minden lépés egy PHP osztály, amelynek egyszerű felületét a következő lépés használja. Nincs összekapcsolási lépés: a feldolgozás AST-t állít elő, amelyet az értelmező közvetlenül bejár.

## Lexer

A `src/Lexer/Lexer.php` a forrásszöveget `Token` példányok adatfolyamává alakítja. Minden token csak olvasható struktúra: típus (`TokenType` felsorolási típus, körülbelül 80 eset), érték, sor, oszlop.

A lexer a következőket kezeli:

- Számkonstansok – decimális, hexadecimális (`0x`), oktális (`0o`), bináris (`0b`), exponenciális alak, számelválasztók.
- Karakterlánc-konstansok – egyszeres és dupla idézőjelek, minden escape-szekvencia, beágyazott kifejezéseket tartalmazó sablonkarakterláncok.
- Reguláriskifejezés-konstansok – teljes `/pattern/flags` tokenizálás, annak figyelembevételével, hogy egy `/` reguláris kifejezésként vagy osztásként értelmezendő-e.
- Azonosítók, kulcsszavak, írásjelek.
- Megjegyzések (egysorosak és blokkok), amelyek mellékszövegként megmaradnak.
- Unicode forrás – UTF-8 bájtok, amelyeket a JS UTF-16 forrásként értelmez.

A kimenet egy generátor, amely lustán szolgáltatja a tokeneket. A parser egytokenes előretekintéssel dolgozza fel őket.

## Elemző

A `src/Parser/Parser.php` kézzel írt Pratt-parser. A kifejezésprecedenciákat a `src/Parser/Precedence.php` határozza meg. Az utasítások saját rekurzív leszállású feldolgozással rendelkeznek.

Az `src/Ast/` alapján megváltoztathatatlan AST-csomópontokat állít elő. Minden csomópont tartalmazza a forrásbeli helyét a veremnyomokhoz és a hibajelentésekhez.

Jellegzetes formák:

- **Kifejezés**: `Literal`, `Identifier`, `BinaryExpression`, `UnaryExpression`, `AssignmentExpression`, `CallExpression`, `MemberExpression`, `ArrowFunction`, `ObjectExpression`, `ArrayExpression`, `ConditionalExpression`, `TemplateLiteral`, `SpreadElement`, `NewExpression`, `ThisExpression`, `SequenceExpression`, `TaggedTemplate`, `ClassExpression`, `YieldExpression`, `AwaitExpression`.
- **Utasítás**: `BlockStatement`, `IfStatement`, `ForStatement`, `ForInStatement`, `ForOfStatement`, `WhileStatement`, `DoWhileStatement`, `SwitchStatement`, `ReturnStatement`, `ThrowStatement`, `TryStatement`, `BreakStatement`, `ContinueStatement`, `ExpressionStatement`.
- **Deklaráció**: `VariableDeclaration`, `FunctionDeclaration`, `ClassDeclaration`, `ImportDeclaration`.
- **Minta** (destrukturálás): `ArrayPattern`, `ObjectPattern`, `RestElement`, `AssignmentPattern`.

A pontosvesszők automatikus beszúrását a specifikációnak megfelelően a parser valósítja meg, nem a lexer.

## Értelmező

A `src/Runtime/Interpreter.php` az AST bejárója. Minden csomópontot meglátogat, és egy `JsValue` értéket ad vissza (vagy egy `Completion` értéket a vezérlési folyamatot módosító utasítások esetén).

Fontos támogató osztályok:

- **`Environment`** (`src/Runtime/Environment.php`) – lexikális hatókörlánc. Minden függvényhívás új környezetet hoz létre, amely a lexikális szülőre mutat. A `var` a függvény hatókörébe kerül; a `let` és a `const` blokkhatókörű, TDZ-vel.
- **`CallStack`** (`src/Runtime/CallStack.php`) – explicit verem mélységi korláttal. A függvény- és metódushívások veremkereteket helyeznek el; a kivételek leépítik ezeket.
- **`Completion`** (`src/Runtime/Completion.php`) – a `normal`, `return`, `throw`, `break` és `continue` diszkriminált uniója, kiegészítve egy opcionális értékkel és címkével.
- **`Reference`** (`src/Runtime/Reference.php`) – a specifikáció Reference típusa a bal oldali operandus kiértékeléséhez hozzárendelésekben és `delete` esetén.

## Értékek

A JavaScript értékek a `src/Value/` alatti PHP objektumok. Mindegyik megvalósítja a `JsValue` interfészt, és saját típuskonverziós metódusokkal rendelkezik (`toNumber()`, `toString()`, `toBoolean()`).

| Osztály | JS típus |
|---|---|
| `JsUndefined` | `undefined` (singleton) |
| `JsNull` | `null` (singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | IEEE 754 double; megőrzi a NaN, ±Infinity, ±0 értékeket |
| `JsString` | UTF-16 karakterlánc-szemantika |
| `JsObject` | Alaposztály tulajdonságtérképpel és prototípuslánccal |
| `JsArray` | A `JsObject` kiterjesztése, hossznyilvántartással |
| `JsFunction` | Lezárás rögzített `Environment` értékkel |
| `JsSymbol` | Symbol primitív érték |
| `JsBigInt` | Tetszőleges pontosság a bcmath segítségével |
| `JsProxy` | Mind a 13 csapda, visszavonható |
| `JsPromise` | Szinkron végrehajtó + mikrotask-sor |
| `JsGenerator` | Kétmódú: keretpillanatképes (bájtkódra fordított törzsek; lásd [Bájtkód-VM § Keretpillanatképes generátorok](https://phasis.inline0.dev/hu/docs/advanced/bytecode-vm)) PHP Fiber-visszaeséssel a `yield*`, a try-with-yield és az aszinkron generátorok esetén |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | gyűjtemények |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | bináris adatok |

A számok kezelése a korábbi hibák legnagyobb forrása: a `NaN !== NaN` feltételnek igaznak kell lennie, a `-0 === 0` feltételnek igaznak kell lennie, a ToNumber szélső esetei pedig pontos, bitszintű odafigyelést igényelnek. A `Spec/TypeConversion` osztály szó szerint valósítja meg a specifikáció algoritmusait, és kimerítő tesztelésen esik át.

## Beépített könyvtár

A `src/BuiltIn/` biztosítja az összes szabványos könyvtári objektumot: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console`, valamint a Web Platform / Fetch csomagokat (`URL`, `Headers`, `Request`, `Response`, `Blob`, `FormData`, teljes Streams, `EventTarget`, `AbortController`/`AbortSignal`, `DOMException`, `structuredClone`, `performance`, `TextEncoder`/`TextDecoder`, `atob`/`btoa`), továbbá a `crypto` + `SubtleCrypto`, `WebSocket`, `XMLHttpRequest` és az eseményciklust (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Minden beépített elem egy statikus `install()` metódus egy osztályon. A `Engine` a létrehozáskor, függőségi sorrendben hívja meg ezeket. Mindegyik meghatározza a prototípusláncát, a metódusokat PHP lezárásokba burkolt `JsFunction` példányokként telepíti, és a konstruktort globálisként teszi elérhetővé.

## PHP↔JS híd

A határon átnyúló értékkonverzió két helyen található:

- **`Phasis\Interop\PhpToJs::convert()`** — PHP értékből `JsValue` lesz; a `setGlobal()`, a `call()` argumentumai és a gazdafüggvények visszatérési értékei használják. Egy PHP hívható értékből olyan `JsFunction` lesz, amelynek törzse meghívja azt.
- **`PhpToJs::toPhp()`** — `JsValue` értékből PHP lesz a gazdafüggvény argumentumainál.
- **`Engine::toPhp()`** (privát) — `JsValue` értékből PHP lesz a `eval()` és a `call()` eredményeinél.

Nincs objektumhíd: a nem hívható PHP objektumok `undefined` értékké alakulnak ([18. probléma](https://github.com/inline0/phasis/issues/18)). Az `src/Bytecode/`-beli `JsToPhp` osztály nem kapcsolódik az együttműködéshez; ez az alább ismertetett transzpiláló. A konverziós szabályokat az [Értékkonverzió](https://phasis.inline0.dev/hu/docs/interop/values) dokumentálja.

## Hibakezelés

Egy el nem kapott JS `throw` értékből PHP `Phasis\Exceptions\JsThrowable` lesz; a motor által előidézett hibák `Phasis\Exceptions\TypeError`, `RangeError` vagy `ReferenceError` formájában jelennek meg, amelyek mind a `Phasis\Exceptions\RuntimeError` alosztályai. A motoron belül egy JS `try` / `catch` elkapja a `JsThrowable` értékeket, bármely `RuntimeError` értéket és a `Phasis\Exceptions\SyntaxError` értéket. Az egyéb PHP kivételek, többek között a gazdafüggvények által dobottak, nem alakulnak át, hanem közvetlenül továbbterjednek a PHP hívóhoz ([20. probléma](https://github.com/inline0/phasis/issues/20)).

Két korlát van: a hívási verem felső határa (`CallStack::maxDepth`, 4,096 keret, továbbá egy 8,192 keretes őr a transzpilált lezárásokhoz), valamint a `maxLoopIterations`, amely a `Engine::setLimit()` segítségével állítható be, és amelyet csak a fastruktúra-bejáró érvényesít ([19. probléma](https://github.com/inline0/phasis/issues/19)). Mindkettő `Phasis\Exceptions\InternalError` hibát dob. Nincs korlát a karakterlánc hosszára, a kimenet méretére vagy a végrehajtási időre.

## Bájtkód-VM

A gyakran futó függvények kétféleképpen fordulnak le. A `JsToPhp` (a `src/Bytecode/` része) PHP lezárást állít elő a numerikus vagy sok lokális változót használó törzsekhez, amelyeket a PHP nyomkövető JIT-je natív kódra fordíthat; a folyamat lefordítja a `new Ctor(args)`, `obj.method(args)` és `this.member` olvasásokat is, így a kanonikus, ciklusban hívott osztálymetódus-minta teljesen megkerüli a VM-et. Minden más lapos bájtkódra fordul, amelyet a `VM::execute` futtat; egyedi hívásiverem-kezelése minden JS hívást egyetlen PHP kereten belül tart, a prototípusmetódusok olvasásához pedig programszámlálóhelyenkénti inline gyorsítótárat használ. Az egyszerű generátortörzsek is bájtkódra fordulnak, és a `Op::YIELD` a PHP Fiber átlépése helyett egy halomkeret-pillanatképet rögzít.

Minden olyan alak, amelyet a fordító nem tud lefordítani (e.g, `with`, újrakötést végző catch-csel használt `try`, `yield*`, aszinkron generátorok), visszaesik a fastruktúra-bejáróra. A teljes működésről lásd a [Bájtkód-VM](https://phasis.inline0.dev/hu/docs/advanced/bytecode-vm) leírást.
