구문분석기 생성기 yacc
DESCRIPTION
구문분석기 생성기 YACC. 문법표현 + C 코드. Yacc. 원시 프로그램. 어휘분석기. y.tab.c. C 코드의 실행결과. Yacc. Yacc Yet Another Compiler Compiler 1975 년 , Johnson LALR(1) 파서 생성기. Lex 와의 결합. 어휘분석기와 구문분석기의 관계 구문분석기 : 토큰을 요구 Yacc: yylex() 를 호출 어휘분석기 : 입력 프로그램을 잘라서 토큰으로 전달 Lex: 토큰 값을 return UNIX 명령어 - PowerPoint PPT PresentationTRANSCRIPT
구문분석기 생성기 YACC
Yacc• Yacc
– Yet Another Compiler Compiler– 1975 년 , Johnson– LALR(1) 파서 생성기
문법표현 + C 코드Yacc
y.tab.c
원시 프로그램C 코드의 실행결과어휘분석기
Lex 와의 결합• 어휘분석기와 구문분석기의 관계
– 구문분석기 : 토큰을 요구• Yacc: yylex() 를 호출
– 어휘분석기 : 입력 프로그램을 잘라서 토큰으로 전달
• Lex: 토큰 값을 return
• UNIX 명령어% vi simple.l simple.y% lex simple.l% yacc -d simple.y% cc -o sim lex.yy.c y.tab.c -ly -ll
Lex Source• 수식문법을 위한 lex source
%{#include "y.tab.h"%}%%[0-9]+ return(NUMBER);[ \t] ;\n return(0);\+ return('+');\* return('*');. { printf("'%c': illegal character\n", yytext[0]); exit(-1); }
Yacc 의 입력• 입력의 구성
– 선언 부분• y.tab.c 에서 사용할 자료구조 , 변수 , 상수를 정의• 생성규칙 부분에서 사용되는 토큰의 이름을 정의
– 생성규칙 부분• 문법의 생성규칙을 기술• 생성규칙이 reduce 될 때 처리할 행위 (C 코드 ) 를 기술
선언 부분%%생성규칙 부분%%사용자 부프로그램 부분
선언 부분• 형태
• 정의 예%token NUMBER
%{ /* y.tab.c 에 복사될 내용 */%}%token 토큰이름 1 토큰이름 2 ...%start 시작문법기호%left 모호한 문법에서 사용%right ' '%nonassoc ' '
생성규칙 부분• 형태
– ::= 기호 대신에 : 기호를 사용– 문자토큰은 ' 와 ' 사이에 둠 ( 대소문자 구별은 없음 )– 생성규칙의 끝에는 ; 기호를 첨가
• 정의 예%%Exp : Exp '+' Term { printf("rule 1\n"); } | Term { printf("rule 2\n"); } ;
%%생성규칙 1 C 코드 1생성규칙 2 C 코드 2...생성규칙 n C 코드 n
Yacc 입력의 예• 수식문법의 예
%token NUMBER%%Exp : Exp '+' Term { printf("rule 1\n"); } | Term { printf("rule 2\n"); } ;
Term : Term '*' Num { printf("rule 3\n"); } | Num { printf("rule 4\n"); } ;
Num : NUMBER { printf("rule 5\n"); } ;
ToyPL 문법 검사기• 문제 : ToyPL 로 작성된 어떤 프로그램이
문법에 적합한지 검사하자 .– 입력 : ToyPL 로 작성되었다고 여겨지는
어떤 프로그램– 출력 : 문법에 맞지 않으면 “ syntax error” 를
출력– 방법 : ToyPL 문법을 yacc 으로 작성
Yacc 실습• 괄호문법
S ::= ( S ) S |
• 괄호문법을 위한 lex source– 괄호 이외의 문자는 모두 무시함
• 괄호문법을 위한 입력 예( a + b ) + ( 4 * ( ( 33 - sum ) / ( sum + 21 ) ) )
Yacc 의 상세 설명• yyerror()
– 문법에 맞지 않는 문장에 대한 오류 메세지– 사용자 부 프로그램 부분에 정의
%%yyerror() { printf("틀린 수식입니다 \n");}
• yyparse()– lex 의 yylex() 에 해당– 응용 예 : 파싱이 시작됨과 끝났음을 출력
• yacc -v– 파싱테이블을 y.output 에 출력
Yacc 실행체계
main()
yyerror()
yyparse() yylex()
main() { . . . yyparse(); . . .}
yyparse() { . . . while (1) { . . . t = yylex(); . . . yyerror(); . . . } . . .}
yylex() { . . . return(T); . . .}
구문지향 번역
구문지향 번역• 구문지향 번역 (syntax directed translation)
– 문법의 각 생성규칙에 대응하여 코드를 생성– 트리를 만들지 않음
• 비교– 트리를 사용한 번역에 비하여 간단하다– 1-pass 번역에만 적용 가능
수식 값의 계산• 수식 값의 계산
– 1+2*3 의 값 ?– 일반적인 방법
• 파스트리를 만든 후에 값을 계산– 다른 방법
• Yacc 에서 문법 기호의 값을 이용
• 문법 기호의 값– 생성규칙
A : B C D– 좌측 기호의 값 = $$– 우측 첫 기호의 값 = $1, 두번째의 값 = $2
문법 기호의 값• 1+2*3 의 유도 과정
Exp=> Exp + Term ( 규칙 1)=> Exp + Term * Num ( 규칙 3)=> Exp + Term * 3 ( 규칙 5)
• 문법규칙1. Exp ::= Exp + Term3. Term ::= Term * Num5. Num ::= 3
3
Term
Term
Num*
$$
$1 $2 $3
3
Exp
Term
Exp Term
Num
+
*
3
Num$$$1
예제• Yacc 의 예 1
A : B C D { printf("%d %d %d => %d", $1, $2, $3, $$); }
• Yacc 의 예 2E : E '+' T { printf("%d + %d = %d\n", $1, $3, $$); }
• Yacc 의 예 3%token NUMBER%%N : NUMBER { printf("%d\n", $1); $$ = $1; }
• 예 3 의 Lex[0-9]+ { yylval = atoi(yytext); return NUMBER; }
토큰의 값• 토큰의 값
– yylval 을 통하여 파서에게 전달• 토큰 값의 타입
– yylval 의 타입– YYSTYPE
• 여러 타입의 값을 허용하려면– C: union 을 사용– Yacc: %union 을 사용
%union• 토큰 값의 종류
– 정수 , 실수 , 스트링 , …– 여러 타입의 토큰을 파서에게 전달
• Yacc 에서 토큰의 타입을 정의%union { double dval; int vblno;}
• 각 토큰의 타입을 지정%token <vblno> NAME%token <dval> NUMBER
문법 기호의 타입• 문법 기호의 타입
– %union 에서 정의– %type 을 이용하여 지정
• 예%union { double dval; struct symtab *symp;}%token <symp> NAME%token <dval> NUMBER%type <dval> expression
평가용 실습• 문제
" 입력되는 수식의 값을 계산하는 lex 와 yacc의 source 을 작성하시오 "
– 허용하는 연산자 : + * - / ( )– 우선순위 : ( ) > * / > + -– 결합순위 : 모두 좌측 결합
• 입력 예(1+2+3)*(2*(51-47)/(1+2*3))
실습용 문법• 덧셈과 곱셈 식을 생성하는 문법
Exp : Exp ‘+’ Term | Exp ‘-’ Term | TermTerm : Term ‘*’ Fact | Term ‘/’ Fact | FactFact : ‘(’ Exp ‘)’ | NUMBER
Pretty Printer• Pretty printer 란 ?
– 입력된 프로그램을 보기 좋게 출력하는 프로그램
– 입력 : 문법에 따라 쓰여진 프로그램– 출력 : 들여쓰기 (indentation) 된 프로그램
• 예– UNIX 의 cb(C beautifier)
포함된 행동• 포함된 행동 (embedded action)
– 생성규칙의 중간에 있는 행동– 예
• 기존 : A : B C D { printf(); }• 포함 : A : B { printf(); } C D
– 실제의 의미는 ?A : B { printf(); } C D==>A : B xx C Dxx : { printf(); }
PP 용 입력 문법• 입력문법
S : If| While| ‘stmt’
If : ‘if’ Cond ‘then’ SWhile : ‘while’ Cond ‘do’ S ‘end’Cond : ‘c0’
입력과 출력의 예• 입력 예
while c0 do while c0 do if c0 then stmt end end
• 출력 예while c0 dowhile c0 doif c0 thenstmtend
end
Yacc 입력의 일부• Yacc 입력의 일부
%{int i, k;
%}%token ii tt ...%%...
If : ii {printf("\n"); for(k=0;k<i;k++) printf(" "); printf("if");} Cond tt {printf("then"); i=i+4;} S {i=i-4;} ;...
%%main() {i = 0;yyparse();
}
ToyPL 의 PP• 문제
– ToyPL 을 위한 Pretty Printer 를 lex 와 yacc를 이용하여 작성하시오
• 입력 예program Sample; proc Fact(n:long)var m:int; begin ... endvar a,b: int; begin ... end .
ToyPL 의 PP 출력• 출력 예
program Sample; proc Fact(n: long) var m: int; begin ... end var a,b: int;begin ...end.
문법의 모호성
파서를 만들 수 없는 문법• 모호한 문법
%% Exp : Exp '-' Exp | Exp '*' Exp | 'a' ;
• yacc 에서는…– 항상 shift/reduce conflict 가 발생– yacc 에서는 억지로 파서를 만듬
Lookahead 가 많이 필요한 문법
• 2 개의 미리보기가 필요 %% Exp : ZeroOne '+' '2' | OneTwo '+' '3' ; ZeroOne: '0' | '1' ; OneTwo : '1' | '2' ;
• 비교 : 미리보기가 1 개만 있어도 됨 %% Exp : ZeroOne '2' | OneTwo '3' ; ZeroOne: '0' | '1' ; OneTwo : '1' | '2' ;
Yacc 은LALR(1)
파서를 생성
수식문법• 뺄셈과 곱셈 식을 생성하는 문법
1. Exp ::= Exp - Exp2. | Exp * Exp3. | Num4. Num ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
1-2*3 의 유도• 좌측유도
E 1 E - E 3 N - E 4 1 - E 2 1 - E * E 3 1 - N * E 4 1 - 2 * E 3 1 - 2 * N 4 1 - 2 * 3
E 2 E * E 1 E - E * E 3 N - E * E 4 1 - E * E 3 1 - N * E 4 1 - 2 * E 3 1 - 2 * N 4 1 - 2 * 3
• 우측유도E 1 E - E 2 E - E * E 3 E - E * N 4 E - E * 3
3 E - N * 3 4 E - 2 * 3 3 N - 2 * 3 4 1 - 2 * 3E 2 E * E 3 E * N 4 E * 3 1 E - E * 3
3 E - N * 3 4 E - 2 * 3 3 N - 2 * 3 4 1 - 2 * 3
12343434
1-2*3 의 유도트리
32
Exp
Exp Exp
Exp Exp
Num Num
Num
1
-
*
21
Exp
Exp Exp
Exp Exp
Num Num
Num
3
*
-
1-2-3 의 유도• 좌측유도
E 1 E - E 3 N - E 4 1 - E 1 1 - E - E 3 1 - N - E 4 1 - 2 - E 3 1 - 2 - N 4 1 - 2 - 3
E 1 E - E 1 E - E - E 3 N - E - E 4 1 - E - E 3 1 - N - E 4 1 - 2 - E 3 1 - 2 - N 4 1 - 2 - 3
• 우측유도E 1 E - E 1 E - E - E 3 E - E - N 4 E - E - 3
3 E - N - 3 4 E - 2 - 3 3 N - 2 - 3 4 1 - 2 - 3E 2 E - E 3 E - N 4 E - 3 1 E - E - 3
3 E - N - 3 4 E - 2 - 3 3 N - 2 - 3 4 1 - 2 - 3
11343434
1-2-3 의 유도트리
32
Exp
Exp Exp
Exp Exp
Num Num
Num
1
-
-
21
Exp
Exp Exp
Exp Exp
Num Num
Num
3
-
-
Yacc 실습• 다음의 yacc 입력으로 파서를 만드시오 .
%token Num%%E : E ‘-’ E { printf(“rule 1\n”); } | E ‘*’ E { printf(“rule 2\n”); } | N { printf(“rule 3\n”); } ;N : Num { printf(“rule 4\n”); } ;
• 다음 입력에 대하여 적용된 생성규칙을 확인하고 파스 트리를 그리시오 .1 - 2 * 31 * 2 - 31 - 2 - 3
모호한 문법• 모호한 문법 (ambiguous grammar)
– 어떤 문장에 대하여 유도트리가 두개이상 존재할 때
– 수식문법은 모호• 예 : If- 문
<Stmt> ::= <AsgnStmt> | <IfStmt> | <WhileStmt> | <ForStmt> | <CallStmt> | <CompStmt> | s<IfStmt> ::= if ( <Cond> ) then <Stmt> | if ( <Cond> ) then <Stmt> else <Stmt><Cond> ::= c
유도트리 그리기• 다음 문장에 대한 유도트리를 모두
그리시오 .if ( c ) then if ( c ) then s else s
• 2 개의 유도트리가 존재
모호한 문법의 문제점• 문장에 대한 여러 해석이 가능• 수식문법
– 1 - 2 * 3 의 값은 ?• If- 문
– 다음의 문장에서 j++ 가 실행되는 조건은 ?– if (i < 0) then if (j < 0) then i++ else j++
해결책• 해결책
– 모호하지 않은 문법으로 변환– yacc 의 모호성 해결 규칙의 사용
• 모호하지 않은 문법으로 변환Exp ::= Exp + Term | TermTerm ::= Term * Num | NumNum ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
3
2
Exp
Term
Exp Term
Num
NumTerm
+
*
1
Num
Yacc 의 모호성 해결• yacc 에서 conflict 해결
– 사용자가 명시하지 않을 경우=> 기본 (default) 규칙
– 사용자가 명시=> 사용자가 우선순위 / 결합순위를 명시
• 기본 규칙– shift/reduce conflict: shift 우선– reduce/reduce conflict: 문법 상에서 먼저
나타나는 규칙을 reduce
우선순위• 사용자가 순서를 명시
– 지정 위치 : 정의 부분– 순위 지정의 대상 : 토큰
• 우선순위– 높은 우선순위의 연산자를 먼저 적어 준다 .– 1 - 2 * 3– 지정 예 :
%left ‘*’ %left ‘-’ %%
결합순위• 결합순위
– 같은 우선 순위의 연산자 사이의 순서– 1 - 2 - 3
• 좌측결합– (1 - 2) - 3– %left ‘-’
• 우측결합– 1 - (2 - 3)– %right ‘-’
결합 / 우선순위로 해결한 예• 결합 / 우선순위로 해결한 예
%left ‘*’%left ‘-’%token Num%%E : E ‘-’ E { printf(“rule 1\n”); } | E ‘*’ E { printf(“rule 2\n”); } | N { printf(“rule 3\n”); } ;N : Num { printf(“rule 4\n”); } ;
Yacc 실습• 결합 / 우선순위를 다음과 같이 지정하여
각각의 파서를 만들자 .%left ‘*’ %right ‘*’ %left ‘-’ %right ‘-’%left ‘-’ %right ‘-’ %left ‘*’ %right ‘*’%% %% %% %%
• 각 파서에 대하여 다음의 입력으로 실행하여 적용된 생성규칙을 확인하고 파스 트리를 그리자 .1 - 2 * 3 1 * 2 - 3 1 - 2 - 3