//Originically created on October 10, 2004 //Modified on January 11, 2011 #include #include #include #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) struct TOKEN; typedef double EVALTYPE; class CEvaluator { protected: TOKEN *m_pRoot; void CopyObject(const CEvaluator& obj); //recursive public: enum ErrorCode { noError=0, tooComplex=-1, invalidMatch=-2, invalidChar=-3, invalidCode=-4, divisionZero=-5, invalidAssign=-6, noToken=-7 }; CEvaluator(): m_pRoot(NULL) { } CEvaluator(EVALTYPE x); //constructs a constant expression CEvaluator(const CEvaluator& obj): m_pRoot(NULL) { CopyObject(obj); } const CEvaluator& operator =(const CEvaluator& obj) { CopyObject(obj); return *this; } ~CEvaluator() { Clear(); } //µ¥ÀÌÅÍ Ã³¸® void Clear(); BOOL IsEmpty() const { return !m_pRoot; } //¼ö½Ä ó¸®, Æò°¡ °ü·Ã int Interpret(PCTSTR pszExp); int Evaluate(EVALTYPE& res, EVALTYPE *pVar=NULL, UINT *pvcg=NULL) const; void Simplify(); BOOL Restore(PTSTR pszStr, BOOL bHex=FALSE) const; //recursive static EVALTYPE QuickEvaluate(PCTSTR pszExp, EVALTYPE *pVar=NULL, int *pErr=NULL); }; struct TOKEN { TOKEN(int cod, EVALTYPE pp); TOKEN(int cod, TOKEN *p1, TOKEN *p2, TOKEN *p3=NULL); int nOpCode; union { EVALTYPE nValue; int nVar; TOKEN *pOper[3]; }; }; class CCalc { public: class CIterator { //for recursive calculation public: PTSTR m_pszTo; BOOL m_bHex; //string restoration void Infix(const TOKEN& rt, BOOL bParen=FALSE); }; struct SIMPLESTACK { //for non-recursive calculation int pos; TOKEN *pt; }; enum { SIZE=32 }; //operator descriptor static PCTSTR m_szOpc[]; static inline BOOL IsOperator(int cod) { return cod>=0; } static int GetPrecedence(int cod); static int IsRightToLeft(int cod); static int GetDegree(int cod); //operation helper static void FreeNode(TOKEN *rt); static TOKEN *GetClone(TOKEN *rt); //recursive static int PopStack(TOKEN **tok_stk, int& tok_pos, int *opc_stk, int& opc_pos, int baseop=999); }; enum ETokenType { //»ó¼ö, º¯¼ö, ¿©´Â°ýÈ£¿Í ´Ý´Â°ýÈ£ CONSTANT=-3, VARIABLE=-2, OPENINGBR=100, CLOSINGBR }; enum EOperatorCode { //operator code //unary: ~ ! - + (0~3) ONECOMPL, LOGICALNOT, UNARYMINUS, UNARYPLUS, //binary * / % + - << >> < > (4~12) TIMES, DIVIDE, MODULOUS, PLUS, MINUS, LEFTSHIFT, RIGHTSHIFT, LESSTHAN, GREATERTHAN, //<= >= == != & ^ | && || (13~21) LESSTHANEQ, GREATERTHANEQ, EQUALTO, INEQUAL, BITAND, BITXOR, BITOR, AND, OR, //? : = , (22~25) CONDITIONAL, CONDITIONAL2, ASSIGN, COMMA, __endopc }; //baseopº¸´Ù ¿ì¼±¼øÀ§°¡ ³ô°Å³ª °°Àº °Í(Áï, Äڵ尪ÀÌ ´õ ÀÛÀº °Í)À» ¸ðµÎ »©³½´Ù. //´Ü, baseop°¡ right-to-leftÀÌ¸é ¾È »©³¿. int CCalc::PopStack(TOKEN **tok_stk, int& tok_pos, int *opc_stk, int& opc_pos, int baseop) { int x,y,z=GetPrecedence(baseop); TOKEN *ps; for(;opc_pos>0;opc_pos--) { x=opc_stk[opc_pos-1]; y=GetPrecedence(x); //½ºÅÃÀÇ ¿¬»êÀÚ, Áï ½Ä¿¡¼­ ¿ÞÂÊ¿¡ ÀÖ´Â ¿¬»êÀÚ°¡ ¿ì¸® ±âÁØ ¿¬»êÀÚº¸´Ù ¿ì¼±¼øÀ§°¡ ³·°Å³ª, //¿ì¼±¼øÀ§°¡ °°¾Æµµ ¹æÇâÀÌ ¹Ý´ë¸é, ³ªÁß¿¡ 󸮵ǵµ·Ï ¿¬»êÀÚ¸¦ ´õ »©³»Áö ¾Ê´Â´Ù. if(x==CONDITIONAL2 && (z>y || baseop==CONDITIONAL2)) { //?:¸¦ »©³¾ Á¶°ÇÀº? baseop ÀÚü°¡ :À̰ųª, baseop°¡ ?:º¸´Ùµµ ¿ì¼± ¼øÀ§°¡ ³·À» ¶§¸¸ //x ¾Õ¿¡´Â CONDITIONALÀÌ ÀÖ¾î¾ß ÇÑ´Ù. ÀÌ°É »©³»µµ·Ï. if(opc_pos<2) { return CEvaluator::invalidMatch; } else { if(opc_stk[opc_pos-2]!=CONDITIONAL) { return CEvaluator::invalidMatch; } //try again with ? operator opc_pos--; x=opc_stk[opc_pos-1]; y=GetPrecedence(x); } } else if(y>z || (y==z && IsRightToLeft(x) && IsRightToLeft(baseop)) ) { break; } //´Ý´Â °ýÈ£°¡ µåµð¾î ¿­¸° °ýÈ£¸¦ ¸¸³µÀ¸¹Ç·Î ÀÓ¹« ¿Ï¼ö if(x==OPENINGBR && baseop==CLOSINGBR) { opc_pos--; return CEvaluator::noError; } y=GetDegree(x); if(tok_pos-y<0) { return CEvaluator::invalidMatch; //stack underflow.. ÇÇ¿¬»êÀÚ°¡ ºÎÁ·ÇÏ´Ù } TOKEN *p1 = tok_stk[tok_pos-y]; TOKEN *p2 = (y>=2) ? tok_stk[tok_pos-y+1]: NULL; TOKEN *p3 = (y==3) ? tok_stk[tok_pos-y+2]: NULL; ps = new TOKEN(x, p1, p2, p3); tok_stk[tok_pos-y] = ps; if(y>=2) { //clear the buffer that has become a child tok_stk[tok_pos-y+1]=NULL; if(y==3) { tok_stk[tok_pos-y+2]=NULL; } } tok_pos-=y-1; //¹®¹ýÀûÀ¸·Î ¸í¹éÇÑ ¿¬»ê ¿À·ù¸¦ ÄÄÆÄÀÏ Å¸ÀÓ¿¡ ¹Ì¸® °¡·Á³½´Ù if(ps->nOpCode==ASSIGN && ps->pOper[0]->nOpCode!=VARIABLE) { return CEvaluator::invalidAssign; } if((ps->nOpCode==DIVIDE || ps->nOpCode==MODULOUS) && (ps->pOper[1]->nOpCode==CONSTANT && ps->pOper[1]->nValue==0) ) { return CEvaluator::divisionZero; } } //¿©´Â °ýÈ£°¡ ºÎÁ·ÇÏ´Ù return baseop==CLOSINGBR ? CEvaluator::invalidMatch: CEvaluator::noError; } int CEvaluator::Interpret(PCTSTR st) { Clear(); int retc=noError, lastopr=0; TOKEN *tok_stk[CCalc::SIZE]={NULL,}; int opc_stk[CCalc::SIZE]; //ÇÇ¿¬»êÀÚ¿Í ¿¬»êÀÚÀÇ ½ºÅà int tok_pos=0, opc_pos=0; while(*st) { //skip whitespaces while(*st==' ' || *st=='\t') { st++; } if(!*st) { break; } int arr_size = ARRAY_SIZE(tok_stk); if(tok_pos==arr_size) { retc=tooComplex; break; } //ÇÇ¿¬»êÀÚ¸¦ ã´Â´Ù if((*st>='0' && *st<='9') || (*st=='.')) // »ó¼ö { //ÇÇ¿¬»êÀÚ if(lastopr==CONSTANT) { retc=invalidMatch; break; } EVALTYPE x = 0; bool bFindDot = false; double dDotValue = 10.0; for(x=0; (*st>='0' && *st<='9') || (*st=='.'); st++) { if (*st=='.') { bFindDot = true; continue; } if (bFindDot) { x = x + double(*st-'0')/(dDotValue); dDotValue *= 10.0; } else { x = (x*10) + (*st-'0'); } } tok_stk[tok_pos] = new TOKEN(CONSTANT, x); tok_pos++; lastopr=CONSTANT; } else if((*st|32)>='a' && (*st|32)<='z') // º¯¼ö { if(lastopr==CONSTANT) { retc=invalidMatch; break; } //¿¬´Þ¾Æ »ó¼ö?? tok_stk[tok_pos] = new TOKEN(VARIABLE, (*st|32)-'a'); tok_pos++; while((*st|32)>='a' && (*st|32)<='z') { st++; } lastopr=CONSTANT; } else // ¿¬»êÀÚ È¤Àº °ýÈ£ { int x,y; for(y=2;y>=1;y--) { for(x=0;CCalc::m_szOpc[x];x++) { if((int)_tcslen(CCalc::m_szOpc[x])==y && _tcsncmp(CCalc::m_szOpc[x], st, y)==0) { goto Exf; } } } Exf: if(*st=='(') { x=OPENINGBR, y=1; } else if(*st==')') { x=CLOSINGBR, y=1; } else if(!CCalc::m_szOpc[x]) { retc=invalidChar; break; } //unrecognized code else if(CCalc::IsOperator(lastopr)) { //+, -°¡ ´ÜÇ×ÀÎÁö ÀÌÇ×ÀÎÁö ÆÇ´Ü if(x==PLUS) { x=UNARYPLUS; } else if(x==MINUS) { x=UNARYMINUS; } } else { if(x==UNARYPLUS) { x=PLUS; } else if(x==UNARYMINUS) { x=MINUS; } } //¾Õ¿¡ ÀÖ´Â ¿¬»êÀÚ Áß, ¿ì¸® ¿¬»êÀÚº¸´Ù ¿ì¼±¼øÀ§°¡ ³ôÀº °ÍÀ» »©³½´Ù (¿©´Â °ýÈ£´Â ¿¹¿Ü) if(x!=OPENINGBR) { retc = CCalc::PopStack(tok_stk, tok_pos, opc_stk, opc_pos, x); if(retc) { break; } } //¿ì¸® ¿¬»êÀÚ¸¦ ½ºÅÿ¡´Ù ³Ö´Â´Ù. (´Ý´Â °ýÈ£´Â ¿¹¿Ü) if(x!=CLOSINGBR) { if(opc_pos==ARRAY_SIZE(opc_stk)) { retc=tooComplex; break; } opc_stk[opc_pos++]=x; lastopr=x; } else { lastopr=CONSTANT; //´Ý´Â °ýÈ£ÀÇ µî±ÞÀº »ó¼öÀÌ´Ù!! } st+=y; } } if(!retc) { //°ýÈ£°í ¹¹°í ÇÒ °Í ¾øÀÌ ½ºÅÿ¡ ³²¾Æ ÀÖ´Â ÇÇ¿¬»êÀÚ¿Í ¿¬»êÀÚµéÀ» ÃÖÁ¾ Á¤¸®ÇÑ´Ù retc=CCalc::PopStack(tok_stk, tok_pos, opc_stk, opc_pos); //ÇÇ¿¬»êÀÚ ½ºÅÿ£ ¿ø¼Ò°¡ µü Çϳª¸¸ ÀÖ¾î¾ß ÇÑ´Ù if(tok_pos!=1 && !opc_pos) { retc= tok_pos ? invalidMatch: noToken; } } if(retc) { for(int x=0;xnOpCode) { case CONSTANT: return new TOKEN(rt->nOpCode, rt->nValue); case VARIABLE: return new TOKEN(rt->nOpCode, rt->nVar); default: return new TOKEN(rt->nOpCode, GetClone(rt->pOper[0]), GetClone(rt->pOper[1]), GetClone(rt->pOper[2])); } } else { return NULL; } } void CCalc::FreeNode(TOKEN *rt) { CCalc::SIMPLESTACK stk[CCalc::SIZE], *cs=stk, *st_top=stk+ARRAY_SIZE(stk)-1; stk[0].pos=0; stk[0].pt=rt; while(cs>=stk) { if(cs==st_top) return; if(cs->pt && CCalc::IsOperator(cs->pt->nOpCode)) { if(cs->pos<3) { cs[1].pt=cs->pt->pOper[cs->pos]; cs->pos++; cs++; cs->pos=0; } //push else { delete cs->pt; cs--; } } else { delete cs->pt; cs--; } //delete leaf node } } void CCalc::CIterator::Infix(const TOKEN& rt, BOOL bParen) { int x; switch(rt.nOpCode) { case CONSTANT: if(m_bHex) { _stprintf_s(m_pszTo, sizeof(m_pszTo)/sizeof(PTSTR), _T("0x%I64x"), rt.nValue); } else { _stprintf_s(m_pszTo, sizeof(m_pszTo)/sizeof(PTSTR), _T("%I64d"), rt.nValue); } while(*m_pszTo) { m_pszTo++; } break; case VARIABLE: _stprintf_s(m_pszTo, sizeof(m_pszTo)/sizeof(PTSTR), _T("%c"), rt.nVar+'a'); while(*m_pszTo) { m_pszTo++; } break; default://1==a ? b==c+2 ? a+5:f: 3==2+3 x=GetPrecedence(rt.nOpCode); if(bParen) { _tcscpy_s(m_pszTo, sizeof(m_pszTo)/sizeof(PTSTR), _T("(")), m_pszTo++; } switch(rt.nOpCode) { case CONDITIONAL: Infix(*rt.pOper[0], GetPrecedence(rt.pOper[0]->nOpCode)>=x); _tcscpy_s(m_pszTo,sizeof(m_pszTo)/sizeof(PTSTR), _T(" ? ")); m_pszTo+=3; Infix(*rt.pOper[1], GetPrecedence(rt.pOper[1]->nOpCode)>x); _tcscpy_s(m_pszTo, sizeof(m_pszTo)/sizeof(PTSTR), _T(" : ")); m_pszTo+=3; Infix(*rt.pOper[2], GetPrecedence(rt.pOper[2]->nOpCode)>x); break; case UNARYMINUS: case UNARYPLUS: case ONECOMPL: case LOGICALNOT: //prefix ´ÜÇ× _tcscpy_s(m_pszTo, sizeof(m_pszTo)/sizeof(PTSTR), m_szOpc[rt.nOpCode]); while(*m_pszTo) { m_pszTo++; } Infix(*rt.pOper[0], GetPrecedence(rt.pOper[0]->nOpCode)>x); break; case ASSIGN: //¿À¸¥ÂÊ¿¡¼­ ¿ÞÂÊÀ¸·Î °áÇÕÇÏ´Â ÀÌÇ× ¿¬»êÀÚ Infix(*rt.pOper[0], GetPrecedence(rt.pOper[0]->nOpCode)>=x); _tcscpy_s(m_pszTo, sizeof(m_pszTo)/sizeof(PTSTR), m_szOpc[rt.nOpCode]); while(*m_pszTo) { m_pszTo++; } Infix(*rt.pOper[1], GetPrecedence(rt.pOper[1]->nOpCode)>x); break; default: //¿ÞÂÊ¿¡¼­ ¿À¸¥ÂÊÀ¸·Î °áÇÕÇÏ´Â ÀÌÇ× ¿¬»êÀÚ Infix(*rt.pOper[0], GetPrecedence(rt.pOper[0]->nOpCode)>x); _tcscpy_s(m_pszTo, sizeof(m_pszTo)/sizeof(PTSTR), m_szOpc[rt.nOpCode]); while(*m_pszTo) { m_pszTo++; } Infix(*rt.pOper[1], GetPrecedence(rt.pOper[1]->nOpCode)>=x); } if(bParen) { _tcscpy_s(m_pszTo, sizeof(m_pszTo)/sizeof(PTSTR), _T(")")), m_pszTo++; } break; } } CEvaluator::CEvaluator(EVALTYPE x): m_pRoot(new TOKEN(CONSTANT, x)) { } void CEvaluator::Clear() { CCalc::FreeNode(m_pRoot); m_pRoot = NULL; } //a? b: c? d: e´Â a? b: (c ? d: e)¿Í °°´Ù. µû¶ó¼­ ¿À¸¥ÂÊ¿¡¼­ ¿ÞÂÊÀ¸·Î °áÇÕ //¿ì¼±¼øÀ§°¡ °°Àº ¿¬»êÀÚµéÀº °áÇÕ ¼ø¼­µµ °°´Ù. int CCalc::GetPrecedence(int cod) { #define SAMEORDER(a,b) if(cod>=a && cod<=b) return a SAMEORDER(ONECOMPL, UNARYPLUS); else SAMEORDER(TIMES, MODULOUS); else SAMEORDER(PLUS, MINUS); else SAMEORDER(LEFTSHIFT, RIGHTSHIFT); else SAMEORDER(LESSTHAN, GREATERTHANEQ); else SAMEORDER(EQUALTO, INEQUAL); else SAMEORDER(CONDITIONAL, CONDITIONAL2); else return cod; #undef SAMEORDER } int CCalc::IsRightToLeft(int cod) { //´ÜÇ× ¿¬»êÀÚ, ´ëÀÔ ¿¬»êÀÚ, ? : ¿¬»êÀÚ. //1? 0?2:3 :4 ÀÌ·± ¹®ÀåÀ» º¸¸é ? ¿¬»êÀÚ´Â ¿À¸¥ÂÊ¿¡¼­ ¿ÞÂÊÀ¸·Î Æò°¡µÈ´Ù return cod==ASSIGN || (cod>=ONECOMPL && cod<=UNARYPLUS) || cod==CONDITIONAL || cod==CONDITIONAL2; } int CCalc::GetDegree(int cod) { if(cod>=ONECOMPL && cod<=UNARYPLUS) { return 1; //´ÜÇ× ¿¬»êÀÚ } else if(cod==CONDITIONAL || cod==CONDITIONAL2) { return 3; //»ïÇ× ¿¬»êÀÚ } else { return 2; //³ª¸ÓÁö´Â ÀÌÇ× ¿¬»êÀÚ } } PCTSTR CCalc::m_szOpc[]={ _T("~"), _T("!"), _T("-"), _T("+"), _T("*"), _T("/"), _T("%"), _T("+"), _T("-"), _T("<<"), _T(">>"), _T("<"), _T(">"), _T("<="), _T(">="), _T("=="), _T("!="), _T("&"), _T("^"), _T("|"), _T("&&"), _T("||"), _T("?"), _T(":"), _T("="), _T(","), NULL }; int CEvaluator::Evaluate(EVALTYPE& res, EVALTYPE *pVar, UINT *pvcg) const { if(!m_pRoot) { return noToken; } struct { EVALTYPE calval[3]; TOKEN *pt; int pos, loc; //NOTE: loc is very likely to be elmiated } stk[CCalc::SIZE], *cs=stk, *st_top=stk+ARRAY_SIZE(stk)-1; stk[0].pos=stk[0].loc=0; stk[0].pt=m_pRoot; int op; while(cs>=stk) { if(cs==st_top) { return tooComplex; } op=cs->pt->nOpCode; switch(op) { case CONSTANT: //for non-operators cs->calval[cs->loc]=cs->pt->nValue; break; case VARIABLE: cs->calval[cs->loc]=pVar ? pVar[cs->pt->nVar]: 0; break; default: { EVALTYPE &ea=cs[1].calval[0], &eb=cs[1].calval[1], &ev=cs->calval[cs->loc]; switch(cs->pos) { //if sufficient number of operands are provided case 1: switch(op) { // case ONECOMPL: // ev=~ea; // goto Q; case LOGICALNOT: ev=!ea; goto Q; case UNARYMINUS: ev=-ea; goto Q; case UNARYPLUS: ev=ea; goto Q; default: if(op>=__endopc || op<0) { return invalidCode; } } break; case 2: switch(op) { //arithemetic case TIMES: ev=ea*eb; goto Q; case DIVIDE: if(eb) { ev=ea/eb; goto Q; } else { return divisionZero; } // case MODULOUS: // if(eb) // { // ev=ea%eb; // goto Q; // } // else // { // return divisionZero; // } case PLUS: ev=ea+eb; goto Q; case MINUS: ev=ea-eb; goto Q; // case LEFTSHIFT: // ev=ea<>eb; // goto Q; //logical case LESSTHAN: ev=eaeb; goto Q; case LESSTHANEQ: ev=ea<=eb; goto Q; case GREATERTHANEQ: ev=ea>=eb; goto Q; case EQUALTO: ev=ea==eb; goto Q; case INEQUAL: ev=ea!=eb; goto Q; //bit // case BITAND: // ev=ea&eb; // goto Q; // case BITXOR: // ev=ea^eb; // goto Q; // case BITOR: // ev=ea|eb; // goto Q; //logic case AND: case OR: ev=!!eb; goto Q; //lazy evaluation //et cetra case CONDITIONAL: ev=eb; goto Q; //it's already handled in a special way case ASSIGN: if(cs->pt->pOper[0]->nOpCode!=VARIABLE) { return invalidAssign; } ev=eb; if(pVar) { pVar[cs->pt->pOper[0]->nVar]=eb; } goto Q; case COMMA: ev=eb; goto Q; default: if(op>=__endopc || op<0) { return invalidCode; } } break; } //preprae to push stack if(cs->pos==1) { //first operand is determined EVALTYPE &ea=cs[1].calval[0], &ev=cs->calval[cs->loc]; switch(op) { case AND: if(!ea) { ev=0; goto Q; } break; //unconditionally false case OR: if(ea) { ev=1; goto Q; } break; //unconditionally true case CONDITIONAL: //decide which token to evaluate cs[1].pt=cs->pt->pOper[ea ? 1:2], cs[1].loc=cs->pos; goto P; } } cs[1].pt=cs->pt->pOper[cs->pos], cs[1].loc=cs->pos; P: cs->pos++, cs++, cs->pos=0; continue; } // default } // end main switch Q: cs--; //pop } // end while res=stk[0].calval[0]; return noError; } TOKEN::TOKEN(int cod, EVALTYPE pp): nOpCode(cod) { if(cod==CONSTANT) { nValue=pp; } else { nVar=(int)pp; } } TOKEN::TOKEN(int cod, TOKEN *p1, TOKEN *p2, TOKEN *p3): nOpCode(cod) { pOper[0]=p1; pOper[1]=p2; pOper[2]=p3; } EVALTYPE CEvaluator::QuickEvaluate(PCTSTR pszExp, EVALTYPE *pVar, int *pErr) { CEvaluator ev; EVALTYPE vp; int ec=ev.Interpret(pszExp); if(ec) { if(pErr) { *pErr=ec; return 0; } } ec=ev.Evaluate(vp, pVar); if(pErr) { *pErr=ec; } return ec ? 0: vp; } BOOL CEvaluator::Restore(PTSTR pszStr, BOOL bHex) const { if(m_pRoot) { CCalc::CIterator is; is.m_pszTo=pszStr; is.m_bHex=bHex; is.Infix(*m_pRoot); return TRUE; } else { *pszStr=0; return FALSE; } } void CEvaluator::CopyObject(const CEvaluator& obj) { if(&obj!=this) { m_pRoot=CCalc::GetClone(obj.m_pRoot); } }