제5화 뚝딱이와 드디어 3

코드의 일생 06

컴파일러는 누가 만들었을까?

컴파일러도 누군가 쓴 프로그램일까?

첫 바퀴의 친구들을 모두 만난 한줄이에게 마지막 궁금증이 남았어.

이번에는 한줄이를 검사해 준 도구 쪽을 들여다볼 차례야.

오늘의 이상한 질문

컴파일러의 시작 파일을 펼치면 첫 열 줄에서 무엇을 만날까?

답은 지도 뒷면에 있어.

씨앗과 함께 펼쳐 보자.

만화

장면 1 길 끝 화분

한줄이그런데 나를 검사해 준 도구는 누가 만들었어?

씨앗사람들이 만들었어.한줄아, 너를 검사한 컴파일러도 글로 쓴 프로그램이야.어떤 말로 썼는지 같이 읽어 볼래?

장면 2 지도 뒤집기

한줄이어? 뒷면에 글이 가득해!

씨앗너를 검사한 컴파일러의 소스야.오늘은 그 첫 열 줄을 옮겨 왔어.

장면 3 닮은 이름

한줄이src/main.tpz? 내 파일 이름이랑 끝이 똑같아!

씨앗컴파일러의 설정 파일이 이 파일을 시작 파일로 적어 두었어.너처럼 이 파일도 토파즈로 썼어.

장면 4 친구들이 기웃

가지어, 이 낱말 어디서 본 것 같은데?

씨앗너희가 이 안에 사는 건 아니야.그래도 첫 바퀴에서 함께 본 일의 이름은 여기 적혀 있어.같이 찾아볼까?

장면 5 귀퉁이의 판 번호

뚝딱이지도 귀퉁이에 5.21.1이라고 쓰여 있네?

씨앗우리가 펼친 토파즈의 판 번호야.이 번호가 있으면 나중에도 어느 판의 소스를 읽었는지 알 수 있어.

이제 지도 뒷면을 직접 읽어 볼 차례야.

작은 실험

지도 뒷면

한줄이의 파일 이름은 main.tpz야.

토파즈 컴파일러의 설정 파일을 보니, 컴파일러의 소스도 src/main.tpz에서 시작한대.

이름 끝이 똑같네.

이번에는 컴파일러 쪽 파일의 첫 열 줄을 펼쳐 보자.

이 쪽은 컴파일러를 실행하지 않아. 뒷면의 글은 토파즈가 공개한 소스에서 그대로 옮겨 왔어.

한줄이의 파일

main.tpz

  1. 1 + 2

6바이트 · 1줄 · 제1화부터 함께한 파일

컴파일러의 시작 파일

src/main.tpz

16,891바이트 · 전체 560줄 가운데 첫 10줄 · 토파즈 5.21.1 공개 소스

토파즈 5.21.1 공개 소스

지도를 뒤집으면 뒷면에 무엇이 있을까?

컴파일러 소스 src/main.tpz, 1행부터 10행
  1. import std.json as jsonBoundary
  2. 빈 줄
  3. import checker { check }
  4. import emitter { emitRust, emitStep }
  5. import layout { normalize }
  6. import lowering { lowerStep, lowerValue }
  7. import parser { parse }
  8. import profile { checkProfile }
  9. import raw { lex }
  10. import resolver { resolvePreview, resolveWithFacts }

첫 바퀴에서 이 친구와 함께 본 일을 떠올려 봐. 어느 줄이 그 일을 하는 이름을 불러올까?

톡톡의 핀을 꽂을 줄

톡톡과 함께 1 + 2를 조각으로 나눴지. 제1화에서 이 일을 어휘 분석이라고 불렀어. 영어로는 lexical analysis야. lexical의 앞부분과 닮은 이름은 어느 줄에 있을까?

톡톡과 함께 1 + 2를 조각으로 나눴지. 제1화에서 이 일을 어휘 분석이라고 불렀어. 영어로는 lexical analysis야. lexical의 앞부분과 닮은 이름은 어느 줄에 있을까?

