Parsegen can now run concurrently with itself, w00t
Moderator: ZSNES Mods
Parsegen can now run concurrently with itself, w00t
Xcode loves to run compilation commands in parallel (two at the same time because there are two cores available). Unfortunately parsegen can't run at the same time as parsegen (in this case two parsegens run at the same time, one operating on cfg.psr, the other on md.psr). This is because they use the same intermediate file names, eatio.*, in the function enhanced_atoi().
Nach, it would be desirable if this limitation could be removed. parsegen could name its intermediate files differently for each invocation, for example to avoid the conflict described above, the intermediate file names could include the family name (either cfg or md), so they wouldn't clash with each other.
Another alternative would be to remove the need to use intermediate files altogether. With a bit of clever thought I'm sure the function could be rewritten in this way.
Nach, it would be desirable if this limitation could be removed. parsegen could name its intermediate files differently for each invocation, for example to avoid the conflict described above, the intermediate file names could include the family name (either cfg or md), so they wouldn't clash with each other.
Another alternative would be to remove the need to use intermediate files altogether. With a bit of clever thought I'm sure the function could be rewritten in this way.
-
- ZSNES Developer
- Posts: 3904
- Joined: Tue Jul 27, 2004 10:54 pm
- Location: Solar powered park bench
- Contact:
I would love to remove the need for temp files, but at the moment the task is a bit to complex.
I'll look into using tmpnam() or something, and perhaps safe_popen(), something which has been on my agenda for a while now.
I'll look into using tmpnam() or something, and perhaps safe_popen(), something which has been on my agenda for a while now.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
_____________
Insane Coding
-
- ZSNES Shake Shake Prinny
- Posts: 5632
- Joined: Wed Jul 28, 2004 4:15 pm
- Location: PAL50, dood !
Re: Parsegen cannot run concurrently with itself
You "just" need a math parser as good as NASM's, i.e. can take params like:hector wrote:With a bit of clever thought I'm sure the function could be rewritten in this way.
(X+Y)*(Z<<T)
and make it return the result. Feel free.
At the same time... this trick was needed when we wanted parsegen to work directly on ASM files (lots of values initialised using operations), so maybe we can just simplify the notation and not allow any math operands in PSR.
What do you think, Nach ?
皆黙って俺について来い!!
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
Code: Select all
<jmr> bsnes has the most accurate wiki page but it takes forever to load (or something)
-
- ZSNES Developer
- Posts: 3904
- Joined: Tue Jul 27, 2004 10:54 pm
- Location: Solar powered park bench
- Contact:
One option is to disallow math in PSR statements.
But while we're still randomely putting stuff into PSR, I'd like to keep the math evaluation to make it easy to cut and paste.
But while we're still randomely putting stuff into PSR, I'd like to keep the math evaluation to make it easy to cut and paste.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
_____________
Insane Coding
Some small (and completely error intolerant) parsing code that will handle those (x+y)*(z<<t) expressions. If you want to add variable support it's not too hard, just stick an isalpha check beside the isdigit check and add a map<string, int> or something to look up the values. Doing macro expansion would be a bit trickier, but still not too hard.
Yes, I realize this code is hideous, I was trying to remember my parsing classes
.
Yes, I realize this code is hideous, I was trying to remember my parsing classes

