From 6e106a2cc52e3857cbd55d67d54b4589f3425c51 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Sat, 20 Oct 2007 20:51:59 +0000 Subject: Made the logger use a uint32_t for it's mask, no more negative confusion. Also brought back the formula class with some cool twists, and added a basic stack class, very cute, fast, and little. --- src/formula.cpp | 4 + src/formula.h | 399 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/logger.cpp | 16 +- src/logger.h | 11 +- src/stack.cpp | 1 + src/stack.h | 71 +++++++++ src/tests/formula.cpp | 11 ++ 7 files changed, 503 insertions(+), 10 deletions(-) create mode 100644 src/formula.cpp create mode 100644 src/formula.h create mode 100644 src/stack.cpp create mode 100644 src/stack.h create mode 100644 src/tests/formula.cpp diff --git a/src/formula.cpp b/src/formula.cpp new file mode 100644 index 0000000..c32386e --- /dev/null +++ b/src/formula.cpp @@ -0,0 +1,4 @@ +#include "formula.h" + +subExceptionDef( ParseException ); + diff --git a/src/formula.h b/src/formula.h new file mode 100644 index 0000000..3809508 --- /dev/null +++ b/src/formula.h @@ -0,0 +1,399 @@ +#ifndef FORMULA_H +#define FORMULA_H + +#include + +#include +//#include "sbuffer.h" + +#include "bu/stack.h" +#include "bu/exceptionbase.h" +#include "bu/hash.h" +#include "bu/fstring.h" + +subExceptionDecl( ParseException ); + +namespace Bu +{ + /** + * Implements a very simple formula parser that allows use of variables and + * custom functions. This is based on a simple calculator-type parser that + * executes as it processes, accounting for operator precedence and + * grouping. + * + * prec = precision, a type to use for all math (except binary ops) + * bin = binary type, a type to hard cast all data to for binary ops + */ + template + class Formula + { + public: + Formula() + { + } + + virtual ~Formula() + { + } + + prec run( const char *sFormula ) + { + for(;;) + { + uint8_t tNum = nextToken( &sFormula ); + if( tNum == symEOS ) + break; + else if( tNum == symSubtract ) + { + tNum = nextToken( &sFormula ); + if( tNum != symNumber ) + throw ParseException( + "Unary minus must be followed by a number, " + "variable, function, or parenthesis."); + sValue.top() = -sValue.top(); + } + else if( tNum == symNot ) + { + tNum = nextToken( &sFormula ); + if( tNum != symNumber ) + throw ParseException( + "Unary, binary not must be followed by a number, " + "variable, function, or parenthesis."); + sValue.top() = static_cast( + ~static_cast(sValue.top()) + ); + } + else if( tNum == symOpenParen ) + { + sOper.push( tNum ); + continue; + } + + oppart: uint8_t tOpr = nextToken( &sFormula ); + if( tOpr == symEOS ) + { + //printf("EOS "); + reduce(); + return sValue.top(); + break; + } + if( !sOper.isEmpty() && getPrec( sOper.top() ) > getPrec( tOpr ) ) + { + reduce(); + } + if( tOpr != symCloseParen ) + { + sOper.push( tOpr ); + } + else + { + reduce( true ); + goto oppart; + } + } + return sValue.top(); + } + + + typedef Hash varHash; + varHash hVars; + + typedef struct Func + { + prec operator()( prec x ) + { + return 0; + } + } Func; + + typedef Hash funcHash; + funcHash hFunc; +/* + typedef struct FuncSin : Func + { + prec operator()( prec x ) + { + return sin( x ); + } + } FuncSin; + */ + + private: + enum + { + symEOS, + symAdd, + symSubtract, + symMultiply, + symDivide, + symOpenParen, + symCloseParen, + symNumber, + symVariable, + symExponent, + symModulus, + + symAnd, + symOr, + symXor, + symNot + }; + + typedef uint8_t symType; + + Bu::Stack sOper; + Bu::Stack sValue; + + private: + symType getPrec( symType nOper ) + { + switch( nOper ) + { + case symNumber: + case symVariable: + case symOpenParen: + case symCloseParen: + return 0; + + case symAdd: + case symSubtract: + return 1; + + case symMultiply: + case symDivide: + case symModulus: + return 2; + + case symAnd: + case symOr: + case symXor: + return 2; + + case symExponent: + case symNot: + return 3; + + default: + return 0; + } + } + + symType nextToken( const char **sBuf ) + { + for(;;) + { + char cbuf = **sBuf; + ++(*sBuf); + switch( cbuf ) + { + case '+': + return symAdd; + + case '-': + return symSubtract; + + case '*': + return symMultiply; + + case '/': + return symDivide; + + case '^': + return symExponent; + + case '%': + return symModulus; + + case '(': + return symOpenParen; + + case ')': + return symCloseParen; + + case '|': + return symOr; + + case '&': + return symAnd; + + case '#': + return symXor; + + case '~': + return symNot; + + case ' ': + case '\t': + case '\n': + case '\r': + break; + + case '\0': + return symEOS; + + default: + if( cbuf == '.' || (cbuf >= '0' && cbuf <= '9') ) + { + char num[50]={cbuf}; + int nPos = 1; + bool bDot = false; + + for(;;) + { + cbuf = **sBuf; + if( cbuf == '.' ) + { + if( bDot == false ) + bDot = true; + else + throw ParseException( + "Numbers cannot have more than one " + ". in them." + ); + } + if( cbuf == '.' || (cbuf >= '0' && cbuf <= '9') ) + { + num[nPos++] = cbuf; + } + else + { + num[nPos] = '\0'; + sValue.push( + static_cast( + strtod( num, NULL ) + ) + ); + return symNumber; + } + ++(*sBuf); + } + } + else if( (cbuf >= 'a' && cbuf <= 'z') || + (cbuf >= 'A' && cbuf <= 'Z') || + (cbuf == '_') ) + { + char tok[50]={cbuf}; + int nPos = 1; + + for(;;) + { + cbuf = **sBuf; + if( (cbuf >= 'a' && cbuf <= 'z') || + (cbuf >= 'A' && cbuf <= 'Z') || + (cbuf >= '0' && cbuf <= '9') || + cbuf == '_' || cbuf == '.' || cbuf == ':' ) + { + tok[nPos++] = cbuf; + } + else + { + tok[nPos] = '\0'; + //printf("Checking variable \"%s\"\n", tok ); + try + { + sValue.push( hVars[tok] ); + return symNumber; + } + catch( HashException &e ) + { + throw ParseException( + "No variable named \"%s\" exists.", + tok + ); + } + } + ++(*sBuf); + } + } + break; + } + } + } + + void reduce( bool bCloseParen = false ) + { + while( !sOper.isEmpty() ) + { + uint8_t nOpr = sOper.top(); + if( nOpr == symOpenParen ) + { + //printf("Found ( stopping reduction.\n"); + if( bCloseParen == true ) + sOper.pop(); + return; + } + sOper.pop(); + + prec dTop = sValue.top(); + sValue.pop(); + + switch( nOpr ) + { + case symAdd: + //printf("%f + %f = %f\n", sValue.top(), dTop, sValue.top()+dTop ); + sValue.top() += dTop; + break; + + case symSubtract: + //printf("%f - %f = %f\n", sValue.top(), dTop, sValue.top()-dTop ); + sValue.top() -= dTop; + break; + + case symMultiply: + //printf("%f * %f = %f\n", sValue.top(), dTop, sValue.top()*dTop ); + sValue.top() *= dTop; + break; + + case symDivide: + //printf("%f / %f = %f\n", sValue.top(), dTop, sValue.top()/dTop ); + sValue.top() /= dTop; + break; + + case symExponent: + //printf("%f ^ %f = %f\n", sValue.top(), dTop, pow(sValue.top(),dTop) ); + sValue.top() = static_cast( + pow( sValue.top(), dTop ) + ); + break; + + case symModulus: + //printf("%f %% %f = %f\n", sValue.top(), dTop, fmod(sValue.top(),dTop) ); + sValue.top() = static_cast( + fmod( sValue.top(), dTop ) + ); + break; + + case symOr: + sValue.top() = static_cast( + static_cast(sValue.top()) | + static_cast(dTop) + ); + break; + + case symAnd: + sValue.top() = static_cast( + static_cast(sValue.top()) & + static_cast(dTop) + ); + break; + + case symXor: + sValue.top() = static_cast( + static_cast(sValue.top()) ^ + static_cast(dTop) + ); + break; + } + } + + if( bCloseParen == true ) + { + throw ParseException( + "Close-paren found without matching open-paren." + ); + } + } + }; +} + +#endif diff --git a/src/logger.cpp b/src/logger.cpp index 8c058d1..fe6a916 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -12,7 +12,7 @@ Bu::Logger::~Logger() { } -void Bu::Logger::log( int nLevel, const char *sFile, const char *sFunction, int nLine, const char *sFormat, ...) +void Bu::Logger::log( uint32_t nLevel, const char *sFile, const char *sFunction, int nLine, const char *sFormat, ...) { if( (nLevel&nLevelMask) == 0 ) return; @@ -112,12 +112,17 @@ void Bu::Logger::setFormat( const Bu::FString &str ) //write( fileno(stdout), sLogFormat.getStr(), sLogFormat.getSize() ); } -void Bu::Logger::setMask( int n ) +void Bu::Logger::setMask( uint32_t n ) { nLevelMask = n; } -void Bu::Logger::setLevel( int n ) +uint32_t Bu::Logger::getMask() +{ + return nLevelMask; +} + +void Bu::Logger::setLevel( uint32_t n ) { int j; for( j = 31; j > 0; j-- ) @@ -134,8 +139,8 @@ void Bu::Logger::setLevel( int n ) } } -void Bu::Logger::hexDump( int nLevel, const char *sFile, const char *sFunction, - int nLine, const void *pDataV, long nDataLen, +void Bu::Logger::hexDump( uint32_t nLevel, const char *sFile, + const char *sFunction, int nLine, const void *pDataV, long nDataLen, const char *lpName ) { log( nLevel, sFile, sFunction, nLine, "Displaying %ld bytes of %s.", nDataLen, lpName ); @@ -176,3 +181,4 @@ void Bu::Logger::hexDump( int nLevel, const char *sFile, const char *sFunction, } log( nLevel, sFile, sFunction, nLine, sBorder.getStr() ); } + diff --git a/src/logger.h b/src/logger.h index f561e88..0e7d812 100644 --- a/src/logger.h +++ b/src/logger.h @@ -69,17 +69,18 @@ namespace Bu virtual ~Logger(); public: - void log( int nLevel, const char *sFile, const char *sFunction, int nLine, const char *sFormat, ...); + void log( uint32_t nLevel, const char *sFile, const char *sFunction, int nLine, const char *sFormat, ...); void setFormat( const Bu::FString &str ); - void setMask( int n ); - void setLevel( int n ); + void setMask( uint32_t n ); + void setLevel( uint32_t n ); + uint32_t getMask(); - void hexDump( int nLevel, const char *sFile, const char *sFunction, int nLine, const void *pData, long nDataLen, const char *lpName ); + void hexDump( uint32_t nLevel, const char *sFile, const char *sFunction, int nLine, const void *pData, long nDataLen, const char *lpName ); private: Bu::FString sLogFormat; - int nLevelMask; + uint32_t nLevelMask; }; } diff --git a/src/stack.cpp b/src/stack.cpp new file mode 100644 index 0000000..7380615 --- /dev/null +++ b/src/stack.cpp @@ -0,0 +1 @@ +#include "stack.h" diff --git a/src/stack.h b/src/stack.h new file mode 100644 index 0000000..955332d --- /dev/null +++ b/src/stack.h @@ -0,0 +1,71 @@ +#ifndef BU_STACK_H +#define BU_STACK_H + +#include + +namespace Bu +{ + template > + class Stack + { + private: + typedef struct Chunk + { + value *pValue; + Chunk *pPrev; + } Chunk; + public: + Stack() : + pTop( NULL ) + { + } + + virtual ~Stack() + { + } + + void push( const value &v ) + { + Chunk *pChnk = new Chunk; + pChnk->pValue = va.allocate( 1 ); + va.construct( pChnk->pValue, v ); + pChnk->pPrev = pTop; + pTop = pChnk; + } + + value &peek() + { + return *pTop->pValue; + } + + value &top() + { + return *pTop->pValue; + } + + value pop() + { + value ret( *pTop->pValue ); + + Chunk *pChnk = pTop; + pTop = pTop->pPrev; + + va.destroy( pChnk->pValue ); + va.deallocate( pChnk->pValue, 1 ); + delete pChnk; + + return ret; + } + + bool isEmpty() + { + return pTop == NULL; + } + + private: + Chunk *pTop; + valuealloc va; + }; +} + +#endif diff --git a/src/tests/formula.cpp b/src/tests/formula.cpp new file mode 100644 index 0000000..5237b87 --- /dev/null +++ b/src/tests/formula.cpp @@ -0,0 +1,11 @@ +#include "bu/formula.h" + +int main( int argc, char *argv[] ) +{ + Bu::Formula uForm; + Bu::Formula dForm; + + printf("u: %s = %u\n", argv[1], uForm.run( argv[1] ) ); + printf("d: %s = %f\n", argv[1], dForm.run( argv[1] ) ); +} + -- cgit v1.2.3