MicroMath+ math parser and execution environment
Switch branches/tags
Nothing to show
Pull request Compare This branch is even with candycode:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


This is the latest (moslty)unedited version of code from an old project; 
I used previous versions of this code to perform evaluation of functional 
representations used to describe geometrical objects, which later on turned out
to be completely useless: it is way easier to directly evaluate any 
expression at run-time through GLSL or (now)OpenCL in parallel and record 
the results into an output array.


MicroMath+ 2.1

MicroMath+ - copyright (c) 2001 Ugo Varetto

	 - transform a mathematical expression into RPN format
	 - compile the RPN expression into a sequence of instructions
	 - execute the compiled program
     The parser returns a list of tokens using only information about
     * any word starting with '_' or a letter and containing
       a mix of letters, digits and '_' is parsed as a name
     * anything in the form '<name>(...)' is assumed to be a
     It's up to the compiler and run-time environment to figure out
     what to do with the parsed functions, operators, values and names.


Changes fromm previous versions:

- added CMake file

- added shared_ptr smart pointer implementation and memory tracer class 
  + oveloaded new and delete to perform automatic memory management and
  detect memory leaks

- added support for multidimensional arguments e.g:
  (x,y,z) = (1,2,3) + (1,2,3) is equivalent to x=1+1, y=2+2, z=3+3

- it is now possible to use multidimensional functions R^n --> R^m
  e.g. dot product (R^3-->R^1): (1,2,3)*(1,2,3) == 1+4+9
- removed operator substitution: operator --> function substitution not
  supported anymore 

- generalized functions and operators, functions are actually
  generic operators supporting left and right multi-dimensional
  input and output parameters

- new representation for RPN notation: operators are now represented as
  name[left_params right_params out_params]
  e.g. (1,2) + (1,2) --> 1 2 1 2 +[2 2 2] if '+' returns two values.
- it is now possible to remove the check on the number of parameters in the

- added @list command in sample program to display all the functions and


Use cmake to generate the project/make files for your platform:

cmake <path to MM+ code>


 math_parser.cpp - math parser implementation
 test.cpp - driver program to test MicroMath+
 mem_tracer.cpp - implementation of memory tracing routines (optional)

Tested on:

GCC 4.0 Mac OS X
GCC 3.4.2 MinGW
MS VS .NET 2003
Intel C++ 8.0 Windows 
GCC 4.4 Linux x86-64
MS VS .NET 2008

To enable the memory tracing option #define MMP_DEBUG_MEMORY


1) Assign 1,2,3 to x,y,z
(x,y,z) = (1,2,3)

2) dot product
1 + (1,1,1)*(1,1,1)

3) add


By default the parser checks the number of arguments for operators and the
compiler checks the number of arguments passed to a function. An error is not
returned if and only if there are entries in the function vector matching
the operator/function name AND the number of input arguments.

e.g. sin(1) works, sin(1,2) doesn't because the compiler cannot find a function
with name == 'sin' accepting 2 arguments; it is possible to turn the check off,
in this case the compiler will only verify that a function with name == 'sin'
is available but won't check the number of arguments at compile time.

a. f(  1, 2  ) --> function accepting two arguments
b. f( (1, 2) ) --> function accepting one argument

if you want (a) and (b) to be equivalent then disable argument check in the

If you want to add support for run-time user defined function have a look at
the add_user_def_function() function in test.cpp.

Operators are considered to be generalized functions with optional arguments
on the right and left side. 
Only operators are checked and parsed during the parse step, in order to have
functions parsed at parse time add the function as an operator into the
operators list passed to the math parser constructor.
E.g. operator_type( "cross3", 1, 0, 6, 3 ), creates and 'operator' with the
following properties:
- name = cross3
- accepts 1 argument
- 0 arguments on the left side
- 1 6-dimensional argument (6 values) on the right side
- returns 3 values (one 3-D value)
this is equivalent to the function call:vcross3(1,2,3,4,5,6)  
In case argument counting is turned on for operators the above expression is NOT
eqivalent to cross3((1,2,3),(4,5,6)) turning operator counting off in the parser
makes the two expressions equivalent

