The purpose of this code is to evaluate simple integer expressions that make use of the C arithmetic and bitwise operators while respecting C operator precedence and parenthesis. I wrote this code with portability in mind, making no use of implementation defined, or undefined constructs to my knowledge. However there are a lot of duplicate patterns here, and if someone could provide some insight as to how to rewrite the code more compactly, and confirm that my code is in fact portable standard C, that would be great.
The code works by dividing the expression into groups separated by the lowest precedence operator, and then dividing those groups into groups separated by the second lowest precedence operator, and so on.
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <setjmp.h>
static jmp_buf jmp;
static unsigned long p6(const char **);
static unsigned long p0(const char **const str) {
if (isdigit(**str)) return strtoul(*str, (char **)str, 0);
else switch (*(*str)++) {
case '+':
return +p0(str);
case '-':
return -p0(str);
case '~':
return ~p0(str);
case '(': {
const unsigned long r = p6(str);
if (*(*str)++ == ')') return r;
}
}
longjmp(jmp, errno = EINVAL);
}
static unsigned long p1(const char **const str) {
unsigned long r = p0(str);
for (;;) {
switch (**str) {
case '*':
++*str;
r *= p0(str);
continue;
case '/':
++*str;
r /= p0(str);
continue;
case '%':
++*str;
r %= p0(str);
continue;
}
return r;
}
}
static unsigned long p2(const char **const str) {
unsigned long r = p1(str);
for (;;) {
switch (**str) {
case '+':
++*str;
r += p1(str);
continue;
case '-':
++*str;
r -= p1(str);
continue;
}
return r;
}
}
static unsigned long p3(const char **const str) {
unsigned long r = p2(str);
for (;;) {
switch (**str) {
case '<':
++*str;
r <<= p2(str);
continue;
case '>':
++*str;
r >>= p2(str);
continue;
}
return r;
}
}
static unsigned long p4(const char **const str) {
unsigned long r = p3(str);
for (; **str == '&'; r &= p3(str)) ++*str;
return r;
}
static unsigned long p5(const char **const str) {
unsigned long r = p4(str);
for (; **str == '^'; r ^= p4(str)) ++*str;
return r;
}
static unsigned long p6(const char **const str) {
unsigned long r = p5(str);
for (; **str == '|'; r |= p5(str)) ++*str;
return r;
}
unsigned long parse(const char **const str) {
return setjmp(jmp) ? 0 : p6(str);
}
#include <stdio.h>
int main(const int argc, const char **argv) {
while (*++argv) {
const unsigned long r = parse(&(const char *){*argv});
if (errno) {
perror(*argv);
errno = 0;
continue;
}
if (printf("%lu\n", r) < 0)
return errno;
}
return 0;
}