
# Parser

Phasis stellt seinen JavaScript-Parser als eigenständiges PHP API bereit. Du kannst Quelltext in einen typisierten AST parsen, ihn durchlaufen und als ESTree-förmige Arrays oder JSON exportieren, ohne ein `Engine` zu erstellen oder etwas auszuführen.

## parseSource

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

Der Einstiegspunkt mit einem Aufruf auf `Phasis\Parser\Parser`. Er parst `$source` und gibt den `Phasis\Ast\Program`-Wurzelknoten zurück.

```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` legt das Grammatikziel fest, benannt nach derselben Option in JS-Parsern:

| Wert | Ziel |
|---|---|
| `script` | klassisches Script. `import`/`export` auf oberster Ebene sind Syntaxfehler, `await` ist ein Bezeichner |
| `module` | ES-Modul. Implizit im Strict Mode; `import`/`export` und `await` auf oberster Ebene werden geparst |

Jeder andere Wert löst `\InvalidArgumentException` aus.

Für genauere Kontrolle (den Strict Mode für einen direkten Eval-Kontext vorab aktivieren, die `//# sourceURL`-Pragma auslesen) erstelle den Parser direkt: `new Parser($source)` sowie `setModuleMode()`, `setStrictMode()` und `parse()`.

## Knoten

Jede AST-Klasse erweitert `Phasis\Ast\Node` und befindet sich unter `Phasis\Ast\{Declaration, Expression, Pattern, Statement}`. Knoten sind einfache schreibgeschützte Wertobjekte:

- `$node->type()` gibt den Typ-String des Knotens zurück, also den Klassennamen ohne Namespace (`'BinaryExpression'`, `'ArrowFunction'`, ...).
- `$node->location` ist ein `Phasis\Lexer\SourceLocation` mit `line` (ab 1), `column` (ab 0) und `offset` (Byte-Offset). Gespeichert werden nur Startpositionen; Knoten enthalten keinen End-Offset.
- Alle übrigen öffentlichen Felder sind die untergeordneten Knoten und Attribute des Knotens (`$binary->left`, `$binary->operator`, `$binary->right`).

Die meisten Typnamen stimmen mit [ESTree](https://github.com/estree/estree) überein. Die Abweichungen:

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

Einige Formen unterscheiden sich ebenfalls: Klassenkörper sind ein einfaches Element-Array am Klassenknoten (ohne `ClassBody`-Wrapper); Import-/Exportnamen, Anweisungsbezeichnungen und `FunctionExpression`-Namen werden als Strings statt als verschachtelte `Identifier`-Knoten gespeichert, und `super` wird als `Identifier` mit dem Namen `super` geparst (es gibt keine `Super`-Knotenklasse). `Phasis\Ast\EstreeSerializer` bringt all das in die ESTree-Form.

## Syntaxfehler

Ungültige Eingaben lösen `Phasis\Exceptions\SyntaxError` aus (meist deren Unterklasse `Phasis\Parser\ParseError`). Die Fehlermeldung endet mit der Position; die strukturierte Ortsangabe ist über die Exception verfügbar:

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

## Walker

`Phasis\Ast\Walker` durchläuft einen Baum in Tiefensuche mit Enter-/Leave-Callbacks, ähnlich wie estraverse:

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

Beide Callbacks erhalten `(Node $node, ?Node $parent)`. Wenn `$enter` `Walker::SKIP` zurückgibt, werden die Kindknoten des Knotens übersprungen; das eigene `$leave` des Knotens wird weiterhin aufgerufen.

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

Der Durchlauf ist schreibgeschützt. Knoten sind schreibgeschützte Wertobjekte, daher ist kein Ersetzen oder Ändern möglich.

## Serializer

`Phasis\Ast\Serializer` wandelt den typisierten AST in Arrays oder JSON um. Dabei verwendet es die Phasis-Typnamen und die Feldanordnung und entfernt parserinterne Cache-Felder:

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

Dies ist das Format, das `bin/phasis --ast` ausgibt.

## EstreeSerializer

`Phasis\Ast\EstreeSerializer` wandelt den typisierten AST stattdessen in ESTree-förmige Arrays um: mit ESTree-Typnamen (gemäß der obigen Abweichungstabelle), ESTree-Feldnamen und -Reihenfolge, erzeugten `ClassBody`-/Specifier-/Source-`Literal`-Knoten sowie einem `start`-Byte-Offset für jeden aus Quelltext geparsten Knoten. `end`-Offsets gibt es nicht, da Knoten nur Startpositionen speichern; String-`Literal`-Knoten enthalten keinen `raw`-Text.

```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()` gibt eine deterministische Zusammenfassung mit einer Zeile pro Knoten aus (Typ plus Attribute `name=` / `value=` / `op=` / `kind=`, Einrückung von zwei Leerzeichen pro Tiefenebene). Das Format ist bytegenau identisch mit der AST-Zusammenfassung, die das acorn-Fixture in `tests/Popular/acorn` erzeugt. Daher prüft dieselbe Referenz sowohl den JS-Parserpfad als auch diesen PHP-Export:

```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` parst eine Datei (Script-Ziel) und gibt den `Serializer`-JSON-Dump mit zwei Leerzeichen Einrückung aus. Die vollständige Übersicht der Flags findest du unter [CLI](https://phasis.inline0.dev/de/docs/cli).
