#include <stdlib.h> #include <malloc.h> #include <ctype.h> #include <stdio.h> #define MAX_BUFSIZE 100 typedef struct _PLEAF pleaf; typedef struct _PARSER parser; struct _PLEAF { pleaf *lc; pleaf *rc; char *token; }; struct _PARSER //파서 트리 { size_t tcnt; //파서 트리에 토큰 수 pleaf *root; //파서 트리의 root char **tokentable; //토큰 테이블 }; parser *ps; //파서 트리를 보관할 변수 char buf[MAX_BUFSIZE+1]; //수식을 보관할 입력 버퍼 int fnAdd(int a,int b) { return a+b; } int fnSub(int a,int b) { return a-b; } int fnMul(int a,int b) { return a*b; } int fnDiv(int a,int b) { if(b) { return a/b; } return 0; } int (*FArr[4])(int,int)={fnAdd,fnSub,fnMul,fnDiv}; pleaf *NewLeaf(char *in) { pleaf *t = 0; t= (pleaf *)malloc(sizeof(pleaf)); t->token = in; t->lc=0; t->rc=0; return t; }
parser *NewParser(size_t max_token) { parser * t = 0; if(max_token) { t = (parser *)malloc(sizeof(parser)); t->tcnt = 0; t->root = 0; t->tokentable = (char **)malloc(sizeof(char *)*max_token); } return t; } void KillParser(parser *in) { free(in->tokentable); free(in); } int IsOperator(char expr) //연산자인지 확인 { switch(expr) { case '+': case '-': case '*': case '/': return 1; } return 0; } int Lexical(char *expr,parser *in) //입력 버퍼를 수식에 사용되는 토큰들로 이루어졌는지 확인하며 토큰 테이블을 구성 { while(*expr) { if(isdigit(*expr)) //수식에 사용되는 토큰 중에 피연산자인 토큰인지 확인하여 토큰 테이블에 보관 { in->tokentable[in->tcnt] = expr; for(;isdigit(*expr);expr++); } else if(IsOperator(*expr)) //수식에 사용되는 토큰 중에 연산자인 토큰인지 확인하여 토큰 테이블에 보관 { in->tokentable[in->tcnt]=expr; expr++; } else //피 연산자나 연산자가 아닌 토큰이라면 수식이 될 수 없음 { return 0; } in->tcnt++; } return 1; } int Syntax(parser *in) { size_t cnt=0; char **base = in->tokentable; if(isdigit(**base)) //토큰 테이블의 시작은 피연산자로 시작되어야 수식 문법에 맞음 { for(cnt = 1,base++; cnt < in->tcnt ; cnt= cnt+2) //토큰 테이블 끝까지 다음을 충족해야 수식 문법에 맞음 { if(IsOperator(**base) && isdigit(**(base+1))) //연산자 와 피 연산자의 쌍으로 구성되야 수식 문법에 맞음 { base = base+2; } else //수식 문법에 맞지 않은 것임 { return 0; } } return 1; } return 0; } void HangOperand(pleaf *inleaf,parser *inp) //피 연산자를 파서트리에 매단다 { pleaf *t = inp->root; while(t->rc) //파서 트리의 우측 자식이 빈 곳을 찾음 - 피 연산자의 부모 연산자를 찾는 것임 { t = t->rc; } t->rc = inleaf; //찾은 부모의 우측 자식으로 피연산자를 매단다 } void HangOperator(pleaf *inleaf,parser *inp)//연산자를 파서트리에 매단다 { char t = *(inleaf->token); char r = *(inp->root->token);
if(((t=='*')||(t=='/')) && ((r== '+')||(r=='-'))) //새로 들어온 연산자가 root의 연산자보다 우선순위가 높을 때 { inleaf->lc = inp->root->rc; inp->root->rc = inleaf; } else { inleaf->lc = inp->root; inp->root = inleaf; } } void Parse(parser *in) //토큰 테이블을 가지고 파서 트리를 형성 { size_t cnt = 0; char **base=0; pleaf *t = 0;
base = in->tokentable; t = NewLeaf(*base); in->root = t; for(base++,cnt = 1;cnt<in->tcnt;base++,cnt=cnt+2) { t = NewLeaf(*base); HangOperator(t,in); base++; t = NewLeaf(*base); HangOperand(t,in); } } int CalAll(pleaf *in) //파서 트리를 후위 순회하면서 계산하면 결과가 나옮 { int rval = 0,lval = 0; int ind = 0; if(in->lc) { lval = CalAll(in->lc); rval = CalAll(in->rc); switch(*(in->token)) { case '/':ind++; case '*':ind++; case '-':ind++; } return FArr[ind](lval,rval); } else { return atoi(in->token); } } int CalCul(parser *in) { return CalAll(in->root); } void GetStr(char *in,size_t size) { size_t i=0; while(i<size) { in[i] = getchar(); if(in[i] == '\n') break; if((in[i] != ' ')&&(in[i]!='\t')) i++;//공백과 탭이 아닐 경우에만 i를 증가 - 즉, 공백과 탭은 제거 } in[i]='\0'; fflush(stdin); } void Init() { ps = NewParser(MAX_BUFSIZE+1); printf("수식을 입력하세요\n"); GetStr(buf,MAX_BUFSIZE); }
void Run() { if(Lexical(buf,ps)) { if(Syntax(ps)) { Parse(ps); printf("%s=%d\n",buf,CalCul(ps)); } else { printf("[%s]는 문법 에러\n",buf); } } else { printf("[%s]는 수식에 사용할 수 없는 어휘 사용\n",buf); }
} void Exit() { KillParser(ps); }
void main() { Init(); Run(); Exit(); } |
댓글