Skip to content

Commit

Permalink
readme and examples
Browse files Browse the repository at this point in the history
  • Loading branch information
miromannino committed May 9, 2015
1 parent 3892863 commit 96c15ef
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 179 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ $(GenFilesFolder): $(BuildFolder)
$(BuildTestFolder): $(BuildFolder)
if [ ! -d $(BuildTestFolder) ]; then mkdir $(BuildTestFolder) ; fi


# ---------------------------------------------------------------------------------------
# Lexer
# ---------------------------------------------------------------------------------------
Expand Down Expand Up @@ -205,6 +206,7 @@ run-tests:
$(BuildTestFolder)/tests
$(BuildTestFolder)/performances


# ---------------------------------------------------------------------------------------
# Clean
# ---------------------------------------------------------------------------------------
Expand Down
312 changes: 146 additions & 166 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,221 +1,201 @@
MExpr
=====
# MExpr

<p>Library that parses mathematical expressions with implicit multiplications.</p>
C++ library that parses human-like arithmetic expressions like the following:

<p>The library uses bison and flex to parse a string that represents a mathematical expression. The parser was made to manage complex expressions like this:</p>
-3xy^2 - 3(_sum(1,2) + _sum(1,2,3))(-5x)

<pre><code>2 + 2yx^2 - 3(_sum(1,2) + _sum(1,2,3))(-5x)
</code></pre>
To understand better, let's make some examples comparing them to the more traditional computer-like notation:

<h3>Implicit multiplications</h3>
<table class="table">
<tr><th>MExpr notation</th><th>Traditional computer-like notation</th></tr>
<tr><td>3xy</td><td>3 \* x \* y</td></tr>
<tr><td>-3(x + y)</td><td>-3 \* (x + y)</td></tr>
<tr><td>(x + y)(x + 4)</td><td>(x + y) \* (x + 4)</td></tr>
</table>

<p>If you write <code>xy</code>, the parser understands that you want to multiply two variables: <code>x</code> and <code>y</code>. But this is not the only case where you can have an implicit multiplication, there are some other examples:</p>

<pre><code>-3xy^2
(3+3)(5-3)
-(3-2)
</code></pre>
## Features

<p>But for this reason, you can't use variables that have more than a single character. Infact, if you write <code>xy</code>, the parser can&rsquo;t distinguish between the variable <code>xy</code> and the multiplication <code>x * y</code>.</p>
### Implicit multiplications

<h3>Functions</h3>
The parser interprets the text to detect implicit multiplication. For example the expression `xy` is interpreted as `x * y`, and the expression `-3(x + y)(x + 4)` as `-3 * (x + y) * (x + 4)`.

<p>Another problems, is that if you write <code>f(x)</code> the parser can&rsquo;t distinguish between the function <code>f</code> called with the <code>x</code> argument and the variable <code>f</code> that multiplies the <code>(x)</code> expression. For this reason, the functions always start with the <code>_</code> character. Now, <code>_f(x)</code> calls the <code>f</code> function and <code>f(x)</code> is a multiplication between the <code>f</code> and <code>x</code> variables.</p>
### One character variables

<p>The function can have more than one argument. For example, you can define the function <code>_sum(a,b)</code> that adds two numbers. Furthermore, you can define <code>_sum(a,b,c)</code> too, that adds three numbers. In fact, the parser can manage overloaded functions distinguishing the functions by the number of arguments.</p>
Like the arithmetic expression which we are used to write in a sheet of paper, the variables are single-character words like: `x`, `y`, etc.

<h3>Environment</h3>
That has opened the possibility to recognize implicit multiplications in a expression like: `xy^2`.

