180 likes | 423 Views
Calculator 구현. Simple Calculator. 문제 " 입력되는 수식의 값을 계산하는 lex 와 yacc 의 source 을 작성하시오 " 허용하는 연산자 : + * - / ( ) 우선순위 : ( ) > * / > + - 결합순위 : 모두 좌측 결합 입력 예 (1+2+3)*(2*(51-47)/(1+2*3)). 실습용 문법. 덧셈과 곱셈 식을 생성하는 문법 Exp : Exp ‘+’ Term | Exp ‘-’ Term | Term
E N D
Simple Calculator • 문제 "입력되는 수식의 값을 계산하는 lex와 yacc의 source을 작성하시오" • 허용하는 연산자: + * - / ( ) • 우선순위: ( ) > * / > + - • 결합순위: 모두 좌측 결합 • 입력 예 (1+2+3)*(2*(51-47)/(1+2*3))
실습용 문법 • 덧셈과 곱셈 식을 생성하는 문법 Exp : Exp ‘+’ Term | Exp ‘-’ Term | Term Term : Term ‘*’ Fact | Term ‘/’ Fact | Fact Fact : ‘(’ Exp ‘)’ | NUMBER
Lex 입력 %{ #include "exp.tab.h" %} %% [0-9]+ {yylval = atoi(yytext); return(NUMBER);} [ \t] ; \n return(0); \+ return('+'); \* return('*'); . { printf("'%c': illegal character\n", yytext[0]); exit(-1); }
Yacc 입력 %{ int result; %} %token NUMBER %% Exp : Exp '+' Term { $$ = $1 + $3; result = $$;} | Term { $$ = $1; } ; Term : Term '*' Num { $$ = $1 * $3; } | Num { $$ = $1; } ; Num : NUMBER { $$ = $1; } ; %% main() { yyparse(); printf("Result is %d\n", result); }
Calculator의 확장-1 • To handle variables with single letter names • To handle multiple expressions, one per line • To use floating point values
사용 예 2/3 = 0.666667 a = 2/7 a = 0.285714 z = a + 1 a/z = 0.222222 $
Yacc 입력 - 1 %{ double vbltable[26]; %} %union { double dval; int vblno; } %token <vblno> NAME %token <dval> NUMBER %left '-' '+' %left '*' '/' %nonassoc UMINUS %type <dval> expression %%
Yacc 입력 - 2 statement_list: statement '\n' | statement_list statement '\n‘ ; statement: NAME '=' expression { vbltable[$1] = $3; } | expression { printf("= %g\n", $1); } ; expression: expression '+' expression { $$ = $1 + $3; } | expression '-' expression { $$ = $1 - $3; } | expression '*' expression { $$ = $1 * $3; } | expression '/' expression { if ( $3 == 0.0 ) yyerror("divide by zero"); else $$ = $1 / $3; } | '-' expression %prec UMINUS { $$ = -$2; } | '(' expression ')' { $$ = $2; } | NUMBER { $$ = $1; } | NAME { $$ = vbltable[$1]; } ;
Calculator의 확장-2 • To allow longer variable names • Symbol table is needed to keep tracks of the name in use #define NSYMS 20 struct symtab { char *name; double value; } symtab[NSYMS]; struct symtab *symlook();
symlook() struct symtab *symlook(char *s) { char *p; struct symtab *sp; for(sp = symtab; sp < &symtab[NSYMS]; sp++) { if(sp->name && !strcmp(sp->name, s)) return sp; if(!sp->name) { sp->name = strdup(s); return sp; } } yyerror("Too many symbols"); exit(1); }
Lex & Yacc 입력 • Lex 입력 : 4.l • Yacc 입력: 4.y • 심볼테이블 헤더: 4hdr.h
Calculator의 확장-3 • To allow mathematical functions • Square root, exponential, logarithm s2 = sqrt(2) s2 = 1.41421 s2*s2 =2
First approach %token SQRT LOG EXP ... %% ... expression: ... | SQRT '(' expression ')' { $$ = sqrt($3);} | LOG '(' expression ')' { $$ = log($3);} | EXP '(' expression ')' { $$ = exp($3);} In lex. ... sqrt return SQRT; log return LOG; exp return EXP; ...
Another approach • Reserved words in the symbol table #define NSYMS 20 struct symtab { char *name; double (*funcptr)(); double value; } symtab[NSYMS]; struct symtab *symlook();
main() { extern double sqrt(), exp(), log(); addfunc("sqrt", sqrt); addfunc("exp", exp); addfunc("log", log); yyparse(); } addfunc(char *name, double (*func)()) { struct symtab *sp = symlook(name); sp->funcptr = func; }
Lex & Yacc 입력 • Lex 입력 : 5.l • Yacc 입력: 5.y • 심볼테이블 헤더: 5hdr.h