
# Парсер

Phasis предоставляет свой парсер JavaScript как отдельный PHP API. Вы можете разобрать исходный текст в типизированный AST, обойти его и экспортировать как массивы в формате ESTree или JSON, не создавая `Engine` и ничего не выполняя.

## parseSource

```php
public static function parseSource(string $source, string $sourceType = 'script'): Program
```

Единая точка входа для вызова `Phasis\Parser\Parser`. Разбирает `$source` и возвращает корневой узел `Phasis\Ast\Program`.

```php
use Phasis\Parser\Parser;

$program = Parser::parseSource('const x = [1, 2].map(n => n * 2);');
$module = Parser::parseSource('import { x } from "./x.js"; export default x;', 'module');
```

`$sourceType` выбирает цель грамматики, названную так же, как соответствующая опция в парсерах JS:

| Значение | Цель |
|---|---|
| `script` | классический скрипт. Верхнеуровневые `import`/`export` вызывают синтаксическую ошибку, `await` — идентификатор |
| `module` | модуль ES. Неявно включён строгий режим; разбираются `import`/`export` и верхнеуровневый `await` |

Любое другое значение вызывает `\InvalidArgumentException`.

Для более точного управления (например, предварительного включения строгого режима для контекста прямого eval и чтения директивы `//# sourceURL`) создайте парсер напрямую: `new Parser($source)` и `setModuleMode()`, `setStrictMode()` и `parse()`.

## Узлы

Каждый класс AST наследуется от `Phasis\Ast\Node` и находится в пространстве имён `Phasis\Ast\{Declaration, Expression, Pattern, Statement}`. Узлы — обычные объекты значений только для чтения:

- `$node->type()` возвращает строку типа узла — базовое имя класса (`'BinaryExpression'`, `'ArrowFunction'`, ...).
- `$node->location` — это `Phasis\Lexer\SourceLocation` с `line` (с нумерацией от 1), `column` (с нумерацией от 0) и `offset` (смещение в байтах). Сохраняются только начальные позиции; у узлов нет конечного смещения.
- Все остальные открытые поля — дочерние элементы и атрибуты узла (`$binary->left`, `$binary->operator`, `$binary->right`).

Большинство имён типов совпадает с [ESTree](https://github.com/estree/estree). Отличия:

| Тип Phasis | Тип ESTree |
|---|---|
| `ArrowFunction` | `ArrowFunctionExpression` |
| `TaggedTemplate` | `TaggedTemplateExpression` |
| `ClassMethod` | `MethodDefinition` |
| `ClassProperty` | `PropertyDefinition` |
| `AssignmentProperty` | `Property` (внутри `ObjectPattern`) |
| `ExportDeclaration` | `ExportNamedDeclaration` / `ExportDefaultDeclaration` / `ExportAllDeclaration` |
| `ImportSpecifier` | `ImportSpecifier` / `ImportDefaultSpecifier` / `ImportNamespaceSpecifier` |

Некоторые формы тоже отличаются: тела классов представлены в узле класса обычным массивом элементов (без обёртки `ClassBody`); имена импортов и экспортов, метки операторов и имена `FunctionExpression` хранятся в виде строк, а не вложенных узлов `Identifier`; кроме того, `super` разбирается как именованный `Identifier` `super` (класса узла `Super` нет). `Phasis\Ast\EstreeSerializer` преобразует всё это в формат ESTree.

## Синтаксические ошибки

При неверном вводе выбрасывается `Phasis\Exceptions\SyntaxError` (обычно его подкласс `Phasis\Parser\ParseError`). Сообщение об ошибке заканчивается позицией, а структурированное расположение доступно в самом исключении:

```php
use Phasis\Exceptions\SyntaxError;
use Phasis\Parser\Parser;

try {
    Parser::parseSource("const a = 1;\nconst b = ;");
} catch (SyntaxError $e) {
    echo $e->getMessage();          // Unexpected token (got ; ";") at line 2, column 10
    echo $e->location->line;        // 2
    echo $e->location->column;      // 10
    echo $e->location->offset;      // 23
}
```

## Обходчик

`Phasis\Ast\Walker` обходит дерево в глубину, вызывая обратные вызовы при входе в узел и выходе из него, как в estraverse:

```php
public static function walk(Node $node, callable $enter, ?callable $leave = null): void
```

Оба обратных вызова получают `(Node $node, ?Node $parent)`. Возврат `Walker::SKIP` из `$enter` исключает дочерние элементы узла из обхода; сам узел всё равно вызывает `$leave`.

```php
use Phasis\Ast\Expression\Identifier;
use Phasis\Ast\Node;
use Phasis\Ast\Walker;

$names = [];
Walker::walk($program, function (Node $node) use (&$names) {
    if ($node instanceof Identifier) {
        $names[] = $node->name;
    }
    if ($node instanceof \Phasis\Ast\Expression\ArrowFunction) {
        return Walker::SKIP;
    }
});
```

Обход выполняется только для чтения. Узлы доступны только для чтения, поэтому заменить или изменить их нельзя.

## Сериализатор

`Phasis\Ast\Serializer` преобразует типизированное AST в массивы или JSON, используя имена типов и структуру полей Phasis; поля кэша парсера удаляются:

```php
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
```

Это формат, который выводит `bin/phasis --ast`.

## EstreeSerializer

`Phasis\Ast\EstreeSerializer` преобразует типизированное AST в массивы в форме ESTree: имена типов ESTree (согласно таблице отклонений выше), имена и порядок полей ESTree, синтезированные узлы `ClassBody` / specifier / source-`Literal` и смещение в байтах `start` для каждого узла, разобранного из исходного кода. Смещений `end` нет, поскольку узлы хранят только начальные позиции, а строковые узлы `Literal` не содержат текста `raw`.

```php
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
public static function summarize(Node $node): string
```

`summarize()` выводит детерминированную сводку: по одной строке на узел (тип и атрибуты `name=` / `value=` / `op=` / `kind=`, отступ в два пробела на уровень вложенности). Формат побайтно совпадает со сводкой AST, которую создаёт тестовый пример acorn в `tests/Popular/acorn`, поэтому один и тот же эталон проверяет оба пути парсера JS и этот экспорт PHP:

```php
use Phasis\Ast\EstreeSerializer;
use Phasis\Parser\Parser;

echo EstreeSerializer::summarize(Parser::parseSource('let x = a + 1;'));
// Program
//   VariableDeclaration kind=let
//     VariableDeclarator
//       Identifier name=x
//       BinaryExpression op=+
//         Identifier name=a
//         Literal value=1
```

## CLI

`bin/phasis --ast script.js` разбирает файл (цель — скрипт) и выводит дамп `Serializer` JSON с отступом в два пробела. Полный список флагов см. в [справочнике по флагамCLI](https://phasis.inline0.dev/ru/docs/cli).
