/* * rpn.c: evalutor of RPN formulas */ #include #include #include #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); }