// Quick primitive expression evaluator // Operation priorities: // 0: () // 1: + - | ^ // 2: * / % & // 3: unary+ unary- ~ // Expect to see a ')' #define PAREN_EXPECT 1 // Eat the closing ')' #define PAREN_EAT 2 static bool get_expression (const char **s, uint32 *v, int priority = 0, int flags = 0) { uint32 b; char *x = get_token (s); if (!*x) { // Got empty token, could be a unary operator or a parenthesis switch (peek_char (s)) { case '(': (*s)++; if (!get_expression (s, v, 0, PAREN_EAT | PAREN_EXPECT)) return false; break; case '+': (*s)++; if (!get_expression (s, v, 3, flags & ~PAREN_EAT)) return false; break; case '-': (*s)++; if (!get_expression (s, v, 3, flags & ~PAREN_EAT)) return false; *v = (uint32)-(int32)*v; break; case '~': (*s)++; if (!get_expression (s, v, 3, flags & ~PAREN_EAT)) return false; *v = ~*v; break; case 0: case ',': return false; default: Complain (C_ERROR ("line %d: Unexpected input '%hs'"), line, *s); return false; } } else { if (*x >= '0' && *x <= '9') { // We got a number char *err; *v = strtoul (x, &err, 0); if (*err) { Complain (C_ERROR ("line %d: Expected a number, got %hs"), line, x); return false; } } // Look through variables else if (!GetVar (x, s, v, ScriptVars, ScriptVarsCount) && !GetVar (x, s, v, UserVars, UserVarsCount)) { Complain (C_ERROR ("line %d: Unknown variable '%hs' in expression"), line, x); return false; } } // Peek next char and see if it is a operator bool unk_op = false; while (!unk_op) { char op = peek_char (s); switch (op) { case '+': case '-': case '|': case '^': if (priority > 1) return true; (*s)++; if (!get_expression (s, &b, 1, flags & ~PAREN_EAT)) return false; switch (op) { case '+': *v += b; break; case '-': *v -= b; break; case '|': *v |= b; break; case '^': *v ^= b; break; } break; case '*': case '/': case '%': case '&': if (priority > 2) return true; (*s)++; if (!get_expression (s, &b, 2, flags & ~PAREN_EAT)) return false; switch (op) { case '*': *v *= b; break; case '/': *v /= b; break; case '%': *v %= b; break; case '&': *v &= b; break; } break; case ')': if (!(flags & PAREN_EXPECT)) { Complain (C_ERROR ("line %d: Unexpected ')'"), line); return false; } if (flags & PAREN_EAT) (*s)++; return true; default: unk_op = true; break; } } if (flags & PAREN_EXPECT) { Complain (C_ERROR ("line %d: No closing ')'"), line); return false; } return true; }