
# Architettura

Phasis è un tree walker a quattro fasi con una VM bytecode opportunistica. Il flusso dei dati è:

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

Ogni fase è una classe PHP con un'interfaccia ridotta, utilizzata dalla fase successiva. Non esiste una fase di collegamento: il parsing produce un AST che l'interprete percorre direttamente.

## Lexer

`src/Lexer/Lexer.php` converte il testo sorgente in una sequenza di istanze `Token`. Ogni token è una struttura di sola lettura: tipo (enumerazione `TokenType`, circa 80 casi), valore, riga e colonna.

Il lexer gestisce:

- Letterali numerici: decimali, esadecimali (`0x`), ottali (`0o`), binari (`0b`), esponenziali e separatori numerici.
- Letterali stringa: apici singoli e doppi, tutte le sequenze di escape e template literal con espressioni annidate.
- Letterali di espressioni regolari: tokenizzazione completa `/pattern/flags`, distinguendo quando un `/` deve essere interpretato come espressione regolare o divisione.
- Identificatori, parole chiave e punteggiatura.
- Commenti su riga e a blocco, conservati come trivia.
- Sorgente Unicode: byte UTF-8 interpretati come sorgente UTF-16 JS.

L'output è un generatore che produce token in modo differito. Il parser li consuma con un token di lookahead.

## Parser

`src/Parser/Parser.php` è un parser Pratt scritto a mano. La precedenza delle espressioni è definita in `src/Parser/Precedence.php`. Le istruzioni usano una propria discesa ricorsiva.

Produce nodi AST immutabili da `src/Ast/`. Ogni nodo include la posizione nel sorgente per le stack trace e la segnalazione degli errori.

Strutture degne di nota:

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

L'inserimento automatico del punto e virgola è implementato nel parser, non nel lexer, come previsto dalle specifiche.

## Interprete

`src/Runtime/Interpreter.php` percorre l'AST. Visita ogni nodo e restituisce un `JsValue` (oppure un `Completion` per le istruzioni che modificano il flusso di controllo).

Classi di supporto principali:

- **`Environment`** (`src/Runtime/Environment.php`) — catena degli scope lessicali. Ogni chiamata di funzione crea un nuovo ambiente che punta al genitore lessicale. `var` viene sollevato allo scope della funzione; `let` / `const` hanno scope di blocco e una TDZ.
- **`CallStack`** (`src/Runtime/CallStack.php`) — stack esplicito con limite di profondità. Le chiamate a funzioni e metodi aggiungono frame; le eccezioni li rimuovono.
- **`Completion`** (`src/Runtime/Completion.php`) — unione discriminata di `normal`, `return`, `throw`, `break`, `continue`, più un valore e un'etichetta facoltativi.
- **`Reference`** (`src/Runtime/Reference.php`) — tipo Reference della specifica per la valutazione del lato sinistro nelle assegnazioni e in `delete`.

## Valori

I valori JavaScript sono oggetti PHP sotto `src/Value/`. Ognuno implementa l'interfaccia `JsValue` e dispone dei propri metodi di conversione del tipo (`toNumber()`, `toString()`, `toBoolean()`).

