
# Arquitectura

Phasis es un recorrido de árbol de cuatro etapas con una máquina virtual de bytecode oportunista. El flujo de datos es:

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

Cada etapa es una clase PHP con una interfaz pequeña que consume la etapa siguiente. No hay paso de enlazado: el análisis sintáctico produce un AST que el intérprete recorre directamente.

## Analizador léxico

`src/Lexer/Lexer.php` convierte el texto fuente en un flujo de instancias `Token`. Cada token es una estructura de solo lectura: tipo (enumeración `TokenType`, ~80 casos), valor, línea y columna.

El analizador léxico procesa:

- Literales numéricos: decimales, hexadecimales (`0x`), octales (`0o`), binarios (`0b`), exponenciales y separadores numéricos.
- Literales de cadena: comillas simples y dobles, todas las secuencias de escape y literales de plantilla con expresiones anidadas.
- Literales de expresiones regulares: tokenización `/pattern/flags` completa, que detecta cuándo `/` debe interpretarse como expresión regular o como división.
- Identificadores, palabras clave y signos de puntuación.
- Comentarios de línea y de bloque, preservados como elementos triviales.
- Fuente Unicode: bytes UTF-8 interpretados como fuente UTF-16 JS.

El resultado es un generador que produce tokens de forma diferida. El analizador sintáctico los consume con un token de anticipación.

## Analizador

`src/Parser/Parser.php` es un analizador sintáctico Pratt escrito a mano. La precedencia de las expresiones está en `src/Parser/Precedence.php`. Las instrucciones tienen su propio análisis descendente recursivo.

Produce nodos AST inmutables a partir de `src/Ast/`. Cada nodo contiene su ubicación en el código fuente para los seguimientos de pila y los informes de errores.

Estructuras destacadas:

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

La inserción automática de punto y coma se implementa en el analizador sintáctico (no en el léxico), según la especificación.

## Intérprete

`src/Runtime/Interpreter.php` recorre el AST. Visita cada nodo y devuelve un `JsValue` (o un `Completion` para las instrucciones que cambian el flujo de control).

Clases de apoyo principales:

- **`Environment`** (`src/Runtime/Environment.php`): cadena de ámbitos léxicos. Cada llamada a función crea un entorno que apunta a su padre léxico. `var` se eleva al ámbito de la función; `let` / `const` tienen ámbito de bloque y una zona muerta temporal (TDZ).
- **`CallStack`** (`src/Runtime/CallStack.php`): pila explícita con límite de profundidad. Las llamadas a funciones y métodos apilan marcos; las excepciones los desenrollan.
- **`Completion`** (`src/Runtime/Completion.php`): unión discriminada de `normal`, `return`, `throw`, `break`, `continue`, más un valor y una etiqueta opcionales.
- **`Reference`** (`src/Runtime/Reference.php`): tipo Reference de la especificación para evaluar el lado izquierdo en asignaciones y `delete`.

## Valores

Los valores JavaScript son objetos PHP bajo `src/Value/`. Cada uno implementa la interfaz `JsValue` y contiene sus propios métodos de conversión de tipo (`toNumber()`, `toString()`, `toBoolean()`).

