[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to "verify" your details, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Programming ➜ General ➜ Expression parser

Expression parser

It is now over 60 days since the last post. This thread is closed.     Refresh page


Pages: 1  2 3  4  5  

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #15 on Thu 16 Jun 2005 09:16 PM (UTC)
Message
Quote:
Using Cygwin with a g++ of 3.3.3, I get "0x435000".
Oh... I remember getting something funny like that when using string constants in certain situations. What's happening is that your code is printing the constant as an integer for some reason. If you make it into a string constructor - string("hello") - that should fix it. But it wasn't a problem specific to MAKE_STRING. I had it in other places as well.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Flannel   USA  (1,230 posts)  Bio
Date Reply #16 on Thu 16 Jun 2005 10:04 PM (UTC)
Message
Is it that? or are you printing the reference to the string?

~Flannel

Messiah of Rose
Eternity's Trials.

Clones are people two.
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #17 on Thu 16 Jun 2005 10:31 PM (UTC)
Message
Sorry. I meant printing the address of the pointer; a string constant is after all just an integer pointing to the first byte of the string and going until the first \0.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Peter Claessens   (4 posts)  Bio
Date Reply #18 on Fri 17 Jun 2005 03:48 PM (UTC)
Message
About some posts ago... Raz is right about the WIN32 definition in cygwin.
First, to clear up some confusion, I use gcc v3.4.4 both under DJGPP and cygwin. I did a little test.
The DJGPP compilation does _not_ define WIN32. Neither does g++ under cygwin _without_ -mno_cygwin option. g++ under cygwin _with_ the -mno_cygwin compilation option _does_ define WIN32.
I didn't expect that it would depend on this switch. In any case, I cannot compile without the -mno_cygwin option, because I use the allegro game programming library, which I am supposed to use with the -mno_cygwin switch.
Regards
P
Top

Posted by KMFDM   China  (1 post)  Bio
Date Reply #19 on Mon 24 Oct 2005 06:44 AM (UTC)
Message
nice piece of work

in parser.cpp Line 414: GetToken() should be GetToken (true)

so does the GetToken() in Line 506, the same problem.

Thanks, Nick. It helps me a lot!!

een nieuwe dag
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #20 on Mon 24 Oct 2005 10:38 AM (UTC)

Amended on Mon 24 Oct 2005 10:39 AM (UTC) by Nick Gammon

Message
Thanks for all the comments, it is nice to see people using it.

Quote:

What you actually want is something more like this:
//instead of "STD_FUNCTION(fabs);", use this:
OneArgumentFunctions ["abs"] = fabs;


I have amended it to use this suggestion, use the fabs function but internally refer to it as abs.

Quote:

...
to the rather obvious and maybe dumb


const double DoMin (const double arg1, const double arg2)
{
/* return min<double>(arg1, arg2);*/ return (arg1 < arg2 ? arg1 : arg2) ;
}



Why not? I have amended it to inline the DoMin and DoMax.

By doing both of these changes most of the WIN32-specific stuff has gone, so whether or not WIN32 is defined should be almost irrelevant.

Quote:

In parser.h:

>#include <math.h>
>#include <time.h>

Uh oh. Why not use the more appropiate C++ files cmath and ctime? You could easily mess up programs that use the C++ header files names with the old C file names.


OK, did that.

Quote:

>using namespace std;

This is fine, but does it really have to be in the header file?


Removed it altogether and put std:: in front of things that needed it.

Quote:

#define MAKE_STRING(msg) \
(((ostringstream&) (ostringstream() << boolalpha << msg)).str())

That just looks wrong.


I can reproduce your test results, although it seems to work OK in the parser. Anyway, I have removed the macro and inlined the couple of places it was used.

Quote:

const double Evaluate (const string program); // get result

Why not a reference? std::string is not cheap to copy.


Not sure why I did that. Hope there wasn't a good reason as I have changed it to use the reference.

Quote:

Down the file, I see dubious floating point comparisons.


What ones exactly? Compare to zero? I think that is defined to work in all cases. Can you give a list of the ones you are doubtful about?

Quote:

Sure. I was just commenting on how he forgot to include the standard header file cstdlib which includes the definition of std::rand().


I didn't forget, I am using the standard library rand, not std::rand. Whether that is bad or not I'm not sure.

Quote:

in parser.cpp Line 414: GetToken() should be GetToken (true)

so does the GetToken() in Line 506, the same problem.


Not sure about that. If I change both of those the argument becomes redundant because all calls are using the argument of true. If you change it as you suggest, this fails:

-+1

This is a valid expression, we are taking a unary minus of the signed number +1.

The parser as supplied will process that, with your suggestion it won't.




Updated version

The version with the above improvements incorporated is available from the same location:


http://www.gammon.com.au/files/utils/parser.tgz


It might be worth putting in a plug for Lua at this point. Lua is a full scripting language with a small footprint, designed to compile on all standard C compilers. It is available for use at no cost.


http://www.lua.org/


My parser has a very small footprint and is suitable for its designed purpose, evaluation simple expressions at runtime. Lua is larger but supports full scripting, including extending with your own C routines. I you need something more powerful check out Lua. :)

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #21 on Mon 24 Oct 2005 09:00 PM (UTC)