<p>The parser has a dynamic environment, that maintains all the value associated to a variable symbol. Moreover, for each function symbol, it maintains a pointer to the associated function. Also, you can redefine a defined function, or change the value of a defined variable (especially if you're drawing a plot).</p>
That is also an assumption which we have in our minds, and that allow us to interprets `xy` as a multiplication between the variables `x` and `y`, rather than the single variable `xy`.

<h3>STDFunctions</h3>
### Bytecode compilation

<p>The library has a set of standard functions that you can use in your expressions. This functions are the same of the math.h standard library.</p>
The library parses the input string and then it builds an abstract syntax tree. For example, with the expression <code>-3xy^2</code>, the parser builds the following abstract syntax tree:

<p>For example:</p>
[ * ]─[ -1 ]
└───[ * ]─[ 3 ]
└───[ * ]─[ x ]
└───[ ^ ]─[ y ]
└───[ 2 ]

<pre><code>_sqrt(_floor(_exp(5)))
</code></pre>
<br/>

<p>uses the math.h functions: <code>sqrt</code>, <code>floor</code> and <code>exp</code>.</p>
<p>At this point, the library can directly evaluate the expression using the tree (browsing it recursively).</p>

<p>The standard constants (like <code>pi</code> and <code>e</code>) will be introduced with the multiple-character variables.</p>
<p>In some cases, for example when you want to draw a plot, you need to evaluate the same expression changing only the value of a variable. For this reason, the library can "compile" the AST to have a more efficient representation of the expression. The generated code is a simple bytecode, that uses a stack to compute operations (similarly to the Java bytecode).</p>

<h2>How it works</h2>
<p>This is the representation of the bytecode generated using the previous expression:</p>

<p>The library parses the input string and then it builds an AST Tree. For example, with the expression <code>-3xy^2</code>, the parser builds this tree:</p>
VAL: -1
VAL: 3
VAR: x
VAR: y
VAL: 2
POW
MUL
MUL
MUL

<pre><code>[ * ]─[ -1 ]
└───[ * ]─[ 3 ]
└───[ * ]─[ x ]
└───[ ^ ]─[ y ]
└───[ 2 ]
</code></pre>
<br/>

<p>Now, the library can evaluate the expression using the tree (this is done browsing recursively this tree).</p>
### Functions

<p>In some cases, for example when you want to draw a plot, you need to evaluate the same expression changing only the value of a variable. For this reason, the library can &ldquo;compiles&rdquo; the AST Tree to have a more efficient representation of the expression. The generated code is a simple bytecode, that uses a stack to compute operations (similarly to the Java bytecode).</p>
MExpr has the possibility to use functions inside the expression.

<p>This is the representation of the bytecode generated using the previous expression:</p>
The library comes with all the functions of _math.h_. Furthermore, one can also
insert new custom functions that can do almost anything!

<pre><code>VAL: -1
VAL: 3
VAR: x
VAR: y
VAL: 2
POW
MUL
MUL
MUL
</code></pre>
All the function names starts with the underscore character, for example `_f(x)`. This notation is important
because it allows to disambiguate expressions like `f(x)`. In fact, that could be interprets as a multiplication
between the variables `f` and `x`, or as the call of the function `f` passing `x` as argument. On the contrary
`_f(x)` has only one interpretation.

<h3>Functions and variables</h3>
#### Function overloading

<p>The Environment has two maps: one for the variables and one for the functions.
After that the expression is parsed, the AST Tree (and the code too) contains the name of the variables (or the name of the functions) and not their values. On the contrary, in the evaluation phase the environment is questioned to get the values and the function pointers.</p>
The functions can be overloaded. For example, one can define the function `_sum(a,b)` that adds two numbers.
Furthermore, one can also define `_sum(a,b,c)`. The parser can manage overloaded functions distinguishing
the functions by the number of parameters.</p>

<p>To distinguish the functions with the same name, but different number of arguments, the environment uses different names. For example, the function <code>_foo</code> with one argument is internally called <code>_foo_1</code>, the other function, with the same name but with two arguments, is internally called <code>_foo_2</code>. This makes the things simpler, instead of using <code>va_arg</code></p>

<h2>How to use it</h2>
### Dynamic environment

<p>The Makefile is configured to create a shared library, you can use it with your C++ programs dynamically linking this library.</p>
The parser has a dynamic environment, that maintains all the value associated to a variable symbol. Moreover, for each function symbol, it maintains a pointer to the associated function.

<p>Let's me show an example of a little program that use it:</p>
This approach allows one to define a variable or a function even after the expression parsing (as well as the expression compilation). For the same reason one can redefine a previously defined function, or change the value of a variable (useful if one is drawing a plot).

#include <iostream>
#include <MExpr.h>
using namespace std;

void myfunc(MExpr::StackType* s){ //myfunc(n) = n*3
s->stack[s->stp-1] *= 3;
}
## How to use it

void myDiv(MExpr::StackType* s){ //myDiv(a,b) = a/b
MExpr::ValueType arg1, arg2;
arg1 = s->stack[s->stp-2];
arg2 = s->stack[s->stp-1];
s->stack[s->stp-2] = arg1 / arg2;
s->stp--;
}
The following examples introduces how to use MExpr in your own project. Those examples are also available in the `test` folder and compiled in the `build/test/` folder.

void mySum2(MExpr::StackType* s){ //mySum2(a,b) = a+b
s->stack[s->stp-2] = s->stack[s->stp-2] + s->stack[s->stp-1];
s->stp--;
}
void mySum3(MExpr::StackType* s){ //mySum2(a,b,c) = a+b+c
s->stack[s->stp-3] = s->stack[s->stp-3]
+ s->stack[s->stp-2]
+ s->stack[s->stp-1];
s->stp -= 2;
### Example 1

This example shows how to create an expression, assign some values to its variables and print its value.

Expression* e = new Expression("-3(4xy^2x-2x)(8x^-(3x)+2y^-2)");
e->setVariable('x', 4);
e->setVariable('y', -5);

cout << e->evaluate() << endl;

<br/>

### Example 2

This example shows how MExpr could be used to repeatedly evaluate the same expression, only changing the value of a variable.
The expression is previously compiled, to allow a faster evaluation.

Expression* e = new Expression("x^2");
e->compile(); //To allow a faster evaluations (e.g. to draw a plot)

for (int i = 0; i < 10; i++) {
e->setVariable('x', i);
cout << e->evaluate() << ' ';
}
cout << endl;

<br/>

### Example 3

This example shows how to print in the console the tree representation and the bytecode representation of the expression.

Expression* e = new Expression("-3xy^2");

cout << "Expression AST: " << endl;
string* s = e->getExprTreeString();
cout << *s;
delete s;

e->compile();

cout << endl << "Compiled expression: " << endl;
s = e->getExprCodeString();
cout << *s;
delete s;

<br/>

### Example 4

This example shows how to create and use a custom function inside the expression.

// myDiv(a, b) = a / b
void myDiv(MExpr::StackType* s) {
MExpr::ValueType arg1, arg2;
arg1 = s->stack[s->stp - 2];
arg2 = s->stack[s->stp - 1];
s->stack[s->stp - 2] = arg1 / arg2;
s->stp--;
}

int main(int argc, char** argv){
MExpr::Expression* e;
MExpr::ValueType ris;

string expr(argv[1]);

try{
/*the Expression constructor parse the string and make the AST Tree */
e = new MExpr::Expression(expr);
}catch(MExpr::Error ex){
cout << "Err: " << ex.what() << endl;
return 0;
}

try{
//example variables definitions
e->setVariable('x', 3);
e->setVariable('y', 2);
e->setVariable('z', 3);
e->setVariable('x', 5); //variable redefinition

//example functions definitions
e->setFunction("_math", &myfunc, 1);
e->setFunction("_div", &myDiv, 2);
e->setFunction("_sum", &mySum2, 2); //example of overloaded function
e->setFunction("_sum", &mySum3, 3); //example of overloaded function

}catch(MExpr::Error ex){
/* Catch errors if variable has strange symbols or function names
* doesn't start with '_' */
cout << "Err: " << ex.what() << endl;
}

try{
e->setVariable('-', 3); //the error will be catch
}catch(MExpr::Error ex){
cout << "Err: " << ex.what() << endl;
}
try{
e->setFunction("nounderscore", &mySum3, 3); //the error will be catch
}catch(MExpr::Error ex){
cout << "Err: " << ex.what() << endl;
}

cout << "AST Tree:" << endl;
string* s = e->getExprTreeString();
cout << *s;
delete s;

try{
cout << "Result: " << e->evaluate() << endl;
}catch(MExpr::Error ex){
cout << "Err: " << ex.what() << endl;
}

e->compile();
cout << "Compiled Expression Code:" << endl;
s = e->getExprCodeString(); //if you have not compiled s will be NULL
if(s != NULL){ cout << *s; delete s; }

try{
/* if you compiled the evaluation is made using the code (most efficient)
* it you have not compiled it is made navigating the AST tree */
cout << e->evaluate() << endl;
}catch(MExpr::Error ex){
cout << "Err: " << ex.what() << endl;
}

delete e;
int main(void) {
Expression* e = new Expression("_div(16,4)");
e->setFunction("_div", &myDiv, 2);

cout << e->evaluate() << endl;

return 0;
}

<p>Now we need to compile it specifing the mexpr library:</p>
<br/>

### Compile your code that uses MExpr

The Makefile is configured to create a shared library, you can use it with your C++ programs dynamically linking this library.

g++ -o myExample -lmexpr myExample.cpp

<pre><code>$ g++ -o myexample -lmexpr myexample.cpp
</code></pre>
<br/>

<p>And now we can test all we want:</p>
## How to compile MExpr

<pre><code>$ ./myexample "_sum(2+2yx^2, -735)-3(_sum(1,2) + _sum(1,2,3))(-5x)"
</code></pre>
- Ensure that you satisfy the requirements.
- Open the Makefile and set your OS changing the `OperatingSystem` variable.

<h2>How to compile</h2>
Run the command you need

<ul>
<li>Ensure that you satisfy the requirements.</li>
<li>Open the Makefile and set your OS changing the <code>OperatingSystem</code> variable.</li>
<li>Type <code>make</code> to compile the library</li>
</ul>
- `make all` to compile the library
- `make run-tests` to run the tests
- `make clean-gtest` to remove the GoogleTest library folder
- `make install` to install the library in `/usr/local/lib`

### Common issues

<p> If you want to install the library in <code>/usr/local/lib</code> type <code>sudo make install</code></p>
- Mac OS X, by default, doesn't have the `/usr/local/lib` and `/usr/local/include` folders, check that you have these folders.
- You can have some problems compiling your examples if you don't install it before. If you don't want to install it, ensure that the `DYLD_LIBRARY_PATH` (or the `LD_LIBRARY_PATH`) was proudly configured.
- Ensure that you have installed `wget`, that is used to download the GoogleTest library

<h3>Common issues</h3>

<ul>
<li>Mac OS X, by default, doesn&rsquo;t have the <code>/usr/local/lib</code> and <code>/usr/local/include</code> folders, check that you have these folders.</li>
<li>You can have some problems compiling your examples if you don&rsquo;t install it before. If you don&rsquo;t want to install it, ensure that the <code>DYLD_LIBRARY_PATH</code> (or the <code>LD_LIBRARY_PATH</code>) was proudly configured.</li>
</ul>
## Requirements

Tested operating systems: OSX, Linux

<h2>Requirements</h2>
To compile the library you need: bison, flex and g++.

<p>Now, the library is done to work with Mac OSX an Linux.
To compile the library you need: bison, flex and g++.</p>
GoogleTests library: but is automatically downloaded in the `gtest` folder during the building process (i.e. `make all`).

<h2>License</h2>
## License

This project is licensed under the MIT license
2 changes: 1 addition & 1 deletion test/example1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
*
*/

#include <stdio.h>
#include <iostream>
#include <MExpr.h>

using namespace std;
using namespace MExpr;

Expand Down
Loading

0 comments on commit 96c15ef

Please sign in to comment.