3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-10-01 17:16:39 +02: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:
rlcamp 2021-11-18 17:33:40 -08:00 committed by GitHub
parent da7d31229b
commit dd250ccf83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 55 deletions

View File

@ -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;
@ -335,7 +336,7 @@ static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int
/* loop over all known units and prefixes looking for a match */
for (const struct named_quantity * possible_quantity = named_quantities; !quantity && possible_quantity < named_quantities + named_quantity_count; possible_quantity++)
/* loop over [full name, abbreviation, alt spelling] of each unit. this is a bit of a mess */
/* loop over [full name, abbreviation, alt spelling] of each unit. this is a bit of a mess */
for (int ipass = 0; !quantity && ipass < 3; ipass++) {
const char * const unit_name = 2 == ipass ? possible_quantity->alt_spelling : 1 == ipass ? possible_quantity->abrv : possible_quantity->name;
if (!unit_name) continue;
@ -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,55 +392,51 @@ 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, "") && 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"))
tmp.value = M_PI;
else if (!strcmp(token, "-pi"))
tmp.value = -M_PI;
else if (!strcmp(token, "i"))
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;
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;
else if (!strcmp(token, "i"))
tmp.value = I;
else if (!strcmp(token, "-i"))
tmp.value = -I;
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, "") && 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;
}
tmp.value = dv;
if (!strcmp(endptr, "i"))
tmp.value *= I;
else if (endptr == token)
return QRPN_ERROR_TOKEN_UNRECOGNIZED;
else if (endptr[0] != '\0' && endptr[1] == '\0') {
double prefix_scale = 1.0;
/* only allow k, M, G to be used in this position */
if ('k' == endptr[0]) prefix_scale = 1e3;
else if ('M' == endptr[0]) prefix_scale = 1e6;
else if ('G' == endptr[0]) prefix_scale = 1e9;
else {
tmp.value = dv;
/* special case: trailing 'f' from floating point literals copied and pasted from C code should be ignored */
else if ('f' == endptr[0]) prefix_scale = 1.0;
else return QRPN_ERROR_TOKEN_UNRECOGNIZED;
if (!strcmp(endptr, "i"))
tmp.value *= I;
else if (endptr == token)
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 */
if ('k' == endptr[0]) prefix_scale = 1e3;
else if ('M' == endptr[0]) prefix_scale = 1e6;
else if ('G' == endptr[0]) prefix_scale = 1e9;
tmp.value *= prefix_scale;
/* special case: trailing 'f' from floating point literals copied and pasted from C code should be ignored */
else if ('f' == endptr[0]) prefix_scale = 1.0;
else return QRPN_ERROR_TOKEN_UNRECOGNIZED;
tmp.value *= prefix_scale;
}
}
}
@ -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);
}

View File

@ -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