00001 // Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies). 00002 // All rights reserved. 00003 // This component and the accompanying materials are made available 00004 // under the terms of "Eclipse Public License v1.0" 00005 // which accompanies this distribution, and is available 00006 // at the URL "http://www.eclipse.org/legal/epl-v10.html". 00007 // 00008 // Initial Contributors: 00009 // Nokia Corporation - initial contribution. 00010 // 00011 // Contributors: 00012 // 00013 // Description: 00014 // 00015 00016 #include <e32std.h> 00017 #include <e32cons.h> 00018 00019 const TInt KMaxCalcCommandBuffer=80; 00020 00021 //Common literal text 00022 00023 00024 _LIT(KTxtErrInExpress," Error in expression, cannot evaluate. "); 00025 00026 00027 CConsoleBase* console; 00028 00029 00031 // Stack classes 00033 00034 // 00035 // Stack element class - linked list of TReals 00036 // 00037 00038 class CRpnStackElement : public CBase 00039 { 00040 friend class CRpnStack ; 00041 00042 private: 00043 CRpnStackElement* iNext ; 00044 TReal iValue ; 00045 00046 public: 00047 static CRpnStackElement* NewL ( const TReal& aReal, CRpnStackElement* aStackElement) ; 00048 void ConstructL (const TReal& aReal, CRpnStackElement* aStackElement) ; 00049 00050 public: 00051 CRpnStackElement() {} ; 00052 } ; 00053 00054 00055 // 00056 // Stack class - just constructor, destructor, push, pop & empty-test. 00057 // 00058 00059 class CRpnStack : public CBase 00060 { 00061 private: 00062 CRpnStackElement* iTop ; // pointer to top of stack element 00063 00064 public: 00065 static CRpnStack* NewL () ; 00066 void ConstructL () ; 00067 00068 ~CRpnStack() ; 00069 TReal Pop () ; 00070 void Push (TReal aReal) ; 00071 TBool IsEmpty () ; 00072 } ; 00073 00074 00076 // Stack class implementations 00078 00079 // stack element construction (2-part) 00080 CRpnStackElement* CRpnStackElement::NewL(const TReal& aReal, CRpnStackElement* aStackElement) 00081 { 00082 CRpnStackElement* self = new (ELeave) CRpnStackElement ; 00083 CleanupStack::PushL(self); 00084 self->ConstructL(aReal, aStackElement); 00085 CleanupStack::Pop(); 00086 return self; 00087 } 00088 00089 00090 void CRpnStackElement::ConstructL(const TReal& aReal, CRpnStackElement* aStackElement) 00091 { 00092 iValue = aReal; 00093 iNext = aStackElement ; 00094 } 00095 00096 00097 // stack construction 00098 CRpnStack* CRpnStack::NewL() 00099 { 00100 CRpnStack* self = new (ELeave) CRpnStack ; 00101 CleanupStack::PushL(self); 00102 self->ConstructL(); 00103 CleanupStack::Pop(); 00104 return self; 00105 } 00106 00107 00108 void CRpnStack::ConstructL() 00109 { 00110 iTop = 0 ; 00111 } 00112 00113 00114 // stack destructor 00115 CRpnStack::~CRpnStack() 00116 { 00117 while (!IsEmpty()) 00118 Pop() ; 00119 } 00120 00121 00122 // stack pop & delete top element 00123 TReal CRpnStack::Pop () 00124 { 00125 TReal value = iTop->iValue ; // get return value 00126 CRpnStackElement* old = iTop ; // keep old top of stack pointer 00127 iTop = iTop->iNext; // move top of stack pointer to next element 00128 delete old ; // delete old top of stack element 00129 old = 0 ; // don't want old used again 00130 return value ; // return the value 00131 } 00132 00133 00134 // stack push new element 00135 void CRpnStack::Push (TReal aReal) 00136 { 00137 TRAPD(err,iTop = CRpnStackElement::NewL(aReal, iTop)) ; 00138 if(err) 00139 { 00140 _LIT(KFormat2,"Push failed: leave code=%d"); 00141 console->Printf(KFormat2,err); 00142 } 00143 } 00144 00145 00146 // stack empty test 00147 TBool CRpnStack::IsEmpty () 00148 { 00149 return (iTop == 0) ; 00150 } 00151 00152 00154 // RPN calculator engine class 00156 00157 class CRpnCalculator 00158 { 00159 private: 00160 static TReal GetIntegerPart(TLex& aInput) ; 00161 static TReal GetFractionalPart(TLex& aInput) ; 00162 static TInt DealWithNum(CRpnStack* aStack, TLex& aInput) ; 00163 static TInt RPNCalcEngineL(const TDesC& aCommand, TReal& aReturnValue) ; 00164 static TInt doRPNCalcEngine(TLex& aInput,CRpnStack* stack,TReal& aReturnValue); 00165 static void DisplayAnswer(TReal aValue) ; 00166 static TBool TextInput(TDes& aBuf) ; 00167 public: 00168 static void RunRPNCalculatorL() ; 00169 } ; 00170 00171 00173 // RPN calculator engine : numeric routines 00175 00176 TReal CRpnCalculator::GetIntegerPart(TLex& aInput) 00177 // Finds a UInt. Also used before decimal point for RPN TReal processing 00178 { 00179 TReal accumulator = 0 ; 00180 00181 while ((aInput.Peek()).IsDigit()) 00182 { 00183 accumulator = (accumulator * 10) + ( (TReal)aInput.Get() - (TReal)'0' ) ; 00184 } 00185 return accumulator ; 00186 } 00187 00188 00189 TReal CRpnCalculator::GetFractionalPart(TLex& aInput) 00190 // Finds a UInt. Used after decimal point for RPN TReal processing 00191 { 00192 TReal accumulator = 0 ; 00193 TReal multiplier = 0.1 ; 00194 00195 while ((aInput.Peek()).IsDigit()) 00196 { 00197 accumulator += ( (TReal)aInput.Get() - (TReal)'0' ) * multiplier ; 00198 multiplier /= 10 ; 00199 } 00200 return accumulator ; 00201 } 00202 00203 00204 TInt CRpnCalculator::DealWithNum(CRpnStack* aStack, TLex& aInput) 00205 // VERY basic scanning to extract and push a (Uint or real) number. 00206 { 00207 TBool negative = EFalse ; 00208 TReal answer = 0 ; 00209 TLexMark startMark ; 00210 00211 // need something to parse 00212 if (aInput.Eos()) 00213 return KErrNotFound ; 00214 if (!(aInput.Peek().IsDigit() || (aInput.Peek() == '.') ) ) 00215 return KErrNotFound ; 00216 00217 // mark where we are, so can unwind 00218 aInput.Mark(startMark) ; 00219 00220 // deal with sign 00221 if (aInput.Peek() == '+') 00222 aInput.Inc() ; 00223 if (aInput.Peek() == '-') 00224 { 00225 aInput.Inc() ; 00226 negative = ETrue ; 00227 } 00228 00229 // check there's something to parse 00230 if (aInput.Eos()) 00231 return KErrNotFound ; 00232 00233 // get number (may be complete integer or first part of a real) 00234 if ((aInput.Peek()).IsDigit()) 00235 answer = CRpnCalculator::GetIntegerPart(aInput) ; 00236 00237 // negate if necessary 00238 if (negative) 00239 answer *= -1 ; 00240 00241 // look for decimal point - if found, parse real number 00242 if (aInput.Peek() == '.') 00243 { // may be dealing with real number. 00244 aInput.Inc() ; 00245 if (!(aInput.Peek()).IsDigit()) 00246 { // found non-digit after decimal point. Error, so rewind & exit 00247 aInput.UnGetToMark(startMark) ; 00248 return KErrCancel ; 00249 } 00250 // now must parse digit(s) after decimal point 00251 answer += CRpnCalculator::GetFractionalPart(aInput) ; 00252 aStack->Push(answer) ; 00253 return KErrNone ; 00254 } 00255 else 00256 { // dealing with integer 00257 aStack->Push(answer) ; 00258 return KErrNone ; 00259 } 00260 } 00261 00262 00264 // Main body of the RPN calculator engine : calculator 00266 00267 TInt CRpnCalculator::doRPNCalcEngine(TLex& aInput,CRpnStack* stack,TReal& aReturnValue) 00268 { 00269 // extract a number if possible & push 00270 // extract token, perform operation & push result 00271 // if token is '=' or at end of string, pop & print value 00272 TInt Err = KErrNone; 00273 TReal operand1 = 0; 00274 TReal operand2 = 0 ; 00275 TReal memory = 0 ; 00276 00277 do 00278 { 00279 aInput.SkipSpace() ; 00280 00281 if (CRpnCalculator::DealWithNum(stack, aInput)== KErrNone) ; // parse for number 00282 00283 /* above line can be replaced by the following equivalent code: 00284 00285 if (aInput.Val(extractReal) == KErrNone) 00286 stack->Push(extractReal) ; 00287 else if (aInput.Val(extractUint) == KErrNone) 00288 stack->Push(TReal(extractUint)) ; 00289 */ 00290 00291 else switch ( aInput.Get() ) 00292 { 00293 case'+' : 00294 if (!stack->IsEmpty()) operand2 = stack->Pop() ; else Err = KErrGeneral ; 00295 if (!stack->IsEmpty()) operand1 = stack->Pop() ; else Err = KErrGeneral; 00296 if (Err==KErrNone) stack->Push (operand1 + operand2) ; 00297 break ; 00298 00299 case'-' : 00300 if (!stack->IsEmpty()) operand2 = stack->Pop() ; else Err = KErrGeneral ; 00301 if (!stack->IsEmpty()) operand1 = stack->Pop() ; else Err = KErrGeneral; 00302 if (Err==KErrNone) stack->Push (operand1 - operand2) ; 00303 break ; 00304 00305 case '*' : 00306 if (!stack->IsEmpty()) operand2 = stack->Pop() ; else Err = KErrGeneral ; 00307 if (!stack->IsEmpty()) operand1 = stack->Pop() ; else Err = KErrGeneral; 00308 if (Err==KErrNone) stack->Push (operand1 * operand2) ; 00309 break ; 00310 00311 case'/' : 00312 if (!stack->IsEmpty()) operand2 = stack->Pop() ; else Err = KErrGeneral ; 00313 if (!stack->IsEmpty()) operand1 = stack->Pop() ; else Err = KErrGeneral; 00314 if (Err==KErrNone) stack->Push (operand1 / operand2) ; 00315 break ; 00316 00317 case '=' : 00318 if ( !(stack->IsEmpty() ) ) 00319 { aReturnValue = stack->Pop() ; 00320 return KErrNone ; 00321 } 00322 else return KErrArgument ; 00323 00324 // not found a valid one-character symbol, try key words... 00325 default : 00326 if (aInput.Offset() > 0) // if not at start of line 00327 aInput.UnGet() ; // restore 'got' character 00328 00329 aInput.Mark() ; // remember where we are 00330 aInput.SkipCharacters() ; // move to end of character token 00331 00332 00333 if ( aInput.TokenLength() != 0 ) // if valid potential token 00334 { 00335 _LIT(KTxtMEMSET,"MEMSET"); 00336 _LIT(KTxtMEMGET,"MEMGET"); 00337 TPtrC token = aInput.MarkedToken() ; // extract token 00338 if ( token.CompareF(KTxtMEMSET) == 0) 00339 { 00340 if ( !(stack->IsEmpty()) ) // MEMSET - store top stack element 00341 memory = stack->Pop() ; 00342 if ( stack->IsEmpty() ) // valid command, but empty stack will cause error, so 00343 stack->Push(memory) ; 00344 } 00345 else if ( token.CompareF(KTxtMEMGET) == 0) 00346 stack->Push (memory) ; // MEMGET - push memory value 00347 else 00348 return KErrNotSupported ; // unrecognised token 00349 } 00350 else // exit - can't be anything else 00351 { 00352 return KErrGeneral ; 00353 } 00354 } ; // end switch 00355 if (Err == KErrGeneral) 00356 // error in expression (usually as there aren't 2 stack elements for token to operate on) 00357 return KErrArgument ; 00358 00359 } while (!aInput.Eos()) ; 00360 00361 if ( !(stack->IsEmpty() ) ) 00362 { 00363 aReturnValue = stack->Pop() ; 00364 return KErrNone ; 00365 } 00366 else return KErrArgument ; 00367 } 00368 00369 00370 00372 // RPN calculator engine : calculator 00374 00375 TInt CRpnCalculator::RPNCalcEngineL(const TDesC& aCommand, TReal& aReturnValue) 00376 { 00377 TInt ret; 00378 TLex input(aCommand); 00379 00380 CRpnStack* stack = CRpnStack::NewL(); 00381 CleanupStack::PushL(stack); 00382 ret = CRpnCalculator::doRPNCalcEngine(input,stack,aReturnValue); 00383 CleanupStack::PopAndDestroy(); 00384 return ret; 00385 } 00386 00387 00388 00390 // RPN calculator UI : display routines 00392 00393 void CRpnCalculator::DisplayAnswer(TReal aValue) 00394 { 00395 TRealFormat format ; 00396 TBuf<0x100> convertRealToString; 00397 00398 // want a TLex from the value 00399 00400 if (convertRealToString.Num(aValue,format) < KErrNone ) // if -ve, is an error, not a string length 00401 console->Printf(KTxtErrInExpress); 00402 else 00403 { 00404 convertRealToString.ZeroTerminate(); 00405 00406 TLex string(convertRealToString) ; 00407 // got a TLex 00408 00409 TLexMark start ; 00410 string.Mark (start) ; // remember start of string position 00411 00412 // run through string, setting 'end' to last digit found 00413 while (!string.Eos() ) 00414 { 00415 if ( !(string.Get() == '0') ) string.Mark() ; 00416 } 00417 00418 string.UnGetToMark() ; // reset next character pointer to last digit 00419 // if Mark points to decimal point and not at Eos (i.e. a zero follows), include the zero 00420 if ( string.Get() == '.' && !string.Eos() ) 00421 string.Mark() ; 00422 00423 // display spaces after entered line 00424 _LIT(KTxtSpaces," "); 00425 console->Write(KTxtSpaces) ; 00426 // set Mark to start of string and display answer 00427 console->Write( string.MarkedToken(start) ) ; 00428 } 00429 } 00430 00431 00433 // RPN calculator UI : line input routine (adapted from tuiedit) 00435 00436 _LIT(KTxtBackSlashSeven,"\7"); 00437 _LIT(KTxtCursor,"_"); 00438 00439 TBool CRpnCalculator::TextInput(TDes& aBuf) 00440 { 00441 TInt pos; 00442 00443 pos = 0; 00444 aBuf.Zero(); 00445 console->SetPos(0); 00446 console->Write(KTxtCursor) ; // "cursor" 00447 console->SetPos(0); 00448 00449 FOREVER 00450 { 00451 TChar gChar=console->Getch(); 00452 switch (gChar) 00453 { 00454 case EKeyEscape: 00455 return (EFalse); 00456 case EKeyEnter: 00457 return (ETrue); 00458 case EKeyBackspace: 00459 if (pos) 00460 { 00461 pos--; 00462 aBuf.Delete(pos,1); 00463 } 00464 break; 00465 default: 00466 if (!gChar.IsPrint()) 00467 break; 00468 else 00469 if ((aBuf.Length()<KMaxCalcCommandBuffer)&&(pos<KDefaultConsWidth-3)) 00470 { 00471 TBuf<0x02> b; 00472 b.Append(gChar); 00473 aBuf.Insert(pos++,b); 00474 } 00475 else 00476 { 00477 console->Write(KTxtBackSlashSeven); 00478 break; 00479 } 00480 } 00481 console->SetPos(pos) ; 00482 console->ClearToEndOfLine(); 00483 console->SetPos(0); 00484 console->Write(aBuf); 00485 console->Write(KTxtCursor) ; // "cursor" 00486 console->SetPos(pos); 00487 } 00488 } 00489 00490 00492 // finally the RPN calculator's driver code 00494 00495 _LIT(KTxtStartingRPNCalc,"Starting RPN Calculator\n\n"); 00496 _LIT(KTxtNewLine," \n"); 00497 _LIT(KTxtInvite,"Type in a Reverse Polish\nexpression.\nPress ENTER to evaluate it\nPress ESC to end\n"); 00498 00499 00500 00501 void CRpnCalculator::RunRPNCalculatorL() 00502 { 00503 TBuf<KMaxCalcCommandBuffer> command; 00504 00505 console->Printf(KTxtStartingRPNCalc); 00506 console->Printf(KTxtInvite); 00507 00508 while (CRpnCalculator::TextInput(command) ) 00509 { 00510 TReal answer; 00511 00512 if (CRpnCalculator::RPNCalcEngineL(command, answer) == KErrNone ) 00513 CRpnCalculator::DisplayAnswer(answer) ; 00514 else 00515 console->Printf(KTxtErrInExpress) ; 00516 00517 console->Printf(KTxtNewLine) ; 00518 console->Printf(KTxtInvite); 00519 } 00520 } 00521 00522 00524 // This section deals with Symbian platform initialisation and ensuring we have a console active 00526 00527 00528 void SetupConsoleL(); 00529 00530 _LIT(KTxtRPNCalcErr,"RPN Calculator example error"); 00531 00532 GLDEF_C TInt E32Main() // main function called by E32 00533 { 00534 CTrapCleanup* cleanup=CTrapCleanup::New(); // get clean-up stack 00535 TRAPD(error,SetupConsoleL()); // more initialization, then do example 00536 __ASSERT_ALWAYS(!error,User::Panic(KTxtRPNCalcErr,error)); 00537 delete cleanup; // destroy clean-up stack 00538 return 0; // and return 00539 } 00540 00541 00542 void SetupConsoleL() // initialize and call example code under cleanup stack 00543 { 00544 _LIT(KTxtIntro,"eulexrpn - RPN Calculator"); 00545 _LIT(KFormat1,"failed: leave code=%d"); 00546 _LIT(KTxtPressAnyKey,"[Press any key to exit]"); 00547 00548 console=Console::NewL(KTxtIntro,TSize(KConsFullScreen,KConsFullScreen)); 00549 CleanupStack::PushL(console); 00550 TRAPD(error, CRpnCalculator::RunRPNCalculatorL()); // perform example function 00551 if (error) 00552 console->Printf(KFormat1, error); 00553 console->Printf(KTxtPressAnyKey); 00554 console->Getch(); // get and ignore character 00555 CleanupStack::PopAndDestroy(); // close console 00556 } 00557 00558
Copyright ©2010 Nokia Corporation and/or its subsidiary(-ies).
All rights
reserved. Unless otherwise stated, these materials are provided under the terms of the Eclipse Public License
v1.0.