/*
64 bit integer arithmetic expression interpreter by Igor van den Hoven.
No restrictions on use. 11/24/2008
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
/*
Operator Priority Function
------------------------------------------------
! 0 logical not
~ 0 bitwise not
* 1 integer multiply
/ 1 integer divide
% 1 integer modulo
d 1 integer random dice roll
+ 2 integer addition
- 2 integer subtraction
<< 3 bitwise shift
>> 3 bitwise shift
> 4 logical greater than
>= 4 logical greater than or equal
< 4 logical less than
<= 4 logical less than or equal
== 5 logical equal (can use wildcards)
!= 5 logical not equal (can use wildcards)
& 6 bitwise and
^ 7 bitwise xor
| 8 bitwise or
&& 9 logical and
^^ 10 logical xor
|| 11 logical or
Parentheses work as expected, strings must be enclosed in quotes and
are evaluated with strcmp.
Example: mathexp("(1+2) * 3 << 4"); would return "144".
*/
typedef struct math_data MATH_DATA;
struct math_data
{
MATH_DATA * next;
MATH_DATA * prev;
int lvl;
int prt;
char * str;
};
#define LINK(link, first, last, next, prev) \
{ \
if (!(first)) \
{ \
(first) = (link); \
} \
else \
{ \
(last)->next = (link); \
} \
(link)->next = NULL; \
(link)->prev = (last); \
(last) = (link); \
}
#define UNLINK(link, first, last, next, prev) \
{ \
if (!(link)->prev) \
{ \
(first) = (link)->next; \
} \
else \
{ \
(link)->prev->next = (link)->next; \
} \
if (!(link)->next) \
{ \
(last) = (link)->prev; \
} \
else \
{ \
(link)->next->prev = (link)->prev; \
} \
(link)->next = NULL; \
(link)->prev = NULL; \
}
#define FALSE 0
#define TRUE 1
long long mathexp(char *str);
long long mathexp_tokenize(char *str);
void mathexp_level(MATH_DATA *node);
void mathexp_compute(MATH_DATA *node);
long long mathtoi(const char *str);
long long mathcmp(const char *left, const char *right);
long long mathdice(const char *left, const char *right);
#define EXP_VARIABLE 0
#define EXP_STRING 1
#define EXP_OPERATOR 2
#define EXP_PR_DICE 0
#define EXP_PR_INTMUL 1
#define EXP_PR_INTADD 2
#define EXP_PR_BITSHIFT 3
#define EXP_PR_LOGLTGT 4
#define EXP_PR_LOGCOMP 5
#define EXP_PR_BITAND 6
#define EXP_PR_BITXOR 7
#define EXP_PR_BITOR 8
#define EXP_PR_LOGAND 9
#define EXP_PR_LOGXOR 10
#define EXP_PR_LOGOR 11
#define EXP_PR_VAR 12
#define EXP_PR_LVL 13
MATH_DATA *mathnode_f;
MATH_DATA *mathnode_l;
MATH_DATA *mathnode_s;
MATH_DATA *mathnode_e;
MATH_DATA *mathnode_n;
int main(int argc, char **argv)
{
srand48(time(NULL));
if (argv[1])
{
printf("mathexp: %s = %lld\n", argv[1], mathexp(argv[1]));
}
else
{
printf("Syntax: %s 'expression'\n", argv[0]);
}
return 0;
}
long long mathexp(char *str)
{
MATH_DATA *node;
if (mathexp_tokenize(str) == FALSE)
{
if (mathnode_f == NULL)
{
/* invalid input error */
}
return 0;
}
node = mathnode_f;
while (node->prev || node->next)
{
if (node->next == NULL || node->next->lvl < node->lvl)
{
mathexp_level(node);
node = mathnode_f;
}
else
{
node = node->next;
}
}
return mathtoi(node->str);
}
void add_mathnode(int lvl, int prt, char *buf)
{
MATH_DATA *node;
node = calloc(1, sizeof(MATH_DATA));
node->lvl = lvl;
node->prt = prt;
node->str = strdup(buf);
LINK(node, mathnode_f, mathnode_l, next, prev);
return;
}
void del_mathnode(MATH_DATA *node)
{
free(node->str);
UNLINK(node, mathnode_f, mathnode_l, next, prev);
free(node);
return;
}
#define MATH_NODE(buffercheck, priority, newstatus) \
{ \
if (buffercheck && pta == buf) \
{ \
return FALSE; \
} \
*pta = 0; \
add_mathnode(level, priority, buf); \
status = newstatus; \
pta = buf; \
}
long long mathexp_tokenize(char *str)
{
char buf[2000], *pti, *pta;
int level, status;
level = 0;
status = EXP_VARIABLE;
pta = (char *) buf;
pti = (char *) str;
while (mathnode_f)
{
del_mathnode(mathnode_f);
}
mathnode_f = mathnode_l = NULL;
while (*pti)
{
switch (status)
{
case EXP_VARIABLE:
switch (*pti)
{
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
*pta++ = *pti++;
break;
case '!':
case '~':
case '+':
case '-':
if (pta != buf)
{
MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR);
}
else
{
*pta++ = *pti++;
}
break;
case '"':
if (pta != buf)
{
/* string character found inside an integer */
return FALSE;
}
*pta++ = *pti++;
status = EXP_STRING;
break;
case '(':
if (pta != buf)
{
/* parenthesis found inside an integer */
return FALSE;
}
*pta++ = *pti++;
MATH_NODE(TRUE, EXP_PR_LVL, EXP_VARIABLE);
level++;
break;
case ' ':
pti++;
break;
default:
MATH_NODE(TRUE, EXP_PR_VAR, EXP_OPERATOR);
break;
}
break;
case EXP_STRING:
switch (*pti)
{
case '"':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR);
break;
default:
*pta++ = *pti++;
break;
}
break;
case EXP_OPERATOR:
switch (*pti)
{
case ' ':
pti++;
break;
case ')':
*pta++ = *pti++;
level--;
MATH_NODE(TRUE, EXP_PR_LVL, EXP_OPERATOR);
break;
case 'd':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_DICE, EXP_VARIABLE);
break;
case '*':
case '/':
case '%':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_INTMUL, EXP_VARIABLE);
break;
case '+':
case '-':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_INTADD, EXP_VARIABLE);
break;
case '<':
*pta++ = *pti++;
switch (*pti)
{
case '<':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_BITSHIFT, EXP_VARIABLE);
break;
case '=':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
break;
default:
MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
break;
}
break;
case '>':
*pta++ = *pti++;
switch (*pti)
{
case '>':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_BITSHIFT, EXP_VARIABLE);
break;
case '=':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
break;
default:
MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
break;
}
break;
case '&':
*pta++ = *pti++;
switch (*pti)
{
case '&':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_LOGAND, EXP_VARIABLE);
break;
default:
MATH_NODE(FALSE, EXP_PR_BITAND, EXP_VARIABLE);
break;
}
break;
case '^':
*pta++ = *pti++;
switch (*pti)
{
case '^':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_LOGXOR, EXP_VARIABLE);
break;
default:
MATH_NODE(FALSE, EXP_PR_BITXOR, EXP_VARIABLE);
break;
}
break;
case '|':
*pta++ = *pti++;
switch (*pti)
{
case '|':
*pta++ = *pti++;
MATH_NODE(FALSE, 7, EXP_VARIABLE);
break;
default:
*pta++ = *pti++;
MATH_NODE(FALSE, 5, EXP_VARIABLE);
break;
}
break;
case '=':
case '!':
*pta++ = *pti++;
switch (*pti)
{
case '=':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_LOGCOMP, EXP_VARIABLE);
break;
default:
/* unknown operator */
return FALSE;
}
break;
default:
/* unknown operator */
return FALSE;
}
break;
}
}
if (level != 0)
{
/* unmatched parenthesis */
return FALSE;
}
if (status != EXP_OPERATOR)
{
MATH_NODE(TRUE, EXP_PR_VAR, EXP_OPERATOR);
}
return TRUE;
}
void mathexp_level(MATH_DATA *node)
{
int priority;
mathnode_e = node;
while (node->prev)
{
if (node->prev->lvl < node->lvl)
{
break;
}
node = node->prev;
}
mathnode_s = node;
for (priority = 0 ; priority < EXP_PR_VAR ; priority++)
{
for (node = mathnode_s ; node ; node = node->next)
{
if (node->prt == priority)
{
mathexp_compute(node);
}
if (node == mathnode_e)
{
break;
}
}
}
node = mathnode_s;
while (node->prev && node->next)
{
if (node->prev->prt == EXP_PR_LVL && node->next->prt == EXP_PR_LVL)
{
node->lvl = node->next->lvl;
del_mathnode(node->next);
del_mathnode(node->prev);
}
else
{
break;
}
}
return;
}
void mathexp_compute(MATH_DATA *node)
{
char temp[2000];
long long value;
switch (node->str[0])
{
case 'd':
if (mathtoi(node->next->str) <= 0)
{
/* invalid dice */
value = 0;
}
else
{
value = mathdice(node->prev->str, node->next->str);
}
break;
case '*':
value = mathtoi(node->prev->str) * mathtoi(node->next->str);
break;
case '/':
if (mathtoi(node->next->str) == 0)
{
/* division zero */
value = mathtoi(node->prev->str) / 1;
}
else
{
value = mathtoi(node->prev->str) / mathtoi(node->next->str);
}
break;
case '%':
if (mathtoi(node->next->str) == 0)
{
/* division zero */
value = mathtoi(node->prev->str) / 1;
}
else
{
value = mathtoi(node->prev->str) % mathtoi(node->next->str);
}
break;
case '+':
value = mathtoi(node->prev->str) + mathtoi(node->next->str);
break;
case '-':
value = mathtoi(node->prev->str) - mathtoi(node->next->str);
break;
case '<':
switch (node->str[1])
{
case '=':
value = mathcmp(node->prev->str, node->next->str) <= 0;
break;
case '<':
value = mathtoi(node->prev->str) << mathtoi(node->next->str);
break;
default:
value = mathcmp(node->prev->str, node->next->str) < 0;
break;
}
break;
case '>':
switch (node->str[1])
{
case '=':
value = mathcmp(node->prev->str, node->next->str) >= 0;
break;
case '>':
value = mathtoi(node->prev->str) >> mathtoi(node->next->str);
break;
default:
value = mathcmp(node->prev->str, node->next->str) > 0;
break;
}
break;
case '&':
switch (node->str[1])
{
case '&':
value = mathtoi(node->prev->str) && mathtoi(node->next->str);
break;
default:
value = mathtoi(node->prev->str) & mathtoi(node->next->str);
break;
}
break;
case '^':
switch (node->str[1])
{
case '^':
value = ((mathtoi(node->prev->str) || mathtoi(node->next->str)) && (!mathtoi(node->prev->str) != !mathtoi(node->next->str)));
break;
default:
value = mathtoi(node->prev->str) ^ mathtoi(node->next->str);
break;
}
break;
case '|':
switch (node->str[1])
{
case '|':
value = mathtoi(node->prev->str) || mathtoi(node->next->str);
break;
default:
value = mathtoi(node->prev->str) | mathtoi(node->next->str);
break;
}
break;
case '=':
value = (mathcmp(node->next->str, node->prev->str) == 0);
break;
case '!':
value = (mathcmp(node->next->str, node->prev->str) != 0);
break;
default:
value = 0;
break;
}
if (node->prev == mathnode_s)
{
mathnode_s = node;
}
if (node->next == mathnode_e)
{
mathnode_e = node;
}
del_mathnode(node->next);
del_mathnode(node->prev);
node->prt = EXP_PR_VAR;
sprintf(temp, "%lld", value);
free(node->str);
node->str = strdup(temp);
}
long long mathtoi(const char *str)
{
switch (str[0])
{
case '!':
return !atoll(&str[1]);
case '~':
return ~atoll(&str[1]);
case '+':
return +atoll(&str[1]);
case '-':
return -atoll(&str[1]);
default:
return atoll(str);
}
}
long long mathcmp(const char *left, const char *right)
{
switch (left[0])
{
case '"':
return strcmp(left, right);
default:
return mathtoi(left) - mathtoi(right);
}
}
long long mathdice(const char *left, const char *right)
{
long long cnt, numdice, sizedice, sum;
numdice = mathtoi(left);
sizedice = mathtoi(right);
for (cnt = sum = 0 ; cnt < numdice ; cnt++)
{
sum += lrand48() % sizedice + 1;
}
return sum;
}