LEX 와 YACC 작성자 : americanojh E-MAIL : americano@korea.ac.kr 1. LEX 가. LEx 소개컴파일러구성을도와주는대표적인소프트웨어도구로써일정한구조에따라입력된내용을변환하는프로그램을만드는데유용하게쓰인다. 입력파일에서일정한패턴을찾아내는간단한텍스트검색프로그램에서소스프로그램을최적화된목적코드 (object code) 로변형하는 C 컴파일러까지다양한프로그램을만드는데도움을준다. 원래 UNIX의산실인 AT&T Bell Laboratories에서 UNIX 시스템의유틸리티로서개발되었다. Lex의개발자는 Mark Lesk 이다. 이후이도구들의유용성이확장되면서 Sun의 Solaris나 Linux와같은 UNIX 계열의운영체제는물론이고, Microsoft Windows 등의상이한운영체제에서도이식되어서널리사용되고있다. 일정한구조를가진입력을받아들이는프로그램에서지속적으로반복하는작업은입력내용을의미있는단위 (unit) 로분해하여그들사이의관련성을파악하는일이다. 입력을토큰이라고하는단위로나누는작업을대개어휘분석 (lexical analysis) 혹은줄여서렉싱 (lexing) 이라고한다. 렉스는몇개의토큰을받아들여주어진토큰을식별할수있는 C 루틴 (routine) 을생성한다. 이루틴을어휘분석기 (lexical analyzer) 또는렉서 (lexer) 나스캐너 (scanner) 라고한다. 한편사용자가렉스에기술하는내용을일컬어렉스명세서 (lex specification) 라한다. 렉스가사용하는토큰설명 (token descriptions) 을보통정규표현식 (regular expressions) 이라하며, grep이나 egrep 같은유닉스명령어에서사용하는패턴을크게확장한버전이다. 렉스는찾고자하는표현식의개수에관계없이렉서가입력텍스트를최대한빠르게검색할수있는형태로이러한정규표현식을바꾸어준다. 나. LEX 동작방법 lex를연동하면구문분석기를만드는 C 소스코드를생성할수있다. lex는설정파일을사용하여각각설정파일을받아들여적절한 C 코드를생성한다. lex 도구가생성하는 C 코드는특정한어휘를식별한후토큰을반환한다. LEX 는 3 개의절로나뉜다. 각절의끝에는 %% 기호를넣는다. 이세부분들중꼭 있어야하는필수부분은두번째부분이고, 첫번째와세번째부분은필요없는
경우생략이가능하다 첫번째절은정의절 (definition section) 이라하며최종프로그램에포함하고자하는 C 프로그램의내용을삽입하는기능을담당한다. 선언 (declaration) 이나정의 (definition) 가포함된다. 그러나최종파일안에반드시포함되어야하는헤더파일이존재하는경우에는이절이단순한삽입이상의중요한기능을담당한다. 이절에존재하는 C 코드는 "%{" 과 "%" 라는특별한분리자 (delimiter) 로둘러싸여있다. 렉스는이분리자사이에있는내용을 C파일에그대로복사한다. 두번째절은규칙절 (rules section) 이다. 본론에해당하는것으로서각프로그램이수행하는일에대한규칙 ( 보통번역규칙 (translation rule) 이라고부른다 ) 을기술하고각각의규칙은두부분, 즉공백으로구분된패턴 (pattern) 과동작 (action) 으로이루어진다. 렉스가생성하는렉서는패턴을인식했을때주어진동작을수행한다. 이러한패턴은유닉스에서흔히사용되는정규표현식 (regular expressions) 과비슷하지만, grep, sed, ed 등에서사용하는표현식보다는좀더확장된내용을담고있다. 패턴이중복될수있으므로렉스에는모호함을제거하는규칙이있다, 렉서가제대로동작하도록하는두가지규칙은다음과같다. 1. 렉스패턴은주어진문자나문자열에오직한번만매치한다. 2. 렉스는현재주어진입력에서패턴에매치하는문자또는문자열중가장길게매치할수있는것 (longest possible match) 에대한동작을수행한다. 세번째절은사용자서브루틴절 (user subroutine section) 이며, 보조프로시저 (auxiliary procedure) 또는지원프로그램 (supporting routines) 을담고있다. 일반적인 C 코드가작성될수있는곳이다. 렉스는생성한 C코드뒤에이절에있는내용을복사한다.
위의그림은렉스가생성한 C 코드인 lex.yy.c 의내부코드이다. 그리고이장면은 마지막에사용자서브루틴절이추가된화면이다. 2. 코드설명 가. 정의절 (definition section) %{ #include<stdio.h> int count_word = 0 int count_number = 0 int count_equal = 0 int count_lbracket = 0 int count_rbracket = 0 int count_mark = 0 % %% 정의절에는최종프로그램에포함하고자하는 C 프로그램의내용을삽입하는기능을담당한다. 삽입되는내용은최종파일에서출력을위해필요한헤더파일과분석된코드의카운트를담당하는변수가포함된다. 이변수들은그다음의규칙절에서사용되며해당되는패턴이나올경우값을 1씩증가시킨다. 최종적으로각토큰의개수를가지게된다.
나. 규칙절 (rules section) ([a-za-z])+ {count_word++ ([0-9])+ {count_number++ "=" {count_equal++ "{" {count_lbracket++ "" {count_rbracket++ \n {count_mark++. {count_mark++ %% 규칙절에서는입력된문자에서매칭되는문자열의패턴과그패턴이나타났을때 해당하는동작으로이루어졌다. 패턴 ([a-za-z])+ 동작 a부터 z까지혹은 A부터 Z까지의문자로만구성되는문자열이 나타났을때 count_word의변수값을 1증가시킨다. ([0-9])+ 0부터 9까지의숫자로구성되는수, 즉 10진수가나타났을때 count_number의변수값을 1증가시킨다. "=" = 의기호가나타나면 count_equal의변수값을 1 증가시킨다. "{" { 의기호가나타나면 count_lbracket의변수값을 1증가시킨다. "" 의기호가나타나면 count_rbracket의변수값을 1증가시킨다. \n 한줄개행이나타나면 count_mark의변수값을 1증가시킨다.. 위의패턴에해당되지않는코드가나타나면 count_mark의변수값을 1증가시킨다. 다. 사용자서브루틴절 (user subroutine section) int main() { yylex() int yywrap() { printf("word = %d\n",count_word) printf("'=' = %d\n",count_equal) printf("'{' = %d\n",countlbracket) printf("'' = %d\n",countrbracket) printf("mark = %d\n",count_mark) printf("number = %d\n", count_number) return 0 return 1 사용자서브루틴절에는각토큰들의개수를출력하는내용의 main 함수와 Lex
Functions 중하나인 yywrap() 가정의되어있다. main 함수를보면먼저 yylex() 함수가있다. 이함수는 yywrap() 와마찬가지로 Lex Functions 중하나로서실행되면입력파일의분석을시작한다. 그다음부터는정의절에서선언되었던변수들의값들을출력하는함수들이다. 이출력을위해서정의절에서각각의변수들과헤더파일이포함되었다. 마지막으로 yywrap 함수는파일의 EOF가나타나면불러지는함수이다. 이함수가 1을리턴하면파싱의종료를지시한다.
3. 출력화면 입력파일 : hw1.c word 3 개 aaa bbb ccc number 3 개 111 222 333 { 3 개 { { { 3 개 '=' 2 개 = = mark 20 개!!!@t#(5 개 ), \n(5 개 ), \t(9 개 ), EOF(1 개 ) 실행화면 : hw1.l
2. YACC 가. YACC 소개본래 YACC는유닉스환경에서실행되는구문분석테이블생성기를말하는것이었으나오늘날에는다른환경에서사용되는제품도개발되어있다. 구문분석테이블생성을위하여사용되는시스템소프트웨어로입력된프로그램명령어의구문을분석하여구문분석트리를자동적으로생성하는작업을수행한다. YACC는입력값에대해원하는것을찾아내는일과, 그찾아낸것들간의관계를따지는프로그램작성을도와준다. 입력값을의미단위 (token) 로나누는것을어휘분석 (Lexical Analysis) 이라고하며, 그런일을하는것을어휘분석기 (Lexical Analyzer 또는 lexer scanner) 라고부른다. 입력값이의미단위로나뉘게되면프로그램은그것들간의관계를따지게된다. 예를들어 C 컴파일러는토큰들이수식인지, 문장인지, 선언문인지, 블록인지, 프로시저인지를판별해낸다. 이런작업이구문분석이고그관계들을정해놓은규칙을문법 (Grammar) 이라고한다. YACC는구문분석기 (Parser) 를생성해주는도구이다. 구문분석기는어휘분석기로부터받은의미단위를문법에맞는지검사하는일을한다. 사용자가문법을정의하고그문법에맞을경우취할행동을 C 언어로기술해주면 YACC가알아서구문분석기를생성해준다. 구문분석기는의미단위를가져와서구문분석을하는프로그램이므로어휘분석을하는프로그램은따로구현이되어야한다. [ 출처 ] YACC [yet another compiler compiler ] 네이버백과사전 나. 야크문법 Yacc 의 Grammar 화일은 Lex Specification 과비슷하다. Yacc Grammar %{ C 선언부분 (C Definition Section) % Yacc 선언부분 (Yacc Definition Section) %% 문법부분 (Grammar Rules Section) %% 사용자정의함수 (User Subroutines)
1) C 선언부분 (C Definition Section) 이곳에쓰여진것들은그대로 y.tab.c에복사가된다. Lex에서와마찬가지로다른부분에서사용할변수가있다면이곳에서미리정의를하고헤더화일을 include해야한다. 2) Yacc 선언부분 (Yacc Definition Section) 문법부분에서사용하는토큰, 결합법칙, 변수나토큰들의타입등을선언한다. 3) 문법부분 (Grammar Rules Section) 인식할문법과그에따라취할 C 언어로된행동정의한다. -> 표시를할수없으므 로 : 으로대신하며, 문법의마지막은 으로끝나야한다. 4) 사용자정의함수 (User Subroutines) 사용자가만들어서사용해야할함수가있으면이곳에정의한다. 다. 동작방식 Yacc는토큰을계속읽어서사용자가정의한문법과맞춰가며구문분석을하게된다. 읽어들인토큰이문법을완전히만족하지않고다른토큰이더필요하다면그토큰을스택에쌓아두는데이것을스택에 shift 한다고해서 shift라고한다. 계속토큰을읽어서스택에있는토큰들과함께문법과비교를했더니문법에만족 을하면스택에있던토큰을꺼내고 (pop) 문법의왼쪽 (LHS) 심볼로대치한다. 이것 을 reduce 라고한다. Yacc가구문분석을하는데도움을주는툴이지만 Yacc도구문분석을할수없는문법이있다. 하나의토큰이여러개의문법에적용되어하나의파스트리가생성되지않고여러개의트리가생성되는모호한문법의경우나, Yacc는다음토큰하나를가져와서살펴보는데, 두개이상의토큰을가져와야분석을할수있는문법등을구문분석할수없다.
3. 코드분석 가. 함수선언 1) 일반형식 [type] 함수명 ([ 전달인자선언 ]) 2) 관련 YACC 문법 [type] 함수명 ([ 전달인자선언 ]) direct_declarator : IDENTIFIER direct_declarator '(' parameter_type_list ')' direct_declarator '(' identifier_list ')' direct_declarator '(' ')' declaration [type] 함수명 declaration_specifiers init_declarator_list '' [type] declaration_specifiers : storage_class_specifier storage_class_specifier declaration_specifiers type_specifier type_specifier declaration_specifiers type_qualifier type_qualifier declaration_specifiers 함수명 init_declarator_list -> init_declarator -> declarator declarator : pointer direct_declarator direct_declarator 3) 관련카운팅코드
direct_declarator direct_declarator '(' parameter_type_list ')' { def_func++ if(check==1) ptr_iden-- else var_iden-- check = 0 direct_declarator '(' identifier_list ')' { def_func++ if(check==1) ptr_iden-- else var_iden-- check = 0 direct_declarator '(' ')' { def_func++ if(check==1) ptr_iden-- else var_iden-- check = 0 4) 카운팅코드분석관련야크문법에함수선언카운팅변수를 1씩증가시킵니다. 단, 이 YACC 문법에들어오기전에포인터변수선언카운팅과일반변수선언카운팅과중복되므로그들의카운팅을 1씩빼주어야합니다. 나. 함수정의 1) 일반형식 [type] 함수명 ([ 전달인자선언 ]) { // 몸체시작내부변수선언 수행문장들 return 반환데이터타입 // 함수몸체종료 2) 관련 YACC 문법 [type] 함수명 { 내용 function_definition : declaration_specifiers declarator declaration_list compound_statement declaration_specifiers declarator compound_statement declarator declaration_list compound_statement declarator compound_statement
[type] declaration_specifiers : storage_class_specifier storage_class_specifier declaration_specifiers type_specifier type_specifier declaration_specifiers type_qualifier type_qualifier declaration_specifiers { 내용 compound_statement : '{' '' '{' statement_list '' '{' declaration_list '' '{' declaration_list statement_list '' 3) 관련카운팅코드 function_definition : declaration_specifiers declarator declaration_list compound_statement (dec_func++ def_func--) declaration_specifiers declarator compound_statement (dec_func++ def_func--) declarator declaration_list compound_statement (dec_func++ def_func--) declarator compound_statement (dec_func++ def_func--) 4) 카운팅코드분석관련야크문법에함수정의카운팅변수를 1씩증가시킵니다. 단, 이 YACC 문법은함수선언 + { 내용 으로구성되므로함수선언카운팅도같이실행됩니다. 따라서함수정의카운팅은 1 더해주되함수선언카운팅은 1씩빼줍니다. 다. 함수호출 1) 일반형식 함수명 ([ 전달인자 ]) 2) 관련 YACC 문법함수명 ([ 전달인자 ]) postfix_expression postfix_expression '(' ')' postfix_expression '(' argument_expression_list ')'
함수명 postfix_expression -> primary_expression primary_expression : IDENTIFIER '(' expression ')' ([ 전달인자 ]) argument_expression_list : assignment_expression argument_expression_list ',' assignment_expression 3) 관련카운팅코드 postfix_expression postfix_expression '(' ')' {cal_func++ postfix_expression '(' argument_expression_list ')' {cal_func++ 4) 카운팅코드분석 관련야크문법에함수정의카운팅변수를 1 씩증가시킵니다. 중복되는것은없으므로따로카운팅변수를빼주지않아도괜찮습니다. 라. 일반변수선언문 1) 일반형식 [type] 변수명 2) 관련 YACC 문법 declaration [type] declaration_specifiers init_declarator_list '' 변수명 [type] declaration_specifiers : storage_class_specifier storage_class_specifier declaration_specifiers type_specifier type_specifier declaration_specifiers type_qualifier type_qualifier declaration_specifiers
변수명 init_declarator_list -> init_declarator -> declarator init_declarator_list -> init_declarator -> declarator '=' initializer declarator -> direct_declarator direct_declarator : IDENTIFIER initializer -> assignment_expression assignment_expression : conditional_expression unary_expression assignment_operator assignment_expression 3) 관련카운팅코드 declarator direct_declarator : pointer direct_declarator {check = 1 direct_declarator : IDENTIFIER { if(check==1) else check = 0 ptr_iden++ var_iden++ 4) 카운팅코드분석변수명은오로지 IDENTIFIER만가능합니다. 하지만이 direct_declarator를통한 IDENTIFIER의접근은일반변수뿐만아니라포인터변수명, 그리고함수명에서도접근이가능합니다. 함수명은좀더복잡한문법이되므로함수카운팅하는곳에서따로카운팅문을만들어주고여기에서는 if문을사용하여포인터변수선언이아닐경우에만일반변수선언카운터를증가시켜주었습니다. 마. 포인터변수선언 1) 일반형식 [type] 포인터변수명 2) 관련 YACC 문법 declaration [type] declaration_specifiers init_declarator_list '' 포인터변수명
[type] declaration_specifiers : storage_class_specifier storage_class_specifier declaration_specifiers type_specifier type_specifier declaration_specifiers type_qualifier type_qualifier declaration_specifiers 포인터 init_declarator_list -> init_declarator -> declarator declarator : pointer direct_declarator 변수명 pointer : '*' '*' type_qualifier_list '*' pointer '*' type_qualifier_list pointer 포인터 변수명 init_declarator_list -> init_declarator -> declarator init_declarator_list -> init_declarator -> declarator '=' initializer declarator -> direct_declarator direct_declarator : IDENTIFIER initializer -> assignment_expression assignment_expression : conditional_expression unary_expression assignment_operator assignment_expression 3) 관련카운팅코드 declarator : pointer direct_declarator {check = 1 direct_declarator direct_declarator : IDENTIFIER { if(check==1) ptr_iden++ else var_iden++ check = 0 4) 카운팅코드분석 일반변수선언문과비슷합니다. 단지포인터변수의접근은꼭 pointer direct_declarator 를통 해접근하므로이때포인터변수임을체크를해주어서카운팅시일반변수와구분합니다.
바. 배열변수선언 1) 일반형식 [type] 변수명 [ 상수 ] 2) 관련 YACC 문법 [type] 변수명 [ 상수 ] declaration declaration_specifiers init_declarator_list '' [type] declaration_specifiers : storage_class_specifier storage_class_specifier declaration_specifiers type_specifier type_specifier declaration_specifiers type_qualifier type_qualifier declaration_specifiers 변수명 [ 상수 ] init_declarator_list -> init_declarator -> declarator -> direct_declarator direct_declarator : IDENTIFIER direct_declarator '[' constant_expression ']' direct_declarator '[' ']' direct_declarator : IDENTIFIER 변수명 [ 상수 ] constant_expression -> conditional_expression -> logical_or_expression -> logical_and_expression -> inclusive_or_expression -> exclusive_or_expression -> and_expression -> equality_expression -> relational_expression -> shift_expression -> additive_expression -> multiplicative_expression -> cast_expression -> unary_expression -> postfix_expression -> primary_expression primary_expression CONSTANT 3) 관련카운팅코드
direct_declarator direct_declarator '[' constant_expression ']' { arr_iden++ if(check==1) ptr_iden-- else var_iden-- check = 0 direct_declarator '[' ']' { arr_iden++ if(check==1) ptr_iden-- else var_iden-- check = 0 4) 카운팅코드분석배열의주포인트는 [ ] 기호입니다. 이기호가있을때만배열이선언되므로그기호를통해문법이정의되는곳에카운팅을해주었습니다. 하지만결국배열변수명은 IDENTIFER 이므로일반변수또는포인터변수와중복됩니다. 따라서일반변수또는포인터변수의값을 1씩빼주면서카운팅을해주었습니다. 사. 수식 1) 일반형식 식 2) 관련 YACC 문법 expression : assignment_expression expression ',' assignment_expression 식 3) 관련카운팅코드 expression : assignment_expression {exp_state++ expression ',' assignment_expression {exp_state++ 4) 카운팅코드분석 YACC의모든식은 expression으로표현됩니다. expression을분석하면연산자를이용한수식들을표현할수있는구문이나옵니다. expression 가식을표현하는가장제한적이면서최상위적인구문이므로이곳에카운팅을해주었습니다.
아. 리턴문 1) 일반형식 return 반환데이터타입 2) 관련 YACC 문법 jump_statement RETURN '' RETURN expression '' return 반환데이터타입 반환데이터타입 expression : assignment_expression expression ',' assignment_expression 3) 관련카운팅코드 jump_statement RETURN '' { ret_stat++ RETURN expression '' { ret_stat++ 4) 카운팅코드분석리턴문은오로지 return 토큰을이용해야만합니다. return 토큰뒤에는아무것도없을수도있고수식이존재할수있습니다. 따라서만약 return 뒤에수식이존재한다면리턴문카운터뿐만아니라수식카운터도같이증가하게됩니다. 자. 선택문 1) 일반형식 switch ( 조건수식 ) { case 상수 1 : 문장들 break case 상수 2 : 문장들 break : default : 문장들 2) 관련 YACC 문법 switch ( 조건수식 ) { 내용 selection_statement SWITCH '(' expression ')' statement
( 조건수식 ) expression : assignment_expression expression ',' assignment_expression { 내용 statement -> labeled_statement labeled_statement CASE constant_expression ':' statement DEFAULT ':' statement 3) 관련카운팅코드 selection_statement SWITCH '(' expression ')' statement {swi_stat++ 4) 카운팅코드분석선택문은 switch라는토큰이있어야만실행되는구문이므로그구문이있는곳에카운팅을해주었습니다. 스위치문은필수적으로조건수식이있어야하므로무조건수식카운터또한증가하게됩니다. 차. 조건문 1) 일반형식 if( 조건문장 ) { 처리문장 다음문장 // 조건이참일때수행 // if 문과별개 if( 조건문장 ) { 처리문장 1 else { 처리문장 2 // 조건이참일때수행 // 조건이거짓일때수행 논리수식? 수식 : 조건수식 2) 관련 YACC 문법 if ( 조건문장 ) { 내용 : IF '(' expression ')' statement if ( 조건문장 ) { 내용 else { 내용 selection_statement IF '(' expression ')' statement ELSE statement
논리수식? 수식 : 조건수식 conditional_expression logical_or_expression '?' expression ':' conditional_expression ( 조건문장 ) 수식 expression : assignment_expression expression ',' assignment_expression statement : labeled_statement compound_statement expression_statement selection_statement iteration_statement jump_statement { 내용 논리수식 logical_or_expression : logical_and_expression logical_or_expression OR_OP logical_and_expression 조건수식 conditional_expression : logical_or_expression logical_or_expression '?' expression ':' conditional_expression 3) 관련카운팅코드 selection_statement : IF '(' expression ')' statement {if_state++ IF '(' expression ')' statement ELSE statement {if_state++ conditional_expression logical_or_expression '?' expression ':' conditional_expression {if_state++ 4) 카운팅코드분석조건문은크게 2가지로나누어집니다. 하나는 IF와 ELSE 라는토큰으로이루어진구문과다른하나는삼항연산자를이용한구문입니다. ELSE의사용여부에따라앞의구문또한작게 2가지로나누어집니다. 따라서그모든구문에카운팅을해주어야합니다. 여기서주의접은문법충돌이발생한다는점입니다. IF '(' expression ')' statement ELSE statement 문장안에는이미 IF '(' expression ')' statement 문장이포함되었기때문입니다. 그문장이모호하기때문에분석실행시문법충돌이발생합니다. 카. 선택문
1) 일반형식 for ( 초기식 조건식 증감식 ) { 반복할문장 While( 조건식 ) { 반복한문장 do { 반복한문장 while( 조건식 ) 2) 관련 YACC 문법 for ( 초기식 조건식 증감식 ) { 내용 iteration_statement FOR '(' expression_statement expression_statement ')' statement FOR '(' expression_statement expression_statement expression ')' statement while ( 조건식 ) { 내용 iteration_statement : WHILE '(' expression ')' statement do { 내용 while ( 조건식 ) iteration_statement DO statement WHILE '(' expression ')' '' expression_statement : '' expression '' 초기식 조건식 (for 문 ) 증감식 조건식 (while 문 ) expression : assignment_expression expression ',' assignment_expression statement : labeled_statement compound_statement expression_statement selection_statement iteration_statement jump_statement { 내용 3) 관련카운팅코드
iteration_statement : WHILE '(' expression ')' statement { for_stat++ DO statement WHILE '(' expression ')' '' { for_stat++ FOR '(' expression_statement expression_statement ')' statement { for_stat++ FOR '(' expression_statement expression_statement expression ')' statement { for_stat++ 4) 카운팅코드분석반복문은 for문 while문 do ~ while 문으로나누어집니다. 따라서 for, while, do 세토큰으로이루어진문법에카운팅을해주었습니다. for문의경우 ( 초기식 조건식 증감식 ) 인데이 3개의식은모두생략가능한테증감식뒤에는 기호가안붙으므로 for문은 2개의문법이정의되었습니다.
4. 전체코드 &{ #include<stdio.h> int def_func = 0 int dec_func = 0 int cal_func = 0 int ptr_iden = 0 int arr_iden = 0 int var_iden = 0 int exp_stat = 0 int swi_stat = 0 int if_state = 0 int for_stat = 0 int ret_stat = 0 int check++ & %token IDENTIFIER CONSTANT STRING_LITERAL SIZEOF %token PTR_OP INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP %token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN %token SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN %token XOR_ASSIGN OR_ASSIGN TYPE_NAME %token TYPEDEF EXTERN STATIC AUTO REGISTER %token CHAR SHORT INT LONG SIGNED UNSIGNED FLOAT DOUBLE CONST VOLATILE VOID %token STRUCT UNION ENUM ELLIPSIS %token CASE DEFAULT IF ELSE SWITCH WHILE DO FOR GOTO CONTINUE BREAK RETURN %start translation_unit %% primary_expression : IDENTIFIER CONSTANT STRING_LITERAL '(' expression ')' postfix_expression : primary_expression postfix_expression '[' expression ']' postfix_expression '(' ')' {cal_func++ postfix_expression '(' argument_expression_list ')' {cal_func++
postfix_expression '.' IDENTIFIER postfix_expression PTR_OP IDENTIFIER postfix_expression INC_OP postfix_expression DEC_OP argument_expression_list : assignment_expression argument_expression_list ',' assignment_expression unary_expression : postfix_expression INC_OP unary_expression DEC_OP unary_expression unary_operator cast_expression SIZEOF unary_expression SIZEOF '(' type_name ')' unary_operator : '&' '*' '+' '-' '~' '!' cast_expression : unary_expression '(' type_name ')' cast_expression multiplicative_expression : cast_expression multiplicative_expression '*' cast_expression multiplicative_expression '/' cast_expression multiplicative_expression '%' cast_expression additive_expression : multiplicative_expression additive_expression '+' multiplicative_expression additive_expression '-' multiplicative_expression
shift_expression : additive_expression shift_expression LEFT_OP additive_expression shift_expression RIGHT_OP additive_expression relational_expression : shift_expression relational_expression '<' shift_expression relational_expression '>' shift_expression relational_expression LE_OP shift_expression relational_expression GE_OP shift_expression equality_expression : relational_expression equality_expression EQ_OP relational_expression equality_expression NE_OP relational_expression and_expression : equality_expression and_expression '&' equality_expression exclusive_or_expression : and_expression exclusive_or_expression '^' and_expression inclusive_or_expression : exclusive_or_expression inclusive_or_expression ' ' exclusive_or_expression logical_and_expression : inclusive_or_expression logical_and_expression AND_OP inclusive_or_expression logical_or_expression : logical_and_expression logical_or_expression OR_OP logical_and_expression
conditional_expression : logical_or_expression logical_or_expression '?' expression ':' conditional_expression {if_state++ assignment_expression : conditional_expression unary_expression assignment_operator assignment_expression assignment_operator : '=' MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN expression : assignment_expression {exp_state++ expression ',' assignment_expression{exp_state++ constant_expression : conditional_expression declaration : declaration_specifiers '' declaration_specifiers init_declarator_list '' declaration_specifiers : storage_class_specifier storage_class_specifier declaration_specifiers type_specifier type_specifier declaration_specifiers
type_qualifier type_qualifier declaration_specifiers init_declarator_list : init_declarator init_declarator_list ',' init_declarator init_declarator : declarator declarator '=' initializer storage_class_specifier : TYPEDEF EXTERN STATIC AUTO REGISTER type_specifier : VOID CHAR SHORT INT LONG FLOAT DOUBLE SIGNED UNSIGNED struct_or_union_specifier enum_specifier TYPE_NAME struct_or_union_specifier : struct_or_union IDENTIFIER '{' struct_declaration_list '' struct_or_union '{' struct_declaration_list '' struct_or_union IDENTIFIER struct_or_union : STRUCT
UNION struct_declaration_list : struct_declaration struct_declaration_list struct_declaration struct_declaration : specifier_qualifier_list struct_declarator_list '' specifier_qualifier_list : type_specifier specifier_qualifier_list type_specifier type_qualifier specifier_qualifier_list type_qualifier struct_declarator_list : struct_declarator struct_declarator_list ',' struct_declarator struct_declarator : declarator ':' constant_expression declarator ':' constant_expression enum_specifier : ENUM '{' enumerator_list '' ENUM IDENTIFIER '{' enumerator_list '' ENUM IDENTIFIER enumerator_list : enumerator enumerator_list ',' enumerator enumerator : IDENTIFIER IDENTIFIER '=' constant_expression
type_qualifier : CONST VOLATILE declarator : pointer direct_declarator {check = 1 direct_declarator direct_declarator : IDENTIFIER { if(check==1) ptr_iden++ else var_iden++ check = 0 '(' declarator ')' direct_declarator '[' constant_expression ']' { arr_iden++ if(check==1) ptr_iden-- else var_iden-- check = 0 direct_declarator '[' ']' { arr_iden++ if(check==1) ptr_iden-- else var_iden-- check = 0 direct_declarator '(' parameter_type_list ')' { def_func++ if(check==1)
ptr_iden-- else var_iden-- check = 0 direct_declarator '(' identifier_list ')' { def_func++ if(check==1) ptr_iden-- else var_iden-- check = 0 direct_declarator '(' ')' { def_func++ if(check==1) ptr_iden-- else var_iden-- check = 0 ZZ pointer : '*' '*' type_qualifier_list '*' pointer '*' type_qualifier_list pointer type_qualifier_list : type_qualifier type_qualifier_list type_qualifier parameter_type_list : parameter_list parameter_list ',' ELLIPSIS parameter_list : parameter_declaration
parameter_list ',' parameter_declaration parameter_declaration : declaration_specifiers declarator declaration_specifiers abstract_declarator declaration_specifiers identifier_list : IDENTIFIER identifier_list ',' IDENTIFIER type_name : specifier_qualifier_list specifier_qualifier_list abstract_declarator abstract_declarator : pointer direct_abstract_declarator pointer direct_abstract_declarator direct_abstract_declarator : '(' abstract_declarator ')' '[' ']' '[' constant_expression ']' direct_abstract_declarator '[' ']' direct_abstract_declarator '[' constant_expression ']' '(' ')' '(' parameter_type_list ')' direct_abstract_declarator '(' ')' direct_abstract_declarator '(' parameter_type_list ')' initializer : assignment_expression '{' initializer_list '' '{' initializer_list ',' '' initializer_list : initializer
initializer_list ',' initializer statement : labeled_statement compound_statement expression_statement selection_statement iteration_statement jump_statement labeled_statement : IDENTIFIER ':' statement CASE constant_expression ':' statement DEFAULT ':' statement compound_statement : '{' '' '{' statement_list '' '{' declaration_list '' '{' declaration_list statement_list '' declaration_list : declaration declaration_list declaration statement_list : statement statement_list statement expression_statement : '' expression '' selection_statement : IF '(' expression ')' statement {if_state++ IF '(' expression ')' statement ELSE statement {if_state++ SWITCH '(' expression ')' statement {swi_stat++
iteration_statement : WHILE '(' expression ')' statement { for_stat++ DO statement WHILE '(' expression ')' '' { for_stat++ FOR '(' expression_statement expression_statement ')' statement { for_stat++ FOR '(' expression_statement expression_statement expression ')' statement { for_stat++ jump_statement : GOTO IDENTIFIER '' CONTINUE '' BREAK '' RETURN '' { ret_stat++ RETURN expression '' { ret_stat++ translation_unit : external_declaration translation_unit external_declaration external_declaration : function_definition declaration function_definition : declaration_specifiers declarator declaration_list compound_statement (dec_func++ def_func-- declaration_specifiers declarator compound_statement (dec_func++ def_func-- declarator declaration_list compound_statement (dec_func++ def_func-- declarator compound_statement (dec_func++ def_func-- %% #include <stdio.h> int main(){ yyparse() printf(" 함수선언 = %d\n",def_func) printf(" 함수정의 = %d\n",dec_func) printf(" 함수호출 = %d\n",cal_func) printf(" 일반변수선언문 = %d\n",ptr_iden)
printf(" 배열변수선언문 = %d\n",arr_iden) printf(" 포인터변수선언문 = %d\n", var_iden) printf(" 선택문 = %d\n", swi_stat) printf(" 조건문 = %d\n", if_state) printf(" 반복문 = %d\n", for_stat) printf(" 리턴문 = %d\n", ret_stat) printf(" 수식 = %d\n", exp_stat) return 0 yyerror(s) char *s { fflush(stdout)