
# Parser

Phasis biedt zijn parser van JavaScript aan als zelfstandige PHP API. Je kunt brontekst parsen naar een getypeerde AST, deze doorlopen en exporteren als arrays in ESTree-vorm of JSON, zonder een `Engine` te maken of iets uit te voeren.

## parseSource

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

Het eenmalige toegangspunt op `Phasis\Parser\Parser`. Parseert `$source` en retourneert het hoofdknooppunt van `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` bepaalt het grammaticadoel, genoemd naar dezelfde optie in parsers van JS:

| Waarde | Doel |
|---|---|
| `script` | klassiek Script. `import`/`export` op het hoogste niveau zijn syntaxfouten; `await` is een identifier |
| `module` | ES Module. Impliciet strikt; `import`/`export` en `await` op het hoogste niveau kunnen worden geparsed |

Elke andere waarde gooit `\InvalidArgumentException`.

Voor meer controle (de strikte modus vooraf instellen voor een context met directe eval, de pragma `//# sourceURL` lezen) kun je de parser rechtstreeks maken: `new Parser($source)` plus `setModuleMode()`, `setStrictMode()` en `parse()`.

## Knooppunten

Elke AST-klasse breidt `Phasis\Ast\Node` uit en bevindt zich onder `Phasis\Ast\{Declaration, Expression, Pattern, Statement}`. Knooppunten zijn gewone, alleen-lezen waardeobjecten:

- `$node->type()` retourneert de typeaanduiding van het knooppunt als tekenreeks; dit is de basisnaam van de klasse (`'BinaryExpression'`, `'ArrowFunction'`, ...).
- `$node->location` is een `Phasis\Lexer\SourceLocation` met `line` (vanaf 1), `column` (vanaf 0) en `offset` (byte-offset). Alleen beginposities worden opgeslagen; knooppunten bevatten geen eindoffset.
- Alle overige openbare velden zijn de kinderen en kenmerken van het knooppunt (`$binary->left`, `$binary->operator`, `$binary->right`).

De meeste typenamen komen overeen met [ESTree](https://github.com/estree/estree). De afwijkingen:

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

Enkele vormen wijken ook af: klasseblokken zijn een gewone array met elementen op de klasseknoop (zonder wrapper van `ClassBody`); import- en exportnamen, labels van statements en namen van `FunctionExpression` worden als tekenreeksen opgeslagen in plaats van als geneste knooppunten van `Identifier`, en `super` wordt geparsed als een `Identifier` met de naam `super` (er bestaat geen knooppuntklasse `Super`). `Phasis\Ast\EstreeSerializer` zet dit alles om naar de vorm van ESTree.

## Syntaxfouten

Ongeldige invoer gooit `Phasis\Exceptions\SyntaxError` (meestal de subklasse `Phasis\Parser\ParseError`). De positie staat aan het einde van het uitzonderingsbericht en de gestructureerde locatie is beschikbaar op de uitzondering:

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

## Doorloper

`Phasis\Ast\Walker` doorloopt een boom diepte-eerst met callbacks voor het betreden en verlaten van knooppunten, volgens de stijl van estraverse:

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

Beide callbacks ontvangen `(Node $node, ?Node $parent)`. Als `$enter` `Walker::SKIP` retourneert, worden de kinderen van het knooppunt overgeslagen; de eigen callback voor `$leave` van het knooppunt wordt nog steeds uitgevoerd.

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

Doorlopen is alleen-lezen. Knooppunten zijn alleen-lezen waardeobjecten, dus vervangen of aanpassen wordt niet ondersteund.

## Serializer

`Phasis\Ast\Serializer` zet de getypeerde AST om naar arrays of JSON met de Phasis-typenamen en veldindeling, waarbij interne cachevelden van de parser worden verwijderd:

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

Dit is de indeling die `bin/phasis --ast` afdrukt.

## EstreeSerializer

`Phasis\Ast\EstreeSerializer` zet de getypeerde AST om naar arrays in ESTree-indeling: ESTree-typenamen (volgens de afwijkingstabel hierboven), ESTree-veldnamen en -volgorde, aangemaakte `ClassBody`-, specifier- en source-`Literal`-nodes, en een `start`-byteoffset op elke node die uit broncode is geparseerd. Er zijn geen `end`-offsets, omdat nodes alleen beginposities opslaan, en string-`Literal`-nodes geen `raw`-tekst bevatten.

```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()` genereert een deterministisch overzicht met één regel per node (type plus `name=` / `value=` / `op=` / `kind=`-kenmerken, twee spaties inspringing per diepteniveau). De indeling is byte voor byte identiek aan het AST-overzicht dat de acorn-fixture in `tests/Popular/acorn` genereert, zodat dezelfde oracle beide parserpaden, dat van JS en deze PHP-export, controleert:

```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` parseert een bestand (scriptdoel) en drukt de `Serializer` JSON-dump af met inspringing van twee spaties. Zie [CLI](https://phasis.inline0.dev/nl/docs/cli) voor de volledige lijst met opties.
