examples/Base/BufsAndStrings/Lexer/Lexer.cpp

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         

Generated by  doxygen 1.6.2