콘텐츠로 이동

Bottom-Up 파싱 완전 정복: 개념부터 LR 파싱까지

Bottom-Up 파싱은 입력 문자열을 잎(leaves, 단말 기호)에서부터 시작 심볼(root)으로 환원해 나가는 방식으로, 오른쪽 최단 유도(Rightmost Derivation)를 역추적합니다.


1. Bottom-Up 파싱 개요

flowchart LR
  A[Leaves (단말 기호)] --> B[Reduce: 프로덕션 역적용]
  B --> C[비단말 기호 환원]
  C --> D{시작 심볼인가?}
  D -- 아니면 --> A
  D -- 예 --> E[Accept]
  classDef accept fill:#cfc,stroke:#393
  class E accept

2. Shift-Reduce 파싱

flowchart LR
  SHIFT[Shift: 토큰을 스택에 push]
  REDUCE[Reduce: RHS → LHS 환원]
  ACCEPT[Accept: 시작 심볼만 남으면 성공]
  ERROR[Error: 파싱 불가]

  SHIFT --> REDUCE
  REDUCE --> SHIFT
  REDUCE --> ACCEPT
  SHIFT --> ERROR
  classDef shift fill:#9f9,stroke:#393
  classDef reduce fill:#ffeb99,stroke:#c90
  classDef accept fill:#cfc,stroke:#393
  classDef error fill:#f99,stroke:#900

3. Handle (핸들) 추적

flowchart LR
  subgraph "스택 상태"
    S0[$]
    S1[$ n]
    S2[$ E]
    S3[$ E +]
    S4[$ E + n]
    S5[$ E]
    S6[$ E']
  end
  S0 -->|shift n| S1
  S1 -->|reduce E→n| S2
  S2 -->|shift +| S3
  S3 -->|shift n| S4
  S4 -->|reduce E→E+n| S5
  S5 -->|reduce E'→E| S6
  classDef shiftState fill:#9f9,stroke:#393
  classDef reduceState fill:#ffeb99,stroke:#c90
  class S1,S3,S4 shiftState
  class S2,S5,S6 reduceState

4. 예시: n + n Shift-Reduce 파싱

sequenceDiagram
    participant Stack as 스택
    participant Input as 입력
    participant Action as 액션

    Stack->>Stack: $
    Input-->>Stack: n
    Action-->>Stack: Shift(n)

    Stack->>Stack: $ n
    Action-->>Stack: Reduce(E→n)

    Stack->>Stack: $ E
    Input-->>Stack: +
    Action-->>Stack: Shift(+)

    Stack->>Stack: $ E +
    Input-->>Stack: n
    Action-->>Stack: Shift(n)

    Stack->>Stack: $ E + n
    Action-->>Stack: Reduce(E→E+n)

    Stack->>Stack: $ E
    Action-->>Stack: Reduce(E'→E)

    Stack->>Stack: $ E'

5. LR 파싱 구조

flowchart TB
  Input["Input 버퍼: a₁ a₂ ... aₙ $"]
  Parser["LR Parser"]
  Stack["스택<br/>(상태, 기호)"]
  Table["Parsing Table"]

  Input --> Parser
  Parser --> Stack
  Parser --> Table
  Table -- "Action/Goto" --> Parser

  classDef parser fill:#def,stroke:#369
  classDef table fill:#bbf,stroke:#369
  class Parser parser
  class Table table

6. 파싱 테이블 생성

flowchart LR
  Items["LR 아이템<br/>(A → α · β)"]
  DFA["LR DFA 상태 집합"]
  Table["Action & Goto 테이블"]

  Items --> DFA
  DFA --> Table

  classDef items fill:#bbf,stroke:#369
  classDef dfa fill:#fbd,stroke:#963
  class Items items
  class DFA dfa

7. 파싱 충돌 (Conflicts)

flowchart LR
  SR[Shift-Reduce 충돌] -->|우선순위 지정| Fix1[Operator Precedence]
  RR[Reduce-Reduce 충돌] -->|문법 개선| Fix2[Grammar Refactoring]
  class SR,RR fill:#fdd,stroke:#900
  class Fix1,Fix2 fill:#dfd,stroke:#090

8. 추가 고려사항

flowchart TB
  Aug[Augmented Grammar] --> Parser
  Parser --> Recovery[오류 복구]
  Amb[Inherent Ambiguity] --> Remark[고유 모호성]
  classDef Aug fill:#ddf,stroke:#339
  classDef Recovery fill:#ffd,stroke:#996
  classDef Amb fill:#fdd,stroke:#933

9. 좌측/우측 최단 유도 및 파싱 방향 차이

9.1 유도(Derivation) 방향

구분 좌측 최단 유도 (Leftmost Derivation) 우측 최단 유도 (Rightmost Derivation)
처리 순서 가장 왼쪽 비단말 먼저 가장 오른쪽 비단말 먼저
파스 트리 생성 위→아래, 왼쪽 자식 우선 위→아래, 오른쪽 자식 우선
핵심 질문 "다음에 무엇을 만들까?" "이것은 무엇으로 만들어졌을까?"

9.2 파서 스택 방향 (LL vs LR)

graph TB
  subgraph "LL 파서 (Top-Down)"
    direction LR
    S0["[S, $]"] --> Predict["Predict: S → aB"]
    Predict --> Push["Push: a, B"]
  end
  subgraph "LR 파서 (Bottom-Up)"
    direction LR
    Init["[$] + input 'ab'"] --> ShiftA["Shift: 'a'"]
    ShiftA --> ShiftB["Shift: 'b'"]
    ShiftB --> Reduce["Reduce: A → ab"]
  end
  • LL 파서: 스택 최상단에서 비단말을 예측(Predict) 및 확장(Expand).
  • LR 파서: 스택 최상단에서 핸들(Handle)을 인식(Reduce).