Bottom-up parsing
Bottom-up paring(상향식 구문 분석):
- Input string을 읽어가면서 reduce에 의해 start symbol을 찾아가는 방법
- Input string이 start symbol로 reduce되면 올바른 문장이라고 판단, 파스트리 출력
- 그렇지 않으면 문법에 맞지 않은 문자으로 판단, 에러 메시지를 출력한다.
- Bottom-up parsing(상향식 구문 분석)은 rightmost derivation(우단 유도)의 역순이다.
reduce, handle:
S⇒*ɑAw⇒ɑβw의 유도 과정이 존재할 때
- reduce(감축) : sentential form ɑβw에서 β를 A로 대체하는 것
- handle(핸들) : β. 즉 한 sentential form에서 reduce되는 부분
Example
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 |
문법이 unambiguous하면 단 하나의 핸들만 존재한다 : deterministic parsing 가능
handle pruning
S⇒w1⇒w2⇒...⇒wn-1⇒wn=w의 rightmost derivation과정이 있을 때,
handle pruning : 각 wi의 sentential form에서 핸들을 찾아 wi-1로 reduce하면서 start symbol로 가는 과정이다.
Shift-reduce parsing
Shift-reduce parsing(이동-감축 구문분석)
Shift-reduce Parser
- stack과 input buffer를 사용하여 구현
- Stack : handle을 찾기 위한 grammar symbols을 유지. $로 초기화
- Input buffer : parsing될 input string을 유지. w$로 초기화(w : 입력 문자열)
Shift(이동) : 현재의 input symbol을 스택의 top에 옮기는 것을 의미. 이 과정은 스택의 톱에 핸들이 나타날 때까지 계속
Reduce(감축) : 핸들의 오른쪽 끝이 스택의 top에 나타나면 핸들의 왼쪽 끝을 찾아서 완전한 형태의 핸들을 찾은 다음, rhs가 핸들인 production rule을 찾아 lhs에 있는 기호로 대체되는 것
Accept : 주어진 string이 주어진 문법에 맞다는 것을 나타냄
Error : 주어진 string이 주어진 문법에 맞지 않다는 것을 나타냄
Shift-reduce parsing 순서
- 초기 상태에서 시작하여 스택(왼쪽)의 top에 핸들이 나타날 때까지 input symbol을 하나씩 stack으로 옮김(shift)
- Shift를 하다가 핸들을 찾으면 production rule을 찾아 lfs에 있는 symbol로 reduce하고 부분 파스 트리를 구성한다.
- 같은 방법으로 계속 진행한다. 핸들(바꿔줘야 할 것)이 찾아지지 않거나 입력 버퍼가 비었는데 시작 기호가 나타나지 않으면 에러 출력
- 입력 문자열을 모두 읽은 후 start symbol을 만나면 accept
Example
Step | Stack | Input symbol | Action |
1 | $ | id+id*id$ | shift id1 |
2 | $id | +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 id |
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 | $ | Reduce T->T*F |
13 | $E+T | $ | Reduce E->E+T |
14 | $E | $ | Accept |
Step | Stack | Input symbol | Action |
1 | $ | id+id*id$ | Shift id1 |
2 | $id | +id*id$ | Reduce E->id |
3 | $E | +id*id$ | Shift + |
4 | $E+ | id*id$ | Shift id2 |
5 | $E+id | *id$ | Reduce E->id |
6 | $E+E | *id$ | Shift * |
7 | $E+E* | id$ | Shift id3 |
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 |
이 문제에서 Shift-reduce parsing의 한계가 보이는데 5단계에서 id를 E로 reduce하였지만, *를 shift할 수도 있고, 6단계에서 *를 shift했지만 E+E가 핸들이 될 수도 있다.
핸들을 어떻게 찾느냐와 어떤 production rule을 적용하는지에 따라서 parsing의 결과가 달라 질 수 있기 때문에 shift-reduce 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을 만들 수 있다.(entry는 두개 이상 올 수 없다.)
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)
- S'->S를 추가하는 이뉴는 Parser로 하여금 S를 S'로 reduce할 경우 이제 파싱을 끝내고 문장을 Accept 하도록 설계하기 위하여 사용한다.
LR(0) items
LR(0) item
- rhs에 dot symbol를 가진 production rule이다.
ex) 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 symbold 오른쪽에 있는 기호
Three types of LR(0) items
- Kernel item : [A->ɑ·β]에서 ɑ≠𝜀 이거나 A=S'인 경우
- Closure item : dot symbol이 rhs 맨 왼쪽에 있는 경우 ( ex : [A->·BCD] )
- Reduce item : dot symbol이 rhs 맨 오른쪽에 있는 경우 ( ex : [A->BCD·] )
Canonical collection
- 어떤 문법의 LR(0) items로 구성된 set
- LR parse table을 구성하는데 반드시 필요
CLOSURE(I)
CLOSURE(I) 구하는 법
- 먼저 I에 속한 모든 LR(0) items를 CLOSURE(I)에 추가한다.
- CLOSURE(I) = I ∪ { [B->·𝛾] | [A->ɑ·Bβ] ∈ CLOSURE(I), B->𝛾 ∈P }
CLOSURE(I) algorithm
begin
CLOSURE(I) = I;
repeat
if [A -> ɑ·Bβ] ∈ CLOSURE(I) and B->𝛾 ∈P
then CLOSURE(I) = CLOSURE(I) ∪ [B->·𝛾]
until CLOSURE가 변하지 않을 때까지
end
CLOSURE([E'->·E]) = { [E'->·E], [E->·E+T], [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을 하나씩 읽겠다는 의미
Example
I = { [E'->E·], [E->E·+T] } 일 때 GOTO(I,+)를 계산해보자.
Canonical collection
LR(0) item set의 canonical collection C0
Example
SLR parser
SLR parsing table 만드는 법
- LR(0) item set과 FOLLOW를 구한다.
- LR(0) item의 canonical collection으로부터 Shift/GOTO action을 구한다.
- Production rule에 있는 nonterminals의 FOLLOW로 부터 reduce를 결정한다.
SLR parser implementation
- Grammar G가 주어진다.
- Augmented grammar G'를 만든다.
- LR(0) item sets의 canonical collection C0를 구한다.
- Reduce action을 결정하기 위해 FOLLOW를 계산한다.
- SLR parsing table을 작성한다.
State | Action | GOTO | |||||||
id | + | * | ( | ) | $ | E | T | F | |
0 | s5 | s4 | 1 | 2 | 3 | ||||
1 | s6 | ACC | |||||||
2 | r2 | s7 | r2 | r2 | |||||
3 | r4 | r4 | r4 | r4 | |||||
4 | s5 | s4 | 8 | 2 | 3 | ||||
5 | r6 | r6 | r6 | r6 | |||||
6 | s5 | s4 | 9 | 3 | |||||
7 | s5 | s4 | 10 | ||||||
8 | s6 | s11 | |||||||
9 | r1 | s7 | r1 | r1 | |||||
10 | r3 | r3 | r3 | r3 | |||||
11 | r5 | r5 | r5 | r5 |
실수 안하는 팁(순서대로 확인)
- reduce 할 수 있는지 확인
- nonterminal로 가는거 확인
- terminal 가는거 확인
LR parsing algorithm
[입력] Input string w와 LR parse table
[출력] 만약 w가 문법에 맞으면 w에 대한 파스 트리를 생성하고, 그렇지 않으면 에러
Step | Stack | Input string | Action |
0 | 0 | id*(id*id)$ | s5 |
1 | 0id5 | *(id*id)$ | r6 |
2 | 0F | *(id*id)$ | GOTO 3 |
3 | 0F3 | *(id*id)$ | r4 |
4 | 0T | *(id*id)$ | GOTO 2 |
5 | 0T2 | *(id*id)$ | s7 |
6 | 0T2*7 | (id*id)$ | s4 |
7 | 0T2*7(4 | id*id)$ | s5 |
8 | 0T2*7(4id5 | *id)$ | r6 |
9 | 0T2*7(4F | *id)$ | GOTO 3 |
10 | 0T2*7(4F3 | *id)$ | r4 |
11 | 0T2*7(4T | *id)$ | GOTO 2 |
12 | 0T2*7(4T2 | *id)$ | s7 |
13 | 0T2*7(4T2*7 | id)$ | s5 |
14 | 0T2*7(4T2*7id5 | )$ | r6 |
15 | 0T2*7(4T2*7F | )$ | GOTO 10 |
16 | 0T2*7(4T2*7F10 | )$ | r3 |
17 | 0T2*7(4T | )$ | GOTO 2 |
18 | 0T2*7(4T2 | )$ | r2 |
19 | 0T2*7(4E | )$ | GOTO 8 |
20 | 0T2*7(4E8 | )$ | s11 |
21 | 0T2*7(4E8)11 | $ | r5 |
22 | 0T2*7F | $ | GOTO 10 |
23 | 0T2*7F10 | $ | r3 |
24 | 0T | $ | GOTO 2 |
25 | 0T2 | $ | r2 |
26 | 0E | $ | GOTO 1 |
27 | 0E1 | $ | acc |
'3-2 > 기초컴파일러' 카테고리의 다른 글
LALR (0) | 2021.11.14 |
---|---|
CLR parser (0) | 2021.11.07 |
Recursive-decent, Predictive Parsing (0) | 2021.10.16 |
Syntax Analysis (0) | 2021.10.16 |
Pushdown Automata (0) | 2021.10.16 |