/* * ============================================================== * * GLOBALS * * =============================================================== */ global char calc_token; global char *expression; /* * ============================================================== * * PUBLIC API IMPLEMENTATION * * =============================================================== */ function __attribute__((cold)) char* calc_format_result(double value, calc_FormatKind format) { static char buffer[TC_CALC_FORMAT_BUFFER_SIZE] = {0}; // Enough for 64-bit binary + '0b' prefix + null long long int_val = cast(long long) value; switch (format) { case CALC_FORMAT_HEX: snprintf(buffer, sizeof buffer, "0x%llX", int_val); break; case CALC_FORMAT_BINARY: { char *ptr = buffer; *ptr++ = '0'; *ptr++ = 'b'; /* find highest set bit */ int msb = 63; while (msb > 0 && !(int_val & (1LL << msb))) msb--; /* convert to binary string */ for (int i = msb; i >= 0; i--) *ptr++ = (int_val & (1LL << i)) ? '1' : '0'; *ptr = '\0'; } break; case CALC_FORMAT_DECIMAL: /* handle both integer and floating-point cases */ if (value == (double)(long long)value) snprintf(buffer, sizeof(buffer), "%lld", (long long)value); else snprintf(buffer, sizeof(buffer), "%g", value); break; default: assert(0); break; } return buffer; } function __attribute__((cold)) double calc_eval_formatted(char *expr, calc_FormatKind format, char *result_str) { double result = calc_eval(expr); if (!isnan(result) && result_str != NULL) strcpy(result_str, calc_format_result(result, format)); // @robustness @abner return result; } function __attribute__((cold)) double calc_eval(char *expr) { assert(expr); assert(strlen(expr) > 0); expression = expr; /* 1st character for look-ahead */ calc_token = *expression++; return calc_bitwise_or(); } function __attribute__((cold)) int calc_match(char expected) { if (calc_token == expected) calc_token = *expression++; else return -1; return 0; } function __attribute__((cold)) double calc_expr(void) { double temp; if (isnan(temp = calc_term())) return NAN; while ((calc_token == '+') || (calc_token == '-')) { switch (calc_token) { case '+': if (calc_match('+') == -1) return NAN; { double t; if (isnan(t = calc_term())) return NAN; temp += t; } break; case '-': if (calc_match('-') == -1) return NAN; { double t; if (isnan(t = calc_term())) return NAN; temp -= t; } break; } } return temp; } function __attribute__((cold)) double calc_term(void) { double temp; if (isnan(temp = calc_exponent())) return NAN; while (calc_token == '*' || calc_token == '/' || calc_token == '%') { char op = calc_token; if (calc_match(op) == -1) return NAN; double t; if (isnan(t = calc_exponent())) return NAN; if (op == '*') temp *= t; else if (op == '/') temp /= t; else if (op == '%') temp = cast(int) temp % cast(int)t; } return temp; } function __attribute__((cold)) double calc_exponent(void) { double base = calc_factor(); if (isnan(base)) return NAN; while (calc_token == '*') { /* look ahead one char without consuming */ if (expression[0] != '*') break; // Not a power operator, just multiplication /* consume both '*' characters */ if (calc_match('*') == -1) return NAN; if (calc_match('*') == -1) return NAN; double exponent = calc_exponent(); // right-associative if (isnan(exponent)) return NAN; base = pow(base, exponent); } return base; } function __attribute__((cold)) double calc_factor(void) { double temp = 0; if (calc_token == '-') { if (calc_match('-') == -1) return NAN; double t; if (isnan(t = calc_factor())) return NAN; return -t; } if (calc_token == '(') { if (calc_match('(') == -1) return NAN; double t; if (isnan(t = calc_bitwise_or())) return NAN; temp = t; if (calc_match(')') == -1) return NAN; } else if (calc_token == '0') { /* check for hex or binary prefixes */ if (expression[0] == 'x' || expression[0] == 'X') { char x = expression[0]; /* handle hex numbers */ if (calc_match('0') == -1) return NAN; if (calc_match(x) == -1) // match 'x' or 'X' return NAN; /* verify we have at least one hex digit */ if (!isxdigit(calc_token)) return NAN; char *start = expression - 1; temp = cast(double) strtoll(start, &expression, 16); calc_token = *expression++; } else if (expression[0] == 'b' || expression[0] == 'B') { char b = expression[0]; /* handle binary numbers */ if (calc_match('0') == -1) return NAN; if (calc_match(b) == -1) // match 'b' or 'B' return NAN; /* verify we have at least one binary digit */ if (expression[0] != '0' && expression[0] != '1') return NAN; char *start = expression - 1; temp = cast(double) strtoll(start, &expression, 2); calc_token = *expression++; } else { return NAN; } } else if (isdigit(calc_token) || calc_token == '.') { char *start = expression - 1; temp = strtod(start, &expression); calc_token = *expression++; } else { return NAN; } return temp; } function __attribute__((cold)) double calc_bitwise_or(void) { double temp = 0; if (isnan(temp = calc_bitwise_xor())) return NAN; while (calc_token == '|') { if (calc_match('|') == -1) return NAN; double t = 0; if (isnan(t = calc_bitwise_xor())) return NAN; temp = (double)((long long)temp | (long long)t); } return temp; } function __attribute__((cold)) double calc_bitwise_xor(void) { double temp = 0; if (isnan(temp = calc_bitwise_and())) return NAN; while (calc_token == '^') { if (calc_match('^') == -1) return NAN; double t = 0; if (isnan(t = calc_bitwise_and())) return NAN; temp = (double)((long long)temp ^ (long long)t); } return temp; } function __attribute__((cold)) double calc_bitwise_and(void) { double temp; if (isnan(temp = calc_shift())) return NAN; while (calc_token == '&') { if (calc_match('&') == -1) return NAN; double t; if (isnan(t = calc_shift())) return NAN; temp = (double)((long long)temp & (long long)t); } return temp; } function __attribute__((cold)) double calc_shift(void) { double temp; if (isnan(temp = calc_expr())) return NAN; while (calc_token == '<' || calc_token == '>') { char first = calc_token; if (calc_match(first) == -1) return NAN; // Check for second < or > if (calc_token != first) return NAN; if (calc_match(first) == -1) return NAN; double t; if (isnan(t = calc_expr())) return NAN; if (first == '<') temp = (double)((long long)temp << (long long)t); else temp = (double)((long long)temp >> (long long)t); } return temp; }