Amended on Mon 24 Oct 2005 09:03 PM (UTC) by Nick Gammon

Message
There is a subtle problem in the parser, in the getrandom function, which no-one has commented on. :)

It initially read:


const int getrandom (const int x)
{
long n;
 
 if (x <= 0)
   return 0;
 n = rand ();
 return (n % x);
}   // end of getrandom


Now this works, in a sense, and is the way a lot of people implement random numbers in a range. Simply take the output from the random number generator and take its modulus to the desired range. Thus if you supply 3, you will get 0, 1, or 2 as the result, which is what is documented.

However the problem is that if you want, say, penny flips, so you call it as getrandom (2).

What the modulus is effectively doing is discarding all bits from the random number, except the low-order bit. Now the low-order bit is not the full information returned by the pseudo-random generator, and therefore is not necessarily as "random" as the whole number.

A second problem is that if someone expects a number in a large range (eg. rand (100000)) they won't get it because the largest number returned by rand is 32767.

A better method might be:


const int getrandom (const int x)
{
  if (x <= 0)
    return 0;
  return (double) rand () / (double) (RAND_MAX + 1) * (double) x;
}   // end of getrandom


What this does is take the entire returned random number, and then divide by the greatest one it can get (RAND_MAX), plus 1, to get a number in the range [0,1). That is, including 0 but excluding 1.

Now we multiply this by the argument to get a number in the range 0 to the argument, but excluding the argument.




For even better random numbers you may want to consider other algorithms, such as the Mersenne Twister "A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator".

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #22 on Mon 24 Oct 2005 09:14 PM (UTC)
Message
If you are confident RAND_MAX on your system is 32767 then you can save a division by writing this:


return (double) rand () * 3.0517578125000000e-5 * (double) x;



Some quick research shows this confidence may be misplaced, on Visual C++ it is indeed defined as 32767 (0x7FFF), however on Unix it seems to be 0x7FFFFFFF, in which case the line would read:


return (double) rand () * 4.6566128709089882e-10* (double) x;


To be general, better leave it at RAND_MAX.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Cash   USA  (626 posts)  Bio
Date Reply #23 on Sun 20 Nov 2005 10:07 PM (UTC)
Message
Just wanted to say thanks, this thing worked wonderfully for my small script interpreter for my Comp. Sci. 1 lab project.

Nice work, as always :)

~Nick Cash
http://www.nick-cash.com
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #24 on Wed 14 Dec 2005 04:47 AM (UTC)
Message
Thank you. :)

