
# Arquitetura

Phasis é um interpretador de árvore em quatro etapas com uma VM de bytecode oportunista. O fluxo de dados é:

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

Cada etapa é uma classe PHP com uma interface pequena, consumida pela etapa seguinte. Não há etapa de vinculação: a análise produz uma AST e o interpretador percorre-a diretamente.

## Analisador léxico

`src/Lexer/Lexer.php` converte o texto-fonte em um fluxo de instâncias de `Token`. Cada token é uma estrutura somente para leitura: tipo (enum `TokenType`, cerca de 80 casos), valor, linha e coluna.

O analisador léxico trata de:

- Literais numéricos — decimais, hexadecimais (`0x`), octais (`0o`), binários (`0b`), exponenciais e separadores numéricos.
- Literais de string — aspas simples e duplas, todas as sequências de escape e literais de modelo com expressões aninhadas.
- Literais de expressões regulares — tokenização completa de `/pattern/flags`, considerando quando uma `/` deve ser interpretada como expressão regular ou divisão.
- Identificadores, palavras-chave e pontuação.
- Comentários de linha e de bloco, preservados como dados auxiliares.
- Código-fonte Unicode — bytes UTF-8 interpretados como o código-fonte UTF-16 de JS.

A saída é um gerador que produz tokens sob demanda. O analisador sintático os consome com uma antecipação de um token.

## Analisador

`src/Parser/Parser.php` é um analisador Pratt escrito à mão. A precedência das expressões fica em `src/Parser/Precedence.php`. As instruções usam sua própria análise descendente recursiva.

Ele produz nós AST imutáveis a partir de `src/Ast/`. Cada nó contém sua localização no código-fonte para rastreamentos de pilha e relatórios de erros.

Formas notáveis:

- **Expressão**: `Literal`, `Identifier`, `BinaryExpression`, `UnaryExpression`, `AssignmentExpression`, `CallExpression`, `MemberExpression`, `ArrowFunction`, `ObjectExpression`, `ArrayExpression`, `ConditionalExpression`, `TemplateLiteral`, `SpreadElement`, `NewExpression`, `ThisExpression`, `SequenceExpression`, `TaggedTemplate`, `ClassExpression`, `YieldExpression`, `AwaitExpression`.
- **Instrução**: `BlockStatement`, `IfStatement`, `ForStatement`, `ForInStatement`, `ForOfStatement`, `WhileStatement`, `DoWhileStatement`, `SwitchStatement`, `ReturnStatement`, `ThrowStatement`, `TryStatement`, `BreakStatement`, `ContinueStatement`, `ExpressionStatement`.
- **Declaração**: `VariableDeclaration`, `FunctionDeclaration`, `ClassDeclaration`, `ImportDeclaration`.
- **Padrão** (desestruturação): `ArrayPattern`, `ObjectPattern`, `RestElement`, `AssignmentPattern`.

A inserção automática de ponto e vírgula é implementada no analisador sintático (não no léxico), conforme a especificação.

## Interpretador

`src/Runtime/Interpreter.php` percorre a AST. Ele visita cada nó e retorna um `JsValue` (ou um `Completion` para instruções que alteram o fluxo de controle).

Principais classes auxiliares:

- **`Environment`** (`src/Runtime/Environment.php`) — cadeia de escopos léxicos. Cada chamada de função cria um novo ambiente ligado ao pai léxico. `var` é içado para o escopo da função; `let` / `const` têm escopo de bloco e uma zona temporal morta (TDZ).
- **`CallStack`** (`src/Runtime/CallStack.php`) — pilha explícita com limite de profundidade. Chamadas de funções e métodos adicionam quadros; exceções os desempilham.
- **`Completion`** (`src/Runtime/Completion.php`) — união discriminada de `normal`, `return`, `throw`, `break`, `continue`, mais um valor e um rótulo opcionais.
- **`Reference`** (`src/Runtime/Reference.php`) — tipo Reference da especificação para avaliar o lado esquerdo em atribuições e `delete`.

## Valores

Os valores JavaScript são objetos PHP sob `src/Value/`. Cada um implementa a interface `JsValue` e tem seus próprios métodos de conversão de tipo (`toNumber()`, `toString()`, `toBoolean()`).

