Skip to content

파서 아키텍처

Oxc는 자체 AST와 파서를 유지하며, Rust로 작성된 JavaScript·TypeScript(JSX·TSX 포함) 파서 중 가장 빠르고 스펙 준수도가 높은 편입니다.

파서는 JS 툴링에서 흔히 핵심 병목이라, 사소한 개선도 다운스트림 도구에 연쇄 효과가 납니다. 자체 파서를 개발함으로써 잘 연구된 성능 기법을 적용·실험할 수 있습니다.

AST 설계 철학

많은 기존 도구가 AST 명세로 estree를 쓰지만, 모호한 노드가 많다는 단점이 있습니다. estree로 개발할 때 그 모호함 때문에 혼란이 자주 생깁니다.

Oxc AST는 estree와 달리 모호한 노드를 없애고 구분된 타입을 둡니다. 예를 들어 일반적인 estree Identifier 대신 BindingIdentifier, IdentifierReference, IdentifierName 같은 구체 타입을 제공합니다.

이 구분은 ECMAScript 명세와 더 잘 맞아 개발 경험을 크게 개선합니다.

AST 노드 타입

rust
// 일반 Identifier 대신
pub struct BindingIdentifier<'a> {
    pub span: Span,
    pub name: Atom<'a>,
}

pub struct IdentifierReference<'a> {
    pub span: Span,
    pub name: Atom<'a>,
    pub reference_id: Cell<Option<ReferenceId>>,
}

pub struct IdentifierName<'a> {
    pub span: Span,
    pub name: Atom<'a>,
}

의미적 명확성

이 방식은 의미를 분명히 합니다.

  • BindingIdentifier: 변수 선언 (let x = 1)
  • IdentifierReference: 변수 사용 (console.log(x))
  • IdentifierName: 속성 이름 (obj.property)

성능 아키텍처

왜 이렇게 빠른가

  • 메모리 아레나: AST를 메모리 아레나에 할당해 빠른 할당·해제
  • 문자열 최적화: 짧은 문자열은 CompactString으로 인라인
  • 힙 사용 최소화: 위 두 가지 외 추가 힙 할당 없음
  • 관심사 분리: 스코프 바인딩·심볼 해석·일부 구문 오류는 의미 분석기에 위임

메모리 관리 세부

아레나 할당

rust
use oxc_allocator::Allocator;

// 모든 AST 노드는 이 아레나에 할당
let allocator = Allocator::default();
let ast_node = allocator.alloc(Expression::NumericLiteral(
    allocator.alloc(NumericLiteral { value: 42.0, span: SPAN })
));

장점:

  • O(1) 할당: 단순 포인터 범프
  • O(1) 해제: 아레나 전체를 한 번에 드롭
  • 캐시 친화: 선형 메모리 배치
  • 단편화 없음: 연속적인 메모리 사용

CompactString 문자열 인터닝

rust
// 길이 ≤ 24바이트 문자열은 인라인 저장(힙 할당 없음)
let short_name = CompactString::from("variableName");  // 스택
let long_name = CompactString::from("a_very_long_variable_name_that_exceeds_limit");  // 힙

대부분의 JavaScript 식별자·문자열 리터럴에 대한 할당을 줄입니다.

파서 아키텍처

2단계 설계

Oxc 파서는 2단계를 따릅니다.

  1. 파싱 단계: 최소한의 의미 분석으로 AST 구조 구축
  2. 의미 단계: 스코프 분석·심볼 해석·고급 오류 검사
rust
// 1단계: AST 파싱
let parser_result = Parser::new(&allocator, &source_text, source_type).parse();

// 2단계: 의미 분석
let semantic_result = SemanticBuilder::new()
    // 파서가 수행하지 않는 추가 구문 검사
    .with_check_syntax_error(true)
    .build(&parser_result.program);

파서 구성 요소

렉서(Lexer)

  • 토큰 생성: 소스 텍스트를 구조화된 토큰으로 변환
  • SIMD 최적화: 공백 건너뛰기에 SIMD 사용
  • 문맥 인식: 정규식과 나눗셈 연산자 모호성 처리

재귀 하강 파서

  • 수작 구현: 최대 성능을 위해 직접 작성
  • 오류 복구: 의미 있는 메시지와 함께 고급 오류 처리
  • 문법 준수: ECMAScript 명세를 정확히 따름

AST 빌더

  • 타입 안전: Rust 타입 시스템으로 정확성 확보
  • 메모리 효율: 아레나 직접 할당
  • 빌더 패턴: 노드 구성을 편하게 하는 메서드

적합성 전략

테스트 스위트 커버리지

  • Test262: ECMAScript 적합성 테스트 100% 통과
  • Babel: Babel 파서 테스트와 99.62% 호환
  • TypeScript: TypeScript 컴파일러 테스트와 99.86% 호환

오류 처리 철학

rust
// 소스 위치가 있는 의미 있는 오류 메시지
pub struct OxcDiagnostic {
    pub message: String,
    pub span: Span,
    pub severity: Severity,
    pub help: Option<String>,
}

파서가 제공하는 것:

  • 정확한 오류 위치: 정확한 소스 위치
  • 복구 전략: 오류 후에도 파싱 계속
  • 실행 가능한 제안: 행동 가능한 오류 메시지

고급 기능

TypeScript 지원

  • 타입 제거: TypeScript 전용 구문 제거
  • 데코레이터 파싱: 실험적 데코레이터 처리
  • Namespace 지원: 모듈·네임스페이스 파싱
  • JSX 통합: TypeScript + JSX(TSX)

연구 영역

  • SIMD 텍스트 처리: 벡터화 문자열 연산
  • 캐시 최적화: 메모리 접근 패턴 최소화
  • 분기 예측: 핫 파싱 경로 최적화
  • 제로카피 파싱: 불필요한 문자열 복사 제거