Code: Select all
#include <cstdio>
#include <cstdlib>
#include <string>
#include <cmath>
#include <map>
using namespace std;
int fastEval(char *&s, int level) {
if (level == 3) {
if (*s == '(') {
int res = fastEval(++s, 0); s++; return res;
} else if (isdigit(*s)) {
int numc, t;
sscanf(s, "%d%n", &t, &numc);
s += numc;
return t;
} else if (*s == '-') {
return -fastEval(++s, 3);
}
}
int val = fastEval(s,level+1);
while (*s) {
char *org = s;
switch (level) {
case 0: if (!((s[0] == '<' && s[1] == '<') || (s[0] == '>' && s[1] == '>'))) return val; ++s; break;
case 1: if (!strchr("+-", *s)) return val; break;
case 2: if (!strchr("*/", *s)) return val; break;
}
int res = fastEval(++s,level+1);
switch (*org) {
case '<': return val << res; case '>': return val >> res;
case '+': return val + res; case '-': return val - res;
case '*': return val * res; case '/': return val / res;
}
}
return val;
}
void doeval(string s) {
int at = 0;
char tmp[5000],*c;
strcpy(tmp, s.c_str()); c = tmp;
printf("%s = %d\n",s.c_str(),fastEval(c,0));
}
int main() {
doeval("5+-3");
doeval("5+3");
doeval("5+3*4");
doeval("5+3/2");
doeval("(5+3)*4");
doeval("(5+3)/2");
doeval("((5+3)-(-2+3))");
doeval("1<<(5+2)");
return 0;
}
bsnes/src/lib/libstring_math.cpp has a function named uint strmath(const char *);
As long as you decode all of the variable names into actual numbers first, it will convert a string mixed with binary, decimal and hex characters using c++ infinitely-nested brackets and math-type ordering (mul before add, etc) and return a numerical result.
I've been meaning to rewrite it to be a bit cleaner, but it works fine as is. And you don't have to use all of libstring to use it, just that and some abin/hex/dectoi functions (included in libstring.cpp). You're free to use it in PSR if you want.
As long as you decode all of the variable names into actual numbers first, it will convert a string mixed with binary, decimal and hex characters using c++ infinitely-nested brackets and math-type ordering (mul before add, etc) and return a numerical result.
I've been meaning to rewrite it to be a bit cleaner, but it works fine as is. And you don't have to use all of libstring to use it, just that and some abin/hex/dectoi functions (included in libstring.cpp). You're free to use it in PSR if you want.
-
- ZSNES Developer
- Posts: 3904
- Joined: Tue Jul 27, 2004 10:54 pm
- Location: Solar powered park bench
- Contact:
gladius, what you wrote is nice, but I found a bug.
Here's after adding on some features:
Notice the last two cases, they should give the same answer.
However the second to last case skips the -(255<<8&3840). So your code is leaving out additional segments when there are no ().
Here's after adding on some features:
Code: Select all
#include <cstdio>
#include <cstdlib>
#include <string>
#include <cmath>
#include <map>
using namespace std;
int fastEval(char *&s, int level)
{
if (level == 6)
{
if (*s == '(')
{
int res = fastEval(++s, 0); s++; return res;
}
else if (isdigit(*s))
{
int numc, t;
sscanf(s, "%d%n", &t, &numc);
s += numc;
return t;
}
else if (*s == '-')
{
return -fastEval(++s, 6);
}
else if (*s == '~')
{
return ~fastEval(++s, 6);
}
}
int val = fastEval(s,level+1);
while (*s)
{
char *org = s;
switch (level)
{
case 0: if (!strchr("|", *s)) return val; break;
case 1: if (!strchr("^", *s)) return val; break;
case 2: if (!strchr("&", *s)) return val; break;
case 3: if (!((s[0] == '<' && s[1] == '<') || (s[0] == '>' && s[1] == '>'))) return val; ++s; break;
case 4: if (!strchr("+-", *s)) return val; break;
case 5: if (!strchr("*/%", *s)) return val; break;
}
int res = fastEval(++s,level+1);
switch (*org)
{
case '|': return val | res;
case '^': return val ^ res;
case '&': return val & res;
case '<': return val << res;
case '>': return val >> res;
case '+': return val + res;
case '-': return val - res;
case '*': return val * res;
case '/': return val / res;
case '%': return val % res;
}
}
return val;
}
void doeval(string s)
{
char tmp[5000],*c;
strcpy(tmp, s.c_str()); c = tmp;
printf("%s = %d\n",s.c_str(),fastEval(c,0));
}
int main()
{
doeval("5+-3");
doeval("5+3");
doeval("5+3*4");
doeval("5+3/2");
doeval("(5+3)*4");
doeval("(5+3)/2");
doeval("((5+3)-(-2+3))");
doeval("1<<(5+2)");
doeval("(11+27*(25>>1)&31");
doeval("(((5+4)*(10+10)))-(10-~0)-(255<<8&3840)");
doeval("((((5+4)*(10+10)))-(10-~0))-(255<<8&3840)");
return 0;
}
However the second to last case skips the -(255<<8&3840). So your code is leaving out additional segments when there are no ().
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
_____________
Insane Coding
Hmm. If we replace sscanf with something more specific (hex/bin/dec/variable reader) it could work out really well.
Recursion seems to work pretty well compared to my idea which was to "preparse" the string and split it apart by brackets first.
I'll take a look at this code a bit later myself, then.
Recursion seems to work pretty well compared to my idea which was to "preparse" the string and split it apart by brackets first.
I'll take a look at this code a bit later myself, then.
There were a few bugs in there, basically it wouldn't handle multiple operators at the same precedence level back to back (5+3+3 = 8 ). Here is the new (hopefully more bug free
) version.
Edit: another bugfix, strchr searches for the first occurence of the characters defined in a string. We really want to check if the current character is the operator. This could be done with strchr("..",s) == s, but that's O(n^2) for the parser then, this is O(n) now.
byuu: sscanf is pretty powerful if you (ab)use it correctly, it can parse hex, octal, or decimal quite easily. Also, this method of writing a parser is known as a recursive descent parser, it's a classic method of writing hand-written parsers.

