
컴파일러를 만들어본 적 있으신가요?
학교에서 컴파일러 수업을 들었거나 토이 언어를 만들어본 적이 있다면 아마 공감하실 텐데요, 컴파일러 구현의 가장 어려운 부분은 각 단계가 너무 큰 덩어리로 엮여 있다는 점이에요. 렉싱, 파싱, 타입 체크, 최적화, 코드 생성이 한 파일에 수백 수천 줄로 뭉쳐 있다 보니, 한 군데를 고치면 옆에서 터지고, 중간 결과물을 검증하기도 어렵거든요.
Nanopass Framework는 이 문제를 정면으로 풀려고 만든 Scheme 기반의 DSL이에요. 핵심 아이디어는 이름 그대로 "컴파일러의 각 단계(pass)를 가능한 한 잘게(nano) 쪼개자"는 거예요. 한 pass는 하나의 아주 작은 변환만 담당하고, pass가 여러 개 쌓여서 전체 컴파일러가 된다는 철학이거든요.
기존 방식이 왜 불편한데요?
전통적인 컴파일러 설계는 보통 2~3개의 큰 중간 표현(IR, Intermediate Representation)을 두는 방식이에요. 예를 들어 소스 → AST → 중간 IR → 기계어 정도로 나눈 뒤, 각 단계에서 많은 일을 한꺼번에 해요. LLVM 같은 대형 프로젝트도 기본적으로는 이 구조를 따라요.
그런데 이렇게 하면 한 단계의 함수가 수백 가지 AST 노드 타입을 다뤄야 하고, 어떤 변환이 끝난 뒤 어떤 형태의 IR이 나와야 하는지가 문서 외에는 표현될 곳이 없어요. 버그가 나면 "이 IR은 여기 오면 안 되는데?" 하는 상황이 자주 생기죠.
Nanopass의 접근
Nanopass는 각 pass 사이의 언어(IR)를 명시적으로 정의하도록 강제해요. 예를 들어 "L1 언어는 let 표현식을 허용하지만, L2 언어는 let을 이미 lambda로 바꾼 상태"처럼, 각 단계 전후의 문법을 DSL로 선언할 수 있어요. 그러고 나서 pass 함수는 L1 → L2 변환만 집중해서 작성하면 되는 거죠.
이 프레임워크의 매력은 각 언어 정의에서 "이전 언어와 달라지는 부분만" 적으면 된다는 데 있어요. 예를 들어 L2를 정의할 때 "L1에서 let 노드 하나만 제거하고 lambda 노드 하나만 추가" 이렇게 적으면 나머지 문법은 자동으로 상속돼요. 그래서 30개 pass로 쪼개 놓아도 각 언어 정의 자체는 몇 줄이면 충분하고, pass 구현도 패턴 매칭으로 아주 짧게 끝나요.
성능은 문제없을까
단계를 쪼개면 매번 IR 트리를 순회하니 느려지지 않을까 싶을 거예요. 실제로 Nanopass를 만든 Indiana 대학의 Kent Dybvig 교수팀이 Chez Scheme 컴파일러를 이 방식으로 재구현했는데, 기존 컴파일러보다 극적으로 빠르지는 않더라도 현저히 느리지도 않으면서 유지보수성은 크게 좋아졌다고 보고됐어요. 교육용 컴파일러 책 "An Incremental Approach to Compiler Construction"의 저자 Abdulaziz Ghuloum도 이 접근을 강하게 옹호했고요. 특히 컴파일러 교육에서 학생들이 pass 하나하나를 테스트하며 단계적으로 쌓아 올릴 수 있다는 장점이 크다고 해요.
다른 프로젝트와의 비교
비슷한 철학의 도구로는 MLIR이 있어요. LLVM 재단에서 추진하는 MLIR은 여러 레벨의 IR을 정의하고 그 사이 변환 패스를 조합하는 인프라를 제공해요. Nanopass가 아카데믹한 Scheme 기반 DSL이라면, MLIR은 산업 현장의 C++ 기반 대형 프레임워크라고 할 수 있어요. Haskell 커뮤니티에서는 Trees that Grow 패턴이 비슷한 결을 가져요. 같은 AST 자료형을 단계별로 확장/축소해 타입 시스템으로 단계를 구분하는 거죠.
한국 개발자에게 주는 시사점
당장 회사 일에서 컴파일러를 쓸 일이 없더라도, Nanopass의 설계 사고는 일반 소프트웨어 설계에도 적용돼요. 예를 들어 큰 ETL 파이프라인을 만들 때, 각 단계의 데이터 스키마를 명시적으로 선언하고 변환 함수를 스키마 간 매핑으로 정의하는 방식이 바로 같은 발상이거든요. 복잡한 도메인 로직을 가진 서비스를 리팩터링할 때, "상태 전이를 아주 작은 스텝으로 쪼개고 각 스텝의 입출력 타입을 명시적으로 표현"하는 건 디버깅 난이도를 크게 낮춰줘요. 언어 이론에 관심이 있는 분이라면 Racket이나 Chez Scheme을 깔고 직접 튜토리얼을 돌려보는 것도 주말 취미로 훌륭해요.
마무리
한 줄로 정리하면, "큰 변환은 작은 변환의 연속으로 쪼개고, 각 단계의 타입을 명시하라"예요. 여러분은 컴파일러나 DSL을 만들어본 경험이 있으세요? 그리고 업무 코드 중에서 Nanopass식 접근이 어울릴 만한 부분이 있다면 어디일까요?
🔗 출처: Hacker News
TTJ 코딩클래스 정규반
월급 외 수입,
코딩으로 만들 수 있습니다
17가지 수익 모델을 직접 실습하고, 1,300만원 상당의 자동화 도구와 소스코드를 받아가세요.
"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"
실제 수강생 후기- 비전공자도 6개월이면 첫 수익
- 20년 경력 개발자 직강
- 자동화 프로그램 + 소스코드 제공