톡톡과 함께 1 + 2를 조각으로 나눴지. 제1화에서 이 일을 어휘 분석이라고 불렀어. 영어로는 lexical analysis야. lexical의 앞부분과 닮은 이름은 어느 줄에 있을까?

톡톡과 함께 1 + 2를 조각으로 나눴지. 제1화에서 이 일을 어휘 분석이라고 불렀어. 영어로는 lexical analysis야. lexical의 앞부분과 닮은 이름은 어느 줄에 있을까?

톡톡과 함께 1 + 2를 조각으로 나눴지. 제1화에서 이 일을 어휘 분석이라고 불렀어. 영어로는 lexical analysis야. lexical의 앞부분과 닮은 이름은 어느 줄에 있을까?

톡톡과 함께 1 + 2를 조각으로 나눴지. 제1화에서 이 일을 어휘 분석이라고 불렀어. 영어로는 lexical analysis야. lexical의 앞부분과 닮은 이름은 어느 줄에 있을까?

톡톡과 함께 1 + 2를 조각으로 나눴지. 제1화에서 이 일을 어휘 분석이라고 불렀어. 영어로는 lexical analysis야. lexical의 앞부분과 닮은 이름은 어느 줄에 있을까?

톡톡과 함께 1 + 2를 조각으로 나눴지. 제1화에서 이 일을 어휘 분석이라고 불렀어. 영어로는 lexical analysis야. lexical의 앞부분과 닮은 이름은 어느 줄에 있을까?

lex가 그 이름이야. lexical의 첫 세 글자와 같아. 전체 파일의 261행이 lex(request.source)로 소스를 넘기고, 바로 다음 줄이 raw.tokens를 꺼내 써.

톡톡과 함께 1 + 2를 조각으로 나눴지. 제1화에서 이 일을 어휘 분석이라고 불렀어. 영어로는 lexical analysis야. lexical의 앞부분과 닮은 이름은 어느 줄에 있을까?

가지의 핀을 꽂을 줄

가지와 함께 조각을 나무로 묶었지. 제2화에서 이 일을 구문 분석이라고 불렀어. 영어로는 parsing이야. 그 낱말과 닮은 이름은 어느 줄에 있을까?

가지와 함께 조각을 나무로 묶었지. 제2화에서 이 일을 구문 분석이라고 불렀어. 영어로는 parsing이야. 그 낱말과 닮은 이름은 어느 줄에 있을까?

가지와 함께 조각을 나무로 묶었지. 제2화에서 이 일을 구문 분석이라고 불렀어. 영어로는 parsing이야. 그 낱말과 닮은 이름은 어느 줄에 있을까?

가지와 함께 조각을 나무로 묶었지. 제2화에서 이 일을 구문 분석이라고 불렀어. 영어로는 parsing이야. 그 낱말과 닮은 이름은 어느 줄에 있을까?

가지와 함께 조각을 나무로 묶었지. 제2화에서 이 일을 구문 분석이라고 불렀어. 영어로는 parsing이야. 그 낱말과 닮은 이름은 어느 줄에 있을까?

가지와 함께 조각을 나무로 묶었지. 제2화에서 이 일을 구문 분석이라고 불렀어. 영어로는 parsing이야. 그 낱말과 닮은 이름은 어느 줄에 있을까?

parse가 그 이름이야. 구문 분석을 영어로 parsing이라고 해. 제2화에서 조각을 나무로 묶어 본 그 일이야.

가지와 함께 조각을 나무로 묶었지. 제2화에서 이 일을 구문 분석이라고 불렀어. 영어로는 parsing이야. 그 낱말과 닮은 이름은 어느 줄에 있을까?

가지와 함께 조각을 나무로 묶었지. 제2화에서 이 일을 구문 분석이라고 불렀어. 영어로는 parsing이야. 그 낱말과 닮은 이름은 어느 줄에 있을까?

