
# 파서

Phasis는 독립 실행형 PHP API로 자체 JavaScript 파서를 제공합니다. 소스 텍스트를 구문 분석해 형식화된 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 Module. 암시적으로 strict 모드가 적용되며 `import`/`export` 및 최상위 `await`를 구문 분석합니다 |

그 밖의 값은 `\InvalidArgumentException`을 발생시킵니다.

직접 평가 컨텍스트에서 strict 모드를 미리 설정하거나 `//# sourceURL` pragma를 읽는 등 세밀한 제어가 필요하면 파서를 직접 생성하세요: `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)`을 받습니다. `$enter`에서 `Walker::SKIP`을 반환하면 노드의 자식 순회를 건너뜁니다. 노드 자체의 `$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` / 지정자 / 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=` 속성, 깊이마다 공백 두 칸 들여쓰기). 형식은 `tests/Popular/acorn`의 acorn 픽스처가 만드는 AST 요약과 바이트 단위로 동일합니다. 따라서 같은 오라클이 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/ko/docs/cli)를 참조하세요.