Code: Select all
#include <cstdio>
#include <cstdlib>
#include <string>
#include <cmath>
#include <map>
using namespace std;
int fastEval(char *&s, int level)
{
if (level == 6)
{
if (*s == '(')
{
int res = fastEval(++s, 0); s++; return res;
}
else if (isdigit(*s))
{
int numc, t;
sscanf(s, "%d%n", &t, &numc);
s += numc;
return t;
}
else if (*s == '-')
{
return -fastEval(++s, 6);
}
else if (*s == '~')
{
return ~fastEval(++s, 6);
}
}
int val = fastEval(s,level+1);
while (*s)
{
char *org = s;
switch (level)
{
case 0: if (*s != '|') return val; break;
case 1: if (*s != '^') return val; break;
case 2: if (*s != '&') return val; break;
case 3: if (!((s[0] == '<' && s[1] == '<') || (s[0] == '>' && s[1] == '>'))) return val; else ++s; break;
case 4: if (!(*s == '+' || *s == '-')) return val; break;
case 5: if (!(*s == '*' || *s == '/' || *s == '%')) return val; break;
}
int res = fastEval(++s,level+1);
switch (*org)
{
case '|': val |= res; break;
case '^': val ^= res; break;
case '&': val &= res; break;
case '<': val <<= res; break;
case '>': val >>= res; break;
case '+': val += res; break;
case '-': val -= res; break;
case '*': val *= res; break;
case '/': val /= res; break;
case '%': val %= res; break;
}
}
return val;
}
void doeval(string s)
{
char tmp[5000],*c;
strcpy(tmp, s.c_str()); c = tmp;
printf("%s = %d\n",s.c_str(),fastEval(c,0));
}
int main()
{
doeval("5+-3");
doeval("5+3");
doeval("5+3*4");
doeval("5+3/2");
doeval("(5+3)*4");
doeval("(5+3)/2");
doeval("((5+3)-(-2+3))");
doeval("1<<(5+2)");
doeval("(11+27)*(25>>1)&31");
doeval("(((5+4)*(10+10)))-(10-~0)-(255<<8&3840)");
doeval("((((5+4)*(10+10)))-(10-~0))-(255<<8&3840)");
return 0;
}
byuu: sscanf is pretty powerful if you (ab)use it correctly, it can parse hex, octal, or decimal quite easily. Also, this method of writing a parser is known as a recursive descent parser, it's a classic method of writing hand-written parsers.
-
- ZSNES Developer
- Posts: 3904
- Joined: Tue Jul 27, 2004 10:54 pm
- Location: Solar powered park bench
- Contact:
gladius:
Seems to work very nicely now.
Can I use this in parsegen?
Seems to work very nicely now.
Can I use this in parsegen?
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
_____________
Insane Coding
Powerful, but not fast. Lacks binary support ( and who the hell uses octal >_< ), and lacks auto detection. You'd have to manually check s[0] == '0' && s[1] == 'x' to detect hex. Which is pretty much what I do anyway, I just wrapped it into a single transformation.gladius wrote:byuu: sscanf is pretty powerful if you (ab)use it correctly, it can parse hex, octal, or decimal quite easily. Also, this method of writing a parser is known as a recursive descent parser, it's a classic method of writing hand-written parsers.
It's also a lot faster, sscanf has all kinds of fun va_list argument parsing voodoo, string decoding, etc just to convert a string to an integer. A custom implementation would speed things up a good deal, but then, we aren't too worried about speed here anyway, are we?
Anyway, this looks good. Does your code handle proper ordering of math operations? eg
5+2*3=11, and not 21? EDIT: so it does, neat. Now all we need is c++-style boolean support.
eg doeval("5+2 > 6 ? (2 + 3) : 5+2 <= 5 ? 7 : !2");
Heh.
Hmm, not sure I understand the first part. If you pass doeval("5") why is it re-entering the function six additional times to reach level=6 to get an actual return value from the function? :/
I'm also curious if I can use your approach in my string library. My library is basically public domain.
Nach&byuu: Sure, this code is public domain, feel free to use it.
byuu: Yes, there is some recursion for each seperate precedence level in the tree, but the elegance and speed (no extra memory needed, besides on the stack) makes up for it I think.
For the single literal case there is a bit of overhead, but it's hard to get away from. In this simple calculator expression parser you could pretty easily optimize the literal parsing, but at a cost in code complexity I think. There might be a really pretty way of doing it though, I just can't think of one
.
byuu: Yes, there is some recursion for each seperate precedence level in the tree, but the elegance and speed (no extra memory needed, besides on the stack) makes up for it I think.
For the single literal case there is a bit of overhead, but it's hard to get away from. In this simple calculator expression parser you could pretty easily optimize the literal parsing, but at a cost in code complexity I think. There might be a really pretty way of doing it though, I just can't think of one

