// rpimpl_scan.cpp // Copyright (c) Menno Rubingh 2016. Web: [http://rubinghsoftware.de] // // MR Mar 2016. // // Definition of the function 'scanRPExpression()': // Scan a RP expression string and convert it into a list of OperationXxx instances. // Used internally in the RPInterpreter implementation. // //--- // Build with g++ option "-std=c++0x" (for static_assert). // #include "rpimpl_oper.h" //OperastionXxx classes. #include "rpimpl_oplist.h" //ParsedOperationList. #include "rpimpl_ex.h" //SContext, Ex. #include <stdio.h> //------------------------------------------------------------------------------------- // Scan a single element of a RP expression. // // If unsuccessful // ==> Leave *oNScan and *oOp unchanged, and return false. // If successful // ==> Set *oNScan to the nr of chars scanned, // set *oOp to a ptr to a newly instantuated OperationXxx instance, // and return true. //------------------------------------------------------------------------------------- // Scan the semicolon operator. static bool sscanSemicolon( char const * iStr, SContext const * iCxt, int * oNScan, OperationBase ** oOp ) { assert( *oNScan == 0 ); assert( *oOp == NULL ); if ( *iStr != ';' ) { return false; } *oNScan = 1; *oOp = new OperationSemicolon( iCxt ); return true; } // Scan one of the binary operator characters '+', '-', '*'. '/', '^'. static bool sscanBinOp( char const * iStr, SContext const * iCxt, int * oNScan, OperationBase ** oOp ) { assert( *oNScan == 0 ); assert( *oOp == NULL ); switch ( *iStr ) { case '+': *oNScan = 1; *oOp = new OperationBinAdd( iCxt ); return true; break; case '-': *oNScan = 1; *oOp = new OperationBinSub( iCxt ); return true; break; case '*': *oNScan = 1; *oOp = new OperationBinMul( iCxt ); return true; break; case '/': *oNScan = 1; *oOp = new OperationBinDiv( iCxt ); return true; break; case '^': *oNScan = 1; *oOp = new OperationBinPow( iCxt ); return true; break; default : return false; break; } } // Scan one of the variable letters 'a' ... 'z' NOT preceded by '='. static bool sscanPushVar( char const * iStr, SContext const * iCxt, int * oNScan, OperationBase ** oOp ) { assert( *oNScan == 0 ); assert( *oOp == NULL ); int kIndex; if ( VarTable::nameToIndex_scan( *iStr, &kIndex ) ) { *oNScan = 1; *oOp = new OperationPushVar( iCxt, kIndex ); return true; } else { return false; } } // Scan '=' followed by one of the variable letters 'a' ... 'z'. static bool sscanPopVar( char const * iStr, SContext const * iCxt, int * oNScan, OperationBase ** oOp ) { assert( *oNScan == 0 ); assert( *oOp == NULL ); if ( iStr[0] != '=' ) { return false; } int kIndex; if ( VarTable::nameToIndex_scan( iStr[1], &kIndex ) ) { *oNScan = 2; *oOp = new OperationPopVar( iCxt, kIndex ); return true; } else { return false; } } // Scan a nonnegative decimal floating-point literal constant expresed using the // characters '.' and '0'-'9'. // Algo: // - Stop scanning at the first char unequal to '.' or '0'-'9'. // - Accumulate the value in an UINT (not double). // - Accumulate the divisor in an UINT (not double), by counting the number // of decimals after the '.' (of which there can be at most one). // - At the end, compute value/divisor. typedef unsigned int uint; static bool char_isDec( char iC, uint * oValue ) { if ( BETWEEN_II( '0', iC, '9' ) ) { *oValue = iC - '0'; return true; } return false; } static bool sscanConst( char const * iStr, SContext const * iCxt, int * oNScan, OperationBase ** oOp ) { static int const DIGMAX_TOTAL = 9; //Max nr of digits in total. static int const DIGMAX_AFTERDOT = 9; //Max nr of digits after dot. static_assert( DIGMAX_TOTAL < sizeof(uint) * 2.4, "DIGMAX_TOTAL too large" ); static_assert( DIGMAX_AFTERDOT < sizeof(uint) * 2.4, "DIGMAX_AFTERDOT too large" ); // Note: 2.4 = a value slightly smaller than than 10log(256) = the // number of decimal digits in an UNSIGNED 8-bit number. assert( *oNScan == 0 ); assert( *oOp == NULL ); // Check the first char. char c0 = *iStr; uint dummy; if ( ! ( (c0=='.') || char_isDec(c0,&dummy) ) ) { return false; } // Scan the characters, accumulate total digit string value and divisor. bool seenDot = false; int nDigTotal = 0; int nDigAfterDot = 0; uint accAll = 0; uint accDivisor = 1; int nScan = 0; for(;;) { char c = iStr[nScan]; uint digvalue; if ( char_isDec( c, &digvalue ) ) { nDigTotal++; if ( nDigTotal > DIGMAX_TOTAL ) { throw Ex( iCxt, "constant too large" ); } accAll *= 10; accAll += digvalue; if ( seenDot ) { nDigAfterDot++; if ( nDigAfterDot > DIGMAX_AFTERDOT ) { throw Ex( iCxt, "too many decimals after dot" ); } accDivisor *= 10; } } else if ( c == '.' ) { if ( seenDot ) { throw Ex( iCxt, "more than one dot in constant" ); } seenDot = true; } else { break; //END OF LOOP. } nScan++; }//for // Compute double value. double rv = (double)accAll / (double)accDivisor; // Create output. *oNScan = nScan; *oOp = new OperationPushConst( iCxt, rv ); return true; } //------------------------------------------------------------------------------------- // Scan ANY of the elements of a RP expression. //------------------------------------------------------------------------------------- static bool sscanAny( char const * iStr, SContext const * iCxt, int * oNScan, OperationBase ** oOp ) { if ( sscanSemicolon( iStr, iCxt, oNScan, oOp ) ) { return true; } else if ( sscanPopVar ( iStr, iCxt, oNScan, oOp ) ) { return true; } else if ( sscanPushVar ( iStr, iCxt, oNScan, oOp ) ) { return true; } else if ( sscanBinOp ( iStr, iCxt, oNScan, oOp ) ) { return true; } else if ( sscanConst ( iStr, iCxt, oNScan, oOp ) ) { return true; } else { return false; } } //------------------------------------------------------------------------------------- // Convert string to ParsedOperationList. // // The string as a whole must consist of the RP expression only, with no // other characters (but space characters are allowed between lexical elements). // // Assumes that the ParsedOperationList is empty on entering the function // (an assertion fails if this assumption is violated). //------------------------------------------------------------------------------------- void scanRPExpression( char const * iStr, ParsedOperationList * oOpList ) { assert( oOpList->getN() == 0 ); SContext cxt; //Inits to character position zero. while ( *iStr != '\0' ) { int nscan = 0; OperationBase * pOp = NULL; if ( *iStr == ' ' ) //Space character: ignore. { nscan = 1; } else if ( sscanAny( iStr, &cxt, &nscan, &pOp ) ) { oOpList->appendElem( &cxt, pOp ); } else { throw Ex( &cxt, "illegal character in RP expression" ); } iStr += nscan; cxt.advance( nscan ); } }