가지와 함께 조각을 나무로 묶었지. 제2화에서 이 일을 구문 분석이라고 불렀어. 영어로는 parsing이야. 그 낱말과 닮은 이름은 어느 줄에 있을까?

타미의 핀을 꽂을 줄

아직 꽂지 않았어

타미와 함께 값의 타입을 살폈지. 그 일의 이름은 어느 줄에 있을까?

check가 그 이름이야. checker.tpz는 첫 주석에서 이 모듈의 일을 타입 검사로 설명해.

타미와 함께 값의 타입을 살폈지. 그 일의 이름은 어느 줄에 있을까?

타미와 함께 값의 타입을 살폈지. 그 일의 이름은 어느 줄에 있을까?

타미와 함께 값의 타입을 살폈지. 그 일의 이름은 어느 줄에 있을까?

타미와 함께 값의 타입을 살폈지. 그 일의 이름은 어느 줄에 있을까?

여기에도 check가 보이네. 제3화에서 본 타입 검사를 맡은 checker 모듈은 어느 줄에서 불러올까?

타미와 함께 값의 타입을 살폈지. 그 일의 이름은 어느 줄에 있을까?

타미가 이름도 살펴봐. 제3화에서 값의 타입을 보던 일은 어디에 있을까?

모모의 핀을 꽂을 줄

아직 꽂지 않았어

모모와 함께 나무를 작업 카드로 옮겨 적었지. 그 일의 이름은 어느 줄에 있을까?

모모와 함께 나무를 작업 카드로 옮겨 적었지. 그 일의 이름은 어느 줄에 있을까?

4행은 emitter에서 두 이름을 불러와. 컴파일러는 emitRust를 호출해 Rust 코드를 만들어. 제4화에서 본 로어링의 이름은 어느 줄에 있을까?

모모와 함께 나무를 작업 카드로 옮겨 적었지. 그 일의 이름은 어느 줄에 있을까?

lowerStep과 lowerValue가 그 이름이야. 둘 다 제4화에서 카드로 옮겨 적어 본 로어링과 이어져.

모모와 함께 나무를 작업 카드로 옮겨 적었지. 그 일의 이름은 어느 줄에 있을까?

모모와 함께 나무를 작업 카드로 옮겨 적었지. 그 일의 이름은 어느 줄에 있을까?

모모와 함께 나무를 작업 카드로 옮겨 적었지. 그 일의 이름은 어느 줄에 있을까?

모모와 함께 나무를 작업 카드로 옮겨 적었지. 그 일의 이름은 어느 줄에 있을까?

네 친구의 핀을 한 번씩 꽂아 보면 발견 카드가 열려.

컴파일러의 시작 파일에서 익숙한 일을 찾았어.

import 줄을 보면 이 파일이 어디에서 어떤 이름을 가져오는지 알 수 있어.

조금 더 내려가면 lex(request.source)처럼 그 이름을 실제로 쓰는 자리도 있어.

네 친구의 줄

  • 톡톡 9행 import raw { lex }

    lex가 그 이름이야. lexical의 첫 세 글자와 같아. 전체 파일의 261행이 lex(request.source)로 소스를 넘기고, 바로 다음 줄이 raw.tokens를 꺼내 써.

  • 가지 7행 import parser { parse }

    parse가 그 이름이야. 구문 분석을 영어로 parsing이라고 해. 제2화에서 조각을 나무로 묶어 본 그 일이야.

  • 타미 3행 import checker { check }

    check가 그 이름이야. checker.tpz는 첫 주석에서 이 모듈의 일을 타입 검사로 설명해.

  • 모모 6행 import lowering { lowerStep, lowerValue }

    lowerStep과 lowerValue가 그 이름이야. 둘 다 제4화에서 카드로 옮겨 적어 본 로어링과 이어져.

