Add tc_calc library
parent
06011829e6
commit
8fefe97975
|
|
@ -0,0 +1,348 @@
|
||||||
|
/*
|
||||||
|
* ==============================================================
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
** Copyright (c) Terminal Click - All rights Reserved
|
||||||
|
**
|
||||||
|
** @author: Abner Coimbre <abner@terminal.click>
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef CALC_H
|
||||||
|
#define CALC_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ==============================================================
|
||||||
|
*
|
||||||
|
* DATA TYPES
|
||||||
|
*
|
||||||
|
* ===============================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CALC_FORMAT_DECIMAL,
|
||||||
|
CALC_FORMAT_HEX,
|
||||||
|
CALC_FORMAT_BINARY,
|
||||||
|
CALC_FORMAT_MAX,
|
||||||
|
} calc_FormatKind;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ==============================================================
|
||||||
|
*
|
||||||
|
* PUBLIC ROUTINES
|
||||||
|
*
|
||||||
|
* ===============================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: The gcc/clang cold attribute below is specifically designed for functions that:
|
||||||
|
*
|
||||||
|
* 1. Are unlikely to be executed during normal program flow
|
||||||
|
* 2. Should be optimized for size rather than speed
|
||||||
|
* 3. Handle uncommon conditions or paths in the code
|
||||||
|
*
|
||||||
|
* Our calculator routines fit this profile because:
|
||||||
|
*
|
||||||
|
* - They're only called when users explicitly invoke the calculator feature
|
||||||
|
* - In a terminal emulator, most operations would be text display/input rather than calculations
|
||||||
|
* - The calculations only happen after a specific user action (pressing Enter)
|
||||||
|
*/
|
||||||
|
|
||||||
|
function __attribute__((cold)) char* calc_format_result(double value, calc_FormatKind format);
|
||||||
|
function __attribute__((cold)) double calc_eval_formatted(char *expr, calc_FormatKind format, char *result_str);
|
||||||
|
function __attribute__((cold)) double calc_eval(char *expr);
|
||||||
|
|
||||||
|
function __attribute__((cold)) int calc_match(char expected);
|
||||||
|
|
||||||
|
function __attribute__((cold)) double calc_expr(void);
|
||||||
|
function __attribute__((cold)) double calc_term(void);
|
||||||
|
function __attribute__((cold)) double calc_exponent(void);
|
||||||
|
function __attribute__((cold)) double calc_factor(void);
|
||||||
|
|
||||||
|
function __attribute__((cold)) double calc_bitwise_or(void);
|
||||||
|
function __attribute__((cold)) double calc_bitwise_xor(void);
|
||||||
|
function __attribute__((cold)) double calc_bitwise_and(void);
|
||||||
|
function __attribute__((cold)) double calc_shift(void);
|
||||||
|
|
||||||
|
#endif /* CALC_H */
|
||||||
Loading…
Reference in New Issue