TinyCIn Lex Yacc
|
TinyC language parser
글쓴이 : 이 형채 <hclee80 atat gmail dotdot com>
최초 수정일 : 2008년 06월 19일 ( 흐림 )
최종 수정일 : 2008년 06월 20일 ( 맑음 )
1. TinyC ? ¶tinyC ( 타이니 씨(0) ) - compiler construction, principles and practice -> kenneth C, rouden ( 컴파일러 제작, 원리와 실제 -> 김재훈|도경구|우균|정덕길|정민수)
책에서 컴파일러의 스캐너 & 파서 ( front-end ) 설명을 위해서 쓰여진 간단한 언어입니다. 영어를 그대로 해석해도 좋을 듯 싶습니다. 별다른 설명이 필요없을 정도로 적은 예약어와 문법입니다. lex와 yacc의 사용법을 익히기 위한 언어라고 생각하시면 됩니다. 물론 Tiny C Compiler 도 존재합니다.
Tiny C -> C-Minus -> C-- -> ANSI C 순서대로 확장되는 개념이라고 생각하시면 됩니다. 물론 ANSI C부터는 표준이고, 그 이하는 표준이 없습니다. 이 말은 곧 정의 및 구현하는 방법에 따라서 달라질수 있습니다. 예약어중 하나인 int 를 integer 라고 정의할수도 있을테고, INTEGER 라고 정의할 수도 있는 것입니다.
lex & yacc 을 설명하는 책, 문서, 사이트 등에서 쉽게 볼수 있는 예제가 expr ( expression ) 예제일 겁니다.이 내용은 결국 우선순위가 있는 calc 계산기 만들기와 동일합니다. 또한, "shift/reduce 충돌" 문제에 대한 내용에서도 언급되는 부분입니다. 그러나, 이 예제만으로 lex & yacc의 사용법은 익힐수 있으나, 정확한 이해는 매우 힘든 것같습니다.
그래서, Tiny C 가 lex & yacc 이용한 프로그래밍언어 파서를 만드는데 가장 좋은 예제인것 같습니다. 물론 lex & yacc가 꼭 프로그래밍언어만을 위한 것은 아닙니다. 응용방법은 무궁무진합니다. 이에 대해서는 추후에 다른 글을 참고하시면 됩니다.
유명한 awk, Perl 같은 문자열처리가 편한 툴 및 언어들처럼 c, c++ 에서도 유연하게 개발하실수 있습니다. awk 는 결국 lex & yacc 의 불편함(?)때문에 나온 것이고, Perl은 awk의 부족함을 채우기 위해서 만들어졌다. awk 주목적 개발목적은 빠른 테스팅(문자열처리)과 간편함을 위함이었다.
2. TinyC lexical ¶
2.1. TinyC.l ¶
%{
%{
#include "list.h"
#define YYSTYPE ListPtr
#include "y.tab.h"
extern YYSTYPE yylval;
%}
%%
"==" return EQ;
"<" return '<';
">" return '>';
"=" return '=';
"+" return '+';
"-" return '-';
"*" return '*';
"/" return '/';
"," return ',';
";" return ';';
"(" return '(';
")" return ')';
"{" return '{';
"}" return '}';
"[" return '[';
"]" return ']';
"int" return INT;
"return" return RETURN;
"if" return IF;
"else" return ELSE;
[0-9]+ { List* lst = NullList();
yylval = AddInteger(lst, atoi(yytext));
return Number;
}
[A-Za-z][A-Za-z0-9]* { List* lst = NullList();
yylval = AddSymbol(lst, MakeSymbol(yytext));
return Identifier;
}
[ \t\n] ;
. return BADTOKEN;
%%
3.1. TinyC.y ¶
/*
tinyc.y -- a parser of Tiny C
*/
%{
#include <stdio.h>
#include "list.h"
#define YYSTYPE ListPtr
static List* MakeBinaryExpr(char* op, List* exp1, List* exp2);
static List* MakeTriple(char* op, List* e1, List* e2, List* e3);
%}
/* yacc produces yyparse() from the following grammar. */
%token Identifier
%token Number
%token '=' '+' '-' '*' '/' ',' ';' '(' ')' '{' '}' '[' ']' '<' '>'
%token INT
%token RETURN
%token IF
%token ELSE
%token EQ /* == */
%token BADTOKEN
%%
program
: function
{}
| program function
{}
| error '}' /* error recovery: ignore tokens until '}' */
{} /* is encountered. */
function
: typename Identifier '(' formal.arguments ')' function.body
{
List* lst;
lst = ConcatLists($1, $2);
lst = AddList(lst, $4);
lst = ConcatLists(lst, $6);
PrintList(lst);
$$ = lst;
}
typename
: INT
{ $$ = AddSymbol(NullList(), "int"); }
| INT '*'
{ $$ = AddSymbol(NullList(), "int*"); }
formal.arguments
: /* empty */
{ $$ = NULL; }
| formal.argument.list
{ $$ = $1; }
formal.argument.list
: formal.argument
{ $$ = MakeList($1); }
| formal.argument.list ',' formal.argument
{ $$ = AddList($1, $3); }
formal.argument
: typename Identifier
{ $$ = MakeTriple("*var*", $2, $1, NULL); }
function.body
: '{' '}' /* empty body */
{ $$ = NULL; }
| '{' statements '}'
{ $$ = $2; }
statements
: statement
{ $$ = MakeList($1); }
| statements statement
{ $$ = AddList($1, $2); }
statement
: declaration
{ $$ = $1; }
| RETURN expression ';' /* return statement */
{ $$ = MakeBinaryExpr("return", $2, NULL); }
| if.statement
| term '=' expression ';' /* assignment */
{ $$ = MakeBinaryExpr("=", $1, $3); }
| expression ';'
{ $$ = MakeBinaryExpr(";", $1, NULL); }
| '{' statements '}'
{ $$ = MakeBinaryExpr("{}", $2, NULL); }
| ';' /* null statement */
{ $$ = NULL; }
declaration
: typename Identifier ';'
{ $$ = MakeTriple("*var*", $2, $1, NULL); }
| typename Identifier '[' Number ']' ';' /* array */
{ $$ = MakeTriple("*var*", $2, $1, $4); }
if.statement
: IF '(' expression ')' statement
/* ??? */
| IF '(' expression ')' statement ELSE statement
/* ??? */
expression
: additive.expression
{ $$ = $1; }
| expression EQ additive.expression
/* ??? */
| expression '>' additive.expression
/* ??? */
| expression '<' additive.expression
/* ??? */
additive.expression
: term
{ $$ = $1; }
| additive.expression '+' term
{ $$ = MakeList(MakeBinaryExpr("+", $1, $3)); }
| additive.expression '-' term
/* ??? */
term
: Identifier
{ $$ = $1; }
| Number
/* ??? */
| Identifier '(' opt.actual.arguments ')' /* function call */
{ $$ = MakeList(ConcatLists($1, $3)); }
| Identifier '[' expression ']' /* array access */
{ $$ = MakeList(MakeBinaryExpr("[]", $1, $3)); }
| '(' expression ')'
{ $$ = $2; }
opt.actual.arguments
: /* empty */
{ $$ = NullList(); }
| actual.arguments
{ $$ = $1; }
actual.arguments
: expression
{ $$ = $1; }
| actual.arguments ',' expression
{ $$ = ConcatLists($1, $3); }
%%
static List* MakeBinaryExpr(char* op, List* exp1, List* exp2)
{
List* lst = NullList();
lst = AddSymbol(lst, op);
lst = ConcatLists(lst, exp1);
return ConcatLists(lst, exp2);
}
static List* MakeTriple(char* op, List* e1, List* e2, List* e3)
{
List* lst = NullList();
lst = AddSymbol(lst, op);
lst = ConcatLists(lst, e1);
lst = ConcatLists(lst, e2);
return ConcatLists(lst, e3);
}
yyerror(msg)
char* msg;
{
#if !defined(YYBISON)
extern int yynerrs;
++yynerrs;
#endif
fprintf(stderr, "Error: %s\n", msg);
}
main()
{
extern int yynerrs;
#ifdef YYDEBUG
yydebug = !0;
#endif
yyparse();
fprintf(stderr, "%d errors.\n", yynerrs);
}
4. Makefile ¶#YACC = bison -dvy YACC = yacc -dv #LIB = -lfl # Linux LIB = -ll CC = gcc -g OBJ = y.tab.o lex.yy.o list.o codegen.o build : $(OBJ) gcc -g -o tinyc $(OBJ) $(LIB) debug : y.tab.c lex.yy.c gcc -DYYDEBUG -o tinyc y.tab.c lex.yy.c $(LIB) list.o : list.c list.h gcc -g -c list.c y.tab.c : tinyc.y $(YACC) tinyc.y lex.yy.c : tinyc.l lex tinyc.l clean : rm -f y.output y.tab.c y.tab.h lex.yy.c a.out tinyc *.s *.o *~ |
You will receive a legacy which will place you above want. |











![[http]](/imgs/http.png)