Further to my comments about random numbers, I see from the Lua source that they do something similar, with an extra twist ... I'll reproduce the relevant line from their code plus comments:



  /* the `%' avoids the (rare) case of r==1, and is needed also because on
     some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
  double r = (double)(rand()%RAND_MAX) / (double )RAND_MAX;


(I changed lua_Number in the source to double to make it clearer).

This is using the % operator (modulus) to coerce the results from rand() into 0 to RAND_MAX minus 1, and then divide by RAND_MAX to get a floating-point number in the range 0 to 1, but excluding 1.

After that, if you want an integer in the range 1 to u (where u is the upper limit, and 1 is the lower limit) you do this:


result = (int) floor (r * u) + 1;



- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Jorick   (1 post)  Bio
Date Reply #25 on Tue 07 Aug 2007 07:40 PM (UTC)
Message
This is exactly what I wanted! Thanks! You've saved me weeks of work.

The only problem is that my project is in C so I had to convert the code to C. Not a problem at all.

I added ^ for power and % for modulo division. I also added a frac() function to get the fractional part of a number.

The code is now part of a programmable syringe pump, where programming is done in a language similar to Basic. The addition of expression parsing makes the pump much more powerful and versatile.

Again, thanks for your parser!
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #26 on Wed 08 Aug 2007 01:37 AM (UTC)
Message
Glad it was helpful.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Djones   USA  (3 posts)  Bio
Date Reply #27 on Sat 31 Jan 2009 12:15 AM (UTC)
Message
Hi,

I just discovered your parser yesterday and found that it worked pretty well for a project I am working on. However, I discovered a problem that I have attempted to address, but I don't know if my solution is necessarily totally correct. I would appreciate another pair of eyes looking at it, and if warranted, perhaps you can update your parser with the correction.

The problem is that the parser fails to recognize values <1 without a leading 0. For example, ".5" is not recognized as a number, while "0.5" is fine. Since I am processing data from a source that I do not control, I want to reduce the risk of failure due to malformed numbers. My solution is below (note the additional check for a decimal point as the first character):


  // look for number
  if ((!ignoreSign &&
        (cFirstCharacter == '+' || cFirstCharacter == '-') &&
        isdigit (cNextCharacter)
      )
      || isdigit (cFirstCharacter)
      // allow decimal numbers without a leading 0. e.g. ".5"
      // Dennis Jones 01-30-2009
      || (cFirstCharacter == '.' && isdigit (cNextCharacter)) )


Is this the best way to solve the problem? Anything else I need to do?

Thanks,
Dennis
Top

Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Reply #28 on Sat 31 Jan 2009 12:37 AM (UTC)
Message
That looks OK to me. Does it work properly?

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Djones   USA  (3 posts)  Bio
Date Reply #29 on Sat 31 Jan 2009 01:14 AM (UTC)
Message
Oh, and by the way, you might like to know that I am using the parser successfully (after a few minor modifications) with C++Builder (Borland/CodeGear), in case anybody wants to know if it works with C++Builder 5/6. I haven't tried with CB2007 or CB2009 yet, but I suspect they will work fine too.

The required changes are as follows:

In parser.cpp (at the top, just after the "parser.h" include):


// Support for Borland C++Builder 5/6
// Dennis Jones, Grass Valley Software, 01-30-2009
#if defined(__BORLANDC__)
  // make methods from <cmath> available without requiring std:: scope resolution
  using namespace std;

  // define NaN
  #include <math.hpp>
  #if (__BORLANDC__ < 0x560)
  // BCB5 doesn't define NaN
  static const Extended NaN = 0.0 / 0.0;
  #endif  // (__BORLANDC__ < 0x560)
#endif  // __BORLANDC__


Note: NaN was needed for a couple of functions I added. If you don't need NaN, you can leave out the definition.

Dennis
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


208,528 views.

This is page 2, subject is 5 pages long:  [Previous page]  1  2 3  4  5  [Next page]

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]