Parsegen can now run concurrently with itself, w00t

Strictly for discussing ZSNES development and for submitting code. You can also join us on IRC at irc.libera.chat in #zsnes.
Please, no requests here.

Moderator: ZSNES Mods

hector
Rookie
Posts: 49
Joined: Fri Jun 09, 2006 12:47 am

Parsegen can now run concurrently with itself, w00t

Post by hector »

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
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

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.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
grinvader
ZSNES Shake Shake Prinny
Posts: 5632
Joined: Wed Jul 28, 2004 4:15 pm
Location: PAL50, dood !

Re: Parsegen cannot run concurrently with itself

Post by grinvader »

hector wrote:With a bit of clever thought I'm sure the function could be rewritten in this way.
You "just" need a math parser as good as NASM's, i.e. can take params like:
(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 ?
皆黙って俺について来い!!

Code: Select all

<jmr> bsnes has the most accurate wiki page but it takes forever to load (or something)
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

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.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
gladius
Rookie
Posts: 16
Joined: Wed Nov 30, 2005 8:10 am
Contact:

Post by gladius »

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 :).

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;
}
byuu

Post by byuu »

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.
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

gladius, what you wrote is nice, but I found a bug.

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;
}
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 ().
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
byuu

Post by byuu »

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.
gladius
Rookie
Posts: 16
Joined: Wed Nov 30, 2005 8:10 am
Contact:

Post by gladius »

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.

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; 
} 
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.
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

gladius:
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
byuu

Post by byuu »

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.
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.

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.
gladius
Rookie
Posts: 16
Joined: Wed Nov 30, 2005 8:10 am
Contact:

Post by gladius »

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 :).
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

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 :)
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
gladius
Rookie
Posts: 16
Joined: Wed Nov 30, 2005 8:10 am
Contact:

Post by gladius »

Glad to hear it worked out well :).
Poobah
Lurker
Posts: 164
Joined: Sun Sep 25, 2005 12:59 pm

Post by Poobah »

byuu wrote:Now all we need is c++-style boolean support.
eg doeval("5+2 > 6 ? (2 + 3) : 5+2 <= 5 ? 7 : !2");
Can't C do that sort of stuff aswell?
byuu

Post by byuu »

Share the enhancements, Nach :)

Poobah, yes c and c++ do this. Evaluating the arguments in a string isn't supported by that function. Not a big deal though, they're easy to add. I'll add them once I fully understand the function.
x >= y -> val = bool(val >= y), !x -> val = !val, etc.
gladius
Rookie
Posts: 16
Joined: Wed Nov 30, 2005 8:10 am
Contact:

Post by gladius »

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 ;).

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; 
} 
byuu

Post by byuu »

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.
gladius
Rookie
Posts: 16
Joined: Wed Nov 30, 2005 8:10 am
Contact:

Post by gladius »

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.
grinvader
ZSNES Shake Shake Prinny
Posts: 5632
Joined: Wed Jul 28, 2004 4:15 pm
Location: PAL50, dood !

Post by grinvader »

Updated thread title accordingly.
皆黙って俺について来い!!

Code: Select all

<jmr> bsnes has the most accurate wiki page but it takes forever to load (or something)
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
byuu

Post by byuu »

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 :

Code: Select all

int fastEval(const char *&s, int level = 0); int doeval(const char *s) { return fastEval(s); }
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 :

Code: Select all

const char str[] = "5+-1";
int r = doeval(str);
  printf("%d\n", r);
  printf("%s\n", str); //still prints "5+-1"
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 :

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)
      ...
  }
But obviously, without the level parameter, it cannot correctly order the operands to determine what gets combined first :/

Also, we can add :

Code: Select all

      else if (*s == '!')
      {
         return !fastEval(++s, maxLevel);
      }
With relative ease. Test case : doeval("!!5<<1", 2);
gladius
Rookie
Posts: 16
Joined: Wed Nov 30, 2005 8:10 am
Contact:

Post by gladius »

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.
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 ':'.

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.
byuu

Post by byuu »

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 :

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;
}
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.
Last edited by byuu on Wed Jun 28, 2006 8:10 pm, edited 2 times in total.
grinvader
ZSNES Shake Shake Prinny
Posts: 5632
Joined: Wed Jul 28, 2004 4:15 pm
Location: PAL50, dood !

Post by grinvader »

byuu wrote:Hmm, I kind of feel like adding logical exclusive or for the hell of it, heh.
x^^y = !(x||y)
You've not made exclusive or (XOR), you've made NOR. Quite a difference.

Code: Select all

XOR|0 1
---+---
  0|0 1
  1|1 0

NOR|0 1
---+---
  0|1 0
  1|0 0
For kicks, a logical XOR would look more like:
x^^y = (x||y)^(x&&y)
皆黙って俺について来い!!

Code: Select all

<jmr> bsnes has the most accurate wiki page but it takes forever to load (or something)
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
byuu

Post by byuu »

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
Last edited by byuu on Wed Jun 28, 2006 8:11 pm, edited 1 time in total.
Post Reply