├── .gitignore ├── Makefile ├── Makefile.Darwin ├── Makefile.Linux ├── MiniParser.cpp ├── MiniParser.hpp ├── README └── example.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *.o 4 | *.a 5 | Makefile.depend 6 | example 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SOURCES = $(CXX_OBJECTS:%.o=%.cpp) $(C_OBJECTS:%.o=%.c) \ 2 | $(MAIN_OBJECT:%.o=%.cpp) 3 | LIB_OBJECTS = $(CXX_OBJECTS) $(C_OBJECTS) 4 | CXX_OBJECTS = MiniParser.o 5 | MAIN_OBJECT = example.o 6 | 7 | ARCH = $(shell uname) 8 | 9 | TARGET = example 10 | LIBTARGET = libMiniParser.a 11 | 12 | COMPILER = g++ 13 | #COMPILER = gfilt 14 | WARNINGFLAGS = -Wall -Wshadow -Woverloaded-virtual -Wno-sign-compare 15 | # WARNINGFLAGS += -Winit-self 16 | 17 | # Does this help? -falign-loops=16 18 | COMPILERFLAGS = $(WARNINGFLAGS) 19 | 20 | COMPILERFLAGS += -g 21 | #COMPILERFLAGS += -O3 22 | #COMPILERFLAGS += -DNDEBUG 23 | 24 | #COMPILERFLAGS += -fno-inline 25 | #COMPILERFLAGS += -finline-functions 26 | COMPILERFLAGS += -fkeep-inline-functions 27 | #COMPILERFLAGS += -Winline 28 | 29 | AR = /usr/bin/ar 30 | ARFLAGS = -rus 31 | 32 | MAKEDEPEND = g++ -MM 33 | DEPENDFILE = Makefile.depend 34 | 35 | INCLUDEFLAGS = 36 | LINKTIMEFLAGS = 37 | include Makefile.$(ARCH) 38 | 39 | # clear out implicit rules 40 | .SUFFIXES: 41 | 42 | all: $(LIBTARGET) $(TARGET) 43 | 44 | $(TARGET): $(MAIN_OBJECT) $(LIBTARGET) 45 | $(COMPILER) -o $@ \ 46 | $(MAIN_OBJECT) \ 47 | $(LIBTARGET) \ 48 | $(FRAMEWORKS) \ 49 | $(INCLUDEFLAGS) $(LINKTIMEFLAGS) 50 | # $(INCLUDEFLAGS) $(LINKTIMEFLAGS) $(LIBTARGET) $(LINKTIMEFLAGS) \ 51 | 52 | 53 | #$(TARGET): $(LIB_OBJECTS) $(MAIN_OBJECT) 54 | # $(COMPILER) -o $@ \ 55 | # $(LIB_OBJECTS) $(MAIN_OBJECT) \ 56 | # $(INCLUDEFLAGS) $(LINKTIMEFLAGS) \ 57 | # $(FRAMEWORKS) 58 | 59 | library: $(LIBTARGET) 60 | 61 | relink: cleantarget all 62 | 63 | 64 | $(LIBTARGET): $(LIB_OBJECTS) 65 | $(AR) $(ARFLAGS) $@ $(LIB_OBJECTS) 66 | 67 | %.o: %.cpp 68 | $(COMPILER) $(COMPILERFLAGS) $(INCLUDEFLAGS) -c $< 69 | 70 | %.o: %.c 71 | $(COMPILER) $(COMPILERFLAGS) $(INCLUDEFLAGS) -c $< 72 | 73 | .PHONY : clean tidy 74 | clean: tidy 75 | rm -f $(TARGET) 76 | rm -f $(LIBTARGET) 77 | rm -f $(DEPENDFILE) 78 | touch $(DEPENDFILE) 79 | 80 | tidy: 81 | rm -f $(LIB_OBJECTS) $(MAIN_OBJECT) 82 | 83 | cleantarget: 84 | rm -f $(TARGET) 85 | 86 | depend: 87 | $(MAKEDEPEND) $(INCLUDEFLAGS) $(SOURCES) > $(DEPENDFILE) 88 | 89 | include $(DEPENDFILE) 90 | 91 | $(DEPENDFILE): 92 | touch $(DEPENDFILE) 93 | -------------------------------------------------------------------------------- /Makefile.Darwin: -------------------------------------------------------------------------------- 1 | INCLUDEFLAGS += 2 | LINKTIMEFLAGS += 3 | FRAMEWORKS = 4 | -------------------------------------------------------------------------------- /Makefile.Linux: -------------------------------------------------------------------------------- 1 | INCLUDEFLAGS += 2 | LINKTIMEFLAGS += 3 | FRAMEWORKS = 4 | -------------------------------------------------------------------------------- /MiniParser.cpp: -------------------------------------------------------------------------------- 1 | #include "MiniParser.hpp" 2 | 3 | /* 4 | Version History: 5 | - 2006-04-08 6 | initial release 7 | - 2006-12-09: 8 | added math functions (sin, cos, log, ln, exp, sqrt) and constants (PI) 9 | improved error handling (removed an infinite loop) 10 | - 2007-04-23: 11 | made the grammar left associative to match natural arithmetic and C 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace 21 | { 22 | typedef MiniParserNode Node ; 23 | 24 | // This is it! 25 | Node* Parse( const std::string& str ) ; 26 | } 27 | 28 | class MiniParserNode 29 | { 30 | public: 31 | typedef MiniParser::IDMap IDMap ; 32 | typedef MiniParser::real real ; 33 | 34 | static MiniParserNode* FromString( const std::string& str ) 35 | { 36 | return Parse( str ) ; 37 | } 38 | 39 | virtual ~MiniParserNode() {} 40 | 41 | virtual real eval( const IDMap& ids ) const = 0 ; 42 | }; 43 | 44 | namespace 45 | { 46 | typedef MiniParserNode::real real ; 47 | 48 | class NodeConstantValue : public Node 49 | { 50 | public: 51 | NodeConstantValue( real val ) : mVal( val ) {} 52 | 53 | real eval( const IDMap& ids ) const 54 | { 55 | return mVal ; 56 | } 57 | 58 | private: 59 | real mVal ; 60 | }; 61 | 62 | class NodeVariableValue : public Node 63 | { 64 | public: 65 | NodeVariableValue( const std::string& var ) : mVar( var ) {} 66 | 67 | real eval( const IDMap& ids ) const 68 | { 69 | IDMap::const_iterator var = ids.find( mVar ) ; 70 | if( ids.end() == var ) 71 | { 72 | std::cerr << "unknown variable: " << mVar << std::endl ; 73 | return 0. ; 74 | } 75 | return var->second ; 76 | } 77 | 78 | private: 79 | const std::string mVar ; 80 | }; 81 | 82 | class NodePlus : public Node 83 | { 84 | public: 85 | NodePlus( Node* lhs, Node* rhs ) : mLHS( lhs ), mRHS( rhs ) { assert( lhs && rhs ) ; } 86 | ~NodePlus() { delete mLHS ; delete mRHS ; } 87 | 88 | real eval( const IDMap& ids ) const 89 | { 90 | return mLHS->eval( ids ) + mRHS->eval( ids ) ; 91 | } 92 | 93 | private: 94 | Node* mLHS ; 95 | Node* mRHS ; 96 | }; 97 | class NodeMinus : public Node 98 | { 99 | public: 100 | NodeMinus( Node* lhs, Node* rhs ) : mLHS( lhs ), mRHS( rhs ) { assert( lhs && rhs ) ; } 101 | ~NodeMinus() { delete mLHS ; delete mRHS ; } 102 | 103 | real eval( const IDMap& ids ) const 104 | { 105 | return mLHS->eval( ids ) - mRHS->eval( ids ) ; 106 | } 107 | 108 | private: 109 | Node* mLHS ; 110 | Node* mRHS ; 111 | }; 112 | class NodeTimes : public Node 113 | { 114 | public: 115 | NodeTimes( Node* lhs, Node* rhs ) : mLHS( lhs ), mRHS( rhs ) { assert( lhs && rhs ) ; } 116 | ~NodeTimes() { delete mLHS ; delete mRHS ; } 117 | 118 | real eval( const IDMap& ids ) const 119 | { 120 | return mLHS->eval( ids ) * mRHS->eval( ids ) ; 121 | } 122 | 123 | private: 124 | Node* mLHS ; 125 | Node* mRHS ; 126 | }; 127 | class NodeDividedBy : public Node 128 | { 129 | public: 130 | NodeDividedBy( Node* lhs, Node* rhs ) : mLHS( lhs ), mRHS( rhs ) { assert( lhs && rhs ) ; } 131 | ~NodeDividedBy() { delete mLHS ; delete mRHS ; } 132 | 133 | real eval( const IDMap& ids ) const 134 | { 135 | return mLHS->eval( ids ) / mRHS->eval( ids ) ; 136 | } 137 | 138 | private: 139 | Node* mLHS ; 140 | Node* mRHS ; 141 | }; 142 | class NodeModulus : public Node 143 | { 144 | public: 145 | NodeModulus( Node* lhs, Node* rhs ) : mLHS( lhs ), mRHS( rhs ) { assert( lhs && rhs ) ; } 146 | ~NodeModulus() { delete mLHS ; delete mRHS ; } 147 | 148 | real eval( const IDMap& ids ) const 149 | { 150 | return fmod( mLHS->eval( ids ), mRHS->eval( ids ) ) ; 151 | } 152 | 153 | private: 154 | Node* mLHS ; 155 | Node* mRHS ; 156 | }; 157 | class NodePow : public Node 158 | { 159 | public: 160 | NodePow( Node* lhs, Node* rhs ) : mLHS( lhs ), mRHS( rhs ) { assert( lhs && rhs ) ; } 161 | ~NodePow() { delete mLHS ; delete mRHS ; } 162 | 163 | real eval( const IDMap& ids ) const 164 | { 165 | return pow( mLHS->eval( ids ), mRHS->eval( ids ) ) ; 166 | } 167 | 168 | private: 169 | Node* mLHS ; 170 | Node* mRHS ; 171 | }; 172 | class NodeNegate : public Node 173 | { 174 | public: 175 | NodeNegate( Node* node ) : mNode( node ) { assert( node ) ; } 176 | ~NodeNegate() { delete mNode ; } 177 | 178 | real eval( const IDMap& ids ) const 179 | { 180 | return -mNode->eval( ids ) ; 181 | } 182 | 183 | private: 184 | Node* mNode ; 185 | }; 186 | class NodeSine : public Node 187 | { 188 | public: 189 | NodeSine( Node* node ) : mNode( node ) { assert( node ) ; } 190 | ~NodeSine() { delete mNode ; } 191 | 192 | real eval( const IDMap& ids ) const 193 | { 194 | return sin( mNode->eval( ids ) ) ; 195 | } 196 | 197 | private: 198 | Node* mNode ; 199 | }; 200 | class NodeCosine : public Node 201 | { 202 | public: 203 | NodeCosine( Node* node ) : mNode( node ) { assert( node ) ; } 204 | ~NodeCosine() { delete mNode ; } 205 | 206 | real eval( const IDMap& ids ) const 207 | { 208 | return cos( mNode->eval( ids ) ) ; 209 | } 210 | 211 | private: 212 | Node* mNode ; 213 | }; 214 | class NodeLog : public Node 215 | { 216 | public: 217 | NodeLog( Node* node ) : mNode( node ) { assert( node ) ; } 218 | ~NodeLog() { delete mNode ; } 219 | 220 | real eval( const IDMap& ids ) const 221 | { 222 | return log10( mNode->eval( ids ) ) ; 223 | } 224 | 225 | private: 226 | Node* mNode ; 227 | }; 228 | class NodeLN : public Node 229 | { 230 | public: 231 | NodeLN( Node* node ) : mNode( node ) { assert( node ) ; } 232 | ~NodeLN() { delete mNode ; } 233 | 234 | real eval( const IDMap& ids ) const 235 | { 236 | return log( mNode->eval( ids ) ) ; 237 | } 238 | 239 | private: 240 | Node* mNode ; 241 | }; 242 | class NodeExp : public Node 243 | { 244 | public: 245 | NodeExp( Node* node ) : mNode( node ) { assert( node ) ; } 246 | ~NodeExp() { delete mNode ; } 247 | 248 | real eval( const IDMap& ids ) const 249 | { 250 | return exp( mNode->eval( ids ) ) ; 251 | } 252 | 253 | private: 254 | Node* mNode ; 255 | }; 256 | class NodeSqrt : public Node 257 | { 258 | public: 259 | NodeSqrt( Node* node ) : mNode( node ) { assert( node ) ; } 260 | ~NodeSqrt() { delete mNode ; } 261 | 262 | real eval( const IDMap& ids ) const 263 | { 264 | return sqrt( mNode->eval( ids ) ) ; 265 | } 266 | 267 | private: 268 | Node* mNode ; 269 | }; 270 | 271 | class MiniTokenizer 272 | { 273 | public: 274 | MiniTokenizer( std::istream& in ) : mIn( in ) { mIn >> std::ws ; advance() ; } 275 | 276 | std::string eat() { std::string result = mPeek ; advance() ; return result ; } 277 | const std::string& peek() { return mPeek ; } 278 | bool eof() { return mIn.eof() ; } 279 | 280 | private: 281 | void advance() 282 | { 283 | if( eof() ) 284 | mPeek = "" ; 285 | else 286 | mIn >> mPeek >> std::ws ; 287 | } 288 | 289 | std::istream& mIn ; 290 | std::string mPeek ; 291 | }; 292 | 293 | void find_replace( std::string& in_this_string, const std::string& find, const std::string& replace ) 294 | { 295 | std::string::size_type pos = 0 ; 296 | while( std::string::npos != (pos = in_this_string.find( find, pos )) ) 297 | { 298 | in_this_string.replace( pos, find.length(), replace ) ; 299 | pos += replace.length() ; 300 | } 301 | } 302 | 303 | Node* ParseValueNode( MiniTokenizer& mt ) ; 304 | 305 | Node* Parse( const std::string& str ) 306 | { 307 | std::string input = str ; 308 | // Our simple tokenizer needs whitespace between all tokens. 309 | find_replace( input, "(", " ( " ) ; 310 | find_replace( input, ")", " ) " ) ; 311 | find_replace( input, "+", " + " ) ; 312 | find_replace( input, "-", " - " ) ; 313 | find_replace( input, "*", " * " ) ; 314 | find_replace( input, "/", " / " ) ; 315 | find_replace( input, "%", " % " ) ; 316 | find_replace( input, "^", " ^ " ) ; 317 | // The exponentiation operator "**" gets turned into "* *" by the above 318 | // find/replaces. Turn it back into "**". 319 | find_replace( input, "* *", "**" ) ; 320 | 321 | /* 322 | // Operator precedence can be implemented with some parentheses. 323 | // See: http://en.wikipedia.org/wiki/Operator-precedence_parser#Alternatives_to_Dijkstra.27s_Algorithm 324 | // First in precedence is exponentiation. 325 | find_replace( input, " ^ ", " ) ^ ( " ); 326 | find_replace( input, " ** ", " ) ** ( " ); 327 | // Then multiplication and division and modulus. 328 | find_replace( input, " * ", " ) ) ) * ( ( ( " ); 329 | find_replace( input, " / ", " ) ) ) / ( ( ( " ); 330 | find_replace( input, " % ", " ) ) ) % ( ( ( " ); 331 | // Finally addition and subtraction. 332 | find_replace( input, " + ", " ) ) ) ) + ( ( ( ( " ); 333 | // NOTE: Whoa, subtraction is complicated by its dual-use as unary minus, which has 334 | // precedence after exponentiation but before multiplication/division/modulus. 335 | // find_replace( input, " - ", " ) ) ) ) - ( ( ( ( " ); 336 | { 337 | std::string& in_this_string = input; 338 | const std::string find( " - " ); 339 | const std::string replace1( " ) ) ) ) - ( ( ( ( " ); 340 | const std::string replace2( " 0 ) ) - ( ( " ); 341 | // const std::string replace2( " - " ); 342 | 343 | std::string::size_type pos = 0 ; 344 | while( std::string::npos != (pos = in_this_string.find( find, pos )) ) 345 | { 346 | bool preceded_by_open_paren = true; 347 | for( int i = pos-1; i >= 0; --i ) 348 | { 349 | if( in_this_string[i] != ' ' && in_this_string[i] != '\r' && in_this_string[i] != '\n' && in_this_string[i] != '\t' && in_this_string[i] != '(' ) 350 | { 351 | preceded_by_open_paren = false; 352 | break; 353 | } 354 | } 355 | 356 | if( preceded_by_open_paren ) 357 | { 358 | in_this_string.replace( pos, find.length(), replace2 ) ; 359 | pos += replace2.length() ; 360 | } 361 | else 362 | { 363 | in_this_string.replace( pos, find.length(), replace1 ) ; 364 | pos += replace1.length() ; 365 | } 366 | } 367 | } 368 | // We need to parse an expression surrounded by four parentheses. 369 | std::istringstream istr( "( ( ( ( ( " + input + " ) ) ) ) )" ) ; 370 | 371 | std::cout << istr.str() << std::endl; 372 | */ 373 | 374 | std::istringstream istr( "( " + input + " )" ) ; 375 | MiniTokenizer tt( istr ) ; 376 | return ParseValueNode( tt ) ; 377 | } 378 | 379 | Node* ParseExpressionNode( MiniTokenizer& mt ) ; 380 | Node* ParseValueNode( MiniTokenizer& mt ) 381 | { 382 | std::string t = mt.eat() ; 383 | if( t.size() == 0 ) 384 | { 385 | std::cerr << "expression cut short" << std::endl ; 386 | return new NodeConstantValue( 0. ) ; 387 | } 388 | 389 | assert( t.size() > 0 ) ; 390 | if( t == "(" ) 391 | { 392 | Node* result = ParseExpressionNode( mt ) ; 393 | std::string rp = mt.eat() ; 394 | if( ")" != rp ) 395 | { 396 | std::cerr << "expression error: expected \")\"" << std::endl ; 397 | delete result ; 398 | return new NodeConstantValue( 0. ) ; 399 | } 400 | return result ; 401 | } 402 | else if( t == "-" ) 403 | { 404 | return new NodeNegate( ParseValueNode( mt ) ) ; 405 | } 406 | else if( t[0] == '.' || (t[0] >= '0' && t[0] <= '9') ) 407 | { 408 | std::istringstream ttof( t ) ; 409 | real val ; 410 | ttof >> val ; 411 | if( !ttof ) 412 | { 413 | std::cerr << "expecting a real number but received: " << t << std::endl ; 414 | return new NodeConstantValue( 0. ) ; 415 | } 416 | else 417 | { 418 | return new NodeConstantValue( val ) ; 419 | } 420 | } 421 | else if( t == "sin" ) 422 | { 423 | if( mt.peek() != "(" ) 424 | { 425 | std::cerr << "built-in function " << t << " must be followed by parentheses." << std::endl ; 426 | return new NodePlus( new NodeConstantValue( 0. ), ParseExpressionNode( mt ) ) ; 427 | } 428 | return new NodeSine( ParseValueNode( mt ) ) ; 429 | } 430 | else if( t == "cos" ) 431 | { 432 | if( mt.peek() != "(" ) 433 | { 434 | std::cerr << "built-in function " << t << " must be followed by parentheses." << std::endl ; 435 | return new NodePlus( new NodeConstantValue( 0. ), ParseExpressionNode( mt ) ) ; 436 | } 437 | return new NodeCosine( ParseValueNode( mt ) ) ; 438 | } 439 | else if( t == "log" ) 440 | { 441 | if( mt.peek() != "(" ) 442 | { 443 | std::cerr << "built-in function " << t << " must be followed by parentheses." << std::endl ; 444 | return new NodePlus( new NodeConstantValue( 0. ), ParseExpressionNode( mt ) ) ; 445 | } 446 | return new NodeLog( ParseValueNode( mt ) ) ; 447 | } 448 | else if( t == "ln" ) 449 | { 450 | if( mt.peek() != "(" ) 451 | { 452 | std::cerr << "built-in function " << t << " must be followed by parentheses." << std::endl ; 453 | return new NodePlus( new NodeConstantValue( 0. ), ParseExpressionNode( mt ) ) ; 454 | } 455 | return new NodeLN( ParseValueNode( mt ) ) ; 456 | } 457 | else if( t == "exp" ) 458 | { 459 | if( mt.peek() != "(" ) 460 | { 461 | std::cerr << "built-in function " << t << " must be followed by parentheses." << std::endl ; 462 | return new NodePlus( new NodeConstantValue( 0. ), ParseExpressionNode( mt ) ) ; 463 | } 464 | return new NodeExp( ParseValueNode( mt ) ) ; 465 | } 466 | else if( t == "sqrt" ) 467 | { 468 | if( mt.peek() != "(" ) 469 | { 470 | std::cerr << "built-in function " << t << " must be followed by parentheses." << std::endl ; 471 | return new NodePlus( new NodeConstantValue( 0. ), ParseExpressionNode( mt ) ) ; 472 | } 473 | return new NodeSqrt( ParseValueNode( mt ) ) ; 474 | } 475 | else if( t == "PI" ) 476 | { 477 | return new NodeConstantValue( M_PI ) ; 478 | } 479 | else 480 | { 481 | return new NodeVariableValue( t ) ; 482 | } 483 | } 484 | 485 | Node* MakeOperationNode( const std::string& op, Node* lhs, Node* rhs ) 486 | { 487 | if( op == "+" ) 488 | { 489 | return new NodePlus( lhs, rhs ) ; 490 | } 491 | else if( op == "-" ) 492 | { 493 | return new NodeMinus( lhs, rhs ) ; 494 | } 495 | else if( op == "*" ) 496 | { 497 | return new NodeTimes( lhs, rhs ) ; 498 | } 499 | else if( op == "/" ) 500 | { 501 | return new NodeDividedBy( lhs, rhs ) ; 502 | } 503 | else if( op == "%" ) 504 | { 505 | return new NodeModulus( lhs, rhs ) ; 506 | } 507 | else if( op == "**" ) 508 | { 509 | return new NodePow( lhs, rhs ) ; 510 | } 511 | else if( op == "^" ) 512 | { 513 | return new NodePow( lhs, rhs ) ; 514 | } 515 | // error: unknown 516 | else 517 | { 518 | std::cerr << "unknown operation: " << op << std::endl ; 519 | delete lhs ; 520 | delete rhs ; 521 | return new NodeConstantValue( 0. ) ; 522 | } 523 | } 524 | 525 | Node* ParseExpressionNode_RightAssociative( MiniTokenizer& mt ) 526 | { 527 | Node* lhs = ParseValueNode( mt ) ; 528 | // short circuit if we're a single value 529 | if( mt.peek() == ")" ) return lhs ; 530 | 531 | std::string op = mt.eat() ; 532 | // error: prematurely expression end 533 | if( op.size() == 0 ) 534 | { 535 | std::cerr << "expression cut short" << std::endl ; 536 | delete lhs ; 537 | return new NodeConstantValue( 0. ) ; 538 | } 539 | 540 | Node* rhs = ParseExpressionNode( mt ) ; 541 | 542 | return MakeOperationNode( op, lhs, rhs ); 543 | } 544 | Node* ParseExpressionNode_LeftAssociative( MiniTokenizer& mt ) 545 | { 546 | Node* lhs = ParseValueNode( mt ) ; 547 | 548 | // short circuit when we reach the right parenthesis 549 | while( mt.peek() != ")" ) 550 | { 551 | std::string op = mt.eat() ; 552 | // error: prematurely expression end 553 | if( op.size() == 0 ) 554 | { 555 | std::cerr << "expression cut short" << std::endl ; 556 | delete lhs ; 557 | return new NodeConstantValue( 0. ) ; 558 | } 559 | 560 | Node* rhs = ParseValueNode( mt ) ; 561 | 562 | lhs = MakeOperationNode( op, lhs, rhs ); 563 | } 564 | 565 | return lhs ; 566 | } 567 | Node* ParseExpressionNode( MiniTokenizer& mt ) 568 | { 569 | // Most grammars are left associative (e.g. natural arithmetic, C). 570 | return ParseExpressionNode_LeftAssociative( mt ) ; 571 | } 572 | 573 | } // ~unnamed namespace 574 | 575 | 576 | MiniParser::MiniParser( const std::string& expression ) 577 | : mParseTree( NULL ) 578 | { 579 | mParseTree = MiniParserNode::FromString( expression ) ; 580 | } 581 | 582 | MiniParser::~MiniParser() 583 | { 584 | delete mParseTree ; 585 | } 586 | 587 | real 588 | MiniParser::eval( const IDMap& ids ) 589 | const 590 | { 591 | if( !mParseTree ) 592 | { 593 | std::cerr << "No parse tree!" << std::endl ; 594 | return 0. ; 595 | } 596 | 597 | if( ids.count( "PI" ) ) 598 | { 599 | std::cerr << "Warning: PI is a built-in constant; the value passed in the IDMap will be ignored." << std::endl ; 600 | } 601 | 602 | return mParseTree->eval( ids ) ; 603 | } 604 | -------------------------------------------------------------------------------- /MiniParser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __MiniParser_hpp__ 2 | #define __MiniParser_hpp__ 3 | 4 | #include 5 | #include 6 | class MiniParserNode ; 7 | 8 | class MiniParser 9 | { 10 | public: 11 | typedef double real ; 12 | typedef std::map< std::string, real > IDMap ; 13 | 14 | // There is no order of operations (everything is evaluated left-associative), 15 | // so use lots of parentheses. 16 | MiniParser( const std::string& expression ) ; 17 | virtual ~MiniParser() ; 18 | 19 | real eval( const IDMap& ids ) const ; 20 | 21 | private: 22 | MiniParserNode* mParseTree ; 23 | }; 24 | 25 | #endif /* __MiniParser_hpp__ */ 26 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Author: Yotam Gingold 2 | License: Public Domain. (I, Yotam Gingold, the author, release this code into the public domain.) 3 | Homepage: http://yotamgingold.com/code/ 4 | GitHub: https://github.com/yig/miniparser 5 | 6 | MiniParser: 7 | A simple C++ expression parser with no external dependencies whatsoever 8 | that can 9 | - generate a parse tree from an expression 10 | - parentheses 11 | - constants 12 | - symbolic variables 13 | - binary operators: 14 | - add: + 15 | - subtract: - 16 | - multiply: * 17 | - divide: / 18 | - modulus: % 19 | - exponent: ^, ** 20 | - unary operators: 21 | - negate: - 22 | - functions: 23 | - sin() 24 | - cos() 25 | - log() (base 10) 26 | - ln() (base e) 27 | - exp() 28 | - sqrt() 29 | - constants (override any variable with the same name): 30 | - PI = 3.14... 31 | NOTE: Order-of-operations is entirely left-associative. There is no 32 | operator precedence. Use parentheses everywhere! 33 | TODO: Operator precedence can be added easily with several lines of 34 | find/replace in the function "Node* Parse( const std::string& str );" 35 | See http://en.wikipedia.org/wiki/Operator-precedence_parser#Alternatives_to_Dijkstra.27s_Algorithm 36 | 37 | example: 38 | An example command line application that demonstrates a class 39 | with a predefined expression evaluated via operator() 40 | as well as an expression with an arbitrary number of variables. 41 | Can be called with two values (x,y) or with an expression followed 42 | by an arbitrary number of values assigned to the letters of the 43 | alphabet (a, b, c, ...). 44 | 45 | 46 | Version History: 47 | - 2006-04-08: 48 | initial release 49 | - 2006-12-09: 50 | added math functions (sin, cos, log, ln, exp, sqrt) and constants (PI) 51 | improved error handling (removed an infinite loop) 52 | - 2007-04-23: 53 | made the grammar left associative to match natural arithmetic and C 54 | - 2007-09-20: 55 | added the modulus operator %. attempted to add order-of-operations, 56 | but was foiled by unary minus (e.g. -a). 57 | -------------------------------------------------------------------------------- /example.cpp: -------------------------------------------------------------------------------- 1 | #include "MiniParser.hpp" 2 | 3 | #include 4 | #include 5 | 6 | class myfunc 7 | { 8 | public: 9 | myfunc() : mp( "x + (y**2)" ) {} 10 | 11 | double operator()( double x, double y ) 12 | { 13 | MiniParser::IDMap ids ; 14 | ids[ "x" ] = x ; 15 | ids[ "y" ] = y ; 16 | 17 | return mp.eval( ids ) ; 18 | } 19 | 20 | private: 21 | MiniParser mp ; 22 | }; 23 | 24 | int main( int argc, char* argv[] ) 25 | { 26 | if( argc == 3 ) 27 | { 28 | myfunc func ; 29 | double x = atof( argv[1] ) ; 30 | double y = atof( argv[2] ) ; 31 | std::cout << "f( " << x << ", " << y << " ): " << func( x, y ) << std::endl ; 32 | } 33 | else if( argc > 3 ) 34 | { 35 | std::cout << "Parsing: " << argv[1] << std::endl ; 36 | MiniParser mp( argv[1] ) ; 37 | 38 | MiniParser::IDMap ids ; 39 | char var[] = "a" ; 40 | for( int i = 2 ; i < argc ; ++i ) 41 | { 42 | var[0] = 'a' + i-2 ; 43 | 44 | ids[ var ] = atof( argv[i] ) ; 45 | } 46 | 47 | std::cout << "eval( " ; 48 | for( 49 | MiniParser::IDMap::const_iterator idit = ids.begin() ; 50 | idit != ids.end() ; 51 | ++idit 52 | ) 53 | { 54 | if( idit != ids.begin() ) 55 | std::cout << ", " ; 56 | 57 | std::cout << idit->first << " = " << idit->second ; 58 | } 59 | std::cout << " ): " << mp.eval( ids ) << std::endl ; 60 | } 61 | else 62 | { 63 | std::cerr << "Usage: " << argv[0] << " x y" << std::endl ; 64 | std::cerr << "\tor" << std::endl ; 65 | std::cerr << "Usage: " << argv[0] << " expression [a [b [c [...]]]]" << std::endl ; 66 | std::cerr << "(Remember: order-of-operations is left-associative! use parentheses!)" << std::endl ; 67 | exit( -1 ) ; 68 | } 69 | 70 | exit( 0 ) ; 71 | } 72 | --------------------------------------------------------------------------------