mirror of
https://github.com/pragma-/pbot.git
synced 2025-01-11 04:22:35 +01:00
Update modules/qrpn (#65)
- Fixed itenlog erroneously disallowing negative arguments - Removed badly behaving exact literal check - Fixed 2 not being a prime number - Fixed whitespace issues - Reordered checks in evaluate_literal for speed and factored out the annoying QRPN_NOT_A_UNIT non-error error code - Added a least significant digit to the definition of a year
This commit is contained in:
parent
da7d31229b
commit
dd250ccf83
@ -93,7 +93,8 @@ static unsigned long long ceil_isqrt(unsigned long long n) {
|
||||
}
|
||||
|
||||
static int isprime(const unsigned long long n) {
|
||||
if (n < 2 || !(n % 2)) return 0;
|
||||
if (2 == n) return 1;
|
||||
else if (1 == n || !(n % 2)) return 0;
|
||||
|
||||
const unsigned long long stop = ceil_isqrt(n);
|
||||
|
||||
@ -222,7 +223,7 @@ static const struct named_quantity named_quantities[] = {
|
||||
{ .value = 0.514444444, .units = { 1, 0, -1, 0, 0, 0, 0 }, .name = "knot", .abrv = "kt", },
|
||||
{ .value = 1609.34, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "mile" },
|
||||
{ .value = 1609.34 / 3600, .units = { 1, 0, -1, 0, 0, 0, 0 }, .abrv = "mph" },
|
||||
{ .value = 86400.0 * 365.242, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "year", .abrv = "a" },
|
||||
{ .value = 86400.0 * 365.2425, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "year", .abrv = "a" },
|
||||
{ .value = 1852.0 * 3, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "league" },
|
||||
{ .value = 9.8066, .units = { 1, 0, -2, 0, 0, 0, 0 }, .name = "g" },
|
||||
{ .value = 0.01, .units = { 1, 0, -2, 0, 0, 0, 0 }, .name = "gal", .abrv = "Gal" },
|
||||
@ -316,7 +317,7 @@ static double datestr_to_unix_seconds(const char * const datestr) {
|
||||
char * after = NULL;
|
||||
microseconds_after_decimal = llrint(strtod(datestr, &after) * 1000000);
|
||||
if (after && *after != '\0')
|
||||
fprintf(stdout, "warning: %s: ignoring \"%s\"\n", __func__, after);
|
||||
fprintf(stderr, "warning: %s: ignoring \"%s\"\n", __func__, after);
|
||||
}
|
||||
|
||||
return (seconds * 1000000 + microseconds_after_decimal) * 1e-6;
|
||||
@ -365,7 +366,7 @@ static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int
|
||||
}
|
||||
|
||||
/* not finding anything above is only an error if there was both a numerator and denominator */
|
||||
if (!quantity) return slash ? QRPN_ERROR_TOKEN_UNRECOGNIZED : QRPN_NOT_A_UNIT;
|
||||
if (!quantity) return QRPN_ERROR_TOKEN_UNRECOGNIZED;
|
||||
|
||||
if (quantity->flags & FLAG_UNIT_ENTERS_AS_OPERAND) {
|
||||
if (S >= QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
|
||||
@ -391,24 +392,13 @@ static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int
|
||||
}
|
||||
|
||||
static int evaluate_literal(struct quantity * stack, int S, const char * const token) {
|
||||
const int unit_ret = evaluate_unit(stack, S, token, 1);
|
||||
|
||||
/* if evaluate unit either errored or succeeded, we're done... */
|
||||
if (unit_ret != QRPN_NOT_A_UNIT) return unit_ret;
|
||||
|
||||
/* otherwise, token was not a unit name, parse it as a simple literal */
|
||||
struct quantity tmp = { 0 };
|
||||
double d = 0, m = 0, s = 0;
|
||||
|
||||
if (strpbrk(token + 1, "d°") && sscanf(token, "%lf%*[d°]%lf%*[m']%lf%*[s\"]", &d, &m, &s)) {
|
||||
const double leading = strtod(token, NULL);
|
||||
tmp.value = copysign(fabs(d) + m / 60.0 + s / 3600.0, leading) * M_PI / 180.0;
|
||||
}
|
||||
else if (strpbrk(token, "T") && strpbrk(token, "Z")) {
|
||||
tmp.value = datestr_to_unix_seconds(token);
|
||||
tmp.units[2] = 1;
|
||||
}
|
||||
else if (!strcmp(token, "pi"))
|
||||
char * endptr = NULL;
|
||||
const double dv = strtod(token, &endptr);
|
||||
|
||||
if (endptr == token) {
|
||||
if (!strcmp(token, "pi"))
|
||||
tmp.value = M_PI;
|
||||
else if (!strcmp(token, "-pi"))
|
||||
tmp.value = -M_PI;
|
||||
@ -416,18 +406,24 @@ static int evaluate_literal(struct quantity * stack, int S, const char * const t
|
||||
tmp.value = I;
|
||||
else if (!strcmp(token, "-i"))
|
||||
tmp.value = -I;
|
||||
else {
|
||||
char * endptr = NULL;
|
||||
const double dv = strtod(token, &endptr);
|
||||
if (fabs(dv) >= (double)(1ULL << 53)) {
|
||||
const long long llv = strtoll(token, NULL, 10);
|
||||
if ((long long)dv != llv) return QRPN_ERROR_INEXACT_LITERAL;
|
||||
else return evaluate_unit(stack, S, token, 1);
|
||||
} else {
|
||||
/* otherwise, token was not a unit name, parse it as a simple literal */
|
||||
double d = 0, m = 0, s = 0;
|
||||
|
||||
if (strpbrk(token + 1, "d°") && sscanf(token, "%lf%*[d°]%lf%*[m']%lf%*[s\"]", &d, &m, &s))
|
||||
tmp.value = copysign(fabs(d) + m / 60.0 + s / 3600.0, d) * M_PI / 180.0;
|
||||
else if (strpbrk(token, "T") && strpbrk(token, "Z")) {
|
||||
tmp.value = datestr_to_unix_seconds(token);
|
||||
tmp.units[2] = 1;
|
||||
}
|
||||
else {
|
||||
tmp.value = dv;
|
||||
|
||||
if (!strcmp(endptr, "i"))
|
||||
tmp.value *= I;
|
||||
else if (endptr == token)
|
||||
return QRPN_ERROR_TOKEN_UNRECOGNIZED;
|
||||
return evaluate_unit(stack, S, token, 1);
|
||||
else if (endptr[0] != '\0' && endptr[1] == '\0') {
|
||||
double prefix_scale = 1.0;
|
||||
/* only allow k, M, G to be used in this position */
|
||||
@ -442,6 +438,7 @@ static int evaluate_literal(struct quantity * stack, int S, const char * const t
|
||||
tmp.value *= prefix_scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (S >= QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
|
||||
stack[S] = tmp;
|
||||
@ -898,7 +895,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
else if (!strcmp(token, "log2")) return evaluate_one_argument_must_be_unitless_real_nonnegative(stack, S, log2);
|
||||
else if (!strcmp(token, "log10")) return evaluate_one_argument_must_be_unitless_real_nonnegative(stack, S, log10);
|
||||
else if (!strcmp(token, "tenlog")) return evaluate_one_argument_must_be_unitless_real_nonnegative(stack, S, tenlog);
|
||||
else if (!strcmp(token, "itenlog")) return evaluate_one_argument_must_be_unitless_real_nonnegative(stack, S, itenlog);
|
||||
else if (!strcmp(token, "itenlog")) return evaluate_one_argument_must_be_unitless_real(stack, S, itenlog);
|
||||
else if (!strcmp(token, "square")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
|
||||
@ -959,8 +956,8 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
}
|
||||
else if (!strcmp(token, "print")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
fprintf_quantity(stdout, stack[S - 1]);
|
||||
fprintf(stdout, "\n");
|
||||
fprintf_quantity(stderr, stack[S - 1]);
|
||||
fprintf(stderr, "\n");
|
||||
return S;
|
||||
}
|
||||
else
|
||||
@ -1222,7 +1219,7 @@ int qrpn_try_string(const struct quantity stack[static QRPN_STACK_SIZE_MAX], con
|
||||
__attribute((weak)) int main(const int argc, const char ** const argv) {
|
||||
if (isatty(STDIN_FILENO) && argc < 2) {
|
||||
/* never reached */
|
||||
fprintf(stdout, "%s: Evaluates an RPN expression with units\n", argv[0]);
|
||||
fprintf(stderr, "%s: Evaluates an RPN expression with units\n", argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -1231,7 +1228,7 @@ __attribute((weak)) int main(const int argc, const char ** const argv) {
|
||||
int S = qrpn_evaluate_tokens(stack, 0, argv + 1, 0);
|
||||
|
||||
if (S < 0) {
|
||||
fprintf(stdout, "error: %s\n", qrpn_strerror(S));
|
||||
fprintf(stderr, "error: %s\n", qrpn_strerror(S));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
@ -44,4 +44,3 @@ void fprintf_quantity(FILE * fh, const struct quantity quantity);
|
||||
#define QRPN_ERROR_TOO_MUCH_STACK -11
|
||||
#define QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT -12
|
||||
#define QRPN_ERROR_INEXACT_LITERAL -13
|
||||
#define QRPN_NOT_A_UNIT -14
|
||||
|
Loading…
Reference in New Issue
Block a user