-
- ZSNES Developer
- Posts: 3904
- Joined: Tue Jul 27, 2004 10:54 pm
- Location: Solar powered park bench
- Contact:
Okay, thanks.
Edit
gladius, I cleaned it up a bit and merged into parsegen, it's working great!
Now I have various issues solved, and parsegen run time went from a couple seconds to instantly for me. Thanks
Edit
gladius, I cleaned it up a bit and merged into parsegen, it's working great!
Now I have various issues solved, and parsegen run time went from a couple seconds to instantly for me. Thanks

May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
_____________
Insane Coding
Well, I thought it would be a bit tricker to do the right-to-left binding of ternary expressions, but it turned out to be pretty easy.
It's even got some basic unit testing now
.
It's even got some basic unit testing now

Code: Select all
#include <cstdio>
#include <string>
using namespace std;
const int maxLevel = 7;
int fastEval(char *&s, int level)
{
if (level == maxLevel)
{
if (*s == '(')
{
int res = fastEval(++s, 0); s++; return res;
}
else if (isdigit(*s))
{
int numc, t;
sscanf(s, "%d%n", &t, &numc);
s += numc;
return t;
}
else if (*s == '-')
{
return -fastEval(++s, maxLevel);
}
else if (*s == '~')
{
return ~fastEval(++s, maxLevel);
}
}
int val = fastEval(s,level+1);
while (*s)
{
char *org = s;
switch (level)
{
case 0: if (*s != '|') return val; break;
case 1: if (*s != '^') return val; break;
case 2: if (*s != '&') return val; break;
case 3: if (!(*s == '<' || *s == '>')) return val; break;
case 4: if (!((s[0] == '<' && s[1] == '<') || (s[0] == '>' && s[1] == '>'))) return val; else ++s; break;
case 5: if (!(*s == '+' || *s == '-')) return val; break;
case 6: if (!(*s == '*' || *s == '/' || *s == '%')) return val; break;
}
int res = fastEval(++s,level+1);
if (level == 3) {
int trueResult = fastEval(++s, level);
int falseResult = fastEval(++s, level);
if (*org == '<') {
if (val < res) val = trueResult; else val = falseResult;
} else {
if (val > res) val = trueResult; else val = falseResult;
}
} else {
switch (*org)
{
case '|': val |= res; break;
case '^': val ^= res; break;
case '&': val &= res; break;
case '<': val <<= res; break;
case '>': val >>= res; break;
case '+': val += res; break;
case '-': val -= res; break;
case '*': val *= res; break;
case '/': val /= res; break;
case '%': val %= res; break;
}
}
}
return val;
}
void doeval(string s, int verify)
{
char tmp[5000],*c;
strcpy(tmp, s.c_str()); c = tmp;
int result = fastEval(c,0);
printf("%s = %d, %s%d\n",s.c_str(), result, result != verify ? "Error, should be: " : "Good: ", verify);
}
int main()
{
doeval("5+-3", 2);
doeval("5+3", 8);
doeval("5+3*4", 17);
doeval("5+3/2", 6);
doeval("(5+3)*4", 32);
doeval("(5+3)/2", 4);
doeval("((5+3)-(-2+3))", 7);
doeval("1<<(5+2)", 128);
doeval("(11+27)*(25>>1)&31", 8);
doeval("(((5+4)*(10+10)))-(10-~0)-(255<<8&3840)", -3671);
doeval("((((5+4)*(10+10)))-(10-~0))-(255<<8&3840)", -3671);
doeval("9<5?3-5:4*2", 8);
doeval("9>5?3-5:4*2", -2);
doeval("9<5?9>5?1:0:4*2", 8);
doeval("9>5?9>5?1:0:4*2", 1);
return 0;
}
Odd, that code doesn't handle ? and : despite being used in doeval, heh.
Let's see... there's :
> >= < <= == != ! ? :
Probably more I'm not recalling off the top of my head. I assume they're all lowest priority (resolved last), but I could be wrong. I have a habit of using ()s way more than is required rather than learning the exact priorities of these expressions.
Then if we want to keep going, we could parse typecasts :P
doeval("bool(31 & 7) ? 1 : 0") -- but I think that's a bit ridiculous, honestly.
I'm mainly wanting the comparator operators for my cross assembler, I'm going to replace the sscanf with a special function so I can detect labels inside expressions and convert them to numbers automatically, thusly yielding :
lda.w #(variable_x>8?2:1)
I think I'll make the main function uint strmath(const char *expression, decoder_proc decoder = strmath_decode);
uint strmath_decode(const char *expression, uint &offset);
Then you can specify your own function that can handle decoding variables, or let the function use the default one that only handles binary, hex, decimal and maybe octal for the masochists.
Let's see... there's :
> >= < <= == != ! ? :
Probably more I'm not recalling off the top of my head. I assume they're all lowest priority (resolved last), but I could be wrong. I have a habit of using ()s way more than is required rather than learning the exact priorities of these expressions.
Then if we want to keep going, we could parse typecasts :P
doeval("bool(31 & 7) ? 1 : 0") -- but I think that's a bit ridiculous, honestly.
I'm mainly wanting the comparator operators for my cross assembler, I'm going to replace the sscanf with a special function so I can detect labels inside expressions and convert them to numbers automatically, thusly yielding :
lda.w #(variable_x>8?2:1)
I think I'll make the main function uint strmath(const char *expression, decoder_proc decoder = strmath_decode);
uint strmath_decode(const char *expression, uint &offset);
Then you can specify your own function that can handle decoding variables, or let the function use the default one that only handles binary, hex, decimal and maybe octal for the masochists.
Yes, the ? and : are syntatic sugar as far as this parser is concerned
. Adding error handling and output wouldn't be hard, at which point you could verifiy those types of things.
http://www.difranco.net/cop2220/op-prec.htm is a good reference for the C operator precedence rules. < and > are in the correct precedence already.
You'd probably want to write it in a more traditional manner, that instead of using the level variable had a different function for each level. That way it more clearly represents the grammar that is being parsed.
For example a basic calculator grammar might look like:
(shamelessly copied from the wikipedia article below)
value <- '-' expr | '(' expr ')' | [0-9]+
product <- expr (('/' | '*') expr)*
sum <- expr (('-' | '+') expr)*
expr <- sum | product | value
You can now see why I start at level 0 and go up to 6 (or whatever) to finally parse a literal. The structure of the grammar implies how the parser is written.
See the wikipedia article on parsing expression grammars http://en.wikipedia.org/wiki/Parsing_expression_grammar for a decent overview of this.

