3
0
mirror of https://github.com/pragma-/pbot.git synced 2025-05-03 21:27:31 +02:00

Update modules/qrpn (#64)

This commit is contained in:
rlcamp 2021-11-14 09:07:00 -08:00 committed by GitHub
parent a2111640af
commit 9172bcd29f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 316 additions and 249 deletions

View File

@ -1,7 +1,5 @@
/* for standalone usage, compile with: cc -Os -Wall -Wextra -Wshadow -march=native qrpn.c -lm -o qrpn */ /* for standalone usage, compile with: cc -Os -Wall -Wextra -Wshadow -march=native qrpn.c -lm -o qrpn */
/* this is mostly old code and needs to be rewritten but it's too useful to discard */
#define _DEFAULT_SOURCE #define _DEFAULT_SOURCE
#define _XOPEN_SOURCE #define _XOPEN_SOURCE
#include "qrpn.h" #include "qrpn.h"
@ -47,6 +45,25 @@ static double arcexsecant(const double x) {
return atan(sqrt(x * x + x * 2.0)); return atan(sqrt(x * x + x * 2.0));
} }
static double tenlog(double x) {
return 10.0 * log10(x);
}
static double itenlog(double x) {
return pow(10.0, x * 0.1);
}
static double complex cpow_checked(const double complex a, const double complex b) {
/* cpow() cannot be trusted to have as much precision as pow() even for integer arguments that fit in 32 bits */
if (!cimag(a) && !cimag(b)) {
const double ar = creal(a), br = creal(b);
if (rint(br) == br) return __builtin_powi(ar, br);
else return pow(ar, br);
}
else return cpow(a, b);
}
static unsigned long long gcd(unsigned long long a, unsigned long long b) { static unsigned long long gcd(unsigned long long a, unsigned long long b) {
while (b) { while (b) {
const unsigned long long t = b; const unsigned long long t = b;
@ -66,29 +83,29 @@ static unsigned long long nchoosek(const unsigned long long n, const unsigned lo
return n_choose_k; return n_choose_k;
} }
static double tenlog(double x) { static unsigned long long ceil_isqrt(unsigned long long n) {
return 10.0 * log10(x); unsigned long long this = n / 2;
if (!this) return n;
for (unsigned long long next = this; (next = (this + n / this) / 2) < this; this = next);
return this * this < n ? this + 1 : this;
} }
static double itenlog(double x) { static int isprime(const unsigned long long n) {
return pow(10.0, x * 0.1); if (n < 2 || !(n % 2)) return 0;
const unsigned long long stop = ceil_isqrt(n);
for (unsigned long long m = 3; m < stop; m += 2)
if (!(n % m)) return 0;
return 1;
} }
static double complex cpow_checked(const double complex a, const double complex b) {
/* cpow() cannot be trusted to have as much precision as pow() even for integer arguments that fit in 32 bits */
if (!cimag(a) && !cimag(b)) {
if (rint(b) == b) return __builtin_powi(a, b);
else return pow(a, b);
}
else return cpowl(a, b);
}
/* end simple math functions */ /* end simple math functions */
struct named_quantity { struct named_quantity {
/* ok this is the only other time you will ever catch me using double complex */ double value;
double complex value;
int8_t units[BASEUNITS]; /* metre, kilogram, second, ampere, kelvin, candela, mol */ int8_t units[BASEUNITS]; /* metre, kilogram, second, ampere, kelvin, candela, mol */
uint8_t flags; uint8_t flags;
char * name; char * name;
@ -299,7 +316,7 @@ static double datestr_to_unix_seconds(const char * const datestr) {
char * after = NULL; char * after = NULL;
microseconds_after_decimal = llrint(strtod(datestr, &after) * 1000000); microseconds_after_decimal = llrint(strtod(datestr, &after) * 1000000);
if (after && *after != '\0') 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; return (seconds * 1000000 + microseconds_after_decimal) * 1e-6;
@ -360,7 +377,7 @@ static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int
int units_out[BASEUNITS]; int units_out[BASEUNITS];
for (size_t iu = 0; iu < BASEUNITS; iu++) { for (size_t iu = 0; iu < BASEUNITS; iu++) {
units_out[iu] = stack[S - 1].units[iu] + quantity->units[iu] * unit_exponent; units_out[iu] = (int8_t)(stack[S - 1].units[iu] + quantity->units[iu] * unit_exponent);
if (units_out[iu] > INT8_MAX || units_out[iu] < INT8_MIN) return QRPN_ERROR_DIMENSION_OVERFLOW; if (units_out[iu] > INT8_MAX || units_out[iu] < INT8_MIN) return QRPN_ERROR_DIMENSION_OVERFLOW;
} }
@ -373,31 +390,6 @@ static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int
else return S; else return S;
} }
static int evaluate_one_argument_must_be_unitless(struct quantity * const stack, int S, double complex (* op)(double complex)) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
stack[S - 1].value = op(stack[S - 1].value);
return S;
}
static int evaluate_one_argument_must_be_unitless_real(struct quantity * const stack, int S, double (* op)(double)) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
stack[S - 1].value = op(stack[S - 1].value);
return S;
}
static int evaluate_one_argument_must_be_unitless_real_nonnegative(struct quantity * const stack, int S, double (* op)(double)) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
if (creal(stack[S - 1].value) < 0 || cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
stack[S - 1].value = op(stack[S - 1].value);
return S;
}
static int evaluate_literal(struct quantity * stack, int S, const char * const token) { static int evaluate_literal(struct quantity * stack, int S, const char * const token) {
const int unit_ret = evaluate_unit(stack, S, token, 1); const int unit_ret = evaluate_unit(stack, S, token, 1);
@ -426,7 +418,12 @@ static int evaluate_literal(struct quantity * stack, int S, const char * const t
tmp.value = -I; tmp.value = -I;
else { else {
char * endptr = NULL; char * endptr = NULL;
tmp.value = strtod(token, &endptr); 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;
}
tmp.value = dv;
if (!strcmp(endptr, "i")) if (!strcmp(endptr, "i"))
tmp.value *= I; tmp.value *= I;
else if (endptr == token) else if (endptr == token)
@ -451,6 +448,32 @@ static int evaluate_literal(struct quantity * stack, int S, const char * const t
return S + 1; return S + 1;
} }
static int evaluate_one_argument_must_be_unitless(struct quantity * const stack, int S, double complex (* op)(double complex)) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
stack[S - 1].value = op(stack[S - 1].value);
return S;
}
static int evaluate_one_argument_must_be_unitless_real(struct quantity * const stack, int S, double (* op)(double)) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
stack[S - 1].value = op(creal(stack[S - 1].value));
return S;
}
static int evaluate_one_argument_must_be_unitless_real_nonnegative(struct quantity * const stack, int S, double (* op)(double)) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
if (cimag(stack[S - 1].value) != 0) return QRPN_ERROR_MUST_BE_REAL;
if (creal(stack[S - 1].value) < 0) return QRPN_ERROR_MUST_BE_NONNEGATIVE;
stack[S - 1].value = op(creal(stack[S - 1].value));
return S;
}
int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const token) { int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const token) {
if (!strcmp(token, "mul") || !strcmp(token, "*")) { if (!strcmp(token, "mul") || !strcmp(token, "*")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
@ -489,8 +512,8 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
const long long a = llrint(stack[S - 2].value); const long long a = llrint(creal(stack[S - 2].value));
const long long b = llrint(stack[S - 1].value); const long long b = llrint(creal(stack[S - 1].value));
if (!b) return QRPN_ERROR_DOMAIN; if (!b) return QRPN_ERROR_DOMAIN;
const long long c = a / b; const long long c = a / b;
stack[S - 2].value = c; stack[S - 2].value = c;
@ -512,19 +535,23 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "mod") || !strcmp(token, "%")) { else if (!strcmp(token, "mod") || !strcmp(token, "%")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS; if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
stack[S - 2].value = fmod(stack[S - 2].value, stack[S - 1].value); if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
stack[S - 2].value = fmod(creal(stack[S - 2].value), creal(stack[S - 1].value));
return S - 1; return S - 1;
} }
else if (!strcmp(token, "hypot")) { else if (!strcmp(token, "hypot")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS; if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
stack[S - 2].value = hypot(stack[S - 2].value, stack[S - 1].value); if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
stack[S - 2].value = hypot(creal(stack[S - 2].value), creal(stack[S - 1].value));
return S - 1; return S - 1;
} }
else if (!strcmp(token, "atan2")) { else if (!strcmp(token, "atan2")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS; if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
stack[S - 2].value = atan2(stack[S - 2].value, stack[S - 1].value); if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
stack[S - 2].value = atan2(creal(stack[S - 2].value), creal(stack[S - 1].value));
memset(stack[S - 2].units, 0, sizeof(int8_t[BASEUNITS])); memset(stack[S - 2].units, 0, sizeof(int8_t[BASEUNITS]));
return S - 1; return S - 1;
} }
@ -551,9 +578,10 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "choose")) { else if (!strcmp(token, "choose")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS; if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
const unsigned long long n = (unsigned long long)llrint(stack[S - 2].value); const unsigned long long n = (unsigned long long)llrint(creal(stack[S - 2].value));
const unsigned long long k = (unsigned long long)llrint(stack[S - 1].value); const unsigned long long k = (unsigned long long)llrint(creal(stack[S - 1].value));
if ((double)n != stack[S - 2].value || if ((double)n != stack[S - 2].value ||
(double)k != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER; (double)k != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
stack[S - 2].value = nchoosek(n, k); stack[S - 2].value = nchoosek(n, k);
@ -563,9 +591,11 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "gcd")) { else if (!strcmp(token, "gcd")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
if (creal(stack[S - 2].value) < 0 || creal(stack[S - 1].value) < 0 ) return QRPN_ERROR_MUST_BE_NONNEGATIVE;
const unsigned long long a = (unsigned long long)llrint(stack[S - 2].value); const unsigned long long a = (unsigned long long)llrint(creal(stack[S - 2].value));
const unsigned long long b = (unsigned long long)lrint(stack[S - 1].value); const unsigned long long b = (unsigned long long)llrint(creal(stack[S - 1].value));
if ((double)a != stack[S - 2].value || if ((double)a != stack[S - 2].value ||
(double)b != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER; (double)b != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
stack[S - 2].value = gcd(a, b); stack[S - 2].value = gcd(a, b);
@ -574,9 +604,11 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "lcm")) { else if (!strcmp(token, "lcm")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
if (creal(stack[S - 2].value) < 0 || creal(stack[S - 1].value) < 0 ) return QRPN_ERROR_MUST_BE_NONNEGATIVE;
const unsigned long long a = (unsigned long long)llrint(stack[S - 2].value); const unsigned long long a = (unsigned long long)llrint(creal(stack[S - 2].value));
const unsigned long long b = (unsigned long long)lrint(stack[S - 1].value); const unsigned long long b = (unsigned long long)llrint(creal(stack[S - 1].value));
if ((double)a != stack[S - 2].value || if ((double)a != stack[S - 2].value ||
(double)b != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER; (double)b != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
@ -585,31 +617,19 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
} }
else if (!strcmp(token, "isprime")) { else if (!strcmp(token, "isprime")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (creal(stack[S - 1].value) < 0 || cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN; if (cimag(stack[S - 1].value) != 0) return QRPN_ERROR_MUST_BE_REAL;
if (creal(stack[S - 1].value) < 0) return QRPN_ERROR_MUST_BE_NONNEGATIVE;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
const unsigned long long x = (unsigned long long)llrint(stack[S - 1].value); const unsigned long long x = (unsigned long long)llrint(creal(stack[S - 1].value));
if ((double)x != (double)creal(stack[S - 1].value)) { if (x > 1ULL << 53) return QRPN_ERROR_DOMAIN;
fprintf(stdout, "%s: %.17g %.17g\n", __func__, (double)x, (double)creal(stack[S - 1].value)); if ((double)x != (double)creal(stack[S - 1].value)) return QRPN_ERROR_MUST_BE_INTEGER;
return QRPN_ERROR_MUST_BE_INTEGER;
}
if (x < 2) stack[S - 1].value = 0;
else {
char isprime = 1;
for (unsigned long long y = 2; y * y <= x; y++) {
if (!(x % y)) {
isprime = 0;
break;
}
}
stack[S - 1].value = isprime;
}
stack[S - 1].value = isprime(x);
return S; return S;
} }
else if (!strcmp(token, "swap")) { else if (!strcmp(token, "swap")) {
struct quantity tmp;
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
tmp = stack[S - 1]; struct quantity tmp = stack[S - 1];
stack[S - 1] = stack[S - 2]; stack[S - 1] = stack[S - 2];
stack[S - 2] = tmp; stack[S - 2] = tmp;
return S; return S;
@ -630,6 +650,13 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
stack[S] = stack[S - 2]; stack[S] = stack[S - 2];
return S + 1; return S + 1;
} }
else if (!strcmp(token, "2over")) {
if (S < 4) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (S + 2 > QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
stack[S] = stack[S - 4];
stack[S + 1] = stack[S - 3];
return S + 2;
}
else if (!strcmp(token, "and")) { else if (!strcmp(token, "and")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
@ -662,13 +689,13 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "le") || !strcmp(token, "lt") || !strcmp(token, "ge") || !strcmp(token, "gt")) { else if (!strcmp(token, "le") || !strcmp(token, "lt") || !strcmp(token, "ge") || !strcmp(token, "gt")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS; if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN; if (cimag(stack[S - 1].value) || cimag(stack[S - 2].value) ) return QRPN_ERROR_DOMAIN;
memset(stack[S - 2].units, 0, sizeof(int8_t[BASEUNITS]));
if (!strcmp(token, "le")) stack[S - 2].value = creal(stack[S - 2].value) <= creal(stack[S - 1].value); if (!strcmp(token, "le")) stack[S - 2].value = creal(stack[S - 2].value) <= creal(stack[S - 1].value);
else if (!strcmp(token, "lt")) stack[S - 2].value = creal(stack[S - 2].value) < creal(stack[S - 1].value); else if (!strcmp(token, "lt")) stack[S - 2].value = creal(stack[S - 2].value) < creal(stack[S - 1].value);
else if (!strcmp(token, "ge")) stack[S - 2].value = creal(stack[S - 2].value) >= creal(stack[S - 1].value); else if (!strcmp(token, "ge")) stack[S - 2].value = creal(stack[S - 2].value) >= creal(stack[S - 1].value);
else if (!strcmp(token, "gt")) stack[S - 2].value = creal(stack[S - 2].value) > creal(stack[S - 1].value); else if (!strcmp(token, "gt")) stack[S - 2].value = creal(stack[S - 2].value) > creal(stack[S - 1].value);
memset(stack[S - 2].units, 0, sizeof(int8_t[BASEUNITS]));
return S - 1; return S - 1;
} }
else if (!strcmp(token, "quadratic")) { else if (!strcmp(token, "quadratic")) {
@ -704,9 +731,8 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
return S - 1; return S - 1;
} }
else if (!strcmp(token, "rot")) { else if (!strcmp(token, "rot")) {
struct quantity tmp;
if (S < 3) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 3) return QRPN_ERROR_NOT_ENOUGH_STACK;
tmp = stack[S - 3]; struct quantity tmp = stack[S - 3];
stack[S - 3] = stack[S - 2]; stack[S - 3] = stack[S - 2];
stack[S - 2] = stack[S - 1]; stack[S - 2] = stack[S - 1];
stack[S - 1] = tmp; stack[S - 1] = tmp;
@ -715,11 +741,12 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "pow")) { else if (!strcmp(token, "pow")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
if (units_are_dimensionless(stack[S - 2].units)) if (units_are_dimensionless(stack[S - 2].units))
stack[S - 2].value = cpow_checked(stack[S - 2].value, stack[S - 1].value); stack[S - 2].value = cpow_checked(stack[S - 2].value, creal(stack[S - 1].value));
else { else {
const long ipowarg = lrint(stack[S - 1].value); const long ipowarg = lrint(creal(stack[S - 1].value));
if ((double)ipowarg != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER; if ((double)ipowarg != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
long long units_out[BASEUNITS]; long long units_out[BASEUNITS];
@ -738,11 +765,12 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "rpow")) { else if (!strcmp(token, "rpow")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
if (units_are_dimensionless(stack[S - 2].units)) if (units_are_dimensionless(stack[S - 2].units))
stack[S - 2].value = cpow_checked(stack[S - 2].value, 1.0 / stack[S - 1].value); stack[S - 2].value = cpow_checked(stack[S - 2].value, 1.0 / stack[S - 1].value);
else { else {
const long long ipowarg = llrint(stack[S - 1].value); const long long ipowarg = llrint(creal(stack[S - 1].value));
if ((double)ipowarg != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER; if ((double)ipowarg != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
for (size_t iu = 0; iu < BASEUNITS; iu++) for (size_t iu = 0; iu < BASEUNITS; iu++)
if ((stack[S - 2].units[iu] / ipowarg) * ipowarg != stack[S - 2].units[iu]) if ((stack[S - 2].units[iu] / ipowarg) * ipowarg != stack[S - 2].units[iu])
@ -756,7 +784,8 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "gamma")) { else if (!strcmp(token, "gamma")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
stack[S - 1].value = tgamma(stack[S - 1].value); if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
stack[S - 1].value = tgamma(creal(stack[S - 1].value));
return S; return S;
} }
else if (!strcmp(token, "br")) { else if (!strcmp(token, "br")) {
@ -765,9 +794,13 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
!units_are_dimensionless(stack[S - 2].units) || !units_are_dimensionless(stack[S - 2].units) ||
!units_are_dimensionless(stack[S - 3].units) || !units_are_dimensionless(stack[S - 3].units) ||
!units_are_dimensionless(stack[S - 4].units)) return QRPN_ERROR_MUST_BE_UNITLESS; !units_are_dimensionless(stack[S - 4].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
if (cimag(stack[S - 1].value) ||
cimag(stack[S - 2].value) ||
cimag(stack[S - 3].value) ||
cimag(stack[S - 4].value)) return QRPN_ERROR_DOMAIN;
const double a[2] = { stack[S - 4].value, stack[S - 3].value }; const double a[2] = { creal(stack[S - 4].value), creal(stack[S - 3].value) };
const double b[2] = { stack[S - 2].value, stack[S - 1].value }; const double b[2] = { creal(stack[S - 2].value), creal(stack[S - 1].value) };
const double d[2] = { b[0] - a[0], b[1] - a[1] }; const double d[2] = { b[0] - a[0], b[1] - a[1] };
double bearing = 0, range = 0; double bearing = 0, range = 0;
if (d[0] || d[1]) { if (d[0] || d[1]) {
@ -794,10 +827,14 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
!units_are_dimensionless(stack[S - 2].units) || !units_are_dimensionless(stack[S - 2].units) ||
!units_are_dimensionless(stack[S - 3].units) || !units_are_dimensionless(stack[S - 3].units) ||
!units_are_dimensionless(stack[S - 4].units)) return QRPN_ERROR_MUST_BE_UNITLESS; !units_are_dimensionless(stack[S - 4].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
if (cimag(stack[S - 1].value) ||
cimag(stack[S - 2].value) ||
cimag(stack[S - 3].value) ||
cimag(stack[S - 4].value)) return QRPN_ERROR_DOMAIN;
/* todo */ /* todo */
const double in[2] = { stack[S - 4].value, stack[S - 3].value }; const double in[2] = { creal(stack[S - 4].value), creal(stack[S - 3].value) };
const double bearing = stack[S - 2].value, range = stack[S - 1].value; const double bearing = creal(stack[S - 2].value), range = creal(stack[S - 1].value);
/* range, bearing, and declination of start point */ /* range, bearing, and declination of start point */
const double a = range, B = bearing, c = M_PI_2 - in[1]; const double a = range, B = bearing, c = M_PI_2 - in[1];
/* declination of endpoint */ /* declination of endpoint */
@ -814,27 +851,29 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
} }
else if (!strcmp(token, "nextafter")) { else if (!strcmp(token, "nextafter")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
stack[S - 1].value = nextafter(stack[S - 1].value, DBL_MAX); if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
stack[S - 1].value = nextafter(creal(stack[S - 1].value), DBL_MAX);
return S; return S;
} }
else if (!strcmp(token, "nextafterf")) { else if (!strcmp(token, "nextafterf")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
stack[S - 1].value = nextafterf(stack[S - 1].value, FLT_MAX); if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
stack[S - 1].value = nextafterf(crealf(stack[S - 1].value), FLT_MAX);
return S; return S;
} }
else if (!strcmp(token, "arg")) { else if (!strcmp(token, "arg")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
stack[S - 1].value = cargf(stack[S - 1].value); stack[S - 1].value = carg(stack[S - 1].value);
return S; return S;
} }
else if (!strcmp(token, "real")) { else if (!strcmp(token, "real")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
stack[S - 1].value = crealf(stack[S - 1].value); stack[S - 1].value = creal(stack[S - 1].value);
return S; return S;
} }
else if (!strcmp(token, "imaginary")) { else if (!strcmp(token, "imaginary")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
stack[S - 1].value = cimagf(stack[S - 1].value); stack[S - 1].value = cimag(stack[S - 1].value);
return S; return S;
} }
else if (!strcmp(token, "hav")) return evaluate_one_argument_must_be_unitless_real(stack, S, hav); else if (!strcmp(token, "hav")) return evaluate_one_argument_must_be_unitless_real(stack, S, hav);
@ -886,9 +925,10 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "date")) { else if (!strcmp(token, "date")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_equivalent(stack[S - 1].units, units_of_time)) return QRPN_ERROR_INCONSISTENT_UNITS; if (!units_are_equivalent(stack[S - 1].units, units_of_time)) return QRPN_ERROR_INCONSISTENT_UNITS;
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
/* year, month, day, hour, minute, second */ /* year, month, day, hour, minute, second */
time_t unixtime = floor(stack[S - 1].value); time_t unixtime = floor(creal(stack[S - 1].value));
const double remainder = stack[S - 1].value - unixtime; const double remainder = creal(stack[S - 1].value) - unixtime;
struct tm unixtime_struct; struct tm unixtime_struct;
gmtime_r(&unixtime, &unixtime_struct); gmtime_r(&unixtime, &unixtime_struct);
if (S + 5 > QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK; if (S + 5 > QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
@ -917,13 +957,19 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
} }
return S; return S;
} }
else if (!strcmp(token, "print")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
fprintf_quantity(stderr, stack[S - 1]);
fprintf(stderr, "\n");
return S;
}
else else
return evaluate_literal(stack, S, token); return evaluate_literal(stack, S, token);
} }
static void fprintf_value(FILE * fh, const double complex value) { static void fprintf_value(FILE * fh, const double complex value) {
if (creal(value) >= 1e6 && !cimag(value)) if (fabs(creal(value)) >= 1e6 && !cimag(value))
fprintf(fh, "%.15g", creal(value)); fprintf(fh, "%.18g", creal(value));
else if ((!creal(value) && cimag(value)) || fabs(creal(value)) * 1e14 < fabs(cimag(value))) { else if ((!creal(value) && cimag(value)) || fabs(creal(value)) * 1e14 < fabs(cimag(value))) {
if (1.0 == cimag(value)) if (1.0 == cimag(value))
@ -1005,83 +1051,66 @@ void fprintf_quantity(FILE * fh, const struct quantity quantity) {
} }
void fprintf_stack(FILE * fh, struct quantity * stack, const int S) { void fprintf_stack(FILE * fh, struct quantity * stack, const int S) {
if (!S) fprintf(stdout, "[stack is empty]"); if (!S) fprintf(fh, "[stack is empty]");
else for (int is = 0; is < S; is++) { else for (int is = 0; is < S; is++) {
if (S > 1) fprintf(fh, "[");
fprintf_quantity(fh, stack[is]); fprintf_quantity(fh, stack[is]);
if (S > 1) fprintf(fh, "]"); if (is + 1 < S) fprintf(fh, ", ");
if (is + 1 < S) fprintf(fh, " ");
} }
} }
int qrpn_try_token(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const token) {
/* ideally we would have a strong guarantee that qrpn_evaluate_token would not mutate the input if it would result in an error */
struct quantity stack_copy[QRPN_STACK_SIZE_MAX];
memcpy(stack_copy, stack, sizeof(struct quantity) * S);
return qrpn_evaluate_token(stack_copy, S, token);
}
char * qrpn_strerror(const int status) { char * qrpn_strerror(const int status) {
if (status >= 0) return "success"; if (status >= 0) return "success";
else if (QRPN_ERROR_TOKEN_UNRECOGNIZED == status) return "unrecognized";
else if (QRPN_ERROR_NOT_ENOUGH_STACK == status) return "not enough args"; else if (QRPN_ERROR_NOT_ENOUGH_STACK == status) return "not enough args";
else if (QRPN_ERROR_INCONSISTENT_UNITS == status) return "inconsistent units"; else if (QRPN_ERROR_INCONSISTENT_UNITS == status) return "inconsistent units";
else if (QRPN_ERROR_MUST_BE_INTEGER == status) return "arg must be integer"; else if (QRPN_ERROR_MUST_BE_INTEGER == status) return "arg must be integer";
else if (QRPN_ERROR_MUST_BE_UNITLESS == status) return "arg must be unitless"; else if (QRPN_ERROR_MUST_BE_UNITLESS == status) return "arg must be unitless";
else if (QRPN_ERROR_TOKEN_UNRECOGNIZED == status) return "unrecognized"; else if (QRPN_ERROR_MUST_BE_REAL == status) return "arg must be real-valued";
else if (QRPN_ERROR_MUST_BE_NONNEGATIVE == status) return "arg must be nonnegative";
else if (QRPN_ERROR_RATIONAL_NOT_IMPLEMENTED == status) return "noninteger units"; else if (QRPN_ERROR_RATIONAL_NOT_IMPLEMENTED == status) return "noninteger units";
else if (QRPN_ERROR_DOMAIN == status) return "domain error"; else if (QRPN_ERROR_DOMAIN == status) return "domain error";
else if (QRPN_ERROR_DIMENSION_OVERFLOW == status) return "dimension overflow"; else if (QRPN_ERROR_DIMENSION_OVERFLOW == status) return "dimension overflow";
else if (QRPN_ERROR_TOO_MUCH_STACK == status) return "insufficient stack space"; else if (QRPN_ERROR_TOO_MUCH_STACK == status) return "insufficient stack space";
else if (QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT == status) return "unmatched control statement"; else if (QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT == status) return "unmatched control statement";
else if (QRPN_ERROR_INEXACT_LITERAL == status) return "unrepresentable literal";
else return "undefined error"; else return "undefined error";
} }
enum control_statement { ELSE_OR_ENDIF, ENDIF, UNTIL_OR_WHILE, REPEAT }; enum control_statement { NONE, ELSE_OR_ENDIF, ENDIF, UNTIL_OR_WHILE, REPEAT };
const char ** find_matching_control_statement(const char ** tp, const enum control_statement looking_for) { const char ** find_matching_control_statement(const char ** tp, const enum control_statement looking_for) {
for (const char * token; (token = *tp); ) { /* used to skip over branches not taken */
if (!strcmp(token, "until") || !strcmp(token, "while")) { for (const char * token; (token = *tp); tp++) {
if (looking_for == UNTIL_OR_WHILE) return tp; if (!strcmp(token, "until") || !strcmp(token, "while"))
else return NULL; return UNTIL_OR_WHILE == looking_for ? tp : NULL;
} else if (!strcmp(token, "repeat"))
else if (!strcmp(token, "repeat")) { return REPEAT == looking_for ? tp : NULL;
if (looking_for == REPEAT) return tp; else if (!strcmp(token, "else") || !strcmp(token, "endif"))
else return NULL; return ELSE_OR_ENDIF == looking_for || ENDIF == looking_for ? tp : NULL;
}
else if (!strcmp(token, "else") || !strcmp(token, "endif")) {
if (looking_for == ELSE_OR_ENDIF || looking_for == ENDIF) return tp;
else return NULL;
}
else if (!strcmp(token, "if")) { else if (!strcmp(token, "if")) {
const char ** tpc = find_matching_control_statement(tp + 1, ELSE_OR_ENDIF); tp = find_matching_control_statement(tp + 1, ELSE_OR_ENDIF);
if (!tpc) return NULL; if (!tp) return NULL;
else if (!strcmp(*tpc, "else")) { else if (!strcmp(*tp, "else")) {
tpc = find_matching_control_statement(tpc + 1, ENDIF); tp = find_matching_control_statement(tp + 1, ENDIF);
if (!tpc) return NULL; if (!tp) return NULL;
} }
tp = tpc + 1;
} }
else if (!strcmp(token, "begin")) { else if (!strcmp(token, "begin")) {
const char ** tpc = find_matching_control_statement(tp + 1, UNTIL_OR_WHILE); tp = find_matching_control_statement(tp + 1, UNTIL_OR_WHILE);
if (!tpc) return NULL; if (!tp) return NULL;
else if (!strcmp(*tpc, "while")) { else if (!strcmp(*tp, "while")) {
tpc = find_matching_control_statement(tpc + 1, REPEAT); tp = find_matching_control_statement(tp + 1, REPEAT);
if (!tpc) return NULL; if (!tp) return NULL;
} }
tp = tpc + 1;
} }
else tp++;
} }
return NULL; return NULL;
} }
int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** const tokens, const char return_on_block_end) { int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** const tokens, const size_t nest_level) {
for (const char ** tp = tokens, * token; (token = *tp); ) { for (const char ** tp = tokens, * token; (token = *tp); tp++) {
if (!strcmp(token, "else") || !strcmp(token, "endif") || !strcmp(token, "until") || !strcmp(token, "while") || !strcmp(token, "repeat")) { if (!strcmp(token, "else") || !strcmp(token, "endif") || !strcmp(token, "until") || !strcmp(token, "while") || !strcmp(token, "repeat"))
if (return_on_block_end) return S; return nest_level ? S : QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
else return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
}
else if (!strcmp(token, "begin")) { else if (!strcmp(token, "begin")) {
const char ** tp_until_or_while = find_matching_control_statement(tp + 1, UNTIL_OR_WHILE); const char ** tp_until_or_while = find_matching_control_statement(tp + 1, UNTIL_OR_WHILE);
if (!tp_until_or_while) return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT; if (!tp_until_or_while) return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
@ -1091,7 +1120,7 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
if (!tp_repeat) return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT; if (!tp_repeat) return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
while (1) { while (1) {
S = qrpn_evaluate_tokens(stack, S, tp + 1, 1); S = qrpn_evaluate_tokens(stack, S, tp + 1, nest_level + 1);
if (S < 0) return S; if (S < 0) return S;
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
@ -1100,16 +1129,14 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
S--; S--;
if (!stack[S].value) break; if (!stack[S].value) break;
S = qrpn_evaluate_tokens(stack, S, tp_while + 1, 1); S = qrpn_evaluate_tokens(stack, S, tp_while + 1, nest_level + 1);
} }
tp = tp_repeat + 1; tp = tp_repeat;
} }
else { else {
const char ** tp_until = tp_until_or_while;
do { do {
S = qrpn_evaluate_tokens(stack, S, tp + 1, 1); S = qrpn_evaluate_tokens(stack, S, tp + 1, nest_level + 1);
if (S < 0) return S; if (S < 0) return S;
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
@ -1118,7 +1145,7 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
S--; S--;
} while (!stack[S].value); } while (!stack[S].value);
tp = tp_until + 1; tp = tp_until_or_while;
} }
} }
else if (!strcmp(token, "if")) { else if (!strcmp(token, "if")) {
@ -1136,32 +1163,66 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
} }
else tp_endif = tp_else_or_endif; else tp_endif = tp_else_or_endif;
/* choose which branch to take */
const char ** tp_branch = stack[S - 1].value ? tp : tp_else;
S--; S--;
if (stack[S].value) S = qrpn_evaluate_tokens(stack, S, tp_branch + 1, nest_level + 1);
S = qrpn_evaluate_tokens(stack, S, tp + 1, 1);
else if (tp_else)
S = qrpn_evaluate_tokens(stack, S, tp_else + 1, 1);
tp = tp_endif + 1; tp = tp_endif;
} }
else { else {
S = qrpn_evaluate_token(stack, S, token); S = qrpn_evaluate_token(stack, S, token);
if (S < 0) return S; if (S < 0) return S;
tp++;
} }
} }
return S; return S;
} }
int qrpn_evaluate_string(struct quantity * const stack, int S, const char * string) {
const char ** tokens = NULL;
size_t T = 0;
char * const copy = strdup(string);
for (char * token, * p = copy; (token = strsep(&p, " ")); ) {
T++;
tokens = realloc(tokens, sizeof(char *) * (T + 1));
tokens[T - 1] = token;
}
tokens[T] = NULL;
S = qrpn_evaluate_tokens(stack, S, tokens, 0);
free(tokens);
free(copy);
return S;
}
int qrpn_try_token(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const token) {
/* ideally we would have a strong guarantee that qrpn_evaluate_token would not mutate the input if it would result in an error */
struct quantity stack_copy[QRPN_STACK_SIZE_MAX];
memcpy(stack_copy, stack, sizeof(struct quantity) * S);
return qrpn_evaluate_token(stack_copy, S, token);
}
int qrpn_try_string(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const string) {
/* ideally we would have a strong guarantee that qrpn_evaluate_token would not mutate the input if it would result in an error */
struct quantity stack_copy[QRPN_STACK_SIZE_MAX];
memcpy(stack_copy, stack, sizeof(struct quantity) * S);
return qrpn_evaluate_string(stack_copy, S, string);
}
#include <unistd.h> #include <unistd.h>
/* if no other main() is linked, this one will be, and provides a simple command line interface */ /* if no other main() is linked, this one will be, and provides a simple command line interface */
__attribute((weak)) int main(const int argc, const char ** const argv) { __attribute((weak)) int main(const int argc, const char ** const argv) {
if (isatty(STDIN_FILENO) && argc < 2) { if (isatty(STDIN_FILENO) && argc < 2) {
/* never reached */ /* 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); exit(EXIT_FAILURE);
} }
@ -1170,7 +1231,7 @@ __attribute((weak)) int main(const int argc, const char ** const argv) {
int S = qrpn_evaluate_tokens(stack, 0, argv + 1, 0); int S = qrpn_evaluate_tokens(stack, 0, argv + 1, 0);
if (S < 0) { if (S < 0) {
fprintf(stdout, "error: %s\n", qrpn_strerror(S)); fprintf(stderr, "error: %s\n", qrpn_strerror(S));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }

View File

@ -19,6 +19,10 @@ int qrpn_evaluate_token(struct quantity * stack, int S, const char * const token
/* same, but operate on a temporary copy of the input and do not mutate the original */ /* same, but operate on a temporary copy of the input and do not mutate the original */
int qrpn_try_token(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const token); int qrpn_try_token(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const token);
int qrpn_try_string(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const string);
int qrpn_evaluate_string(struct quantity * const stack, int S, const char * string);
/* given the value returned from a function above, return a pointer to a string literal */ /* given the value returned from a function above, return a pointer to a string literal */
char * qrpn_strerror(const int status); char * qrpn_strerror(const int status);
@ -27,15 +31,17 @@ char * qrpn_strerror(const int status);
void fprintf_stack(FILE * fh, struct quantity * stack, const int S); void fprintf_stack(FILE * fh, struct quantity * stack, const int S);
void fprintf_quantity(FILE * fh, const struct quantity quantity); void fprintf_quantity(FILE * fh, const struct quantity quantity);
/* status codes returned by qrpn_evaluate_token */ #define QRPN_ERROR_TOKEN_UNRECOGNIZED -1
#define QRPN_ERROR_NOT_ENOUGH_STACK -1 #define QRPN_ERROR_NOT_ENOUGH_STACK -2
#define QRPN_ERROR_INCONSISTENT_UNITS -2 #define QRPN_ERROR_INCONSISTENT_UNITS -3
#define QRPN_ERROR_MUST_BE_INTEGER -3 #define QRPN_ERROR_MUST_BE_INTEGER -4
#define QRPN_ERROR_TOKEN_UNRECOGNIZED -4 #define QRPN_ERROR_MUST_BE_UNITLESS -5
#define QRPN_ERROR_RATIONAL_NOT_IMPLEMENTED -5 #define QRPN_ERROR_MUST_BE_REAL -6
#define QRPN_ERROR_MUST_BE_UNITLESS -6 #define QRPN_ERROR_MUST_BE_NONNEGATIVE -7
#define QRPN_ERROR_DOMAIN -7 #define QRPN_ERROR_RATIONAL_NOT_IMPLEMENTED -8
#define QRPN_ERROR_DIMENSION_OVERFLOW -8 #define QRPN_ERROR_DOMAIN -9
#define QRPN_ERROR_TOO_MUCH_STACK -9 #define QRPN_ERROR_DIMENSION_OVERFLOW -10
#define QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT -10 #define QRPN_ERROR_TOO_MUCH_STACK -11
#define QRPN_NOT_A_UNIT -11 #define QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT -12
#define QRPN_ERROR_INEXACT_LITERAL -13
#define QRPN_NOT_A_UNIT -14