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:
parent
a2111640af
commit
9172bcd29f
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user