| Clase | Tipo JS |
|---|---|
| `JsUndefined` | `undefined` (singleton) |
| `JsNull` | `null` (singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | double IEEE 754; conserva NaN, ±Infinity, ±0 |
| `JsString` | semántica de cadenas UTF-16 |
| `JsObject` | objeto base con mapa de propiedades y cadena de prototipos |
| `JsArray` | amplía `JsObject` con seguimiento de longitud |
| `JsFunction` | cierre con `Environment` capturado |
| `JsSymbol` | primitivo Symbol |
| `JsBigInt` | precisión arbitraria mediante bcmath |
| `JsProxy` | los 13 interceptores, revocables |
| `JsPromise` | ejecutor síncrono + cola de microtareas |
| `JsGenerator` | modo dual: instantánea de marco (cuerpos compilados a bytecode; véase [Bytecode VM § Generadores con instantánea de marco](https://phasis.inline0.dev/es/docs/advanced/bytecode-vm)) con alternativa de Fiber PHP para `yield*`, try-with-yield y generadores asíncronos |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | colecciones |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | datos binarios |

El manejo de números es la mayor fuente histórica de errores: `NaN !== NaN` debe ser verdadero, `-0 === 0` debe ser verdadero, y los casos límite de ToNumber requieren precisión a nivel de bits. La clase `Spec/TypeConversion` implementa literalmente los algoritmos de la especificación y se ha probado exhaustivamente.

## Biblioteca integrada

`src/BuiltIn/` proporciona todos los objetos de la biblioteca estándar: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console`, además de los paquetes 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` y el bucle de eventos (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Cada función integrada es un método estático `install()` de una clase. `Engine` las invoca en orden de dependencias durante la construcción. Cada una define su cadena de prototipos, instala métodos como instancias `JsFunction` envueltas en cierres PHP y expone el constructor como global.

## Puente PHP↔JS

La conversión de valores entre ambos lados se realiza en dos lugares:

- **`Phasis\Interop\PhpToJs::convert()`**: valor PHP a `JsValue`, usado por `setGlobal()`, los argumentos de `call()` y los valores devueltos por funciones del host. Una función invocable PHP se convierte en un `JsFunction` cuyo cuerpo la llama.
- **`PhpToJs::toPhp()`**: `JsValue` a PHP para los argumentos de una función del host.
- **`Engine::toPhp()`** (privado): `JsValue` a PHP para los resultados de `eval()` y `call()`.

No hay puente para objetos: los objetos PHP que no son invocables se convierten en `undefined` ([issue #18](https://github.com/inline0/phasis/issues/18)). La clase `JsToPhp` de `src/Bytecode/` no está relacionada con la interoperabilidad; es el transpilador descrito más abajo. Las reglas de conversión se documentan en [Conversión de valores](https://phasis.inline0.dev/es/docs/interop/values).

## Gestión de errores

Un JS `throw` no capturado se convierte en un PHP `Phasis\Exceptions\JsThrowable`; los errores que genera el propio motor se presentan como `Phasis\Exceptions\TypeError`, `RangeError` o `ReferenceError`, todos subclases de `Phasis\Exceptions\RuntimeError`. Dentro del motor, un JS `try` / `catch` captura `JsThrowable`, cualquier `RuntimeError` y `Phasis\Exceptions\SyntaxError`. Las demás excepciones PHP, incluidas las generadas por funciones del host, no se convierten y se propagan directamente al llamador PHP ([issue #20](https://github.com/inline0/phasis/issues/20)).

Hay dos límites: el máximo de la pila de llamadas (`CallStack::maxDepth`, 4,096 marcos, más un límite de 8,192 marcos para cierres transpilados) y `maxLoopIterations`, que se configura mediante `Engine::setLimit()` y solo aplica el recorrido del árbol ([issue #19](https://github.com/inline0/phasis/issues/19)). Ambos generan `Phasis\Exceptions\InternalError`. No hay límites de longitud de cadena, tamaño de salida ni tiempo de ejecución.

## Máquina virtual de bytecode

Las funciones de uso intensivo se compilan de dos maneras. `JsToPhp` (en `src/Bytecode/`) genera un cierre PHP para cuerpos con muchas operaciones numéricas o variables locales que el JIT de trazado de PHP puede compilar a código nativo; la canalización compila lecturas de `new Ctor(args)`, `obj.method(args)` y `this.member`, lo que basta para que el patrón canónico de método de clase dentro de un bucle evite por completo la máquina virtual. Todo lo demás se compila a bytecode plano que consume `VM::execute`, con una pila de llamadas personalizada que mantiene cada llamada JS dentro de un único marco PHP y una caché en línea por PC para las lecturas de métodos de prototipo. Los cuerpos sencillos de generador también se compilan a bytecode, y `Op::YIELD` captura una instantánea del marco en el montón en lugar de pasar por Fiber PHP.

Cualquier estructura que el compilador no pueda compilar (e.g. `with`, `try` con un catch reasignado, `yield*`, generadores asíncronos) vuelve al recorrido del árbol. Consulte [Máquina virtual de bytecode](https://phasis.inline0.dev/es/docs/advanced/bytecode-vm) para conocer el mecanismo completo.