http://www.difranco.net/cop2220/op-prec.htm is a good reference for the C operator precedence rules. < and > are in the correct precedence already.
You'd probably want to write it in a more traditional manner, that instead of using the level variable had a different function for each level. That way it more clearly represents the grammar that is being parsed.
For example a basic calculator grammar might look like:
(shamelessly copied from the wikipedia article below)
value <- '-' expr | '(' expr ')' | [0-9]+
product <- expr (('/' | '*') expr)*
sum <- expr (('-' | '+') expr)*
expr <- sum | product | value
You can now see why I start at level 0 and go up to 6 (or whatever) to finally parse a literal. The structure of the grammar implies how the parser is written.
See the wikipedia article on parsing expression grammars http://en.wikipedia.org/wiki/Parsing_expression_grammar for a decent overview of this.
-
- ZSNES Shake Shake Prinny
- Posts: 5632
- Joined: Wed Jul 28, 2004 4:15 pm
- Location: PAL50, dood !
Updated thread title accordingly.
皆黙って俺について来い!!
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
Code: Select all
<jmr> bsnes has the most accurate wiki page but it takes forever to load (or something)
Oh, god... I just realized what you were doing... char *&s... it always amazes me how after ten years of programming in c/c++, I still manage to come across new... "tricks", like that. I would've never thought that'd be possible.
You can optimize this a bit by replacing doeval with this, by the way :
That apparently creates a copy of the pointer when calling doeval (since it lacks &), that can then be passed to fastEval and modified without affecting the original const char string passed to doeval, ala :
Anyway, the right-hand side stuff looks really shaky right now :/
Without any kind of verification at all, how are you even terminating the groups? eg try :
doeval("5 < 7 ? 2 + 3 : 1");
It looks like that will return 2 and not 5. But I didn't actually try that just yet. No compiler on this PC.
Otherwise, the more I study the code the more ingenious it seems, nice work :D
I had come up with something like :
But obviously, without the level parameter, it cannot correctly order the operands to determine what gets combined first :/
Also, we can add :
With relative ease. Test case : doeval("!!5<<1", 2);
You can optimize this a bit by replacing doeval with this, by the way :
Code: Select all
int fastEval(const char *&s, int level = 0); int doeval(const char *s) { return fastEval(s); }
Code: Select all
const char str[] = "5+-1";
int r = doeval(str);
printf("%d\n", r);
printf("%s\n", str); //still prints "5+-1"
Without any kind of verification at all, how are you even terminating the groups? eg try :
doeval("5 < 7 ? 2 + 3 : 1");
It looks like that will return 2 and not 5. But I didn't actually try that just yet. No compiler on this PC.
Otherwise, the more I study the code the more ingenious it seems, nice work :D
I had come up with something like :
Code: Select all
int strmath(char *&s, int lhs = 0) {
int result = lhs;
if(*s == '(') {
result = strmath(++s, result);
s++;
}
if(*s == '+') {
result += strmath(++s, result);
}
while(*s && *s != ')') {
if(isdigit(*s) == false)
...
}
Also, we can add :
Code: Select all
else if (*s == '!')
{
return !fastEval(++s, maxLevel);
}
This works correctly (it outputs 5). Basically what happens is it evaluates the 5 < 7, then it sits around waiting for the two functions to return. The next call to fastEval starts at the 2, then goes all the way to level 6 to evaluate the 2, then it falls slowly back through the levels returning val each time until it reaches level 5. At that point it matches a '+', so it doesn't return, and evaluates the expression as an addition. Then it does the whole cycle over again, but a ':' does not match any valid operators so it falls all the way out, then we return 5 from the first call to fastEval, with s pointing at the ':'.byuu wrote:Without any kind of verification at all, how are you even terminating the groups? eg try :
doeval("5 < 7 ? 2 + 3 : 1");
It looks like that will return 2 and not 5. But I didn't actually try that just yet. No compiler on this PC.
Btw, there was a small bug with the ternary expressions, trueResult and falseResult should both use fastEval(++s, 0), not fastEval(++s, level). Now an expression like "9 > 5 ? 1 | 2 : 0" correctly outputs 3.
Hmm, so then what happens if you evaluate an expression with relational comparisons and without a ternary conditional? eg :
"5>2<<1?3:4" == 3?
Ok, here's my attempt :
It's your code gladius, but I added a few more things to it. Unary plus, unary logical negation, all six relational comparisons, and logical and + or. The only other one I think is appropriate for a parser is the ternary conditional, but ouch. Getting that in there completely proper seems rather difficult. I'll try anyway, though.
Hmm, I kind of feel like adding logical exclusive or for the hell of it, heh.
x^^y = !(x||y)
I wonder if anyone actually uses a logical exclusive or in any other languages.
Also, is there any specific reasoning why the bitwise/logical and/or/xor operators are all on separate levels? I realize that's the way the doc explains it, just curious why it doesn't work on the same level like + and - or *, / and %. They're basically the same thing, and if you want to do stuff like x||y&&x||z, then just use brackets x.x
Would make the code a good deal less recursive.
EDIT: updated code to include ternary expressions. Currently broken, though.
"5>2<<1?3:4" == 3?
Ok, here's my attempt :
Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define maxlevel 11
int strmath_rdp(const char *&s, int level = 0) {
if(level == maxlevel) {
if(*s == '(') {
int result = strmath_rdp(++s, 0);
s++;
return result;
} else if(*s == '+') {
return +strmath_rdp(++s, maxlevel);
} else if(*s == '-') {
return -strmath_rdp(++s, maxlevel);
} else if(*s == '!') {
return !strmath_rdp(++s, maxlevel);
} else if(*s == '~') {
return ~strmath_rdp(++s, maxlevel);
} else if(*s >= '0' && *s <= '9') {
int num, len;
sscanf(s, "%d%n", &num, &len);
s += len;
return num;
}
}
int lhs = strmath_rdp(s, level + 1);
while(*s) {
const char *org = s;
int a = *org;
int b = *org ? *(org + 1) : 0;
switch(level) {
case 0:
if(a == '?')break;
return lhs;
case 1:
if(a == '|' && b == '|') { s++; break; }
return lhs;
case 2:
if(a == '&' && b == '&') { s++; break; }
return lhs;
case 3:
if(a == '|' && b != '|')break;
return lhs;
case 4:
if(a == '^')break;
return lhs;
case 5:
if(a == '&' && b != '&')break;
return lhs;
case 6:
if(a == '=' && b == '=') { s++; break; }
if(a == '!' && b == '=') { s++; break; }
return lhs;
case 7:
if(a == '<' && b == '=') { s++; break; }
if(a == '>' && b == '=') { s++; break; }
if(a == '<')break;
if(a == '>')break;
return lhs;
case 8:
if(a == '<' && b == '<') { s++; break; }
if(a == '>' && b == '>') { s++; break; }
return lhs;
case 9:
if(a == '+')break;
if(a == '-')break;
return lhs;
case 10:
if(a == '*')break;
if(a == '/')break;
if(a == '%')break;
return lhs;
}
int rhs = strmath_rdp(++s, level + 1);
a = *org;
b = *org ? *(org + 1) : 0;
if(a == '?') {
int tr = strmath_rdp(++s, level);
int fr = strmath_rdp(++s, level);
printf("{%d,%d,%d}\n", lhs, tr, fr);
lhs = (lhs) ? tr : fr;
}
else if(a == '|' && b == '|') lhs = (lhs || rhs);
else if(a == '&' && b == '&') lhs = (lhs && rhs);
else if(a == '|' && b != '|') lhs |= rhs;
else if(a == '^') lhs ^= rhs;
else if(a == '&' && b != '&') lhs &= rhs;
else if(a == '=' && b == '=') lhs = (lhs == rhs);
else if(a == '!' && b == '=') lhs = (lhs != rhs);
else if(a == '<' && b == '=') lhs = (lhs <= rhs);
else if(a == '>' && b == '=') lhs = (lhs >= rhs);
else if(a == '<' && b != '<') lhs = (lhs < rhs);
else if(a == '>' && b != '>') lhs = (lhs > rhs);
else if(a == '<' && b == '<') lhs <<= rhs;
else if(a == '>' && b == '>') lhs >>= rhs;
else if(a == '+') lhs += rhs;
else if(a == '-') lhs -= rhs;
else if(a == '*') lhs *= rhs;
else if(a == '/') lhs /= rhs;
else if(a == '%') lhs %= rhs;
}
return lhs;
}
#undef maxlevel
int strmath(const char *eval) {
return strmath_rdp(eval);
}
int main() {
printf("%10s = %5d, %5d\n", "5+3*7", strmath("5+3*7"), 5+3*7);
printf("%10s = %5d, %5d\n", "+3-2", strmath("+3-2"), +3-2);
printf("%10s = %5d, %5d\n", "0||1", strmath("0||1"), 0||1);
printf("%10s = %5d, %5d\n", "2==3", strmath("2==3"), 2==3);
printf("%10s = %5d, %5d\n", "!(3>=2)", strmath("!(3>=2)"), !(3>=2));
printf("%10s = %5d, %5d\n", "2<<4", strmath("2<<4"), 2<<4);
printf("%10s = %5d, %5d\n", "1>2?3:4", strmath("1>2?3:4"), 1>2?3:4);
getch();
return 0;
}
Hmm, I kind of feel like adding logical exclusive or for the hell of it, heh.
x^^y = !(x||y)
I wonder if anyone actually uses a logical exclusive or in any other languages.
Also, is there any specific reasoning why the bitwise/logical and/or/xor operators are all on separate levels? I realize that's the way the doc explains it, just curious why it doesn't work on the same level like + and - or *, / and %. They're basically the same thing, and if you want to do stuff like x||y&&x||z, then just use brackets x.x
Would make the code a good deal less recursive.
EDIT: updated code to include ternary expressions. Currently broken, though.
Last edited by byuu on Wed Jun 28, 2006 8:10 pm, edited 2 times in total.
-
- ZSNES Shake Shake Prinny
- Posts: 5632
- Joined: Wed Jul 28, 2004 4:15 pm
- Location: PAL50, dood !
You've not made exclusive or (XOR), you've made NOR. Quite a difference.byuu wrote:Hmm, I kind of feel like adding logical exclusive or for the hell of it, heh.
x^^y = !(x||y)
Code: Select all
XOR|0 1
---+---
0|0 1
1|1 0
NOR|0 1
---+---
0|1 0
1|0 0
x^^y = (x||y)^(x&&y)
皆黙って俺について来い!!
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
Code: Select all
<jmr> bsnes has the most accurate wiki page but it takes forever to load (or something)
Oops, NAND you're right :P
Well in that case, x^^y == !x != !y.
EDIT: ok, added code above that should do ternary expressions, but it doesn't. The printf prints out {0,4,32} for "1>2?3:4". And I have no idea why. Too hard to follow the logic through this recursive tree x.x
Well in that case, x^^y == !x != !y.
EDIT: ok, added code above that should do ternary expressions, but it doesn't. The printf prints out {0,4,32} for "1>2?3:4". And I have no idea why. Too hard to follow the logic through this recursive tree x.x
Last edited by byuu on Wed Jun 28, 2006 8:11 pm, edited 1 time in total.