#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <ctype.h>
#include "lex.h"
#define USE_LONG_LONG
#ifdef USE_LONG_LONG
#define LONG long long
#define ULONG unsigned long long
#define STRTOUL strtoull
#define STR1 "%20llu 0x%.16llx signed: %20lld"
#define STR2 "%20llu 0x%.16llx"
#define STR3 " char: "
#define STR4 "%20lu 0x%-16.8lx signed: %20ld"
#define STR5 "%20lu 0x%-16.8lx"
#else
#define LONG long
#define ULONG unsigned long
#define STRTOUL strtoul
#define STR1 "%10lu\t 0x%.8lx\t signed: %10ld"
#define STR2 "%10lu\t 0x%.8lx"
#define STR3 " char: "
#define STR4 STR1
#define STR5 STR2
#endif
ULONG parse_expression(char *str);
ULONG assignment_expr(char **str);
ULONG do_assignment_operator(char **str, char *var_name);
ULONG logical_or_expr(char **str);
ULONG logical_and_expr(char **str);
ULONG or_expr(char **str);
ULONG xor_expr(char **str);
ULONG and_expr(char **str);
ULONG equality_expr(char **str);
ULONG relational_expr(char **str);
ULONG shift_expr(char **str);
ULONG add_expression(char **str);
ULONG term(char **str);
ULONG factor(char **str);
ULONG get_value(char **str);
int get_var(char *name, ULONG *val);
void set_var(char *name, ULONG val);
void do_input(void);
char *skipwhite(char *str);
typedef struct variable
{
char *name;
ULONG value;
struct variable *next;
}variable;
variable dummy = { NULL, 0L, NULL };
variable *vars=&dummy;
variable *lookup_var(char *name);
variable *add_var(char *name, ULONG value);
char *get_var_name(char **input_str);
void parse_args(int argc, char *argv[]);
int (*set_var_lookup_hook(int (*func)(char *name, ULONG *val)))
(char *name, ULONG *val);
ULONG last_result = 0;
static int
special_vars(char *name, ULONG *val)
{
if (strcmp(name, "time") == 0)
*val = (ULONG)time(NULL);
else if (strcmp(name, "rand") == 0)
*val = (ULONG)rand();
else if (strcmp(name, "dbg") == 0)
*val = 0x82969;
else
return 0;
return 1;
}
int
main(int argc, char *argv[])
{
set_var_lookup_hook(special_vars);
if (argc > 1)
parse_args(argc, argv);
else
do_input();
return 0;
}
static void
print_result(ULONG value)
{
int i;
ULONG ch, shift;
if ((signed LONG)value < 0)
{
if ((signed LONG)value < (signed LONG)(LONG_MIN))
printf(STR1, value, value, value);
else
printf(STR4, (long)value, (long)value, (long)value);
}
else if ((ULONG)value <= (ULONG)ULONG_MAX)
printf(STR5, (long)value, (long)value);
else
printf(STR2, value, value);
printf(STR3);
for(i=sizeof(ULONG)-1; i >= 0; i--)
{
shift = i * 8;
ch = ((ULONG)value & ((ULONG)0xff << shift)) >> shift;
if (isprint((int)ch))
printf("%c", (char)(ch));
else
printf(".");
}
printf("\n");
}
void
parse_args(int argc, char *argv[])
{
int i, len;
char *buff;
ULONG value;
for(i=1, len=0; i < argc; i++)
len += strlen(argv[i]);
len++;
buff = malloc(len*sizeof(char));
if (buff == NULL)
return;
buff[0] = '\0';
while (--argc > 0)
{
strcat(buff, *++argv);
}
value = parse_expression(buff);
print_result(value);
free(buff);
}
void
do_input(void)
{
ULONG value;
char buff[256], *ptr;
while(fgets(buff, 256, stdin) != NULL)
{
if (buff[0] != '\0' && buff[strlen(buff)-1] == '\n')
buff[strlen(buff)-1] = '\0';
for(ptr=buff; isspace(*ptr) && *ptr; ptr++)
;
if (*ptr == '\0')
continue;
value = parse_expression(buff);
print_result(value);
}
}
ULONG
parse_expression(char *str)
{
ULONG val;
char *ptr = str;
ptr = skipwhite(ptr);
if (*ptr == '\0')
return last_result;
val = assignment_expr(&ptr);
ptr = skipwhite(ptr);
while (*ptr == SEMI_COLON && *ptr != '\0')
{
ptr++;
if (*ptr == '\0')
continue;
val = assignment_expr(&ptr);
}
last_result = val;
return val;
}
ULONG
assignment_expr(char **str)
{
ULONG val;
char *orig_str;
char *var_name;
variable *v;
*str = skipwhite(*str);
orig_str = *str;
var_name = get_var_name(str);
*str = skipwhite(*str);
if (**str == EQUAL && *(*str+1) != EQUAL)
{
*str = skipwhite(*str + 1);
val = assignment_expr(str);
if ((v = lookup_var(var_name)) == NULL)
add_var(var_name, val);
else
v->value = val;
}
else if (((**str == PLUS || **str == MINUS || **str == OR ||
**str == TIMES || **str == DIVISION || **str == MODULO ||
**str == AND || **str == XOR) && *(*str+1) == EQUAL) ||
strncmp(*str, "<<=", 3) == 0 || strncmp(*str, ">>=", 3) == 0)
{
val = do_assignment_operator(str, var_name);
}
else
{
*str = orig_str;
val = logical_or_expr(str);
*str = skipwhite(*str);
if (**str == EQUAL)
{
fprintf(stderr, "Left hand side of expression is not assignable.\n");
}
}
if (var_name)
free(var_name);
return val;
}
ULONG
do_assignment_operator(char **str, char *var_name)
{
ULONG val;
variable *v;
char operator;
operator = **str;
if (operator == SHIFT_L || operator == SHIFT_R)
*str = skipwhite(*str + 3);
else
*str = skipwhite(*str + 2);
val = assignment_expr(str);
v = lookup_var(var_name);
if (v == NULL)
{
v = add_var(var_name, 0);
if (v == NULL)
return 0;
}
if (operator == PLUS)
v->value += val;
else if (operator == MINUS)
v->value -= val;
else if (operator == AND)
v->value &= val;
else if (operator == XOR)
v->value ^= val;
else if (operator == OR)
v->value |= val;
else if (operator == SHIFT_L)
v->value <<= val;
else if (operator == SHIFT_R)
v->value >>= val;
else if (operator == TIMES)
v->value *= val;
else if (operator == DIVISION)
{
if (val == 0)
fprintf(stderr, "Divide by zero!\n");
v->value /= val;
}
else if (operator == MODULO)
{
if (val == 0)
fprintf(stderr, "Modulo by zero!\n");
v->value %= val;
}
else
{
fprintf(stderr, "Unknown operator: %c\n", operator);
v->value = 0;
}
return v->value;
}
ULONG
logical_or_expr(char **str)
{
ULONG val, sum = 0;
*str = skipwhite(*str);
sum = logical_and_expr(str);
*str = skipwhite(*str);
while(**str == OR && *(*str + 1) == OR)
{
*str = skipwhite(*str + 2);
val = logical_and_expr(str);
sum = (val || sum);
}
return sum;
}
ULONG
logical_and_expr(char **str)
{
ULONG val, sum = 0;
*str = skipwhite(*str);
sum = or_expr(str);
*str = skipwhite(*str);
while(**str == AND && *(*str + 1) == AND)
{
*str = skipwhite(*str + 2);
val = or_expr(str);
sum = (val && sum);
}
return sum;
}
ULONG
or_expr(char **str)
{
ULONG val, sum = 0;
*str = skipwhite(*str);
sum = xor_expr(str);
*str = skipwhite(*str);
while(**str == OR && *(*str+1) != OR)
{
*str = skipwhite(*str + 1);
val = xor_expr(str);
sum |= val;
}
return sum;
}
ULONG
xor_expr(char **str)
{
ULONG val, sum = 0;
*str = skipwhite(*str);
sum = and_expr(str);
*str = skipwhite(*str);
while(**str == XOR)
{
*str = skipwhite(*str + 1);
val = and_expr(str);
sum ^= val;
}
return sum;
}
ULONG
and_expr(char **str)
{
ULONG val, sum = 0;
*str = skipwhite(*str);
sum = equality_expr(str);
*str = skipwhite(*str);
while(**str == AND && *(*str+1) != AND)
{
*str = skipwhite(*str + 1);
val = equality_expr(str);
sum &= val;
}
return sum;
}
ULONG
equality_expr(char **str)
{
ULONG val, sum = 0;
char op;
*str = skipwhite(*str);
sum = relational_expr(str);
*str = skipwhite(*str);
while((**str == EQUAL && *(*str+1) == EQUAL) ||
(**str == BANG && *(*str+1) == EQUAL))
{
op = **str;
*str = skipwhite(*str + 2);
val = relational_expr(str);
if (op == EQUAL)
sum = (sum == val);
else if (op == BANG)
sum = (sum != val);
}
return sum;
}
ULONG
relational_expr(char **str)
{
ULONG val, sum = 0;
char op, equal_to=0;
*str = skipwhite(*str);
sum = shift_expr(str);
*str = skipwhite(*str);
while(**str == LESS_THAN || **str == GREATER_THAN)
{
equal_to = 0;
op = **str;
if (*(*str+1) == EQUAL)
{
equal_to = 1;
*str = *str+1;
}
*str = skipwhite(*str + 1);
val = shift_expr(str);
if (op == LESS_THAN && equal_to == 0)
sum = ((LONG)sum < (LONG)val);
else if (op == LESS_THAN && equal_to == 1)
sum = ((LONG)sum <= (LONG)val);
else if (op == GREATER_THAN && equal_to == 0)
sum = ((LONG)sum > (LONG)val);
else if (op == GREATER_THAN && equal_to == 1)
sum = ((LONG)sum >= (LONG)val);
}
return sum;
}
ULONG
shift_expr(char **str)
{
ULONG val, sum = 0;
char op;
*str = skipwhite(*str);
sum = add_expression(str);
*str = skipwhite(*str);
while((strncmp(*str, "<<", 2) == 0) || (strncmp(*str, ">>", 2) == 0))
{
op = **str;
*str = skipwhite(*str + 2);
val = add_expression(str);
if (op == SHIFT_L)
sum <<= val;
else if (op == SHIFT_R)
sum >>= val;
}
return sum;
}
ULONG
add_expression(char **str)
{
ULONG val, sum = 0;
char op;
*str = skipwhite(*str);
sum = term(str);
*str = skipwhite(*str);
while(**str == PLUS || **str == MINUS)
{
op = **str;
*str = skipwhite(*str + 1);
val = term(str);
if (op == PLUS)
sum += val;
else if (op == MINUS)
sum -= val;
}
return sum;
}
ULONG
term(char **str)
{
ULONG val, sum = 0;
char op;
sum = factor(str);
*str = skipwhite(*str);
if (**str != TIMES && **str != DIVISION && **str != MODULO &&
**str != PLUS && **str != MINUS && **str != OR &&
**str != AND && **str != XOR && **str != BANG &&
**str != NEGATIVE && **str != TWIDDLE && **str != RPAREN &&
**str != LESS_THAN && **str != GREATER_THAN && **str != SEMI_COLON &&
strncmp(*str, "<<", 2) != 0 && strncmp(*str, ">>", 2) &&
**str != EQUAL && **str != '\0')
{
fprintf(stderr, "Parsing stopped: unknown operator %s\n", *str);
return sum;
}
while(**str == TIMES || **str == DIVISION || **str == MODULO)
{
op = **str;
*str = skipwhite(*str + 1);
val = factor(str);
if (op == TIMES)
sum *= val;
else if (op == DIVISION)
{
if (val == 0)
fprintf(stderr, "Divide by zero!\n");
sum /= val;
}
else if (op == MODULO)
{
if (val == 0)
fprintf(stderr, "Modulo by zero!\n");
sum %= val;
}
}
return sum;
}
ULONG
factor(char **str)
{
ULONG val=0;
char op = NOTHING, have_special=0;
char *var_name, *var_name_ptr;
variable *v;
if (**str == NEGATIVE || **str == PLUS || **str == TWIDDLE || **str == BANG)
{
op = **str;
if ((op == NEGATIVE && *(*str + 1) == NEGATIVE) ||
(op == PLUS && *(*str + 1) == PLUS))
{
*str = *str + 1;
have_special = 1;
}
*str = skipwhite(*str + 1);
var_name_ptr = *str;
}
val = get_value(str);
*str = skipwhite(*str);
if (have_special)
{
var_name = get_var_name(&var_name_ptr);
if (var_name == NULL)
{
fprintf(stderr, "Can only use ++/-- on variables.\n");
return val;
}
if ((v = lookup_var(var_name)) == NULL)
{
v = add_var(var_name, 0);
if (v == NULL)
return val;
}
free(var_name);
if (op == PLUS)
val = ++v->value;
else
val = --v->value;
}
else
{
switch(op)
{
case NEGATIVE : val *= -1;
break;
case BANG : val = !val;
break;
case TWIDDLE : val = ~val;
break;
}
}
return val;
}
ULONG
get_value(char **str)
{
ULONG val;
char *var_name;
variable *v;
if (**str == SINGLE_QUOTE)
{
unsigned int i;
*str = *str + 1;
val = 0;
for(i=0; **str && **str != SINGLE_QUOTE && i < sizeof(LONG); *str+=1,i++)
{
if (**str == '\\')
*str += 1;
val <<= 8;
val |= (ULONG)((unsigned)**str);
}
if (**str != SINGLE_QUOTE)
{
fprintf(stderr, "Warning: character constant not terminated or too "
"long (max len == %ld bytes)\n", sizeof(LONG));
while(**str && **str != SINGLE_QUOTE)
*str += 1;
}
else if (**str != '\0')
*str += 1;
}
else if (isdigit(**str))
{
val = STRTOUL(*str, str, 0);
*str = skipwhite(*str);
}
else if (**str == USE_LAST_RESULT)
{
val = last_result;
*str = skipwhite(*str+1);
}
else if (**str == LPAREN)
{
*str = skipwhite(*str + 1);
val = assignment_expr(str);
if (**str == RPAREN)
*str = *str + 1;
else
fprintf(stderr, "Mismatched paren's\n");
}
else if (isalpha(**str) || **str == '_')
{
if ((var_name = get_var_name(str)) == NULL)
{
fprintf(stderr, "Can't get var name!\n");
return 0;
}
if (get_var(var_name, &val) == 0)
{
fprintf(stderr, "No such variable: %s (assigning value of zero)\n",
var_name);
val = 0;
v = add_var(var_name, val);
if (v == NULL)
return 0;
}
*str = skipwhite(*str);
if (strncmp(*str, "++", 2) == 0 || strncmp(*str, "--", 2) == 0)
{
if ((v = lookup_var(var_name)) != NULL)
{
val = v->value;
if (**str == '+')
v->value++;
else
v->value--;
*str = *str + 2;
}
else
{
fprintf(stderr, "%s is a read-only variable\n", var_name);
}
}
free(var_name);
}
else
{
fprintf(stderr, "Expecting left paren, unary op, constant or variable.");
fprintf(stderr, " Got: `%s'\n", *str);
return 0;
}
return val;
}
static int (*external_var_lookup)(char *name, ULONG *val) = NULL;
int (*set_var_lookup_hook(int (*func)(char *name, ULONG *val)))(char *name, ULONG *val)
{
int (*old_func)(char *name, ULONG *val) = external_var_lookup;
external_var_lookup = func;
return old_func;
}
variable *
lookup_var(char *name)
{
variable *v;
for(v=vars; v; v=v->next)
if (v->name && strcmp(v->name, name) == 0)
return v;
return NULL;
}
variable *
add_var(char *name, ULONG value)
{
variable *v;
ULONG tmp;
if (external_var_lookup)
if (external_var_lookup(name, &tmp) != 0)
{
fprintf(stderr, "Can't assign/create %s, it is a read-only var\n",name);
return NULL;
}
v = (variable *)malloc(sizeof(variable));
if (v == NULL)
{
fprintf(stderr, "No memory to add variable: %s\n", name);
return NULL;
}
v->name = strdup(name);
v->value = value;
v->next = vars;
vars = v;
return v;
}
void
set_var(char *name, ULONG val)
{
variable *v;
v = lookup_var(name);
if (v != NULL)
v->value = val;
else
add_var(name, val);
}
int
get_var(char *name, ULONG *val)
{
variable *v;
v = lookup_var(name);
if (v != NULL)
{
*val = v->value;
return 1;
}
else if (external_var_lookup != NULL)
{
return external_var_lookup(name, val);
}
return 0;
}
#define DEFAULT_LEN 32
char *
get_var_name(char **str)
{
int i, len=DEFAULT_LEN;
char *buff;
if (isalpha(**str) == 0 && **str != '_')
return NULL;
buff = (char *)malloc(len*sizeof(char));
if (buff == NULL)
return NULL;
i=0;
while(**str && (isalnum(**str) || **str == '_'))
{
if (i >= len-1)
{
len *= 2;
buff = (char *)realloc(buff, len);
if (buff == NULL)
return NULL;
}
buff[i++] = **str;
*str = *str+1;
}
buff[i] = '\0';
while (isalnum(**str) || **str == '_')
*str = *str+1;
return buff;
}
char *
skipwhite(char *str)
{
if (str == NULL)
return NULL;
while(*str && (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\f'))
str++;
return str;
}