|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|