Difference between operators and functions:
Operators/functions passed to the parser at construction time can have any
number of spaces between the operator's name and the operands.
Functions are interpreted as functions if and only if no space is present
between the function name and opening parenthesis:
function(...) OK
function (...) WRONG interpreted as name (...)

It is possible to add all the functions as operators at construction time.

Stack operations

f(1,2,3) --> 1 2 3 f[3] --> function accepting three parameters

At run-time the following sequence of events takes place:

|Operation                   | Stack

number 1 pushed on the stack | 1 <-- top

number 2 pushed on the stack | 2 <-- top
                             | 1

number 3 pushed on the stack | 3 <-- top
                             | 2
                             | 1 

function f invoked: parameters are popped from the stack
in reverse order 

There are three options to deal with the ordering of operands:

1) ask the parser to reverse all the operands:

f(1,2,3) --> 3 2 1 f[3]

2) define functions as operators and set the swap flag on
per function/operator basis; check the way assignment operators
are created in the test program

3) do nothing and properly handle the parameters in f's body



#include "compiler.h"
#include "execution.h"
#include "vm.h"
#include "def_rte.h"
#include "math_parser.h"


void TestParser( const string& expr )
  typedef mmath_plus::operator_type op_t;
  // define operators for parser
  op_t ops_array[] = { // example of function accepting 6 parameters and returning
  	               // 3 values
                       op_t( "cross3", 1, 0, 6, 3 ),
                       op_t( "^", 2 ), op_t( "*", 2, 3, 3, 1 ),
                       op_t( "*", 2 ), op_t( "/", 2 ),
                       op_t( "-", 1, 0, 1, 1 ), op_t( "-", 2 ),
                       op_t( "-", 2, 3, 3, 3 ),
                       op_t( "+", 2, 3, 3, 3 ), op_t( "+", 2 ),
                       op_t( "=", 2, 1, 1, 1, true ),  
                       op_t( "=", 2, 3, 3, 3, true )   // 2 arguments, swap arguments
                                                       // to place variable name just
                                                       // before assignment operator
                                                       // x = 2 --> 2 x =
                                                       // This makes it easy to find
                                                       // the variable when the assignment
                                                       // operator is found. 
  // anything in the form 'function_name( x1, x2,..., xn)' is assumed to be
  // a function accepting n arguments and returning 1 value.
  // It is possible to define functions accepting N arguments and returning M
  // values as operators; in this case the functions will have to be declared together with
  // the operators list if parse-tme argument check is required, check the cross3 function
  // above.
  vector< op_t > ops( ops_array, ops_array + sizeof( ops_array ) / sizeof( op_t ) );

  // generate default run-time environment
  // the default run time environment supports all the standard C math functions.
  rte< double > rt = generate_default_rte< double >(); // Check memory allocation here
  // build parser
  math_parser mp( ops,
                  &cout );
  // create compiler and virtual machine for execution
  compiler< double > c( compiler< double >::COUNT_ARGS,
                        compiler< double >::CREATE_VARS );
  vm< rte< double > > m( rt );
  // program
  rte< double >::prog_type program;

  // parse
  math_parser::Tokens vt = mp.parse( expr );
  // compile
  program = c.compile( vt, rt );
  // run program
  m.prog( &program );
  // print values on stack    
  if( !m.rte().stack.empty() )
  	// print result i.e. value on top of stack
    cout << '\n' << "RESULT: "; 
	while( !m.rte().stack.empty() )
		cout << m.rte().stack.top() << ' ';
		// remove value on top of stack
	cout << endl;

	the execution is based on a stack and an array of instructions
    executed sequentially: this works fine for scalar values
    but it is far from optimal in the case of multi-dimensional operands;
    I have been testing for some time with a multi_stack structure that allows to
    get/set/reference multiple values at once and at functions that get
    references to operands in the stack without removing the values but
    changing the values in place where possible. Not sure when/if the new
    approach is going to be implemented.