| Classe | tipo JS |
|---|---|
| `JsUndefined` | `undefined` (singleton) |
| `JsNull` | `null` (singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | double IEEE 754; conserva NaN, ±Infinity, ±0 |
| `JsString` | semantica delle stringhe UTF-16 |
| `JsObject` | oggetto base con mappa delle proprietà e catena dei prototipi |
| `JsArray` | estende `JsObject` con il tracciamento della lunghezza |
| `JsFunction` | closure con `Environment` acquisito |
| `JsSymbol` | primitivo Symbol |
| `JsBigInt` | precisione arbitraria tramite bcmath |
| `JsProxy` | tutti i 13 trap, revocabile |
| `JsPromise` | esecutore sincrono + coda di microtask |
| `JsGenerator` | doppia modalità: snapshot del frame (corpi compilati in bytecode; vedi [Generatori con snapshot del frame in Bytecode VM](https://phasis.inline0.dev/it/docs/advanced/bytecode-vm)) con fallback a PHP Fiber per `yield*`, try-with-yield e generatori asincroni |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | raccolte |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | dati binari |

La gestione dei numeri è la principale fonte storica di bug: `NaN !== NaN` deve essere true, `-0 === 0` deve essere true e i casi limite di ToNumber richiedono un'attenzione precisa a livello di bit. La classe `Spec/TypeConversion` implementa alla lettera gli algoritmi della specifica ed è sottoposta a test esaustivi.

## Libreria integrata

`src/BuiltIn/` fornisce tutti gli oggetti della libreria standard: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console`, oltre ai pacchetti Web Platform / Fetch (`URL`, `Headers`, `Request`, `Response`, `Blob`, `FormData`, Streams completo, `EventTarget`, `AbortController`/`AbortSignal`, `DOMException`, `structuredClone`, `performance`, `TextEncoder`/`TextDecoder`, `atob`/`btoa`), `crypto` + `SubtleCrypto`, `WebSocket`, `XMLHttpRequest` e il ciclo di eventi (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Ogni elemento integrato è un metodo statico `install()` di una classe. `Engine` li chiama in ordine di dipendenza durante la costruzione. Ognuno definisce la propria catena di prototipi, installa i metodi come istanze `JsFunction` racchiuse in closure PHP ed espone il costruttore come globale.

## Bridge PHP↔JS

La conversione dei valori attraverso il confine avviene in due punti:

- **`Phasis\Interop\PhpToJs::convert()`** — valore PHP in `JsValue`, usato per gli argomenti di `setGlobal()`, `call()` e i valori restituiti dalle funzioni host. Un callable PHP diventa un `JsFunction` il cui corpo lo chiama.
- **`PhpToJs::toPhp()`** — `JsValue` in PHP per gli argomenti di una funzione host.
- **`Engine::toPhp()`** (privato) — `JsValue` in PHP per i risultati di `eval()` e `call()`.

Non esiste un bridge per gli oggetti: gli oggetti PHP che non sono callable vengono convertiti in `undefined` ([problema #18](https://github.com/inline0/phasis/issues/18)). La classe `JsToPhp` in `src/Bytecode/` non è correlata all'interoperabilità: è il transpiler descritto di seguito. Le regole di conversione sono documentate in [Conversione dei valori](https://phasis.inline0.dev/it/docs/interop/values).

## Gestione degli errori

Un JS `throw` non intercettato diventa un PHP `Phasis\Exceptions\JsThrowable`; gli errori generati direttamente dal motore vengono segnalati come `Phasis\Exceptions\TypeError`, `RangeError` o `ReferenceError`, tutte sottoclassi di `Phasis\Exceptions\RuntimeError`. All'interno del motore, un JS `try` / `catch` intercetta `JsThrowable`, qualsiasi `RuntimeError` e `Phasis\Exceptions\SyntaxError`. Le altre eccezioni PHP, incluse quelle generate dalle funzioni host, non vengono convertite e si propagano direttamente al chiamante PHP ([problema #20](https://github.com/inline0/phasis/issues/20)).

Esistono due limiti: il tetto dello stack delle chiamate (`CallStack::maxDepth`, 4,096 frame, più una protezione di 8,192 frame per le closure transpiled) e `maxLoopIterations`, impostato tramite `Engine::setLimit()` e applicato solo dal tree-walker ([problema #19](https://github.com/inline0/phasis/issues/19)). Entrambi generano `Phasis\Exceptions\InternalError`. Non ci sono limiti alla lunghezza delle stringhe, alle dimensioni dell'output o al tempo di esecuzione.

## VM bytecode

Le funzioni più usate vengono trasformate in due modi. `JsToPhp` (in `src/Bytecode/`) genera una closure PHP per i corpi con molte operazioni numeriche o variabili locali, che il JIT di tracciamento di PHP può compilare in codice nativo; la pipeline trasforma le letture di `new Ctor(args)`, `obj.method(args)` e `this.member`, quanto basta per consentire al modello canonico di un metodo di classe in un ciclo di bypassare completamente la VM. Tutto il resto viene compilato in bytecode lineare, eseguito da `VM::execute` con uno stack di chiamate personalizzato che mantiene ogni chiamata JS all'interno di un unico frame PHP e una cache inline per PC per le letture di metodi dei prototipi. Anche i corpi semplici dei generatori vengono compilati in bytecode: `Op::YIELD` acquisisce uno snapshot del frame heap invece di passare attraverso PHP Fiber.

Qualsiasi struttura che il compilatore non trasforma (e.g. `with`, `try` con un catch che ridefinisce la variabile, `yield*`, generatori asincroni) ricade nel tree-walker. Per il meccanismo completo, vedi [VM bytecode](https://phasis.inline0.dev/it/docs/advanced/bytecode-vm).
