mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-30 06:03:49 +08:00
math: Give a proper error for division by zero
This errored out *later* because the result was infinite or NaN, but it didn't actually stop evaluation. I'm not sure if there is a way to get floating point math to turn an infinity back into something that doesn't depend on a literal infinity, but division by zero conceptually isn't a thing we can support. There's entire branches of maths dedicated to figuring out what dividing by "basically zero" means and we don't have to get into it.
This commit is contained in:
parent
7f1e9bf57f
commit
5edba044a3
|
@ -177,6 +177,8 @@ static const wchar_t *math_describe_error(const te_error_t &error) {
|
||||||
return _(L"Unexpected token");
|
return _(L"Unexpected token");
|
||||||
case TE_ERROR_LOGICAL_OPERATOR:
|
case TE_ERROR_LOGICAL_OPERATOR:
|
||||||
return _(L"Logical operations are not supported, use `test` instead");
|
return _(L"Logical operations are not supported, use `test` instead");
|
||||||
|
case TE_ERROR_DIV_BY_ZERO:
|
||||||
|
return _(L"Division by zero");
|
||||||
case TE_ERROR_UNKNOWN:
|
case TE_ERROR_UNKNOWN:
|
||||||
return _(L"Expression is bogus");
|
return _(L"Expression is bogus");
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -114,7 +114,10 @@ struct state {
|
||||||
|
|
||||||
[[nodiscard]] te_error_t error() const {
|
[[nodiscard]] te_error_t error() const {
|
||||||
if (type_ == TOK_END) return {TE_ERROR_NONE, 0};
|
if (type_ == TOK_END) return {TE_ERROR_NONE, 0};
|
||||||
te_error_t err{error_, static_cast<int>(next_ - start_) + 1};
|
// If we have an error position set, use that,
|
||||||
|
// otherwise the current position.
|
||||||
|
const wchar_t *tok = errpos_ ? errpos_ : next_;
|
||||||
|
te_error_t err{error_, static_cast<int>(tok - start_) + 1};
|
||||||
if (error_ == TE_ERROR_NONE) {
|
if (error_ == TE_ERROR_NONE) {
|
||||||
// If we're not at the end but there's no error, then that means we have a
|
// If we're not at the end but there's no error, then that means we have a
|
||||||
// superfluous token that we have no idea what to do with.
|
// superfluous token that we have no idea what to do with.
|
||||||
|
@ -129,6 +132,7 @@ struct state {
|
||||||
|
|
||||||
const wchar_t *start_;
|
const wchar_t *start_;
|
||||||
const wchar_t *next_;
|
const wchar_t *next_;
|
||||||
|
const wchar_t *errpos_{nullptr};
|
||||||
|
|
||||||
te_fun_t current_{NAN};
|
te_fun_t current_{NAN};
|
||||||
void next_token();
|
void next_token();
|
||||||
|
@ -520,8 +524,17 @@ double state::term() {
|
||||||
auto ret = factor();
|
auto ret = factor();
|
||||||
while (type_ == TOK_INFIX && (current_ == mul || current_ == divide || current_ == fmod)) {
|
while (type_ == TOK_INFIX && (current_ == mul || current_ == divide || current_ == fmod)) {
|
||||||
auto fn = current_;
|
auto fn = current_;
|
||||||
|
auto tok = next_;
|
||||||
next_token();
|
next_token();
|
||||||
ret = fn(ret, factor());
|
auto ret2 = factor();
|
||||||
|
if (ret2 == 0 && (fn == divide || fn == fmod)) {
|
||||||
|
// Division by zero (also for modulo)
|
||||||
|
type_ = TOK_ERROR;
|
||||||
|
error_ = TE_ERROR_DIV_BY_ZERO;
|
||||||
|
// Error position is the "/" or "%" sign for now
|
||||||
|
errpos_ = tok;
|
||||||
|
}
|
||||||
|
ret = fn(ret, ret2);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ typedef enum {
|
||||||
TE_ERROR_MISSING_OPERATOR = 6,
|
TE_ERROR_MISSING_OPERATOR = 6,
|
||||||
TE_ERROR_UNEXPECTED_TOKEN = 7,
|
TE_ERROR_UNEXPECTED_TOKEN = 7,
|
||||||
TE_ERROR_LOGICAL_OPERATOR = 8,
|
TE_ERROR_LOGICAL_OPERATOR = 8,
|
||||||
TE_ERROR_UNKNOWN = 9
|
TE_ERROR_DIV_BY_ZERO = 9,
|
||||||
|
TE_ERROR_UNKNOWN = 10
|
||||||
} te_error_type_t;
|
} te_error_type_t;
|
||||||
|
|
||||||
typedef struct te_error_t {
|
typedef struct te_error_t {
|
||||||
|
|
|
@ -151,9 +151,14 @@ not math -s 12
|
||||||
not math 2^999999
|
not math 2^999999
|
||||||
# CHECKERR: math: Error: Result is infinite
|
# CHECKERR: math: Error: Result is infinite
|
||||||
# CHECKERR: '2^999999'
|
# CHECKERR: '2^999999'
|
||||||
not math 1 / 0
|
printf '<%s>\n' (not math 1 / 0 2>&1)
|
||||||
# CHECKERR: math: Error: Result is infinite
|
# CHECK: <math: Error: Division by zero>
|
||||||
# CHECKERR: '1 / 0'
|
# CHECK: <'1 / 0'>
|
||||||
|
# CHECK: < ^>
|
||||||
|
printf '<%s>\n' (math 1 % 0 - 5 2>&1)
|
||||||
|
# CHECK: <math: Error: Division by zero>
|
||||||
|
# CHECK: <'1 % 0 - 5'>
|
||||||
|
# CHECK: < ^>
|
||||||
|
|
||||||
# Validate "x" as multiplier
|
# Validate "x" as multiplier
|
||||||
math 0x2 # Hex
|
math 0x2 # Hex
|
||||||
|
@ -259,8 +264,9 @@ math pow 2 x cos'(-pi)', 2
|
||||||
# This used to take ages, see #8170.
|
# This used to take ages, see #8170.
|
||||||
# If this test hangs, that's reintroduced!
|
# If this test hangs, that's reintroduced!
|
||||||
math 'ncr(0/0, 1)'
|
math 'ncr(0/0, 1)'
|
||||||
# CHECKERR: math: Error: Result is infinite
|
# CHECKERR: math: Error: Division by zero
|
||||||
# CHECKERR: 'ncr(0/0, 1)'
|
# CHECKERR: 'ncr(0/0, 1)'
|
||||||
|
# CHECKERR: ^
|
||||||
|
|
||||||
# Variadic functions require at least one argument
|
# Variadic functions require at least one argument
|
||||||
math min
|
math min
|
||||||
|
|
Loading…
Reference in New Issue
Block a user