| Classe | Tipo JS |
|---|---|
| `JsUndefined` | `undefined` (singleton) |
| `JsNull` | `null` (singleton) |
| `JsBoolean` | `true` / `false` |
| `JsNumber` | double IEEE 754; preserva NaN, ±Infinity, ±0 |
| `JsString` | semântica de strings UTF-16 |
| `JsObject` | objeto base com mapa de propriedades e cadeia de protótipos |
| `JsArray` | estende `JsObject` com controle de comprimento |
| `JsFunction` | fechamento com `Environment` capturado |
| `JsSymbol` | primitivo Symbol |
| `JsBigInt` | precisão arbitrária por meio de bcmath |
| `JsProxy` | todas as 13 interceptações, revogável |
| `JsPromise` | executor síncrono + fila de microtarefas |
| `JsGenerator` | modo duplo: instantâneo do quadro (corpos compilados em bytecode; consulte [VM de bytecode § Geradores com instantâneo do quadro](https://phasis.inline0.dev/pt/docs/advanced/bytecode-vm)), com alternativa de Fiber PHP para `yield*`, try-with-yield e geradores assíncronos |
| `JsMap`, `JsSet`, `JsWeakMap`, `JsWeakSet`, `JsWeakRef`, `JsFinalizationRegistry` | coleções |
| `JsArrayBuffer`, `JsSharedArrayBuffer`, `JsDataView`, `JsTypedArray` | dados binários |

O tratamento de números é, historicamente, a maior fonte de bugs: `NaN !== NaN` precisa ser verdadeiro, `-0 === 0` precisa ser verdadeiro, e os casos extremos de ToNumber exigem precisão exata no nível dos bits. A classe `Spec/TypeConversion` implementa os algoritmos da especificação literalmente e foi testada exaustivamente.

## Biblioteca integrada

`src/BuiltIn/` fornece todos os objetos da biblioteca padrão: `Array`, `String`, `Object`, `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `Promise`, `Proxy`, `Reflect`, `Symbol`, `BigInt`, `TypedArray`, `Temporal`, `Intl`, `console`, além dos pacotes 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 o loop de eventos (`setTimeout`, `setInterval`, `queueMicrotask`, `AsyncContext`).

Cada recurso integrado é um método estático `install()` de uma classe. `Engine` os chama em ordem de dependência durante a construção. Cada um define sua cadeia de protótipos, instala métodos como instâncias de `JsFunction` envolvidas por closures PHP e expõe o construtor como global.

## Ponte PHP↔JS

A conversão de valores entre os dois lados acontece em dois pontos:

- **`Phasis\Interop\PhpToJs::convert()`** — valor PHP para `JsValue`, usado por `setGlobal()`, argumentos de `call()` e valores retornados por funções do host. Uma função chamável PHP se torna um `JsFunction` cujo corpo a chama.
- **`PhpToJs::toPhp()`** — de `JsValue` para PHP nos argumentos de uma função do host.
- **`Engine::toPhp()`** (privado) — de `JsValue` para PHP nos resultados de `eval()` e `call()`.

Não há ponte para objetos: objetos PHP que não são chamáveis são convertidos em `undefined` ([problema #18](https://github.com/inline0/phasis/issues/18)). A classe `JsToPhp` em `src/Bytecode/` não tem relação com interoperabilidade; ela é o transpiler descrito abaixo. As regras de conversão estão documentadas em [Conversão de valores](https://phasis.inline0.dev/pt/docs/interop/values).

## Tratamento de erros

Um JS `throw` não capturado se torna um PHP `Phasis\Exceptions\JsThrowable`; erros gerados pelo próprio mecanismo aparecem como `Phasis\Exceptions\TypeError`, `RangeError` ou `ReferenceError`, todos subclasses de `Phasis\Exceptions\RuntimeError`. Dentro do mecanismo, um JS `try` / `catch` captura `JsThrowable`, qualquer `RuntimeError` e `Phasis\Exceptions\SyntaxError`. Outras exceções PHP, incluindo as lançadas por funções do host, não são convertidas e se propagam diretamente ao chamador PHP ([problema #20](https://github.com/inline0/phasis/issues/20)).

Há dois limites: o teto da pilha de chamadas (`CallStack::maxDepth`, 4,096 quadros, mais uma proteção de 8,192 quadros para closures transpiladas) e `maxLoopIterations`, definido por `Engine::setLimit()`, aplicado apenas pelo interpretador de árvore ([problema #19](https://github.com/inline0/phasis/issues/19)). Ambos lançam `Phasis\Exceptions\InternalError`. Não há limites para o comprimento das strings, o tamanho da saída ou o tempo de execução.

## VM de bytecode

Funções muito usadas são compiladas de duas formas. `JsToPhp` (em `src/Bytecode/`) gera uma closure PHP para corpos com muitos cálculos numéricos ou uso intenso de variáveis locais, que o JIT de rastreamento de PHP pode compilar para código nativo. O pipeline compila leituras de `new Ctor(args)`, `obj.method(args)` e `this.member`, o que basta para que o padrão canônico de método de classe dentro de um loop ignore completamente a VM. Todo o restante é compilado para bytecode linear, consumido por `VM::execute`, com uma pilha de chamadas personalizada que mantém cada chamada JS dentro de um único quadro PHP e um cache inline por PC para leituras de métodos de protótipo. Corpos simples de geradores também são compilados para bytecode, com `Op::YIELD` capturando um instantâneo do quadro na heap em vez de atravessar a Fiber PHP.

Qualquer estrutura que o compilador não compile (e.g. `with`, `try` com um catch que redefine a ligação, `yield*`, geradores assíncronos) recorre ao interpretador de árvore. Consulte [VM de bytecode](https://phasis.inline0.dev/pt/docs/advanced/bytecode-vm) para ver o mecanismo completo.