다른 import 줄도 읽어 보기
  • 1행은 표준 라이브러리의 std.json 모듈을 jsonBoundary라는 이름으로 불러와.
  • 2행은 원문에 있는 빈 줄이야.
  • 4행은 emitter에서 두 이름을 불러와. 컴파일러는 emitRust를 호출해 Rust 코드를 만들어. 제4화에서 본 로어링의 이름은 어느 줄에 있을까?
  • 5행은 layout에서 normalize를 불러와. 262행에서 raw.tokens를 받아 쓰는 이름이야. normalize가 줄바꿈을 어떻게 다루는지 제7화에서 보기
  • 8행은 profile에서 checkProfile을 불러와. 여기에도 check가 보이네. 제3화에서 본 타입 검사를 맡은 checker 모듈은 어느 줄에서 불러올까?
  • 10행은 resolver에서 resolvePreview와 resolveWithFacts를 불러와. resolve는 이름이 무엇을 가리키는지 찾는다는 뜻이야.

컴퓨터는 설치된 topaz 파일을 실행해. 우리가 지금 읽는 글은 토파즈가 따로 공개한 소스야.

이 기록은 어디서 왔을까?

설정 파일 topaz.toml에 entry = "src/main.tpz"라고 적혀 있어.

여기에는 전체 560줄 가운데 첫 10줄만 옮겨 왔어. 전체 파일은 공식 소스 압축에 들어 있어.

공개 소스 압축
topaz-source-snapshot-v5.21.1.tar.gz
압축 바이트
305,303
압축 sha256
d3a0e5365b8a39605f095a7c07b43556848a95042e7efd346ab84c95e583cf3d
파일
compiler/crates/topaz_self_frontend/topaz/src/main.tpz
파일 바이트
16,891
파일 sha256
ef16cb39e9956e7e3aaed8023f9a45388c49a17bd9336a6e6f29dfa5aeb63bd3
받은 날
2026-11-02

옮겨 온 소스는 같은 판의 라이선스와 고지를 따라. LICENSE 읽기, 새 창 NOTICE 읽기, 새 창

지도와 핀은 이야기 속 그림이야. 뒷면의 글과 숫자는 공개된 원문에서 왔어.

진짜 속사정

컴파일러도 누군가 쓴 프로그램이야

지도 뒷면에 옮겨 적은 글은 컴파일러의 소스 코드야. 한줄이처럼 누군가 글로 써 둔 프로그램이지.

토파즈 문서는 기본 컴파일러는 토파즈로 작성했습니다.라고 적어 두었어. 툴체인 상태 문서, 새 창

자기가 번역하는 언어로 작성되어 자기 소스도 컴파일할 수 있는 컴파일러를 셀프호스팅 컴파일러라고 해.

이 컴파일러의 폴더에는 .tpz 파일이 50개 있어. 시작 파일 main.tpz는 560줄이고, 한줄이의 main.tpz는 한 줄이야.

컴파일러 안에서도 let으로 값에 이름을 붙여. 261행에는 let raw = lex(request.source)가 있고, 바로 다음 줄이 그 raw.tokens를 꺼내 써.

제1화부터 제4화에서 본 토큰, 나무, 타입, 작업 기록은 토파즈 5.21.1의 기본 컴파일러로 남겼어. 오늘 펼친 건 바로 그 컴파일러의 공개 소스야.

옮겨 온 소스는 같은 판의 라이선스와 고지를 따라. LICENSE 읽기, 새 창 NOTICE 읽기, 새 창

친구들이 이 안에 사는 걸까?

아니. 친구들은 이야기 속 모습이야. 컴파일러는 일을 여러 파일과 이름으로 나눠 적고, 그 가운데 첫 바퀴에서 본 일과 닮은 이름이 있을 뿐이야.

3행부터 10행까지의 import 줄은 모듈 이름의 낱말 차례로 놓여 있어. 일하는 차례와는 달라. 일을 나누는 방법과 차례는 컴파일러마다 다를 수 있어.

모든 컴파일러가 자기 언어로 쓰였을까?

아니. 어떤 컴파일러는 자기가 번역하는 언어로 쓰고, 어떤 컴파일러는 다른 언어로 써.

