Table of Contents

한눈에 보는 파이프라인

시작하기에서 5줄로 파싱을 돌려봤죠.
그 5줄 안에서는 사실 여러 단계가 순서대로 일어납니다.
이 장에서는 그 전체 흐름(파이프라인) 을 큰 그림으로 봅니다.
세부는 각자 자기 장에서 다루니, 여기선 "지도"만 머리에 넣으세요.

텍스트가 결과가 되기까지

우리 입력 a + a * a가 어떤 길을 걷는지 따라가 봅시다.

   "a + a * a"            ← ① 그냥 글자들의 나열 (텍스트)
        │
        ▼  렉싱 (Lexing)
   [a] [+] [a] [*] [a]    ← ② 의미 있는 조각 = 토큰(token)들
        │
        ▼  파싱 (Parsing)
        Expr              ← ③ 문법 구조 = 파스 트리(parse tree)
       /  |  \
     Expr '+' Term
      |       / | \
     Term  Term '*' Factor
      ...
        │
        ▼  (그 다음 단계들 — 의미 분석, 코드 생성 …)
     실행 / 번역 / 컴파일

각 화살표가 하나의 변환 단계입니다.

① 텍스트 → ② 토큰 : "렉싱(Lexing)"

컴퓨터에게 a + a * a는 처음엔 공백 포함 9글자일 뿐이에요.
첫 단계는 이걸 의미 있는 최소 단위(토큰) 로 쪼개는 겁니다.

  • a → "이름(식별자)" 토큰
  • + → "더하기 연산자" 토큰
  • * → "곱하기 연산자" 토큰
  • 공백 → 버림

이 일을 하는 게 렉서(lexer) 또는 토크나이저(tokenizer) 예요.

② 토큰 → ③ 트리 : "파싱(Parsing)"

이제 토큰들이 문법에 맞게 배열됐는지 확인하고, 그 구조를 트리로 만듭니다.
예를 들어 a + a * a에서 곱하기가 더하기보다 먼저 묶여야 한다는 것(우선순위)도 이 트리 모양으로 드러납니다.

a + a * a   →   a + (a * a)      ← '*'가 더 깊이(먼저) 묶임

이 일을 하는 게 파서(parser) 입니다.
그리고 Janglim이 집중하는 부분이 바로 여기예요.

③ 트리 → 그 다음

파스 트리가 나오면, 그 뒤로는 목적에 따라 갈립니다.
계산기라면 트리를 따라 값을 계산하고, 컴파일러라면 의미를 분석하고 기계어/LLVM IR로 번역합니다.
Janglim이 dogfooding(자기 검증) 용으로 만든 AJ 언어는 여기까지 갑니다.

LR 파서의 숨은 0단계: "테이블 미리 만들기"

위 그림에는 사실 숨은 단계가 하나 더 있어요.
파서는 토큰을 받기 전에, 문법을 분석해서 파싱 테이블(parse table) 이라는 거대한 표를 미리 계산합니다.

이 표는 "지금 이런 상태이고 다음 토큰이 이거라면 → 이렇게 행동하라"를 전부 적어둔 행동 지침서예요.
파싱할 때 파서는 생각하지 않고 이 표만 보고 기계적으로 움직입니다.
그래서 LR 파싱은 빠르고 정확해요.

💡 그래서 LR 파서를 "결정적(deterministic) 파서"라고 불러요. 표의 각 칸에는 행동이 딱 하나로 정해져 있어요. 그래서 파서는 매 순간 갈림길에서 고민하거나 되돌아가지(backtrack) 않고 한 길로만 갑니다. 이렇게 다음 행동이 늘 유일하게 정해지는 파서를 결정적 파서라고 하고, LR이 그 대표예요. (문법에 충돌이 있거나, 여러 갈래를 탐색하는 GLR/LGLR 방식을 쓰면 결정적이 아니게 되는데 — 그건 뒤의 충돌 장에서 다뤄요.)

문법  ──(미리 분석)──▶  파싱 테이블  ──(파싱 때 참고)──▶  결정(shift? reduce?)

이 표를 만들려면 FIRST/FOLLOW, LR 아이템, 정준 집합 같은 개념이 필요합니다.
이 표를 만드는 여정이 곧 매뉴얼의 본론이에요.
우리는 FIRST/FOLLOW부터 시작합니다.

코드의 층(layer) — 살짝만

Janglim 내부는 여러 모듈이 층층이 쌓여 있습니다.
아래로 갈수록 더 기초적인 부품이에요.

  ┌─────────────────────────────────────────────┐
  │  AJ 언어 → 의미분석 → LLVM IR (컴파일러)        │  ← 윗단 (응용)
  ├─────────────────────────────────────────────┤
  │  파서 엔진: 정준 집합 · 파싱 테이블 · 드라이버    │
  ├─────────────────────────────────────────────┤
  │  문법(Grammar) 컨테이너                         │
  ├─────────────────────────────────────────────┤
  │  심볼 · 렉서 · FIRST/FOLLOW · 정규화 · 파스트리  │  ← 핵심 부품 모음
  ├─────────────────────────────────────────────┤
  │  토큰 타입 · 공통 헬퍼                          │  ← 밑단 (기초)
  └─────────────────────────────────────────────┘

재밌는 점: FIRST/FOLLOW가 밑단 근처 에 있죠? 코드 구조상으론 아주 기초 부품이에요. 하지만 개념을 배우는 순서로는 파싱 테이블 직전에 배우는 게 가장 잘 이해됩니다(왜 필요한지가 그때 와닿거든요). 그래서 이 매뉴얼은 코드 층 순서가 아니라 이해 순서로 갑니다. 각 페이지마다 "이 개념은 어느 층/파일"인지는 따로 표시해 둘게요.

전체 모듈 층 지도는 나중에 부록 에서 따로 정리할 거예요.

다음 장

이제 지도를 손에 넣었으니, LR 파싱의 첫 핵심 개념으로 들어갑시다.

👉 FIRST / FOLLOW