wecing wecing -4 years ago 47
C++ Question

How to store parsed function expressions for plugging-in many times?

As the topic indicates, my program needs to read several function expressions and plug-in different variables many times. Parsing the whole expression again every time I need to plug-in a new value is definitely way too ugly, so I need a way to store parsed expression.

The expression may look like

2x + sin(tan(5x)) + x^2
. Oh, and the very important point -- I'm using C++.

Currently I have three ideas on it, but all not very elegant:


  1. Storing the S-expression as a tree; evaluate it by recurring. It may
    be the old-school way to handle this, but it's ugly, and I would
    have to handle with different number of parameters (like + vs. sin).

  2. Composing anonymous functions with
    boost::lambda
    . It may work nice,
    but personally I don't like boost.

  3. Writing a small python/lisp script, use its native lambda
    expression and call it with IPC... Well, this is crazy.



So, any ideas?

UPDATE:

I did not try to implement support for parenthesis and functions with only one parameter, like
sin()
.

I tried the second way first; but I did not use
boost::lambda
, but a feature of gcc which could be used to create (fake) anonymous functions I found from here. The resulting code has 340 lines, and not working correctly because of scoping and a subtle issue with stack.

Using lambda could not make it better; and I don't know if it could handle with scoping correctly. So sorry for not testing boost::lambda.

Storing the parsed string as S-expressions would definitely work, but the implementation would be even longer -- maybe ~500 lines? My project is not that kind of gigantic projects with tens of thousands lines of code, so devoting so much energy on maintaining that kind of twisted code which would not be used very often seems not a nice idea.

So finally I tried the third method -- it's awesome! The Python script has only 50 lines, pretty neat and easy to read. But, on the other hand, it would also make python a prerequisite of my program. It's not that bad on *nix machines, but on windows... I guess it would be very painful for the non-programmers to install Python. So is lisp.

However, my final solution is opening bc as a subprocess. Maybe it's a bad choice for most situations, however, it fits me well.

On the other hand, for projects work only under *nix or already have python as a prerequisite, personally I recommend the third way if the expression is simple enough to be parsed with hand-written parser. If it's very complicated, like Hurkyl said, you could consider creating a mini-language.

Answer Source

Why not use a scripting language designed for exactly this kind of purpose? There are several such languages floating around, but my experience is with lua.

I use lua to do this kind of thing "all the time". The code to embed and parse an expression like that is very small. It would look something like this (untested):

std::string my_expression = "2*x + math.sin( math.tan( x ) ) + x * x";

//Initialise lua and load the basic math library.
lua_State * L = lua_open();
lua_openmath(L);

//Create your function and load it into lua
std::string fn = "function myfunction(x) return "+my_expression+"end";
luaL_dostring( L, fn.c_str(), fn.size() );

//Use your function

for(int i=0; i<10; ++i)
{
  // add the function to the stack
  lua_getfield(L, LUA_GLOBALSINDEX, "myfunction");
  // add the argument to the stack 
  lua_pushnumber(L, i);
  // Make the call, using one argument and expecting one result.
  // stack looks like this : FN ARG
  lua_pcall(L,1,1)
  // stack looks like this now : RESULT
  // so get the result and print it 
  double result = lua_getnumber(L,-1);
  std::cout<<i<<" : "<<result<<std::endl;
  // The result is still on the stack, so clean it up.
  lua_pop(L,1);
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download