고급 LR 파서 알고리즘 구현 (IELR) 및 Lrama 적용 사례

[JA] The Implementations of Advanced LR Parser Algorithm / Junichi Kobayashi @junk0612

작성자
RubyKaigi
발행일
2025년 05월 27일

핵심 요약

  • 1 IELR은 LR 파서보다 정확하게 다음 토큰을 예측하고, 충돌 정보를 전파하여 오토마톤 상태를 분할함으로써 LR 파서의 한계를 극복하는 고급 LR 파서 알고리즘입니다.
  • 2 Ruby의 내부 파서 생성에 사용되는 Lrama에 IELR을 구현하는 과정에서 발생한 성능 문제와 이를 해결하기 위한 최적화 기법(루프 처리, 캐싱, 불필요한 어노테이션 제거)을 상세히 설명합니다.
  • 3 IELR 구현을 통해 CRuby의 파서 호환성을 확인하고, Ruby 언어의 복잡한 문법 문제(예: '네 가지 do' 문제) 해결에 IELR을 적용할 가능성을 모색합니다.

도입

발표자는 A와 시스템 매니지먼트의 코바야시 씨로, Lrama 파서 제너레이터의 커미터이자 레일즈 엔지니어입니다. 본 발표는 '고급 LR 파서 알고리즘의 구현'이라는 주제로, 특히 IELR(Improved/Informed LR) 알고리즘을 Ruby의 파서 제너레이터인 Lrama에 구현한 경험과 그 과정에서 얻은 통찰을 공유합니다. IELR은 기존 LR 파서의 한계를 극복하고 더 정확한 구문 분석을 가능하게 하는 알고리즘으로, Ruby 언어의 복잡한 문법 문제를 해결할 잠재력을 가지고 있습니다.

Lrama 및 IELR 소개

  • Lrama: Bison과 호환되는 Ruby 기반 파서 제너레이터로, Ruby 3.3부터 CRuby의 내부 파서(parse.y) 생성에 활용되고 있습니다. 현재 CRuby 3.5를 위한 0.7 버전이 개발 중입니다.
  • IELR (Informed/Improved LR): 2010년 논문으로 발표된 LR 파서 생성 알고리즘의 일종입니다. 이미 읽어 들인 토큰 정보를 활용하여 다음에 나올 수 있는 토큰을 LR 파서보다 정확하게 예측합니다. Python에는 이 알고리즘의 구현체가 존재합니다.

LR 파서의 기본 동작 및 한계

  • LR 파서: 하향식(bottom-up) 구문 분석 알고리즘으로, 시프트(shift)와 리듀스(reduce) 두 가지 액션을 사용합니다.
    • 시프트: 렉서로부터 다음 토큰을 읽어 프로그램 진행.
    • 리듀스: 읽어 들인 토큰들을 묶어 상위 노드 생성.
  • 선행 탐색(Lookahead): 특정 규칙에 매칭되었을 때 리듀스를 수행할지 결정하기 위해 다음 토큰을 미리 확인하는 과정입니다(LR(1)의 ‘1’이 이를 의미).
  • 충돌(Conflict): 선행 탐색만으로는 동작을 유일하게 결정할 수 없는 상황을 의미합니다. 시프트-리듀스 충돌(shift-reduce conflict)과 리듀스-리듀스 충돌(reduce-reduce conflict)이 있습니다.

IELR의 충돌 해결 메커니즘

  • LR과 IELR의 Lookahead Set 차이:
    • LR: 언어 전체에서 특정 규칙 다음으로 나타날 수 있는 토큰 집합을 사용합니다.
    • IELR: 파서가 지금까지 읽어 들인 토큰에 의해 필터링된 부분집합 내에서 다음에 나타날 수 있는 토큰 집합을 구성하여, 더 정확한 예측을 가능하게 합니다.
  • Mysterious Conflicts: IELR에서 해결되지만 LR에서는 발생하는 특정 유형의 충돌입니다. LR 파서의 상태 병합(state merging) 과정에서 발생하며, IELR은 이를 해결하기 위해 상태를 분할합니다.

Lrama IELR 구현

  • 기본 아이디어:
    1. 충돌이 발생하는 상태에 어노테이션을 부착합니다.
    2. 이 어노테이션 정보를 이전 상태로 역전파(backward propagation)합니다.
    3. 초기 상태부터 Lookahead Set을 재계산하며 전방으로 전파(forward propagation)합니다.
    4. 어노테이션이 부착된 상태로 Lookahead Set을 전파할 때, 전파 대상 상태와의 ‘호환성(compatibility)’을 확인합니다.
    5. 호환성이 없는 경우, 해당 상태를 분할하고 새로운 상태를 생성하여 전이 경로를 변경합니다.
  • 성능 최적화:
    1. 오토마톤 루프: 어노테이션 역전파 시 무한 루프 발생. 이전에 전파된 정보를 추적하여 해결.
    2. Lookahead Set 계산 지연: 캐시 변수 도입 및 SCC(Strongly Connected Components) 알고리즘을 활용한 사전 계산으로 최적화.
    3. 불필요한 어노테이션 생성: 전파 시 관련성 체크를 통해 필요한 어노테이션만 생성하도록 개선.
  • 결과: 최적화 후 parse.y 대상 파서 생성 시간이 2시간 이상에서 약 1분으로 단축되었습니다.

향후 계획

  • 속도 개선: 현재 1분도 여전히 느리다는 피드백에 따라 추가적인 속도 최적화가 필요합니다.
  • Ruby 언어 응용: Ruby의 ‘네 가지 do’ 문제(괄호 있는/없는 메서드 호출의 do, while do, lambda do)와 같이 복잡한 문법 구조를 IELR의 강력한 분석 능력으로 해결할 가능성을 모색합니다.

결론

본 발표는 고급 LR 파서 알고리즘인 IELR의 이론적 배경과 Lrama에 이를 구현한 실질적인 과정을 상세히 다루었습니다. IELR은 기존 LR 파서가 가진 선행 탐색의 한계를 극복하고, 충돌 정보를 역전파하고 Lookahead Set을 전파하며 상태 호환성을 기반으로 오토마톤 상태를 분할함으로써 복잡한 문법 구조에서도 정확한 파싱을 가능하게 합니다. Lrama 구현 과정에서 발생한 성능 병목 현상을 성공적으로 해결하며, CRuby의 파서 생성에 대한 IELR의 적용 가능성을 입증하였습니다. 향후 IELR의 지속적인 최적화와 Ruby 언어의 고유한 문법적 난제 해결에 기여할 잠재력을 보여주었습니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!