examples/Base/BufsAndStrings/Lexer/Lexer.cpp

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

Generated by  doxygen 1.6.2