토파즈 5.21.1 기록과 공개 소스

더 들여다보기

컴파일러가 둘이라고?

기록에 적힌 것 토파즈 5.21.1에 상태를 물으면 기본 컴파일러와 참조 컴파일러를 따로 적어.

Default compiler: self
Reference compiler: rust

기록에 적힌 것 기본 컴파일러에는 self, 참조 컴파일러에는 rust라고 적혀 있어.

토파즈 문서는 같은 입력의 결과를 견주어 볼 때 이 참조 컴파일러를 쓰는 예를 보여 줘., 새 창

기록에 적힌 것 1 + 2를 세 번 검사했어. 쪽지 받침에는 어느 컴파일러를 골랐는지 적혀 있어.

기록에 적힌 것 아무것도 고르지 않은 검사에는 current-default, 이름을 붙여 고른 검사에는 explicit이라고 적혀.

이 사이트가 맞대 본 것 화면 창의 두 줄은 세 번 모두 바이트까지 같았어.

두 컴파일러가 같은 일을 하는지 나란히 견주는 건 더 먼 바퀴의 몫이야.

원문 파일 topaz compiler statustopaz compiler status --json

출처 기록 읽어 보기

제4화에서 모모의 카드를 남긴 1 + 2 관측에는 provenance.json이라는 출처 파일도 있어.

기록에 적힌 것 제4화 출처에는 생성한 Rust가 연이은 두 단계에서 바이트까지 같았다고 적혀 있어.

기록에 적힌 것 중간 기록의 고정점은 성립했다고 적혀 있지 않아.

기록에 적힌 것 실행 파일의 바이트 대조에는 아직 돌리지 않았다는 값이 있어.

기록에 적힌 것 이 컴파일러의 링크 도구 기록에는 rustc 1.96.0이 적혀 있어.

이 사이트가 맞대 본 것 5.21.0 판과 5.21.1 판의 출처 기록에서 고른 아홉 항목의 값이 같았어. 두 출처 파일의 전체 바이트는 달랐어.

씨앗이라는 꾸러미

토파즈 5.21.1의 컴파일러 씨앗은 압축 꾸러미 하나와 그 목록으로 공개돼 있어.

이 사이트가 맞대 본 것 씨앗 꾸러미에는 토파즈로 쓴 컴파일러 소스, 거기서 생성한 Rust, 함께 쓰는 런타임 코드와 빌드 스크립트가 있어.

이 사이트가 맞대 본 것 설치된 컴파일러의 출처와 씨앗 목록에서 식별자 셋이 맞았어.

이 사이트가 맞대 본 것 꾸러미에서 꺼낸 Rust 파일의 해시도 목록에 적힌 값과 같았어.

씨앗 꾸러미
topaz-bootstrap-seed-v5.21.1.tar.gz
꾸러미 바이트
1,926,265
꾸러미 sha256
cf6a09b66fa5197472808fc01388e799b394686fb705b8e977cc39962583e974

맞대 본 식별자 셋

  • 설치된 컴파일러의 출처compiler.sourceSetIdsha256:17d598ebcb0dea65195aba8c0b3ab20f94003a7f343714f7b1df25d790cdacd5
    씨앗 목록sourceReuse.compilerSourceSetIdsha256:17d598ebcb0dea65195aba8c0b3ab20f94003a7f343714f7b1df25d790cdacd5
  • 설치된 컴파일러의 출처compiler.runtimeSourceSetIdsha256:85ed8d3b0571c34bc40c3ffd1be8bcda2a539d6c82c92b417c834507200d9c60
    씨앗 목록sourceReuse.runtimeSourceSetIdsha256:85ed8d3b0571c34bc40c3ffd1be8bcda2a539d6c82c92b417c834507200d9c60
  • 설치된 컴파일러의 출처compiler.generatedRustSha256sha256:5f32cee07a1adea1c67ba8ac8fbd4f3ee4051f019a7856fcdb228740a1d4b8b1
    씨앗 목록sourceReuse.generatedRustSha256sha256:5f32cee07a1adea1c67ba8ac8fbd4f3ee4051f019a7856fcdb228740a1d4b8b1

