mirror of
https://github.com/pragma-/pbot.git
synced 2024-11-19 10:29:30 +01:00
modules/qrpn: replace stderr with stdout; remove extraneous whitespace throughout
PBot modules do not send the stderr stream to the channel. Instead, the output is logged in <module-filename>-stderr, for private debugging purposes. PBot expects output intended for channels to be on stdout.
This commit is contained in:
parent
9172bcd29f
commit
da7d31229b
@ -70,7 +70,7 @@ static unsigned long long gcd(unsigned long long a, unsigned long long b) {
|
|||||||
b = a % b;
|
b = a % b;
|
||||||
a = t;
|
a = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ static unsigned long long nchoosek(const unsigned long long n, const unsigned lo
|
|||||||
unsigned long long n_choose_k = n * (n - 1) / 2;
|
unsigned long long n_choose_k = n * (n - 1) / 2;
|
||||||
for (size_t kr = 3; kr <= k; kr++)
|
for (size_t kr = 3; kr <= k; kr++)
|
||||||
n_choose_k *= (n + 1 - kr) / kr;
|
n_choose_k *= (n + 1 - kr) / kr;
|
||||||
|
|
||||||
return n_choose_k;
|
return n_choose_k;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,15 +88,15 @@ static unsigned long long ceil_isqrt(unsigned long long n) {
|
|||||||
if (!this) return n;
|
if (!this) return n;
|
||||||
|
|
||||||
for (unsigned long long next = this; (next = (this + n / this) / 2) < this; this = next);
|
for (unsigned long long next = this; (next = (this + n / this) / 2) < this; this = next);
|
||||||
|
|
||||||
return this * this < n ? this + 1 : this;
|
return this * this < n ? this + 1 : this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int isprime(const unsigned long long n) {
|
static int isprime(const unsigned long long n) {
|
||||||
if (n < 2 || !(n % 2)) return 0;
|
if (n < 2 || !(n % 2)) return 0;
|
||||||
|
|
||||||
const unsigned long long stop = ceil_isqrt(n);
|
const unsigned long long stop = ceil_isqrt(n);
|
||||||
|
|
||||||
for (unsigned long long m = 3; m < stop; m += 2)
|
for (unsigned long long m = 3; m < stop; m += 2)
|
||||||
if (!(n % m)) return 0;
|
if (!(n % m)) return 0;
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ static const struct named_quantity named_quantities[] = {
|
|||||||
{ .value = 1.0, .units = { 0, 0, 0, 0, 1, 0, 0 }, .name = "kelvin", .abrv = "K", .flags = FLAG_SI_BASE_UNIT },
|
{ .value = 1.0, .units = { 0, 0, 0, 0, 1, 0, 0 }, .name = "kelvin", .abrv = "K", .flags = FLAG_SI_BASE_UNIT },
|
||||||
{ .value = 1.0, .units = { 0, 0, 0, 0, 0, 1, 0 }, .name = "candela", .abrv = "Cd", .flags = FLAG_SI_BASE_UNIT },
|
{ .value = 1.0, .units = { 0, 0, 0, 0, 0, 1, 0 }, .name = "candela", .abrv = "Cd", .flags = FLAG_SI_BASE_UNIT },
|
||||||
{ .value = 1.0, .units = { 0, 0, 0, 0, 0, 0, 1 }, .name = "mole", .abrv = "mol", .flags = FLAG_SI_BASE_UNIT },
|
{ .value = 1.0, .units = { 0, 0, 0, 0, 0, 0, 1 }, .name = "mole", .abrv = "mol", .flags = FLAG_SI_BASE_UNIT },
|
||||||
|
|
||||||
/* si derived units */
|
/* si derived units */
|
||||||
{ .value = 1.0, .units = { 0, 0, -1, 0, 0, 0, 0 }, .name = "hertz", .abrv = "Hz", .flags = FLAG_SI_DERIVED_UNIT },
|
{ .value = 1.0, .units = { 0, 0, -1, 0, 0, 0, 0 }, .name = "hertz", .abrv = "Hz", .flags = FLAG_SI_DERIVED_UNIT },
|
||||||
{ .value = 1.0, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "newton", .abrv = "N", .flags = FLAG_SI_DERIVED_UNIT },
|
{ .value = 1.0, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "newton", .abrv = "N", .flags = FLAG_SI_DERIVED_UNIT },
|
||||||
@ -182,7 +182,7 @@ static const struct named_quantity named_quantities[] = {
|
|||||||
// { .value = 1.0, .units = { 2, 0, -2, 0, 0, 0, 0 }, .name = "gray", .abrv = "Gy" },
|
// { .value = 1.0, .units = { 2, 0, -2, 0, 0, 0, 0 }, .name = "gray", .abrv = "Gy" },
|
||||||
|
|
||||||
{ .value = 100e3, { -1, 1, -2, 0, 0, 0, 0 }, .name = "bar" },
|
{ .value = 100e3, { -1, 1, -2, 0, 0, 0, 0 }, .name = "bar" },
|
||||||
|
|
||||||
{ .value = 60.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "minute", .abrv = "min" },
|
{ .value = 60.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "minute", .abrv = "min" },
|
||||||
{ .value = 3600.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "hour", .abrv = "h" },
|
{ .value = 3600.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "hour", .abrv = "h" },
|
||||||
{ .value = 86400.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "day" },
|
{ .value = 86400.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "day" },
|
||||||
@ -201,9 +201,9 @@ static const struct named_quantity named_quantities[] = {
|
|||||||
{ .value = 3600.0, .units = { 0, 0, 1, 1, 0, 0, 0 }, .abrv = "Ah" },
|
{ .value = 3600.0, .units = { 0, 0, 1, 1, 0, 0, 0 }, .abrv = "Ah" },
|
||||||
{ .value = 1.0e-2, .units = { 2, 0, -2, 0, 0, 0, 0 }, .name = "rad" },
|
{ .value = 1.0e-2, .units = { 2, 0, -2, 0, 0, 0, 0 }, .name = "rad" },
|
||||||
{ .value = 10e-6, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "dyne" },
|
{ .value = 10e-6, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "dyne" },
|
||||||
|
|
||||||
{ .value = 3.7e10, .units = { 0, 0, -1, 0, 0, 0, 0 }, .name = "curie", .abrv = "Ci" },
|
{ .value = 3.7e10, .units = { 0, 0, -1, 0, 0, 0, 0 }, .name = "curie", .abrv = "Ci" },
|
||||||
|
|
||||||
{ .value = 4.92892159375e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "teaspoon", .abrv = "tsp" },
|
{ .value = 4.92892159375e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "teaspoon", .abrv = "tsp" },
|
||||||
{ .value = 14.78676478125e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "tablespoon", .abrv = "Tbsp" },
|
{ .value = 14.78676478125e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "tablespoon", .abrv = "Tbsp" },
|
||||||
{ .value = 29.5735295625e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "floz" },
|
{ .value = 29.5735295625e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "floz" },
|
||||||
@ -249,13 +249,13 @@ static const struct named_quantity named_quantities[] = {
|
|||||||
{ .value = 101.325e3, .units = { -1, 1, -2, 0, 0, 0, 0 }, .name = "atmosphere", .abrv = "atm" },
|
{ .value = 101.325e3, .units = { -1, 1, -2, 0, 0, 0, 0 }, .name = "atmosphere", .abrv = "atm" },
|
||||||
{ .value = 745.699872, .units = { 2, 1, -3, 0, 0, 0, 0 }, .name = "horsepower", .abrv = "hp" },
|
{ .value = 745.699872, .units = { 2, 1, -3, 0, 0, 0, 0 }, .name = "horsepower", .abrv = "hp" },
|
||||||
{ .value = 0.3048 * 6.0, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "fathom" },
|
{ .value = 0.3048 * 6.0, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "fathom" },
|
||||||
|
|
||||||
{ .value = 0.0254, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "inch", .abrv = "in" },
|
{ .value = 0.0254, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "inch", .abrv = "in" },
|
||||||
{ .value = 0.3048, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "foot", .abrv = "ft" },
|
{ .value = 0.3048, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "foot", .abrv = "ft" },
|
||||||
{ .value = 0.9144, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "yard", .abrv = "yd" },
|
{ .value = 0.9144, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "yard", .abrv = "yd" },
|
||||||
{ .value = 201.168, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "furlong" },
|
{ .value = 201.168, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "furlong" },
|
||||||
{ .value = 3.08567758e16, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "parsec", .abrv = "pc" },
|
{ .value = 3.08567758e16, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "parsec", .abrv = "pc" },
|
||||||
|
|
||||||
{ .value = 0.45359237, .units = { 0, 1, 0, 0, 0, 0, 0 }, .name = "lbm" },
|
{ .value = 0.45359237, .units = { 0, 1, 0, 0, 0, 0, 0 }, .name = "lbm" },
|
||||||
{ .value = 4.448222, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "lbf" },
|
{ .value = 4.448222, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "lbf" },
|
||||||
{ .value = 6.35029318, .units = { 0, 1, 0, 0, 0, 0, 0 }, .name = "stone", .abrv = "st" },
|
{ .value = 6.35029318, .units = { 0, 1, 0, 0, 0, 0, 0 }, .name = "stone", .abrv = "st" },
|
||||||
@ -271,7 +271,7 @@ static int units_are_power_of(const struct quantity * const test, const struct n
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!exponent) return 0;
|
if (!exponent) return 0;
|
||||||
|
|
||||||
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
||||||
if (test->units[iu] != base->units[iu] * exponent) return 0;
|
if (test->units[iu] != base->units[iu] * exponent) return 0;
|
||||||
|
|
||||||
@ -288,11 +288,11 @@ static int units_are_dimensionless(const int8_t in[BASEUNITS]) {
|
|||||||
|
|
||||||
static double datestr_to_unix_seconds(const char * const datestr) {
|
static double datestr_to_unix_seconds(const char * const datestr) {
|
||||||
int64_t seconds = 0, microseconds_after_decimal = 0;
|
int64_t seconds = 0, microseconds_after_decimal = 0;
|
||||||
|
|
||||||
if (strchr(datestr, 'T') && strchr(datestr, 'Z')) {
|
if (strchr(datestr, 'T') && strchr(datestr, 'Z')) {
|
||||||
/* if input is a date string */
|
/* if input is a date string */
|
||||||
struct tm unixtime_struct = { 0 };
|
struct tm unixtime_struct = { 0 };
|
||||||
|
|
||||||
/* if input has colons and dashes, and a subsecond portion... */
|
/* if input has colons and dashes, and a subsecond portion... */
|
||||||
if (strchr(datestr, '-') && strchr(datestr, ':') && strchr(datestr, '.')) {
|
if (strchr(datestr, '-') && strchr(datestr, ':') && strchr(datestr, '.')) {
|
||||||
const uint64_t microseconds_remainder_with_integer = strtod(datestr + 18, NULL) * 1000000;
|
const uint64_t microseconds_remainder_with_integer = strtod(datestr + 18, NULL) * 1000000;
|
||||||
@ -309,46 +309,46 @@ static double datestr_to_unix_seconds(const char * const datestr) {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
strptime(datestr, "%Y%m%dT%H%M%SZ", &unixtime_struct);
|
strptime(datestr, "%Y%m%dT%H%M%SZ", &unixtime_struct);
|
||||||
|
|
||||||
seconds = timegm(&unixtime_struct);
|
seconds = timegm(&unixtime_struct);
|
||||||
} else {
|
} else {
|
||||||
/* otherwise, input is a number */
|
/* otherwise, input is a number */
|
||||||
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(stderr, "warning: %s: ignoring \"%s\"\n", __func__, after);
|
fprintf(stdout, "warning: %s: ignoring \"%s\"\n", __func__, after);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (seconds * 1000000 + microseconds_after_decimal) * 1e-6;
|
return (seconds * 1000000 + microseconds_after_decimal) * 1e-6;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int S, const char * const token, const int exponent_sign) {
|
static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int S, const char * const token, const int exponent_sign) {
|
||||||
const char * slash = strchr(token, '/');
|
const char * slash = strchr(token, '/');
|
||||||
if (slash && exponent_sign < 0) return QRPN_ERROR_TOKEN_UNRECOGNIZED;
|
if (slash && exponent_sign < 0) return QRPN_ERROR_TOKEN_UNRECOGNIZED;
|
||||||
|
|
||||||
const char * const carat = strrchr(token, '^');
|
const char * const carat = strrchr(token, '^');
|
||||||
const size_t bytes_before_carat = carat ? (size_t)(carat - token) : (slash ? (size_t)(slash - token) : strlen(token));
|
const size_t bytes_before_carat = carat ? (size_t)(carat - token) : (slash ? (size_t)(slash - token) : strlen(token));
|
||||||
const long long unit_exponent = exponent_sign * (carat ? strtoll(carat + 1, NULL, 10) : 1);
|
const long long unit_exponent = exponent_sign * (carat ? strtoll(carat + 1, NULL, 10) : 1);
|
||||||
|
|
||||||
const struct named_quantity * quantity = NULL;
|
const struct named_quantity * quantity = NULL;
|
||||||
const struct si_prefix * prefix = NULL;
|
const struct si_prefix * prefix = NULL;
|
||||||
|
|
||||||
/* loop over all known units and prefixes looking for a match */
|
/* 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++)
|
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++) {
|
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;
|
const char * const unit_name = 2 == ipass ? possible_quantity->alt_spelling : 1 == ipass ? possible_quantity->abrv : possible_quantity->name;
|
||||||
if (!unit_name) continue;
|
if (!unit_name) continue;
|
||||||
|
|
||||||
/* get number of bytes in unit name because we're gonna need it many times */
|
/* get number of bytes in unit name because we're gonna need it many times */
|
||||||
const size_t unit_len = strlen(unit_name);
|
const size_t unit_len = strlen(unit_name);
|
||||||
|
|
||||||
if (bytes_before_carat < unit_len || memcmp(token + bytes_before_carat - unit_len, unit_name, unit_len)) continue;
|
if (bytes_before_carat < unit_len || memcmp(token + bytes_before_carat - unit_len, unit_name, unit_len)) continue;
|
||||||
|
|
||||||
const size_t bytes_before_unit = bytes_before_carat - unit_len;
|
const size_t bytes_before_unit = bytes_before_carat - unit_len;
|
||||||
|
|
||||||
prefix = NULL;
|
prefix = NULL;
|
||||||
|
|
||||||
/* loop over known si prefixes */
|
/* loop over known si prefixes */
|
||||||
for (const struct si_prefix * possible_prefix = si_prefixes; !prefix && possible_prefix < si_prefixes + sizeof(si_prefixes) / sizeof(si_prefixes[0]); possible_prefix++) {
|
for (const struct si_prefix * possible_prefix = si_prefixes; !prefix && possible_prefix < si_prefixes + sizeof(si_prefixes) / sizeof(si_prefixes[0]); possible_prefix++) {
|
||||||
/* if looking for SI unit abbreviations, admit SI prefix abbreviations */
|
/* if looking for SI unit abbreviations, admit SI prefix abbreviations */
|
||||||
@ -358,48 +358,48 @@ static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int
|
|||||||
if (bytes_before_unit == prefix_len && !memcmp(token, prefix_name, bytes_before_unit))
|
if (bytes_before_unit == prefix_len && !memcmp(token, prefix_name, bytes_before_unit))
|
||||||
prefix = possible_prefix;
|
prefix = possible_prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if there were bytes before the unit but they didn't match any known prefix, then dont treat as a unit */
|
/* if there were bytes before the unit but they didn't match any known prefix, then dont treat as a unit */
|
||||||
if (!bytes_before_unit || prefix)
|
if (!bytes_before_unit || prefix)
|
||||||
quantity = possible_quantity;
|
quantity = possible_quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* not finding anything above is only an error if there was both a numerator and denominator */
|
/* 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 slash ? QRPN_ERROR_TOKEN_UNRECOGNIZED : QRPN_NOT_A_UNIT;
|
||||||
|
|
||||||
if (quantity->flags & FLAG_UNIT_ENTERS_AS_OPERAND) {
|
if (quantity->flags & FLAG_UNIT_ENTERS_AS_OPERAND) {
|
||||||
if (S >= QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
|
if (S >= QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
|
||||||
S++;
|
S++;
|
||||||
stack[S - 1] = (struct quantity) { .value = 1 };
|
stack[S - 1] = (struct quantity) { .value = 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!S) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
if (!S) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||||
|
|
||||||
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] = (int8_t)(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
||||||
stack[S - 1].units[iu] = units_out[iu];
|
stack[S - 1].units[iu] = units_out[iu];
|
||||||
|
|
||||||
stack[S - 1].value *= pow(prefix ? prefix->scale * quantity->value : quantity->value, unit_exponent);
|
stack[S - 1].value *= pow(prefix ? prefix->scale * quantity->value : quantity->value, unit_exponent);
|
||||||
|
|
||||||
if (slash) return evaluate_unit(stack, S, slash + 1, -exponent_sign);
|
if (slash) return evaluate_unit(stack, S, slash + 1, -exponent_sign);
|
||||||
else return S;
|
else 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);
|
||||||
|
|
||||||
/* if evaluate unit either errored or succeeded, we're done... */
|
/* if evaluate unit either errored or succeeded, we're done... */
|
||||||
if (unit_ret != QRPN_NOT_A_UNIT) return unit_ret;
|
if (unit_ret != QRPN_NOT_A_UNIT) return unit_ret;
|
||||||
|
|
||||||
/* otherwise, token was not a unit name, parse it as a simple literal */
|
/* otherwise, token was not a unit name, parse it as a simple literal */
|
||||||
struct quantity tmp = { 0 };
|
struct quantity tmp = { 0 };
|
||||||
double d = 0, m = 0, s = 0;
|
double d = 0, m = 0, s = 0;
|
||||||
|
|
||||||
if (strpbrk(token + 1, "d°") && sscanf(token, "%lf%*[d°]%lf%*[m']%lf%*[s\"]", &d, &m, &s)) {
|
if (strpbrk(token + 1, "d°") && sscanf(token, "%lf%*[d°]%lf%*[m']%lf%*[s\"]", &d, &m, &s)) {
|
||||||
const double leading = strtod(token, NULL);
|
const double leading = strtod(token, NULL);
|
||||||
tmp.value = copysign(fabs(d) + m / 60.0 + s / 3600.0, leading) * M_PI / 180.0;
|
tmp.value = copysign(fabs(d) + m / 60.0 + s / 3600.0, leading) * M_PI / 180.0;
|
||||||
@ -434,15 +434,15 @@ static int evaluate_literal(struct quantity * stack, int S, const char * const t
|
|||||||
if ('k' == endptr[0]) prefix_scale = 1e3;
|
if ('k' == endptr[0]) prefix_scale = 1e3;
|
||||||
else if ('M' == endptr[0]) prefix_scale = 1e6;
|
else if ('M' == endptr[0]) prefix_scale = 1e6;
|
||||||
else if ('G' == endptr[0]) prefix_scale = 1e9;
|
else if ('G' == endptr[0]) prefix_scale = 1e9;
|
||||||
|
|
||||||
/* special case: trailing 'f' from floating point literals copied and pasted from C code should be ignored */
|
/* 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 if ('f' == endptr[0]) prefix_scale = 1.0;
|
||||||
else return QRPN_ERROR_TOKEN_UNRECOGNIZED;
|
else return QRPN_ERROR_TOKEN_UNRECOGNIZED;
|
||||||
|
|
||||||
tmp.value *= prefix_scale;
|
tmp.value *= prefix_scale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (S >= QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
|
if (S >= QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
|
||||||
stack[S] = tmp;
|
stack[S] = tmp;
|
||||||
return S + 1;
|
return S + 1;
|
||||||
@ -451,7 +451,7 @@ static int evaluate_literal(struct quantity * stack, int S, const char * const t
|
|||||||
static int evaluate_one_argument_must_be_unitless(struct quantity * const stack, int S, double complex (* op)(double complex)) {
|
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 (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 = op(stack[S - 1].value);
|
stack[S - 1].value = op(stack[S - 1].value);
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
@ -459,7 +459,7 @@ static int evaluate_one_argument_must_be_unitless(struct quantity * const stack,
|
|||||||
static int evaluate_one_argument_must_be_unitless_real(struct quantity * const stack, int S, double (* op)(double)) {
|
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 (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 = op(creal(stack[S - 1].value));
|
stack[S - 1].value = op(creal(stack[S - 1].value));
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
@ -469,7 +469,7 @@ static int evaluate_one_argument_must_be_unitless_real_nonnegative(struct quanti
|
|||||||
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) != 0) return QRPN_ERROR_MUST_BE_REAL;
|
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 (creal(stack[S - 1].value) < 0) return QRPN_ERROR_MUST_BE_NONNEGATIVE;
|
||||||
|
|
||||||
stack[S - 1].value = op(creal(stack[S - 1].value));
|
stack[S - 1].value = op(creal(stack[S - 1].value));
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
@ -486,10 +486,10 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
|
|
||||||
/* note that we perform all possible validation before we mutate any state */
|
/* note that we perform all possible validation before we mutate any state */
|
||||||
stack[S - 2].value *= stack[S - 1].value;
|
stack[S - 2].value *= stack[S - 1].value;
|
||||||
|
|
||||||
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
||||||
stack[S - 2].units[iu] = units_out[iu];
|
stack[S - 2].units[iu] = units_out[iu];
|
||||||
|
|
||||||
return S - 1;
|
return S - 1;
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "div") || !strcmp(token, "/")) {
|
else if (!strcmp(token, "div") || !strcmp(token, "/")) {
|
||||||
@ -499,7 +499,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
units_out[iu] = stack[S - 2].units[iu] - stack[S - 1].units[iu];
|
units_out[iu] = stack[S - 2].units[iu] - stack[S - 1].units[iu];
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
stack[S - 2].value /= stack[S - 1].value;
|
stack[S - 2].value /= stack[S - 1].value;
|
||||||
|
|
||||||
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
||||||
@ -509,7 +509,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
}
|
}
|
||||||
else if (!strcmp(token, "idiv")) {
|
else if (!strcmp(token, "idiv")) {
|
||||||
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;
|
||||||
|
|
||||||
const long long a = llrint(creal(stack[S - 2].value));
|
const long long a = llrint(creal(stack[S - 2].value));
|
||||||
@ -569,7 +569,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
else if (!strcmp(token, "chs")) {
|
else if (!strcmp(token, "chs")) {
|
||||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||||
stack[S - 1].value *= -1;
|
stack[S - 1].value *= -1;
|
||||||
|
|
||||||
/* always choose positive imaginary side of negative real line because [1] [chs] [sqrt] is pretty common */
|
/* always choose positive imaginary side of negative real line because [1] [chs] [sqrt] is pretty common */
|
||||||
if (__imag__ stack[S - 1].value == -0)
|
if (__imag__ stack[S - 1].value == -0)
|
||||||
__imag__ stack[S - 1].value = 0;
|
__imag__ stack[S - 1].value = 0;
|
||||||
@ -585,7 +585,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
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);
|
||||||
|
|
||||||
return S - 1;
|
return S - 1;
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "gcd")) {
|
else if (!strcmp(token, "gcd")) {
|
||||||
@ -660,28 +660,28 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
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;
|
||||||
|
|
||||||
stack[S - 2].value = (!!stack[S - 2].value) && (!!stack[S - 1].value);
|
stack[S - 2].value = (!!stack[S - 2].value) && (!!stack[S - 1].value);
|
||||||
return S - 1;
|
return S - 1;
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "or")) {
|
else if (!strcmp(token, "or")) {
|
||||||
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;
|
||||||
|
|
||||||
stack[S - 2].value = (!!stack[S - 2].value) || (!!stack[S - 1].value);
|
stack[S - 2].value = (!!stack[S - 2].value) || (!!stack[S - 1].value);
|
||||||
return S - 1;
|
return S - 1;
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "not")) {
|
else if (!strcmp(token, "not")) {
|
||||||
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 = !stack[S - 1].value;
|
stack[S - 1].value = !stack[S - 1].value;
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "eq")) {
|
else if (!strcmp(token, "eq")) {
|
||||||
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 = stack[S - 2].value == stack[S - 1].value;
|
stack[S - 2].value = stack[S - 2].value == 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;
|
||||||
@ -713,16 +713,16 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
const double complex b = stack[S - 2].value;
|
const double complex b = stack[S - 2].value;
|
||||||
const double complex c = stack[S - 1].value;
|
const double complex c = stack[S - 1].value;
|
||||||
const double complex discriminant = b * b - 4.0 * a * c;
|
const double complex discriminant = b * b - 4.0 * a * c;
|
||||||
|
|
||||||
const double complex d = 0.5 / a;
|
const double complex d = 0.5 / a;
|
||||||
const double complex e = csqrt(discriminant);
|
const double complex e = csqrt(discriminant);
|
||||||
|
|
||||||
/* well-conditioned floating point method of getting roots, which avoids subtracting two nearly equal magnitude numbers */
|
/* well-conditioned floating point method of getting roots, which avoids subtracting two nearly equal magnitude numbers */
|
||||||
const double complex r1 = __real__ e > 0 ? (-b - e) * d : (-b + e) * d;
|
const double complex r1 = __real__ e > 0 ? (-b - e) * d : (-b + e) * d;
|
||||||
const double complex r0 = c / (r1 * a);
|
const double complex r0 = c / (r1 * a);
|
||||||
stack[S - 3].value = r1;
|
stack[S - 3].value = r1;
|
||||||
stack[S - 2].value = r0;
|
stack[S - 2].value = r0;
|
||||||
|
|
||||||
for (size_t iu = 0; iu < BASEUNITS; iu++) {
|
for (size_t iu = 0; iu < BASEUNITS; iu++) {
|
||||||
stack[S - 3].units[iu] = units_out[iu];
|
stack[S - 3].units[iu] = units_out[iu];
|
||||||
stack[S - 2].units[iu] = units_out[iu];
|
stack[S - 2].units[iu] = units_out[iu];
|
||||||
@ -754,7 +754,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
units_out[iu] = stack[S - 2].units[iu] * ipowarg;
|
units_out[iu] = stack[S - 2].units[iu] * ipowarg;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
stack[S - 2].value = cpow_checked(stack[S - 2].value, stack[S - 1].value);
|
stack[S - 2].value = cpow_checked(stack[S - 2].value, stack[S - 1].value);
|
||||||
|
|
||||||
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
||||||
@ -783,7 +783,7 @@ 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;
|
||||||
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
||||||
stack[S - 1].value = tgamma(creal(stack[S - 1].value));
|
stack[S - 1].value = tgamma(creal(stack[S - 1].value));
|
||||||
return S;
|
return S;
|
||||||
@ -822,7 +822,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
stack[S - 1].units[0] = 0;
|
stack[S - 1].units[0] = 0;
|
||||||
stack[S - 1].value /= 6371000.0;
|
stack[S - 1].value /= 6371000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!units_are_dimensionless(stack[S - 1].units) ||
|
if (!units_are_dimensionless(stack[S - 1].units) ||
|
||||||
!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) ||
|
||||||
@ -841,12 +841,12 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
const double b = ahav( hav(a - c) + sin(a) * sin(c) * hav(B) );
|
const double b = ahav( hav(a - c) + sin(a) * sin(c) * hav(B) );
|
||||||
/* change in longitude */
|
/* change in longitude */
|
||||||
const double A = atan2( sin(B) * sin(a) * sin(c), cos(a) - cos(c) * cos(b) );
|
const double A = atan2( sin(B) * sin(a) * sin(c), cos(a) - cos(c) * cos(b) );
|
||||||
|
|
||||||
/* endpoint longitude is start plus delta */
|
/* endpoint longitude is start plus delta */
|
||||||
stack[S - 4].value = in[0] + A;
|
stack[S - 4].value = in[0] + A;
|
||||||
/* endpoint latitude is 90 degrees minus endpoint declination */
|
/* endpoint latitude is 90 degrees minus endpoint declination */
|
||||||
stack[S - 3].value = M_PI_2 - b;
|
stack[S - 3].value = M_PI_2 - b;
|
||||||
|
|
||||||
return S - 2;
|
return S - 2;
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "nextafter")) {
|
else if (!strcmp(token, "nextafter")) {
|
||||||
@ -901,11 +901,11 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
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_nonnegative(stack, S, itenlog);
|
||||||
else if (!strcmp(token, "square")) {
|
else if (!strcmp(token, "square")) {
|
||||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||||
|
|
||||||
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
||||||
if (stack[S - 1].units[iu] * 2 > INT8_MAX || stack[S - 1].units[iu] * 2 < INT8_MIN)
|
if (stack[S - 1].units[iu] * 2 > INT8_MAX || stack[S - 1].units[iu] * 2 < INT8_MIN)
|
||||||
return QRPN_ERROR_DIMENSION_OVERFLOW;
|
return QRPN_ERROR_DIMENSION_OVERFLOW;
|
||||||
|
|
||||||
stack[S - 1].value = stack[S - 1].value * stack[S - 1].value;
|
stack[S - 1].value = stack[S - 1].value * stack[S - 1].value;
|
||||||
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
||||||
stack[S - 1].units[iu] *= 2;
|
stack[S - 1].units[iu] *= 2;
|
||||||
@ -959,8 +959,8 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
|||||||
}
|
}
|
||||||
else if (!strcmp(token, "print")) {
|
else if (!strcmp(token, "print")) {
|
||||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||||
fprintf_quantity(stderr, stack[S - 1]);
|
fprintf_quantity(stdout, stack[S - 1]);
|
||||||
fprintf(stderr, "\n");
|
fprintf(stdout, "\n");
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -980,10 +980,10 @@ static void fprintf_value(FILE * fh, const double complex value) {
|
|||||||
fprintf(fh, "%gi", cimag(value));
|
fprintf(fh, "%gi", cimag(value));
|
||||||
} else {
|
} else {
|
||||||
fprintf(fh, "%g", creal(value));
|
fprintf(fh, "%g", creal(value));
|
||||||
|
|
||||||
if (cimag(value) && fabs(cimag(value)) * 1e14 > fabs(creal(value))) {
|
if (cimag(value) && fabs(cimag(value)) * 1e14 > fabs(creal(value))) {
|
||||||
fprintf(fh, " %c ", cimag(value) > 0 ? '+' : '-');
|
fprintf(fh, " %c ", cimag(value) > 0 ? '+' : '-');
|
||||||
|
|
||||||
fprintf(fh, "%gi", fabs(cimag(value)));
|
fprintf(fh, "%gi", fabs(cimag(value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -992,17 +992,17 @@ static void fprintf_value(FILE * fh, const double complex value) {
|
|||||||
static void fprintf_quantity_si_base(FILE * fh, const struct quantity quantity) {
|
static void fprintf_quantity_si_base(FILE * fh, const struct quantity quantity) {
|
||||||
/* only use si base units */
|
/* only use si base units */
|
||||||
fprintf_value(fh, quantity.value);
|
fprintf_value(fh, quantity.value);
|
||||||
|
|
||||||
static const char * si_base_unit_abbreviations[BASEUNITS] = { "m", "kg", "s", "A", "K", "Cd", "mol" };
|
static const char * si_base_unit_abbreviations[BASEUNITS] = { "m", "kg", "s", "A", "K", "Cd", "mol" };
|
||||||
|
|
||||||
for (size_t iu = 0; iu < BASEUNITS; iu++) {
|
for (size_t iu = 0; iu < BASEUNITS; iu++) {
|
||||||
if (quantity.units[iu] > 0)
|
if (quantity.units[iu] > 0)
|
||||||
fprintf(fh, " %s", si_base_unit_abbreviations[iu]);
|
fprintf(fh, " %s", si_base_unit_abbreviations[iu]);
|
||||||
|
|
||||||
if (quantity.units[iu] > 1)
|
if (quantity.units[iu] > 1)
|
||||||
fprintf(fh, "^%d", quantity.units[iu]);
|
fprintf(fh, "^%d", quantity.units[iu]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
||||||
if (quantity.units[iu] < 0)
|
if (quantity.units[iu] < 0)
|
||||||
fprintf(fh, " %s^%d", si_base_unit_abbreviations[iu], quantity.units[iu]);
|
fprintf(fh, " %s^%d", si_base_unit_abbreviations[iu], quantity.units[iu]);
|
||||||
@ -1016,14 +1016,14 @@ static void fprintf_quantity_si(FILE * fh, const struct quantity quantity) {
|
|||||||
/* i hate everything about this and so should you */
|
/* i hate everything about this and so should you */
|
||||||
if (named->flags & (FLAG_SI_BASE_UNIT | FLAG_SI_DERIVED_UNIT) && (exponent = units_are_power_of(&quantity, named)) * sign > 0) {
|
if (named->flags & (FLAG_SI_BASE_UNIT | FLAG_SI_DERIVED_UNIT) && (exponent = units_are_power_of(&quantity, named)) * sign > 0) {
|
||||||
fprintf_value(fh, quantity.value / named->value);
|
fprintf_value(fh, quantity.value / named->value);
|
||||||
|
|
||||||
fprintf(fh, " %s", named->name ? named->name : named->abrv);
|
fprintf(fh, " %s", named->name ? named->name : named->abrv);
|
||||||
if (1 != exponent) fprintf(fh, "^%d", exponent);
|
if (1 != exponent) fprintf(fh, "^%d", exponent);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if we get here we're just looping through the SI base units */
|
/* if we get here we're just looping through the SI base units */
|
||||||
return fprintf_quantity_si_base(fh, quantity);
|
return fprintf_quantity_si_base(fh, quantity);
|
||||||
}
|
}
|
||||||
@ -1138,20 +1138,20 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
|
|||||||
do {
|
do {
|
||||||
S = qrpn_evaluate_tokens(stack, S, tp + 1, nest_level + 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;
|
||||||
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;
|
||||||
|
|
||||||
S--;
|
S--;
|
||||||
} while (!stack[S].value);
|
} while (!stack[S].value);
|
||||||
|
|
||||||
tp = tp_until_or_while;
|
tp = tp_until_or_while;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "if")) {
|
else if (!strcmp(token, "if")) {
|
||||||
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;
|
||||||
|
|
||||||
const char ** tp_else_or_endif = find_matching_control_statement(tp + 1, ELSE_OR_ENDIF);
|
const char ** tp_else_or_endif = find_matching_control_statement(tp + 1, ELSE_OR_ENDIF);
|
||||||
const char ** tp_else = NULL, ** tp_endif;
|
const char ** tp_else = NULL, ** tp_endif;
|
||||||
|
|
||||||
@ -1162,11 +1162,11 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
|
|||||||
if (!tp_endif) return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
|
if (!tp_endif) return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
|
||||||
}
|
}
|
||||||
else tp_endif = tp_else_or_endif;
|
else tp_endif = tp_else_or_endif;
|
||||||
|
|
||||||
/* choose which branch to take */
|
/* choose which branch to take */
|
||||||
const char ** tp_branch = stack[S - 1].value ? tp : tp_else;
|
const char ** tp_branch = stack[S - 1].value ? tp : tp_else;
|
||||||
S--;
|
S--;
|
||||||
|
|
||||||
S = qrpn_evaluate_tokens(stack, S, tp_branch + 1, nest_level + 1);
|
S = qrpn_evaluate_tokens(stack, S, tp_branch + 1, nest_level + 1);
|
||||||
|
|
||||||
tp = tp_endif;
|
tp = tp_endif;
|
||||||
@ -1182,21 +1182,21 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
|
|||||||
int qrpn_evaluate_string(struct quantity * const stack, int S, const char * string) {
|
int qrpn_evaluate_string(struct quantity * const stack, int S, const char * string) {
|
||||||
const char ** tokens = NULL;
|
const char ** tokens = NULL;
|
||||||
size_t T = 0;
|
size_t T = 0;
|
||||||
|
|
||||||
char * const copy = strdup(string);
|
char * const copy = strdup(string);
|
||||||
for (char * token, * p = copy; (token = strsep(&p, " ")); ) {
|
for (char * token, * p = copy; (token = strsep(&p, " ")); ) {
|
||||||
T++;
|
T++;
|
||||||
tokens = realloc(tokens, sizeof(char *) * (T + 1));
|
tokens = realloc(tokens, sizeof(char *) * (T + 1));
|
||||||
tokens[T - 1] = token;
|
tokens[T - 1] = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens[T] = NULL;
|
tokens[T] = NULL;
|
||||||
|
|
||||||
S = qrpn_evaluate_tokens(stack, S, tokens, 0);
|
S = qrpn_evaluate_tokens(stack, S, tokens, 0);
|
||||||
|
|
||||||
free(tokens);
|
free(tokens);
|
||||||
free(copy);
|
free(copy);
|
||||||
|
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1222,21 +1222,21 @@ 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) {
|
__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(stderr, "%s: Evaluates an RPN expression with units\n", argv[0]);
|
fprintf(stdout, "%s: Evaluates an RPN expression with units\n", argv[0]);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct quantity stack[QRPN_STACK_SIZE_MAX];
|
struct quantity stack[QRPN_STACK_SIZE_MAX];
|
||||||
|
|
||||||
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(stderr, "error: %s\n", qrpn_strerror(S));
|
fprintf(stdout, "error: %s\n", qrpn_strerror(S));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf_stack(stdout, stack, S);
|
fprintf_stack(stdout, stack, S);
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user