1
0
Fork 0
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

270 lines
8.0 KiB
C

/*
* rpn.c: evalutor of RPN formulas
*/
#include <syslog.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "rpn.h"
#include "trie.h"
#include "grow.h"
// Wrapper functions between grow's void*s and our struct trie_retvals
bool stack_push (struct trie_retval val, struct grow *stack) {
struct trie_retval *p_val = (struct trie_retval *) malloc(sizeof(struct trie_retval));
if (p_val == NULL) {
syslog(cfg_log_facility | LOG_ERR, "rpn: stack_push: malloc failed: %m");
return false;
}
*p_val = val;
return grow_push((void *)p_val, stack);
}
struct trie_retval stack_pop(struct grow *stack) {
struct trie_retval *p_val = (struct trie_retval *) grow_pop(stack);
struct trie_retval val = *p_val;
free(p_val);
return val;
}
static uint64_t parse_var_name(char str[], char *out[]) {
uint64_t off;
for (off = 0;
('A' <= str[off] && str[off] <= 'Z')
|| ('0' <= str[off] && str[off] <= '9')
|| str[off] == '_'
; off++) {
if (off < cfg_var_name_max_len) {
(*out)[off] = str[off];
} else {
syslog(cfg_log_facility | LOG_NOTICE, "rpn: variable name too long, trucating char '%c'", str[off]);
}
}
// off is pointing past end of variable name;
(*out)[off] = '\0';
return off - 1; //last character of variable name
}
static uint64_t do_cmd(char str[], struct grow *stack) {
// Check whether a command exists and if so, perform it
if(0){
//alignment of others
//Bonus unconditional jump for compiling with -O0, maybe
} else if (strncmp("def", str, 3) == 0) {
struct trie_retval arg = stack_pop(stack);
if (arg.def) { // true might not be 1, I am not sure.
stack_push((struct trie_retval) {1, true, NULL}, stack);
} else {
stack_push((struct trie_retval) {0, true, NULL}, stack);
}
return 2;
} else if (strncmp("swap", str, 4) == 0) {
struct trie_retval arg1 = stack_pop(stack);
struct trie_retval arg2 = stack_pop(stack);
stack_push(arg1, stack);
stack_push(arg2, stack);
return 3;
} else if (strncmp("dup", str, 3) == 0) {
struct trie_retval arg = stack_pop(stack);
stack_push(arg, stack);
stack_push(arg, stack);
return 2;
} else if (strncmp("del", str, 3) == 0) {
stack_pop(stack);
return 2;
} else {
syslog(cfg_log_facility | LOG_NOTICE, "rpn: Unknown command starting with '%c'", str[0]);
return 0;
}
}
static bool rpn_eval_modify (char rpn[], bool modify, bool assoc, uint64_t task) {
uint64_t rpnoff;
struct grow *stack = grow_init(true);
char buf[cfg_var_name_max_len + 1];
int64_t val = 0;
// Sign of val==0
bool val_neg = false;
//definition of temporary variables, because of switch's goto-y behavior
struct trie_retval arg;
struct trie_retval arg1;
struct trie_retval arg2;
// this leads to dangerous code -- it is not clear, what are the values.
// but it has to be done this way due to how C switch works
// parse the RPN
for (rpnoff=0; rpn[rpnoff] != '\0' && rpn[rpnoff] != '?'; rpn++) {
switch (rpn[rpnoff]) {
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
; //Label...
int digit = rpn[rpnoff] - '0';
val = val * 10 + digit;
if (val >= 0 && val_neg == true) {
val *= -1;
}
if (rpn[rpnoff+1] < '0' || '9' < rpn[rpnoff+1]) { //ASCII
// end of number
stack_push((struct trie_retval) {val, true, NULL}, stack);
val = 0;
val_neg = false;
}
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
case 'V': case 'W': case 'X': case 'Y': case 'Z':
rpnoff += parse_var_name(rpn + rpnoff, &buf);
stack_push(trie_lookup(buf),stack);
if (modify) {
if (assoc){
trie_assoc(buf, task);
} else {
trie_unassoc(buf, task);
}
}
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
case 'v': case 'w': case 'x': case 'y': case 'z':
rpnoff += do_cmd(rpn + rpnoff, stack);
break;
case '+':
; //Label...
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
stack_push((struct trie_retval) {arg1.val + arg2.val, true, NULL}, stack);
break;
case '-': // May be unary or binary
if ('0' <= rpn[rpnoff+1] && rpn[rpnoff] <= '9') {
// Unary
// It's shame two's complement doesn't have -0, it would help here
val_neg = true;
} else {
// Binary
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
stack_push((struct trie_retval) {arg1.val - arg2.val, true, NULL}, stack);
}
break;
case '*':
; //Label...
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
stack_push((struct trie_retval) {arg1.val * arg2.val, true, NULL}, stack);
break;
case '/':
; //Label...
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
stack_push((struct trie_retval) {arg1.val / arg2.val, true, NULL}, stack);
break;
case '%': //Modulo
; //Label...
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
stack_push((struct trie_retval) {arg1.val % arg2.val, true, NULL}, stack);
break;
case '^': //Bit XOR
; //Label...
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
stack_push((struct trie_retval) {arg1.val ^ arg2.val, true, NULL}, stack);
break;
case '~': //Bit NOT
; //Label...
arg = stack_pop(stack);
stack_push((struct trie_retval) {~(arg.val), true, NULL}, stack);
break;
case '!': //Logical NOT
; //Label...
arg = stack_pop(stack);
if (arg.val != 0) {
stack_push((struct trie_retval) {0,true, NULL}, stack);
} else {
stack_push((struct trie_retval) {1,true, NULL}, stack);
}
break;
case '=': //Equality
; //Label...
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
stack_push((struct trie_retval) {arg1.val == arg2.val, true, NULL}, stack);
break;
case '>': //(mind the operand order)
; //Label...
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
stack_push((struct trie_retval) {arg1.val > arg2.val, true, NULL}, stack);
break;
case '<': //(mind the operand order)
; //Label...
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
stack_push((struct trie_retval) {arg1.val < arg2.val, true, NULL}, stack);
break;
case '|': //Bit or logical OR
; //Label...
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
if (rpn[rpnoff+1] == '|') {
// Logical OR
rpnoff++;
stack_push((struct trie_retval) {arg1.val || arg2.val, true, NULL}, stack);
} else {
// Bit OR
stack_push((struct trie_retval) {arg1.val | arg2.val, true, NULL}, stack);
}
break;
case '&': //Bit or logical AND
; //Label...
arg1 = stack_pop(stack);
arg2 = stack_pop(stack);
if (rpn[rpnoff+1] == '&') {
// Logical AND
rpnoff++;
stack_push((struct trie_retval) {arg1.val && arg2.val, true, NULL}, stack);
} else {
// Bit AND
stack_push((struct trie_retval) {arg1.val & arg2.val, true, NULL}, stack);
}
break;
case ' ':
break;
default:
syslog(cfg_log_facility | LOG_NOTICE, "rpn: Unexpected char: %c", rpn[rpnoff]);
break;
}
}
// Return the variable at top of the stack and discard stack
struct trie_retval retval = stack_pop(stack);
grow_drop(stack);
if (retval.def == false || retval.val == 0) {
return false;
} else {
return true;
}
}
/*
* Mathematics is the art of giving the same name to different things.
* - Henri Poincaré
* Programming is the art of giving different names to the same thing.
* - Obvious from the code below.
* (except maybe it is not called art)
*/
bool rpn_eval(char rpn[]) {
return rpn_eval_modify(rpn, false, false, 0);
}
bool rpn_eval_assoc(char rpn[], uint64_t task) {
return rpn_eval_modify(rpn, true, true, task);
}
bool rpn_eval_unassoc(char rpn[], uint64_t task) {
return rpn_eval_modify(rpn, true, false, task);
}