
# Razčlenjevalnik

Phasis izpostavlja svoj razčlenjevalnik JavaScript kot samostojen PHP API. Izvorno besedilo lahko razčlenite v tipiziran AST, ga prehodite in izvozite kot polja v obliki ESTree ali JSON, ne da bi ustvarili `Engine` ali karkoli izvedli.

## parseSource

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

Enoklicna vstopna točka za `Phasis\Parser\Parser`. Razčleni `$source` in vrne korensko vozlišče `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` izbere slovnični cilj, poimenovan enako kot možnost v razčlenjevalnikih JS:

| Vrednost | Cilj |
|---|---|
| `script` | klasični Script. Vrhunska `import`/`export` sta skladenjski napaki, `await` pa je identifikator |
| `module` | modul ES. Implicitno strog način; `import`/`export` in vrhnji `await` se razčlenijo |

Katera koli druga vrednost sproži `\InvalidArgumentException`.

Za podrobnejši nadzor (vnaprejšnja nastavitev strogega načina za kontekst neposrednega eval, branje pragma `//# sourceURL`) ustvarite razčlenjevalnik neposredno: `new Parser($source)` ter `setModuleMode()`, `setStrictMode()` in `parse()`.

## Vozlišča

Vsak razred AST razširja `Phasis\Ast\Node` in je v prostoru imen `Phasis\Ast\{Declaration, Expression, Pattern, Statement}`. Vozlišča so navadni objekti vrednosti samo za branje:

- `$node->type()` vrne niz tipa vozlišča, ki je osnovno ime razreda (`'BinaryExpression'`, `'ArrowFunction'`, ...).
- `$node->location` je `Phasis\Lexer\SourceLocation` z `line` (od 1), `column` (od 0) in `offset` (odmik v bajtih). Shranjeni so samo začetni položaji; vozlišča nimajo končnega odmika.
- Vsa druga javna polja so otroci in atributi vozlišča (`$binary->left`, `$binary->operator`, `$binary->right`).

Večina imen tipov se ujema s [ESTree](https://github.com/estree/estree). Odstopanja:

| Tip Phasis | Tip ESTree |
|---|---|
| `ArrowFunction` | `ArrowFunctionExpression` |
| `TaggedTemplate` | `TaggedTemplateExpression` |
| `ClassMethod` | `MethodDefinition` |
| `ClassProperty` | `PropertyDefinition` |
| `AssignmentProperty` | `Property` (znotraj `ObjectPattern`) |
| `ExportDeclaration` | `ExportNamedDeclaration` / `ExportDefaultDeclaration` / `ExportAllDeclaration` |
| `ImportSpecifier` | `ImportSpecifier` / `ImportDefaultSpecifier` / `ImportNamespaceSpecifier` |

Nekaj oblik se razlikuje: telesa razredov so navadno polje elementov na vozlišču razreda (brez ovoja `ClassBody`), imena uvoza in izvoza, oznake stavkov ter imena `FunctionExpression` so shranjena kot nizi namesto kot ugnezdena vozlišča `Identifier`, `super` pa se razčleni kot `Identifier` z imenom `super` (razred vozlišča `Super` ne obstaja). `Phasis\Ast\EstreeSerializer` vse to preoblikuje v obliko ESTree.

## Skladenjske napake

Neveljaven vhod sproži `Phasis\Exceptions\SyntaxError` (običajno njegov podrazred `Phasis\Parser\ParseError`). Sporočilo izjeme se konča s položajem, strukturirana lokacija pa je na voljo v izjema:

```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
}
```

## Sprehod po drevesu

`Phasis\Ast\Walker` prehodi drevo po globini s povratnimi klici ob vstopu in izstopu, podobno kot estraverse:

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

Oba povratna klica prejmeta `(Node $node, ?Node $parent)`. Če `$enter` vrne `Walker::SKIP`, se pregled podrejenih vozlišč preskoči; sproži pa se `$leave` za samo vozlišče.

```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;
    }
});
```

Prehod je samo za branje. Vozlišča so nespremenljivi objekti vrednosti, zato zamenjava ali spreminjanje nista podprta.

## Serializator

`Phasis\Ast\Serializer` pretvori tipizirani AST v polja ali JSON z uporabo imen tipov in postavitve polj Phasis, pri čemer odstrani notranja polja predpomnilnika razčlenjevalnika:

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

To je oblika, ki jo izpiše `bin/phasis --ast`.

## EstreeSerializer

`Phasis\Ast\EstreeSerializer` namesto tega pretvori tipizirani AST v polja v obliki ESTree: imena tipov ESTree (glede na zgornjo tabelo odstopanj), imena in vrstni red polj ESTree, ustvarjena vozlišča `ClassBody` / specifier / source-`Literal` ter odmik v bajtih `start` za vsako vozlišče, razčlenjeno iz izvorne kode. Odmikov `end` ni, ker vozlišča shranjujejo samo začetne položaje; vozlišča nizov `Literal` pa ne vsebujejo besedila `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()` izpiše determinističen povzetek z eno vrstico na vozlišče (tip ter atributi `name=` / `value=` / `op=` / `kind=`, zamik dveh presledkov na raven globine). Oblika je bajtno enaka povzetku AST, ki ga ustvari preizkusni primer acorn v `tests/Popular/acorn`, zato isti referenčni sistem preverja tako pot razčlenjevalnika JS kot ta izvoz 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` razčleni datoteko (cilj Script) in izpiše izpis `Serializer` JSON z zamikom dveh presledkov. Za celoten opis zastavic glejte [CLI](https://phasis.inline0.dev/sl/docs/cli).
