
# Парсер

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` | класичний 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 fixture у `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` розбирає файл (у режимі script) і виводить дамп `Serializer` JSON з відступом у два пробіли. Повний опис прапорців див. у [CLIдовіднику](https://phasis.inline0.dev/uk/docs/cli).
