
# Arhitektura

Phasis je drevesni sprehajalnik s štirimi stopnjami in oportunističnim VM za bajtno kodo. Pot podatkov je:

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

Vsaka stopnja je razred PHP z majhnim vmesnikom, ki ga uporablja naslednja stopnja. Koraka povezovanja ni — razčlenjevanje ustvari AST, po katerem interpreter hodi neposredno.

## Leksikalni analizator

`src/Lexer/Lexer.php` pretvori izvorno besedilo v tok primerkov `Token`. Vsak žeton je nespremenljiva struktura: tip (naštevanje `TokenType`, približno 80 primerov), vrednost, vrstica, stolpec.

Leksikalni analizator obravnava:

- Številske literale — decimalne, šestnajstiške (`0x`), osmiške (`0o`), dvojiške (`0b`), eksponentne in s številsko ločnico.
- Nizovne literale — enojne in dvojne narekovaje, vsa ubežna zaporedja, predloge z ugnezdenimi izrazi.
- Literale regularnih izrazov — popolno tokenizacijo `/pattern/flags` z upoštevanjem, ali naj bo `/` regularni izraz ali deljenje.
- Identifikatorje, ključne besede, ločila.
- Komentarje (vrstične in blokovne), ohranjene kot nepomembno besedilo.
- Izvorno kodo Unicode — bajti UTF-8, interpretirani kot izvorna koda JS v UTF-16.

Izhod je generator, ki žetone ustvarja sproti. Razčlenjevalnik jih porablja z vpogledom za en žeton naprej.

## Razčlenjevalnik

`src/Parser/Parser.php` je ročno napisan razčlenjevalnik Pratt. Prednost operatorjev v izrazih je določena v `src/Parser/Precedence.php`. Stavki uporabljajo lastno rekurzivno spuščanje.

Ustvari nespremenljiva vozlišča AST iz `src/Ast/`. Vsako vozlišče vsebuje lokacijo v izvorni kodi za sledenje skladu in poročanje o napakah.

Značilne oblike:

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

Samodejno vstavljanje podpičij je v skladu s specifikacijo implementirano v razčlenjevalniku (ne v leksikalnem analizatorju).

## Interpreter

`src/Runtime/Interpreter.php` je sprehajalnik AST. Obišče vsako vozlišče in vrne `JsValue` (ali `Completion` za stavke, ki spremenijo potek izvajanja).

Ključni podporni razredi:

- **`Environment`** (`src/Runtime/Environment.php`) — veriga leksikalnih obsegov. Vsak klic funkcije ustvari novo okolje, povezano z nadrejenim leksikalnim obsegom. `var` se dvigne na obseg funkcije; `let` / `const` sta omejena na blok in imata TDZ.
- **`CallStack`** (`src/Runtime/CallStack.php`) — eksplicitni sklad z omejitvijo globine. Klici funkcij in metod dodajo okvirje; izjeme jih odvijejo.
- **`Completion`** (`src/Runtime/Completion.php`) — označena unija `normal`, `return`, `throw`, `break`, `continue` z neobvezno vrednostjo in oznako.
- **`Reference`** (`src/Runtime/Reference.php`) — referenčni tip iz specifikacije za vrednotenje leve strani pri prirejanjih in `delete`.

## Vrednosti

Vrednosti JavaScript so objekti PHP pod `src/Value/`. Vsak implementira vmesnik `JsValue` in ima lastne metode za pretvorbo tipov (`toNumber()`, `toString()`, `toBoolean()`).

