// // Calculator.cpp // #include "StdAfx.h" // Ignore truncation warnings #pragma warning(disable: 4786) #include "FiniteStateMachine.h" #include "Logging.h" #include "math.h" #include "float.h" // // May need to modify operand type and value if mixed math, // hence the need to pass the complete operand // static int parens=0; typedef double (*DblFcnPtr1arg)(double a); typedef double (*DblFcnPtr2arg)(double a, double b); // Revamped STL because it missed a few operators #include "mystack.h" //---------------------------------------------------------------------- // // OPERATOR handling: + - * / ** etc. // // character strings are used to match against to find operator. Problem // not solved is ** versus *. ** is just tested first. Unclear how // to handle this problem. // //---------------------------------------------------------------------- typedef enum { opNULL=0, opPOWER=1, opPLUS=2, opMINUS=3, opTIMES=4, opDIVIDE=5, opEQ=6, opNE=7, opLT=8, opLE=9, opGT=10, opGE=11, opAND=12, opOR=13 , opNOT=14, opLFPAREN=15, opRTPAREN=16, opASIN=17, opATAN=18, opCOS=19, opEXP=20, opLOG=21, opLOG10=22, opSIN=23, opSQRT=24, opTAN=25, opCOMMA=26, opENDMARK=17 } OPTYPE; struct operations { static double add(double a, double b) { return a+b; } static double sub(double a, double b) { return a-b; } static double mult(double a, double b) { return a*b; } static double div(double a, double b) { return a/b; } static double equals(double a, double b) { return a == b ; } static double ne(double a, double b) { return a != b ; } static double ge(double a, double b) { return a >= b ; } static double gt(double a, double b) { return a > b ; } static double le(double a, double b) { return a <= b ; } static double lt(double a, double b) { return a <b ; } static double and(double a, double b) { return ((int) a) && ((int) b) ; } static double or(double a, double b) { return ((int) a) || ((int) b) ; } static double bitand(double a, double b) { return ((int) a) & ((int) b) ; } static double bitor(double a, double b) { return ((int) a) | ((int) b) ; } }; // // Kludge to allow power "**" to be checked before times "*" // Better to use regular expression or pattern match. // struct OPTOKEN { String op; int operands; int rank; OPTOKEN(String n_op, int n_operands, int n_rank){ op=n_op; operands=n_operands; rank=n_rank; } }; OPTOKEN optoken[28] = { OPTOKEN(String(L""), 0, 0), OPTOKEN(String(L"**"), 2, 15), /* POWER*/ OPTOKEN(String(L"+"), 2, 12), /* PLUS */ OPTOKEN(String(L"-"), 2, 12), /* MINUS */ OPTOKEN(String(L"*"), 2, 13), /* TIMES*/ OPTOKEN(String(L"/"), 2, 13), /* DIVIDE*/ OPTOKEN(String(L"=="), 2, 9), /* EQ */ OPTOKEN(String(L"!="), 2, 10), /* NE*/ OPTOKEN(String(L"<"), 2, 10), /* LT*/ OPTOKEN(String(L"<="), 2, 10), /* LE*/ OPTOKEN(String(L">"), 2, 10), /* GT*/ OPTOKEN(String(L">="), 2, 10), /* GE*/ OPTOKEN(String(L"&&"), 2, 5), /* AND*/ OPTOKEN(String(L"||"), 2, 4), /* OR*/ OPTOKEN(String(L"!"), 1, 14), /* NOT*/ OPTOKEN(String(L"("), 0, 1), OPTOKEN(String(L")"), 0, 1), OPTOKEN(String(L"ASIN"), 1, 1), OPTOKEN(String(L"ATAN"), 1, 1), OPTOKEN(String(L"COS"), 1, 1), OPTOKEN(String(L"EXP"), 1, 1), OPTOKEN(String(L"LOG"), 1, 1), OPTOKEN(String(L"LOG10"), 1, 1), OPTOKEN(String(L"SIN"), 1, 1), OPTOKEN(String(L"SQRT"), 1, 1), OPTOKEN(String(L"TAN"), 1, 1), OPTOKEN(String(L","), 0, 2), OPTOKEN(String(L"end-mark"), 0, 1) }; OPTYPE findOpToken(String token) { for(int i=0; i< 28; i++){ if(!optoken[i].op.CompareNoCase(token)) return (OPTYPE) i; } return (OPTYPE) 0; } struct CToken { String type; String value; union { OPTYPE optype; double dvalue; int ivalue; }; }; // //---------------------------------------------------------------------- // int getNextToken(String &line, CToken & token ) // // //---------------------------------------------------------------------- // int getNextToken(String &line, CToken & token ) { int j=0; WCHAR match; int i=0; token.type.Nullify(); token.value.Nullify(); token.optype=opNULL; line.LeftTrim(); // check for quoted string as token if(line.Size()==0) return 0; if(line[i] == L'(' ){ token.value=L"("; token.optype=findOpToken(token.value); token.type=L"left-paren"; line=line.Mid(2); return(1); } if(line[i] == L')' ){ token.value=L")"; token.optype=findOpToken(token.value); token.type=L"right-paren"; line=line.Mid(2); return(1); } if(line[i] == L',' ){ token.value=line[i]; token.optype=findOpToken(token.value); token.type=L"operator"; line=line.Mid(2); return(1); } if((line[i] == L'\'') || (line[i] == L'\"')) { match=line[i]; int n=line.Find(match,ffNone, 1); if(n==0) return 0; token.value=line.Mid(1,n-1); token.type=match; line=line.Mid(n+1); return(1); } #if 0 // Match operand without number if((line[i]==L'+') || (line[i]==L'-') /*&& !iswdigit(line[i+1])*/){ token.value=line[i]; token.optype=findOpToken(token.value); token.type=L"operator"; line=line.Mid(2); return 1; } #endif while(wcsrchr(L"*/><=|&!+-", line[i])) i++; if(i>0) { token.value=line.Mid(1,i); token.optype=findOpToken(token.value); token.type=L"operator"; if(optoken[token.optype].operands==1) token.type=L"unaryoperator"; line=line.Mid(i+1); return 1; } // Match regular alphanumeric token // if(!iswalnum(line[i]) && (line[i]!=L'_')) return(0); if(iswdigit(line[i])) { int n; n = swscanf((BSTR) line,L"%lf%n", &token.dvalue, &i); if(n>0) { token.type=L"number"; line=line.Mid(i+1); return 1; } /** // Not double try integer - shouldn't need this int j; n = swscanf((BSTR) line,L"%d%n", &token.ivalue, &j); if(n>0) { token.type=L"integer"; line=line.Mid(i+1); return 1; } */ return 0; } while(i<line.Size() && (iswalpha(line[i]) || (line[i]==L'_'))) i++; if(i>0) { token.value=line.Mid(1,i); token.type=L"token"; line=line.Mid(i+1); DblFcnPtr1arg mathfcn=0; token.optype=findOpToken(token.value); if(token.optype!=0) token.type="unaryoperator"; // Requires 2 arguments */ #if 0 if(!strcmp(token,"ATAN2")) mathfcn=atan2; if(!strcmp(token,"POW")) mathfcn=pow; #endif return 1; } return 0; } /** @include CalculatorDescription.html */ class CCalculator : public CFiniteStateMachine { public: String expression; String errmessage; std::mystack<double> operands; std::mystack<OPTYPE> operators; CToken token; CToken lookaheadtoken; String line; double result; bool echo; CCalculator(String expr=L"") { setName(L"Calculator"); line=expr; setupFSM(); resetFSM(); } ~CCalculator() { operands.clear(); operators.clear(); } /** * Get the next token from the line. If no token remaining, set end-mark as token. * Process token type as event. * @return S_OK always. */ CToken getNextToken() { CToken token; if(::getNextToken(line, token) >0) { } else token.type=L"end-mark"; return token; } /** * Evaluates a operation as given by an OPTYPE op, * and operands from the operand stack. * Pops number of operands depending on operator type, * calculates, and pushes the result back on the operand stack. * @return 0 ok. * @return 1 couldn't find matching operation based on operator. */ static int opEVAL(OPTYPE op, std::mystack<double> &operands) { if(optoken[op].operands==2) CLogging::Log.log("Evaluate Op %S with %f and %f to give", (BSTR) optoken[op].op, operands.peek(0), operands.peek(1)); if(optoken[op].operands==1) CLogging::Log.log("Evaluate Op %S with %f to give", (BSTR) optoken[op].op, operands.peek(0)); if(operands.size()< optoken[op].operands) return -1; // Do arithmetic switch(op) { // Handle Binary Ops case opNULL: break; case opPLUS: operands.push(operands.pop()+operands.pop()); break; case opMINUS: operands.push(-operands.pop()+operands.pop()); break; case opTIMES: operands.push(operands.pop()*operands.pop()); break; case opDIVIDE: { double a = operands.pop(); double b= operands.pop(); operands.push(b/a); // operands.swap(); operands.push(operands.pop()/operands.pop()); } break; case opEQ: operands.push(operands.pop() == operands.pop()); break; case opNOT: operands.push(!(int) operands.pop()); break; case opNE: operands.push(operands.pop() != operands.pop()); break; case opLT: operands.push(operands.pop()>=operands.pop()); break; case opLE: operands.push(operands.pop()>operands.pop()); break; case opGT: operands.push(operands.pop()<=operands.pop()); break; case opGE: operands.push(operands.pop()<operands.pop()); break; case opAND: operands.push(operands.pop() && operands.pop()); break; case opOR: operands.push(operands.pop() || operands.pop()); break; // case opPOWER: c=(int) pow((double) a , (double) b); break; case opASIN: operands.push(asin(operands.pop())); break; case opATAN: operands.push(atan(operands.pop())); break; case opCOS: operands.push(cos(operands.pop())); break; case opEXP: operands.push(exp(operands.pop())); break; case opLOG: operands.push(log(operands.pop())); break; case opLOG10: operands.push(log10(operands.pop())); break; case opSIN: operands.push(sin(operands.pop())); break; case opSQRT: operands.push(sqrt(operands.pop())); break; case opTAN: operands.push(tan(operands.pop())); break; case opCOMMA: break; // skip, should have count of operands default: return 1; } CLogging::Log.log(" Result %f\n", operands.top()); return 0; } /** * Stack number action. * If current token is of type operator, then push token.optype onto operators stack; * else raise internal fail event "Expecting Operator". * @return S_OK always. States handles errors. */ HRESULT stackNumber(CFiniteStateMachine * fsm, CFSMEvent * event) { CLogging::Log.log("stackNumber action\n"); ::Sleep(5); if(token.type==L"number") operands.push(token.dvalue); else handleEvent(new CFSMFailedEvent("fail", "Expecting Number")); return S_OK; } /** * Condition to check if valid signed number, when expecting regular number/operand. * If current token is of type opPLUS or opMINUS, then determine sign, and * do lookahead of next token. If next token type is of number, return true, otherwiswe false. * If current token is not of type opPLUS or opMINUS, then return false. * @return true unary operator with number, false otherwise. */ bool checkSignedNumber() { bool truth; if((token.optype==opPLUS) || (token.optype==opMINUS)){ int sign=0; if(token.optype==opPLUS) sign=1; if(token.optype==opMINUS) sign=-1; if(lookaheadtoken.type==L"number") truth= true; else truth=false; } else truth=false; CLogging::Log.log("checkSignedNumber condition = %s\n",( (truth)? "true" : "false")); return truth; } /** * Stack signed number, when expecting regular number/operand. * If current token is of type opPLUS or opMINUS, then determine sign, and * do lookahead (cheating yes.) by getting next token. If type is of number, push * number on operands stack, else issue internal Fail event "Expecting number after sign". * If current token is not of type opPLUS or opMINUS, then issue internal Fail event Illegal Operator". * @return S_OK always. States handles errors. */ HRESULT stacksignednumber(CFiniteStateMachine * fsm, CFSMEvent * event) { // This will double check conditional guardian, CLogging::Log.log("allowsignednumber action\n"); if((token.optype==opPLUS) || (token.optype==opMINUS)){ int sign=0; if(token.optype==opPLUS) sign=1; if(token.optype==opMINUS) sign=-1; if(sign==0) handleEvent(new CFSMFailedEvent("fail", "Internal Error Handling sign")); if(lookaheadtoken.type==L"number"){ operands.push(sign*lookaheadtoken.dvalue); // This is the part that's a bit clunky. lookaheadtoken=getNextToken(); } else handleEvent(new CFSMFailedEvent("fail", "Expecting number after sign")); } else handleEvent(new CFSMFailedEvent("fail", "Illegal Operator")); return S_OK; } /** * Action to stack unary operator on operator stack. * If current token is not unary operator, issue internal Fail event "Expecting Unary Operator". * @return S_OK always. States handles errors. */ HRESULT stackunaryoperator(CFiniteStateMachine * fsm, CFSMEvent * event) { CLogging::Log.log("allowunaryoperator action\n"); if(optoken[token.optype].operands ==1) { operators.push(token.optype); return S_OK; } else handleEvent(new CFSMFailedEvent("fail", "Expecting Unary Operator")); return S_OK; } /** * Stack operator action. * If current token is of type operator, then push token.optype onto operators stack; * else raise internal fail event "Expecting Operator". * @return S_OK always. States handles errors. */ HRESULT stackOperator(CFiniteStateMachine * fsm, CFSMEvent * event) { CLogging::Log.log("stackOperator action\n"); if(token.type==L"operator") operators.push(token.optype); else handleEvent(new CFSMFailedEvent("fail", "Expecting Operator")); return S_OK; } /** * Stack left parenthesis action. * @return S_OK always. */ HRESULT stackLeftParen(CFiniteStateMachine * fsm, CFSMEvent * event) { CLogging::Log.log("stackLeftParen action\n"); operators.push(token.optype); return S_OK; } /** * Dummy nop action. * @return S_OK always. */ HRESULT noOperation(CFiniteStateMachine * fsm, CFSMEvent * event) { return S_OK; } /** * Unstack operators that are of greater precedence than the current operator. * If the operator on the top is of greater rank than the current token operator, * evaluate the top of the operator stack. */ HRESULT unstackGEOperator(CFiniteStateMachine * fsm, CFSMEvent * event) { CLogging::Log.log("unstackGEOperator action\n"); if(operators.size()<1) return S_OK; if(optoken[operators.top()].rank > optoken[token.optype].rank){ opEVAL(operators.pop(),operands); } ::Sleep(4); return S_OK; } /** * Unstack all operators on the operator stack. * If operators evaluation is missing operand, raise internal fail event, "Missing Operaand". * @return S_OK always. State transitions handle failures. */ HRESULT unstackAllOperators(CFiniteStateMachine * fsm, CFSMEvent * event) { CLogging::Log.log("unstackAllOperators action\n"); while((operators.size()>1 && optoken[operators.top()].rank > optoken[opENDMARK].rank) ) { if(opEVAL(operators.pop(),operands)<0){ handleEvent(new CFSMFailedEvent("fail", "Missing Operaand")); break; } } ::Sleep(2); return S_OK; } /** * Terminate calculation. Should be opENDMARK on the operators stack. * If proper ending, sets done flag to -1 so calculation loop ends. * Otherwise, the calculation failed, and issue internal failed event. * @return S_OK even when transition to fail state. */ HRESULT terminate(CFiniteStateMachine * fsm, CFSMEvent * event) { CLogging::Log.log("terminate action\n"); if((operands.size()>1) && (operators.top()!=opENDMARK) ){ handleEvent(new CFSMFailedEvent("fail", "Failed calculation")); } else { m_doneFlag=1; result=operands.pop(); } return S_OK; } /** * Failure of some sort. If Event is of type CFSMFailedEvent then log error message. * Otherwise, specify non-specific error. * Sets done flag to -1. * @return S_OK error message logged. */ HRESULT fail(CFiniteStateMachine * fsm, CFSMEvent * event) { CLogging::Log.log("fail action\n"); // Problems with using CRuntime and CObject #if 0 if(event->IsKindOf( RUNTIME_CLASS( CFSMFailedEvent ) ) ) // this is a bug if you don't use a pointer // CRuntimeClass* prt = event->GetRuntimeClass(); //if(!strcmp( prt->m_lpszClassName, "CFSMFailedEvent" ) ) { //if IsKindOf is true, then cast is all right CFSMFailedEvent * failedevent= (CFSMFailedEvent *) event; errmessage= failedevent->errmessage; } else { errmessage=L"Fail: Non Specific Reason"; } #endif errmessage=L"Fail: Non Specific Reason"; m_doneFlag=-1; return S_OK; } /** * Failure of some signed number sort. * If current token is not of type opPLUS or opMINUS, then issue internal Fail event Illegal Operator". * Else lookahead token was not a number so issue internal Fail event "Expecting number after sign". * Sets done flag to -1. * @return S_OK error message logged. */ HRESULT failedSignedNumber(CFiniteStateMachine * fsm, CFSMEvent * event) { CLogging::Log.log("failedSignedNumberr action\n"); if(!((token.optype==opPLUS) || (token.optype==opMINUS))) errmessage=L"Failed: Illegal Operator"; else errmessage=L"Failed: Expecting number after sign"; m_doneFlag=-1;; return S_OK; } /** * If top operator is left parenthesis, then unstack and evaluate operators. * Has kludge to check for unary operator such as fcn in fcn(x) before ( * Obvious kludge as some are fcn(x,y), to be handled later * If operators is not opLFPAREN then issue internal fail event because of "Mismatched Parentheses". * @return S_OK even if failed. */ HRESULT unstackIfLeftParen(CFiniteStateMachine * fsm, CFSMEvent * event) { if(operators.top() == opLFPAREN){ operators.pop(); // Check for unary operator such as fcn in fcn(x) before ( // Obvious kludge as some are fcn(x,y), to be handled later if((operators.size()>1) && optoken[operators.top()].operands==1) { if(opEVAL(operators.pop(),operands)<0){ handleEvent(CFSMFailedEvent("fail", "Missing Operaand")); } } } else handleEvent(new CFSMFailedEvent("fail", "Mismatched Parentheses")); return S_OK; } /** * Setup assigns event, actions, and state transition to all states in the calculator. */ void setupFSM(){ // As long as there is this within this list, can't save to file CFSMTransitionPtr transition; this->firstState= String("EXPECTING-OPERAND"); transition=addTransition("EXPECTING-OPERAND", "operator", "EXPECTING-OPERATOR", new CFSMAction(this, (FSMObjectFunction) &CCalculator::stacksignednumber, L"stacksignednumber"), NULL); transition->addCondition(new CFSMCondition(this,(FSMConditionFunction) &CCalculator::checkSignedNumber, L"checkSignedNumber" )); transition=addTransition("EXPECTING-OPERAND", "operator", "EXPECTING-OPERATOR", new CFSMAction(this, (FSMObjectFunction) &CCalculator::failedSignedNumber, L"failedSignedNumber"), NULL); transition->addCondition(new CFSMCondition(this,(FSMConditionFunction) &CCalculator::checkSignedNumber, L"NOT(checkSignedNumber)", true )); addTransition("EXPECTING-OPERAND", "unaryoperator", "EXPECTING-OPERAND", new CFSMAction(this, (FSMObjectFunction) &CCalculator::stackunaryoperator, L"allowunaryoperator"), NULL); addTransition("EXPECTING-OPERAND", "number", "EXPECTING-OPERATOR", new CFSMAction(this, (FSMObjectFunction) &CCalculator::stackNumber, L"stackNumber"), NULL); addTransition("EXPECTING-OPERAND", "left-paren", "EXPECTING-OPERAND", new CFSMAction(this, (FSMObjectFunction) &CCalculator::stackLeftParen, L"stackLeftParen"), NULL); addTransition("EXPECTING-OPERAND", "end-mark", "TERMINATE-PROGRAM", new CFSMAction(this, (FSMObjectFunction) &CCalculator::terminate, L"terminate"), NULL); addTransition("EXPECTING-OPERAND", "fail", "FAILED", new CFSMAction(this, (FSMObjectFunction) &CCalculator::fail, L"fail"), NULL); // after stacking operand, expect an operator addTransition("EXPECTING-OPERATOR", "operator", "EXPECTING-OPERAND", new CFSMAction(this, (FSMObjectFunction) &CCalculator::unstackGEOperator, L"unstackGEOperator"), new CFSMAction(this, (FSMObjectFunction) &CCalculator::stackOperator, L"stackOperator"), NULL); addTransition("EXPECTING-OPERATOR", "number", "FAILED", new CFSMAction(this, (FSMObjectFunction) &CCalculator::noOperation, L"noOperation"), NULL); addTransition("EXPECTING-OPERATOR", "right-paren", "EXPECTING-OPERATOR", new CFSMAction(this, (FSMObjectFunction) &CCalculator::unstackAllOperators, L"unstackAllOperators"), new CFSMAction(this, (FSMObjectFunction) &CCalculator::unstackIfLeftParen, L"unstackIfLeftParen"), NULL); addTransition("EXPECTING-OPERATOR", "end-mark", "TERMINATE-PROGRAM", new CFSMAction(this, (FSMObjectFunction) &CCalculator::unstackAllOperators, L"unstackAllOperators"), NULL); addTransition("EXPECTING-OPERATOR", "fail", "FAILED", new CFSMAction(this, (FSMObjectFunction) &CCalculator::fail, L"fail"), NULL); addTransition("TERMINATE-PROGRAM", "end-mark", "TERMINATE-PROGRAM", new CFSMAction(this, (FSMObjectFunction) &CCalculator::terminate, L"terminate"), NULL); addTransition("TERMINATE-PROGRAM", "fail", "FAILED", new CFSMAction(this, (FSMObjectFunction) &CCalculator::fail, L"fail"), NULL); } /** * Reset calculator FSM and looping logic. * Set looping doneFlag to false. * CFiniteStateMachine::resetFSM(); * Clear operators stack, operands stack, and push opENDMARK onto operators stack. */ HRESULT resetFSM() { m_doneFlag=false; CFiniteStateMachine::resetFSM(); operators.clear(); operands.clear(); operators.push(opENDMARK); errmessage=L""; return S_OK; } /** * Calculate is the main function for the class. It accepts an expression, resets the logic, * and loops parsing and then transitioning the FSM to handle the token. * @return result as a string. Error message if failed. */ String calculate(String expression) { String answer=L""; line=expression; resetFSM(); lookaheadtoken=getNextToken(); while(!isDone()) { CLogging::Log.log("\n"); CLogging::Log.log("Current State %S\n", (BSTR) getCurrentStateName()); token=lookaheadtoken; lookaheadtoken=getNextToken(); handleEvent((BSTR) token.type); CLogging::Log.log("Event = %S", (BSTR) token.type); // CLogging::Log.log("Token Type = %S, Test = %S", (BSTR) token.type, (BSTR) line); if(token.type==L"operator") CLogging::Log.log(" Token Value = %S\n", (BSTR) optoken[token.optype].op); else if(token.type==L"number") CLogging::Log.log("Token Value = %f\n", token.dvalue); else CLogging::Log.log("\n"); updateFSM(); } if(errmessage==L""){ answer.Append("%f", result); return answer; } else return errmessage; } }; void testCalculator() { String test=L"hello world 123 * 456E1"; CToken token; /** while(getNextToken(test, token) >0) { CLogging::Log.log("Token Type = %S, Test = %S", (BSTR) token.type, (BSTR) test); if(token.type==L"token") CLogging::Log.log("Token Value = %S\n", (BSTR) token.value); else if(token.type==L"integer") CLogging::Log.log("Token Value = %d\n", token.ivalue); else if(token.type==L"double") CLogging::Log.log("Token Value = %f\n", token.dvalue); else CLogging::Log.log("\n"); } */ CCalculator * calc = new CCalculator(L"123 * 456E1"); calc->IncRef(); calc->debug=9; calc->echo=true; CLogging::Log.log(1, "\nBefore Testing Calculator Pointer Info\n%S", (BSTR) CFSMState::classInfo()); CLogging::Log.log("Calculate -22*4=%S\n", (BSTR) calc->calculate(L"-22*4")); CLogging::Log.log("Calculate -*22+4=%S\n", (BSTR) calc->calculate(L"*22+4")); CLogging::Log.log("Calculate 2+2+4=%S\n", (BSTR) calc->calculate(L"2+2+4")); CLogging::Log.log("Calculate 1==1=%S\n", (BSTR) calc->calculate(L"1==1")); CLogging::Log.log("Calculate 0==1=%S\n", (BSTR) calc->calculate(L"0==1")); CLogging::Log.log("Calculate !(0==1)=%S\n", (BSTR) calc->calculate(L"!(0==1)")); CLogging::Log.log("Calculate (6-2)*5=%S\n", (BSTR) calc->calculate(L"(6-2)*5")); CLogging::Log.log("Calculate sqrt(2+2)+6=%S\n", (BSTR) calc->calculate(L"sqrt(2+2)+6")); CLogging::Log.log(1, "\n\nDone Testing, now release to avoid leaks"); CLogging::Log.log(1, "\nDone Testing Calculator Pointer Info\n%S", (BSTR) CFSMState::classInfo()); CLogging::Log.flush(); String html=calc->toHTML(); String timing=calc->timingToHTML(); timing.SaveFile("calculatorTiming.html"); html.SaveFile("calculator.html"); // html.SaveFile("D:\\omac\\omacapi\\fsmlibnew\\doc\\calculatorFSM.html"); calc->forceCurrentState("EXPECTING-OPERAND"); CFSMEventPtr ptr; calc->processEvent(ptr=new CFSMFailedEvent("fail", "Missing Operand")); html=calc->toHTML(-1); html.SaveFile("failedCalculatorHTML.html"); calc->releaseFSM(); calc->DecRef(); } class CMacroFSM : public CCalculator { public: /* BEGIN_STATE_MAP FIRST_STATE_MAP_ENTRY(EXPECTING-OPERAND) STATE_MAP_ENTRY(EXPECTING-OPERATOR) STATE_MAP_ENTRY(TERMINATE-PROGRAM) STATE_MAP_ENTRY(FAILED) END_STATE_MAP */ BEGIN_TRANSITION_MAP(CMacroFSM) TRANSITION_FIRST_STATE_ENTRY(EXPECTING-OPERAND) TRANSITION_ENTRY_WITH_ACTION(EXPECTING-OPERAND, operator, EXPECTING-OPERATOR, new CFSMAction(this, (FSMObjectFunction) &CCalculator::stacksignednumber, L"stacksignednumber")) TRANSITION_ENTRY_WITH_ACTION(EXPECTING-OPERAND, operator, EXPECTING-OPERATOR, new CFSMAction(this, (FSMObjectFunction) &CCalculator::stacksignednumber, L"stacksignednumber")) ADD_TRANSITION_CONDITION(this, CCalculator, checkSignedNumber) TRANSITION_ENTRY(EXPECTING-OPERAND, operator, EXPECTING-OPERATOR) ADD_TRANSITION_ACTION(this, CCalculator, failedSignedNumber) ADD_INVERTED_TRANSITION_CONDITION(this, CCalculator, checkSignedNumber) TRANSITION_ENTRY_WITH_ACTION(EXPECTING-OPERAND, unaryoperator, EXPECTING-OPERAND, new CFSMAction(this, (FSMObjectFunction) &CCalculator::stackunaryoperator, L"allowunaryoperator")) TRANSITION_ENTRY_WITH_ACTION(EXPECTING-OPERAND, number, EXPECTING-OPERATOR, new CFSMAction(this, (FSMObjectFunction) &CCalculator::stackNumber, L"stackNumber")) TRANSITION_ENTRY_WITH_ACTION(EXPECTING-OPERAND, left-paren, EXPECTING-OPERAND, new CFSMAction(this, (FSMObjectFunction) &CCalculator::stackLeftParen, L"stackLeftParen")) TRANSITION_ENTRY_WITH_ACTION(EXPECTING-OPERAND, end-mark, TERMINATE-PROGRAM,new CFSMAction(this, (FSMObjectFunction) &CCalculator::terminate, L"terminate")) TRANSITION_ENTRY(EXPECTING-OPERAND, fail, FAILED) ADD_TRANSITION_ACTION(this, CCalculator, fail) // after stacking operand, expect an operator TRANSITION_ENTRY(EXPECTING-OPERATOR, operator, EXPECTING-OPERAND) ADD_TRANSITION_ACTION(this, CCalculator, unstackGEOperator) ADD_TRANSITION_ACTION(this, CCalculator, stackOperator) TRANSITION_ENTRY(EXPECTING-OPERATOR, number, FAILED) ADD_TRANSITION_ACTION(this, CCalculator, noOperation) TRANSITION_ENTRY(EXPECTING-OPERATOR, right-paren, EXPECTING-OPERATOR) ADD_TRANSITION_ACTION(this, CCalculator, unstackAllOperators) ADD_TRANSITION_ACTION(this, CCalculator, unstackIfLeftParen) TRANSITION_ENTRY(EXPECTING-OPERATOR, end-mark, TERMINATE-PROGRAM) ADD_TRANSITION_ACTION(this, CCalculator, unstackAllOperators) TRANSITION_ENTRY(EXPECTING-OPERATOR, fail, FAILED) ADD_TRANSITION_ACTION(this, CCalculator,fail) TRANSITION_ENTRY_WITH_ACTION(TERMINATE-PROGRAM, end-mark, TERMINATE-PROGRAM, new CFSMAction(this, (FSMObjectFunction) &CCalculator::terminate, L"terminate")) TRANSITION_ENTRY_WITH_ACTION(TERMINATE-PROGRAM, fail, FAILED, new CFSMAction(this, (FSMObjectFunction) &CCalculator::fail, L"fail")) END_TRANSITION_MAP }; void macroTest() { /* CMacroFSM macrofsm; CFSMStatePtr * states = macrofsm.getFSMStates(); CLogging::Log.log("MACRO STATES DUMP\n"); for(int i=0; states[i]!=NULL; i++) { CLogging::Log.log("State[%d]=%S\n", i, (BSTR) states[i]->getName()); states[i]=NULL; } */ CMacroFSM calc; calc.IncRef(); calc.debug=9; calc.echo=true; CLogging::Log.log("Calculate (120*4)/(6*5)=%S\n", (BSTR) calc.calculate(L"(120*4)/(6*5)")); calc.releaseFSM(); }