mojo's Blog
Bottom-up Parsing 본문
Bottom-up Parsing
Bottom-up parsing:
- Input string을 읽어가면서 reduce에 의해 start symbol을 찾아가는 방법
- Input string이 start symbol로 reduce되면 올바른 문장이라고 판단, 파스 트리 출력
- 그렇지 않으면 문법에 맞지 않은 문장으로 판단, 에러 메시지를 출력한다.
reduce, handle:
S ⇒* αAw ⇒ αβw의 유도 과정이 존재할 때
- reduce: sentential form αβw에서 β를 A로 대체하는 것
- handle: β. 즉 한 sentential form에서 reduce되는 부분.
ex ) rightmost derivation 과정을 보이고 parse tree를 그리고 handle 을 찾기
Sentence:
id + id ∗ id
① E → E + T
② E → T
③ T → T * F
④ T → F
⑤ F → (E)
⑥ F → id
1. rightmost derivation 과정을 보이자.
E => E + T (1)
=> E + T * F (3)
=> E + T * id (6)
=> E + F * id (4)
=> E + id * id (6)
=> T + id * id (2)
=> F + id * id (4)
=> id + id * id (6)
따라서 right parse : 6 4 2 6 4 6 3 1
2. parse tree 를 보이자.
3. handle 을 찾아보자.
Sentential form | handle | production rule |
id + id * id | id1 | F -> id (6) |
F + id * id | F | T -> F (4) |
T + id * id | T | E -> T (2) |
E + id * id | id2 | F -> id (6) |
E + F * id | F | T -> F (4) |
E + T * id | id3 | F -> id (6) |
E + T * F | T * F | T -> T * F (3) |
E + T | E + T | E -> E + T (1) |
E |
꿀팁 : production rule 부분에 right parse 를 차례로 작성한 다음에 구하자.
문법이 unambiguous하면 단 하나의 핸들만 존재한다: deterministic parsing 가능
handle pruning
S ⇒ ω1 ⇒ ω2 ⇒ ··· ⇒ ωn-1 ⇒ ωn = w의 rightmost derivation 과정이 있을 때,
- handle pruning: 각 ωi 의 sentential form에서 핸들을 찾아 ωi-1 로 reduce하면서
start symbol로 가는 과정
Shift-reduce parsing
Shift-reduce Parser:
stack과 input buffer를 사용하여 구현
- Stack: handle을 찾기위한 grammar symbols을 유지. $로 초기화
- Input buffer: parsing될 input string을 유지. w$로 초기화
다음과 같은 행동을 갖는다:
- Shift: 현재의 input symbol을 스택의 top에 옮기는 것을 의미.
이 과정은 스택의 톱에 핸들이 나타날 때까지 계속
- Reduce: 핸들의 오른쪽 끝이 스택의 top에 나타나면 핸들의 왼 쪽 끝을 찾아서 완전한 형태의 핸들을 찾은 다음, rhs가 핸들인 production rule을 찾아 lhs에 있는 기호로 대체되는 것
- Accept: 주어진 string이 주어진 문법에 맞다는 것을 나타냄
- Error: 주어진 string이 주어진 문법에 맞지 않다는 것을 나타냄
① 초기 상태에서 시작하여 스택의 top에 핸들이 나타날 때까지 input symbol을 하나씩
stack으로 옮김 (shift).
② Shift를 하다가 핸들을 찾으면 production rule을 찾아 lfs에 있는 symbol로 reduce하고
부분 파스 트리를 구성한다.
③ 같은 방법으로 계속 진행한다. 핸들이 찾아지지 않거나 입력 버퍼가 비었는데 시작 기호가
나타나지 않으면 에러 출력.
④ 입력 문자열을 모두 읽은 후 start symbol을 만나면 accept.
ex ) Shift-reduce parsing 을 해보자.
Sentence: id + id ∗ id
① E → E + T
② E → T
③ T → T * F
④ T → F
⑤ F → (E)
⑥ F → id
Step | Stack | Input symbol | Action |
1 | $ | id + id * id$ | shift id1 |
2 | $id1 | + id * id$ | Reduce F -> id |
3 | $F | + id * id$ | Reduce T -> F |
4 | $T | + id * id$ | Reduce E -> T |
5 | $E | + id * id$ | shift + |
6 | $E + | id * id$ | shift id2 |
7 | $E + id | * id$ | Reduce F -> id |
8 | $E + F | * id$ | Reduce T -> F |
9 | $E + T | * id$ | shift * |
10 | $E + T * | id$ | shift id |
11 | $E + T * id | $ | Reduce F -> id |
12 | $E + T * F | $ | T -> T * F |
13 | $E + T | $ | E -> E + T |
14 | $E | $ | Accept! |
ex ) Shift-reduce parsing 을 해보자 (위와는 약간 다른 Rule을 가진)
Sentence: id + id ∗ id
① E → E + E
② E → E ∗ E
③ E → id
④ E → (E)
먼저 rightmost derivation 을 구해보자.
E => E + E (1)
=> E + E * E (2)
=> E + E * id (3)
=> E + id * id (3)
=> id + id * id (3)
Step | Stack | Input symbol | Action |
1 | $ | id + id * id$ | shift id |
2 | $id | + id * id$ | Reduce E -> id |
3 | $E | + id * id$ | shift + |
4 | $E + | id * id$ | shift id |
5 | $E + id | * id$ | Reduce E -> id |
6 | $E + E | * id$ | shift * |
7 | $E + E * | id$ | shift id |
8 | $E + E * id | $ | Reduce E -> id |
9 | $E + E * E | $ | Reduce E -> E * E |
10 | $E + E | $ | Reduce E -> E + E |
11 | $E | $ | Accept |
단계 5에서 id를 E로 reduce 하였지만, ∗ 를 shift 할 수도 있음
단계 6에서 ∗ 를 shift 했지만 E + E가 핸들이 될 수도 있음
즉, 문제가 있는 parsing 임을 알 수 있다.
Limitations:
- 핸들을 어떻게 찾을 것인가?
- 어떤 production rule을 적용할 것인가?
Solution:
- LR parsing
LR parser
LR(k) parser (Left-to-right and Right parse):
- L: input string을 왼쪽에서 오른쪽으로 읽어가며, R: right parse를 생성
- k: 한번에 읽는 input symbol의 개수. k=1일 경우 생략 가능
Strengths:
- Unambiguous CFG 로 쓰여진 모든 언어에 대해 구성 가능
- Deterministic parsing
- backtracking이 없음
- 에러 검출이 용이
Weakness:
- Top-down parser에 비해 구현이 복잡함
Parsing table in LR parser: action과 GOTO 로 구성
Variants: parsing table을 구성하는 방법에 따라서 분류
- SLR (simple LR): LR(0) item sets + FOLLOW 로 구성
- CLR (canonical LR): LR(1) item sets으로 구성
- LALR (lookahead LR): LR(0) item sets + lookahead 또는 LR(1) item sets으로 구성
LR(k) Grammar
- LR(k) grammar는 모든 entry에 대해 유일하게 정의되는 parsing table을 만들 수 있다.
Augmented Grammar
- Grammar G = (VN, VT, P, S)에 augment(첨가)된 문법
- G' = (VN∪{S'}, VT, P∪{S'→S}, S')로 표현됨
- S': start symbol
- S'→S : augmented production rule
Note:
production rule S'→S를 추가하는 이유
- Parser로 하여금 S를 S’로 reduce할 경우 이제 파싱을 끝내고 문장을 Accept 하도록 설계하기 위하여 사용
ex ) Augmented grammar를 만들어보기.
① E → E + E
② E → E * E
③ E → id
④ E → (E)
P :
E -> E + E | E * E | id | (E)
에서 S' -> E 를 추가해주자.
P' :
S' -> E
E -> E + E | E * E | id | (E)
따라서 G = ({S', E}, {+, *, id, (, )}, P', S') 이다.
LR(0) items
LR(0) item - rhs에 dot symbol를 가진 production rule이다
Production rule A → BCD의 LR(0) items:
[A → •BCD], [A → B•CD], [A → BC•D], [A → BCD•]
- A → B•CD: B를 읽었고 CD는 앞으로 읽을 기호임을 나타낸다.
- A → BCD•: BCD를 읽었고 이제 BCD를 A로 reduce할 수 있음을 의미한다.
Marking:
LR(0) item을 얻기 위해 production rule에 dot symbol을 추가하는 것
Mark symbol:
LR(0) item에서 dot symbol 오른쪽에 있는 기호
Three types of LR(0) items - Kernel item:
[A → α•β]에서 α ≠ ε이거나 A = S′인 경우
- Closure item: dot symbol이 rhs 맨 왼쪽에 있는 경우 (e.g., [A →•α])
- Reduce item: dot symbol이 rhs 맨 오른쪽에 있는 경우 (e.g., [A → α•])
Canonical collection (중요):
- 어떤 문법의 LR(0) items로 구성된 set
- LR parse table을 구성하는데 반드시 필요
CLOSURE(I)
CLOSURE(I), I는 문법 G에 대한 LR(0) item set.
CLOSURE(I)는 다음 규칙으로 구한다:
- 먼저 I에 속한 모든 LR(0) items를 CLOSURE(I)에 추가한다.
- CLOSURE(I) = I ∪ {[B → •γ] | [A → α•Bβ] ∈ CLOSURE(I), B→γ ∈P}
(1) [A → α•Bβ] ∈ CLOSURE(I)이고 B → γ가 존재하는 경우에, B →•γ가 CLOSURE(I)에 포함되어
있지 않으면 이 item을 CLOSURE(I)에 추가한다.
(2) 이 규칙은 새로운 LR(0) item이 CLOSURE(I)에 더 이상 추가될 수 없을 때까지 반복적으로
계속 적용된다.
CLOSURE(I) Algorithm
[입력] 문법 G, LR(0) item sets I
[출력] CLOSURE(I)
[방법]
begin
CLOSURE(I) = I;
repeat
if [A → α•Bβ]∈ CLOSURE(I) and B→γ ∈P
then CLOSURE(I) = CLOSURE(I) ∪[B → •γ]
until CLOSURE가 변하지 않을 때까지
end
ex ) LR(0) item E′ → •E와 E → E +•T의 CLOSURE를 구해보자.
Eʹ → E
E → E + T | T
T → T * F | F
F → (E) | id
CLOSURE([E′ → •E])
= {[E′ → •E], [E → •E + T], [E → •T], [T → •T * F], [T → •F], [F → •(E)], [F → •id]}
CLOSURE([E → E + •T])
= {[E → E + •T], [T → •T * F], [T → •F], [F → •(E)], [F → •id]}
GOTO function
LR(0) item의 GOTO Function
- Item set I와 X ∈ V에 대해, GOTO(I, X) = CLOSURE({[A → αX•β] | [A → α•Xβ] ∈ I}).
- Parsing을 위해서 mark symbol을 하나씩 읽는다는 의미
ex ) I = {[E′ → E•], [E → E•+ T]}일 때 GOTO(I, +)를 계산해보자.
P:
Eʹ → E
E → E + T | T
T → T * F | F
F → (E) | id
GOTO(I, +) = CLOSURE([E → E•+ T]) // 이때 {+} ∈ V 이며 [E → α•+β] ∈ I 를 만족하며
// [E′ → E•] 는 {+} 기호가 없으므로 만족하지 못함
CLOSURE([E → E + •T])
= {[E → E + •T], [T → •T * F], [T → •F], [F → •(E)], [F → •id]}
'학교에서 들은거 정리 > 기초컴파일러구성' 카테고리의 다른 글
Semantic Analysis (0) | 2021.11.20 |
---|---|
LALR Parsing (0) | 2021.11.12 |
CLR Parse / LALR Parse (0) | 2021.11.05 |
SLR Parsing / CLR Parser (0) | 2021.11.04 |
Canonical Collection (0) | 2021.10.30 |