| Razred | Tip JS |
|---|---|
| `JsUndefined` | `undefined` (singleton) |
| `JsNull` | `null` (singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | dvojna natančnost IEEE 754; ohrani NaN, ±Infinity, ±0 |
| `JsString` | semantika nizov UTF-16 |
| `JsObject` | osnovni objekt z zemljevidom lastnosti in verigo prototipov |
| `JsArray` | razširja `JsObject` s sledenjem dolžini |
| `JsFunction` | zaprtje z zajetim `Environment` |
| `JsSymbol` | primitivni Symbol |
| `JsBigInt` | poljubna natančnost prek bcmath |
| `JsProxy` | vseh 13 pasti, preklicljiv |
| `JsPromise` | sinhroni izvajalnik + čakalna vrsta mikronalog |
| `JsGenerator` | dvojni način: posnetek okvira (telesa, prevedena v bajtno kodo; glejte [VM za bajtno kodo § Generatorji s posnetkom okvira](https://phasis.inline0.dev/sl/docs/advanced/bytecode-vm)) s povratkom na PHP Fiber za `yield*`, try-with-yield in asinhrone generatorje |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | zbirke |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | binarni podatki |

Obravnava števil je največji zgodovinski vir napak: `NaN !== NaN` mora biti resničen, `-0 === 0` mora biti resničen, robni primeri ToNumber pa zahtevajo natančno obravnavo na ravni bitov. Razred `Spec/TypeConversion` dobesedno implementira algoritme iz specifikacije in je temeljito preizkušen.

## Vgrajena knjižnica

`src/BuiltIn/` zagotavlja vse objekte standardne knjižnice: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console` ter pakete Web Platform / Fetch (`URL`, `Headers`, `Request`, `Response`, `Blob`, `FormData`, polne tokove Streams, `EventTarget`, `AbortController`/`AbortSignal`, `DOMException`, `structuredClone`, `performance`, `TextEncoder`/`TextDecoder`, `atob`/`btoa`), `crypto` + `SubtleCrypto`, `WebSocket`, `XMLHttpRequest` in zanko dogodkov (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Vsaka vgrajena komponenta je statična metoda `install()` razreda. `Engine` jih med ustvarjanjem pokliče v vrstnem redu odvisnosti. Vsaka določi svojo verigo prototipov, namesti metode kot primerke `JsFunction`, ovite okoli zaprtij PHP, in konstruktor razkrije kot globalno vrednost.

## Most PHP↔JS

Pretvorba vrednosti čez mejo poteka na dveh mestih:

- **`Phasis\Interop\PhpToJs::convert()`** — vrednost PHP v `JsValue`; uporablja se za argumente `setGlobal()` in `call()` ter povratne vrednosti gostiteljskih funkcij. Klicna vrednost PHP postane `JsFunction`, katere telo jo pokliče.
- **`PhpToJs::toPhp()`** — `JsValue` v PHP za argumente gostiteljske funkcije.
- **`Engine::toPhp()`** (zasebno) — `JsValue` v PHP za rezultate `eval()` in `call()`.

Mostu za objekte ni: objekti PHP, ki niso klicljivi, se pretvorijo v `undefined` ([težava št. 18](https://github.com/inline0/phasis/issues/18)). Razred `JsToPhp` v `src/Bytecode/` ni povezan z interoperabilnostjo; gre za transpiler, opisan spodaj. Pravila pretvorbe so dokumentirana v [Pretvorba vrednosti](https://phasis.inline0.dev/sl/docs/interop/values).

## Obravnava napak

Neujeta napaka JS `throw` postane PHP `Phasis\Exceptions\JsThrowable`; napake, ki jih sproži sam pogon, se prikažejo kot `Phasis\Exceptions\TypeError`, `RangeError` ali `ReferenceError`, ki so vsi podrazredi `Phasis\Exceptions\RuntimeError`. Znotraj pogona JS `try` / `catch` ujame `JsThrowable`, vse `RuntimeError` in `Phasis\Exceptions\SyntaxError`. Druge izjeme PHP, tudi tiste, ki jih sprožijo gostiteljske funkcije, se ne pretvorijo in se neposredno prenesejo klicatelju PHP ([težava št. 20](https://github.com/inline0/phasis/issues/20)).

Obstajata dve omejitvi: zgornja meja klicnega sklada (`CallStack::maxDepth`, 4,096 okvirjev in dodatna varovalka za 8,192 okvir pri transpajliranih zaprtjih) ter `maxLoopIterations`, nastavljena prek `Engine::setLimit()`, ki jo uveljavlja le drevesni sprehajalnik ([težava št. 19](https://github.com/inline0/phasis/issues/19)). Obe sprožita `Phasis\Exceptions\InternalError`. Omejitev dolžine nizov, velikosti izhoda ali časa izvajanja ni.

## VM za bajtno kodo

Funkcije, ki se pogosto izvajajo, se pretvorijo na dva načina. `JsToPhp` (v `src/Bytecode/`) ustvari zaprtje PHP za telesa z veliko številskimi operacijami ali lokalnimi spremenljivkami, ki jih sledilni JIT v PHP lahko prevede v izvorno kodo; cevovod pretvori branje `new Ctor(args)`, `obj.method(args)` in `this.member`, kar zadošča, da značilni vzorec metode razreda v zanki v celoti obide VM. Vse drugo se prevede v plosko bajtno kodo, ki jo porablja `VM::execute`; uporablja lasten klicni sklad, ki vsak klic JS ohrani znotraj enega samega okvira PHP, ter predpomnilnik za posamezni PC pri branju metod prototipov. Tudi preprosta telesa generatorjev se prevedejo v bajtno kodo; `Op::YIELD` zajame posnetek okvira na kopici namesto prehoda čez PHP Fiber.

Vse oblike, ki jih prevajalnik ne pretvori (e.g. `with`, `try` s ponovno vezanim catch, `yield*`, asinhroni generatorji), se vrnejo k drevesnemu sprehajalniku. Celoten mehanizem je opisan v razdelku [VM za bajtno kodo](https://phasis.inline0.dev/sl/docs/advanced/bytecode-vm).