꾸러미에서 꺼낸 파일로 다시 구한 해시 generated/compiler.rs 5f32cee07a1adea1c67ba8ac8fbd4f3ee4051f019a7856fcdb228740a1d4b8b1

이번에 사이트가 한 일은 여기까지야.

이 사이트가 맞대 본 것 5.21.0 판과 5.21.1 판의 씨앗 목록에서 컴파일러의 .tpz 파일 50개를 같은 경로끼리 맞대 봤어. 아홉 파일의 해시가 달랐어. 우리가 펼친 src/main.tpz의 해시는 두 판에서 같았어.

해시가 달랐던 파일

  • checker/call_complete.tpz
  • checker/call_facts.tpz
  • checker/expression_control.tpz
  • checker/expression_core.tpz
  • checker/members_capabilities.tpz
  • checker/statements.tpz
  • checker/type_algebra.tpz
  • emitter/emit.tpz
  • resolver/scopes_walk.tpz

기록에 적힌 것 발행자는 씨앗 꾸러미를 빌드했는지 built에, Rust를 다시 만들었는지 regenerated에 적어 두었어.

built
true
regenerated
true

기록에 적힌 것 씨앗의 안내문은 이렇게 적어. The compiler written in Topaz performs parsing, checking, lowering and Rust generation.

다음 바퀴에서 물어볼 것

다시 만든 Rust는 꾸러미에 든 Rust와 바이트까지 같을까?

옮겨 온 소스는 같은 판의 라이선스와 고지를 따라. LICENSE 읽기, 새 창 NOTICE 읽기, 새 창

한 줄 정리

오늘 알게 된 것

컴파일러도 누군가 어떤 언어로 써 놓은 프로그램이야.

한줄이를 검사한 토파즈의 기본 컴파일러는 토파즈로 썼어.

씨앗이 펼친 src/main.tpz가 컴파일러의 시작 파일이라는 건 어디에서 찾았을까?

답 펼쳐 보기

설정 파일 topaz.toml에 entry = "src/main.tpz"라고 적혀 있어.

그 길을 따라가서 컴파일러를 쓴 토파즈 코드를 읽었어.

이제 컴파일러를 보면 그 뒷면의 소스도 떠오르겠지?

속사정 도감낱말 다시 보기컴파일러셀프호스팅 컴파일러

돌아보기

첫 바퀴 · 01~06

  1. 01

    톡톡과 1 + 2를 조각으로 나눠 봤어.

    제1화 다시 읽기
  2. 02

    가지와 더하기의 두 수가 나무에서 어디에 붙는지 봤어.

    제2화 다시 읽기
  3. 03

    타미와 1 + "사과"에서 타입 검사가 왜 멈췄는지 읽었어.

    제3화 다시 읽기
  4. 04

    모모의 작업 카드에서 1 + 2의 두 수를 다시 찾았어.

    제4화 다시 읽기
  5. 05

    뚝딱이와 출력하는 줄을 넣은 프로그램의 기록을 읽었어. 화면 창에 시작과 3이 차례로 남았어.

    제5화 다시 읽기
  6. 06

    씨앗과 지도를 뒤집어, 우리 코드를 검사한 컴파일러의 소스를 펼쳐 봤어.

첫 바퀴에서는 토파즈 5.21.1로 1 + 2와 몇 가지 다른 예를 살펴봤어.

다음 바퀴는 톡톡의 정거장에서 새 질문으로 시작해.

다음 이야기

제7화띄어 쓴 자리, 줄 바꾼 자리

톡톡나 아직 할 말이 남았어. 빈칸 이야기야.

길의 첫 정거장에서 톡톡이 가위를 다시 들었어.

빈칸과 줄바꿈은 언제 뜻이 될까?

다음에는 첫 정거장으로 돌아가 보자.