파서 아키텍처
Oxc는 자체적인 AST와 파서를 유지하고 있으며, 이는 지금까지 루스트로 작성된 가장 빠르고 표준 준수성이 높은 자바스크립트 및 타입스크립트(이상의 JSX와 TSX 포함) 파서입니다.
파서는 자바스크립트 도구에서 종종 핵심 성능 장애물이 되기 때문에, 소소한 개선 사항이라도 하류 도구에 계단식으로 영향을 미칠 수 있습니다. 자체 파서를 개발함으로써 잘 연구된 성능 기법을 탐색하고 구현할 기회를 갖게 됩니다.
AST 설계 철학
많은 기존 자바스크립트 도구들이 estree를 그들의 AST 사양으로 사용하지만, 명백한 단점은 모호한 노드가 과도하게 많다는 점입니다. 이러한 모호성은 estree를 사용할 때 개발 과정에서 혼란을 초래하기 쉽습니다.
Oxc AST는 estree AST와 달리 모호한 노드를 제거하고 명확한 유형을 도입합니다. 예를 들어, 일반적인 estree Identifier 대신에, Oxc AST는 BindingIdentifier, IdentifierReference, IdentifierName과 같은 구체적인 유형을 제공합니다.
이러한 명확한 구분은 더 엄밀한 웹 표준 사양과 일치하도록 해서 개발 경험을 크게 향상시킵니다.
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)
성능 아키텍처
얼마나 빠른가?
- 메모리 아레나: memory arena를 사용하여 빠른 할당 및 해제
- 문자열 최적화: 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"); // 힙에 할당이는 대부분의 자바스크립트 식별자와 문자열 리터럴에 대한 메모리 할당 횟수를 줄입니다.
파서 아키텍처
이중 단계 설계
Oxc 파서는 이중 단계 접근 방식을 따릅니다:
- 파싱 단계: 최소한의 심층 분석으로 AST 구조를 생성
- 심층 단계: 스코프 분석, 심볼 해결, 고급 오류 검사 수행
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 빌더
- 타입 안전성: 루스트의 타입 시스템을 활용해 정확성 보장
- 메모리 효율성: 직접 아레나 할당
- 빌더 패턴: 편리한 노드 생성 메서드
표준 준수 전략
테스트 세트 커버리지
- Test262: ECMAScript 표준 준수 테스트에서 100% 통과
- Babel: Babel 파서 테스트에서 99.62% 호환성
- TypeScript: 타입스크립트 컴파일러 테스트에서 99.86% 호환성
오류 처리 철학
rust
// 소스 위치 정보를 포함한 의미 있는 오류 메시지
pub struct OxcDiagnostic {
pub message: String,
pub span: Span,
pub severity: Severity,
pub help: Option<String>,
}파서는 다음과 같은 기능을 제공합니다:
- 정확한 오류 위치: 정확한 소스 위치
- 복구 전략: 오류 발생 후 파싱을 계속 진행
- 유용한 제안: 실질적인 조치를 취할 수 있는 오류 메시지
고급 기능
타입스크립트 지원
- 타입 구문 제거: 타입스크립트 전용 구문 제거
- 데코레이터 파싱: 실험적인 데코레이터 처리
- 네임스페이스 지원: 모듈 및 네임스페이스 전체 파싱
- JSX 통합: 타입스크립트 + JSX (TSX) 지원
연구 영역
- SIMD 텍스트 처리: 벡터화된 문자열 연산
- 캐시 최적화: 메모리 접근 패턴 최소화
- 브랜치 예측: 핫한 파싱 경로 최적화
- 영역 복사 없는 파싱: 불필요한 문자열 복사 제거
