Opale
  presentation
  news
  faq
  docs
  team
  download
  Modules
 
2d
 
3d
 
ode
  matrix
 
parser
Applications
Bugs
 

Mathematical Functions Parser

Ok, let's talk about mathematical functions capture & their interpretation in a scientific program, when you want to give to the user the ability to enter his own function in character string shape. For example, with an equation solving program ("Enter the equation to solve ...") or even with a function plotter ("Enter the equation to plot f(x) = ..."). Once the string captured, a subroutine interprets this string and evaluates the function as is. Such subroutine is called usually a parser of mathematical expressions.

Within the project Opale, under LGPL licence, we have developed a parser in Java. First of all used in the function plotter & differential equation solving project, and then everywhere where a mathematical expression must be evaluated.

The applet, below, allows you to test a part of the functionalities of this parser. This is the first version, so don't hesitate to report any bugs .

To use this applet, enter a mathematical expression in the first line, which can contain variables such as x, y, z & t ; in the next lines you can enter the value of those variables (0 per default) and by clicking on the button the result will be displayed in the last line.

In the next lines, we'll talk briefly about the design used in the parser ; then a description of its functionalities and its evolution. We'll talk also about its use in your java programs.



Parser design : Abstract

In this section, we'll present briefly a method to program a parser without any details. The goal is to evaluate a mathematical expression containing usual operators (+,-,*,/), numbers, variables (x,y,...), constants (Pi,...) and functions (cos, sin, exp ...).
For example : cos(Pi)+2-5*x.

First of all, you need to build a tree from the string ; second of all, walk thru the tree from the root to evaluate the expression value. The most delicate part of this algorithm is the construction of the tree. Once built, it must be stored in an adapted data structure and it can be evaluate as many time as needed (for several values of x for example) without rebuilding it before each evaluation.

The corresponding tree to our exmample is : syntaxic tree

The evaluation of the expression is realized as we said by walking thru the tree from its root. Practically it can done easily with a recursive algorithm.


Opale parser functionalities

The parser provided by Opale is just at its first version. The applet of this page allows to test quickly its validity on expressions containing variables as x,y,z or t. Nevertheless all the abilities given by this parser are not accessible in this applet. We enumerate then, in bulk, the functionalities of this first version :

  • Operators : +,-,*,/,(,),^
  • Functions : cos, sin, tan, acos, asin, atan, sqrt, log, log10, exp, abs, rnd, rad, deg
  • Constants : Pi, e (Euler constant)
  • Whatever variable (x, y, x1, t, y23, a etc...)
  • Variables can also contain mathematical expressions (ex.: evalutate cos(x) with x=2*Pi ; try this in the applet)
  • Can use user defined functions (ex.:f(x) = 2*sin(Pi+x))
  • No difference between low & upcaps

Let's talk about futur :

  • Scientific notation (ex.: 2e3)
  • Support more mathematical & physical constants
  • Ability to use functions with two variables
  • Add functions such as max(x,y), min(x,y), mod(x,y)
  • Improve input error diagnostic in the string
  • Evaluate tests & boolean values (ex.: 4 > 3 )

How to use it ?

You can download the parser as an independant unit to use in your program. Here is the archive oparser.jar which contains all necessary classes to use it. This work is under licence LGPL and we invite you to read carrefully this licence before any use.

Let's talk a bit of the use of those classes in your programs. The most important classes are only 4 and are in the oparser package. It would be better to insert the line 'import oparser.*;' in your program and to set the environment variable CLASSPATH carefully : in fact it must contains the full path to oparser.jar or you must compile each time with the correct classpath :

javac -classpath your_directory/oparser.jar YourProgram.java

You can read the on-line classes documentation (not yet completed).

Example 1 :

The base class is Parser. Let's begin by a simple example which evaluate an expression :


import oparser.*;

public class Test1
{
  public static void main(String[] arg)
  {
    Parser parser = new Parser("4-2*sin(x-Pi)*2");
    parser.addVar("x");
    parser.parseString();
    
    parser.setVar("x",1);
    System.out.println(parser.eval());
	
    parser.setVar("x","Pi");
    System.out.println(parser.eval());	
  }
}

For any compilation problems, don't hesitate to contact us.

This example is easy : we create a parser object by giving the string to translate. Then, we declare the used variables, here "x", with the method addVar(String). We call the method parseString() to build the tree. Once the tree built, we can set variables value with setVar(String, double) or setVar(String, String), and then evaluate the expression as many times as needed with the method eval().

Example 2 :

Here is another example with 2 variables x,y. We evaluate the expression sqrt(x*x+y*y) for several values of those variables in a loop.


import oparser.*;

public class Test2
{
  public static void main(String[] arg)
  {
    Parser parser = new Parser("sqrt(x^2+y^2)");
    parser.addVar("x");
    parser.addVar("y");
    parser.parseString();
    for (int i=0;i<5;i++)
      for (int j=0;j<5;j++)
      {
        parser.setVar("x",i);
	parser.setVar("y",j);
	System.out.println("sqrt("+i+"*"+i+"+"+j+"*"+j+") = "+parser.eval());
      } 
  }
}

Dont forget that we build the tree with the method parseString() only once before evaluation.

Example 3 :

Let's finish with an example where this time we define a user function.


import oparser.*;

public class Test3
{
  public static void main(String[] arg)
  {
    SUnaryFunction f = new SUnaryFunction("f","z","3*z-z*z");
    System.out.println("f(2) = "+f.eval(2));
    Parser parser = new Parser("2*f(x)*f(x)");
    parser.addVar("x");
    parser.addFunc(f);
    parser.parseString();
    parser.setVar("x",-2);
    System.out.println("2*f(-2)*f(-2) = "+parser.eval());
    }
}

In this example, with SUnaryFunction class, we define a function, called user function, with a character string. In our case, we define the function called "f", with the variable "z" by f(z) = 3*z-z*z.

We display then first value of f(2) by calling method eval() of SUnaryFunction class.

Then we remark that it's possible to use in a Parser instance our user function "f". You just need, after creating a Parser, to indicate to it functions to be known with the method addFunc(SUnaryFunction). In our example we give it the function f. Then, as previously we indicate the variable (here x : it's of course possible to define a function of z and use it after as a function of another variable, here x).

At the end, we display the result with the method eval as previously.

Don't hesitate to give your opinion or question on this topic.


Opale Team : January 31 2004 23:14:10.






valid xhtml image