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
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:
1
2 3
4
5
It is now over 60 days since the last post. This thread is closed.
Refresh page
top