diff --git a/src/builtins/math.cpp b/src/builtins/math.cpp index c7d136add..9aba8f290 100644 --- a/src/builtins/math.cpp +++ b/src/builtins/math.cpp @@ -177,6 +177,8 @@ static const wchar_t *math_describe_error(const te_error_t &error) { return _(L"Unexpected token"); case TE_ERROR_LOGICAL_OPERATOR: 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: return _(L"Expression is bogus"); default: diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index dd499e25a..af4f4802f 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -114,7 +114,10 @@ struct state { [[nodiscard]] te_error_t error() const { if (type_ == TOK_END) return {TE_ERROR_NONE, 0}; - te_error_t err{error_, static_cast(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(tok - start_) + 1}; if (error_ == TE_ERROR_NONE) { // 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. @@ -129,6 +132,7 @@ struct state { const wchar_t *start_; const wchar_t *next_; + const wchar_t *errpos_{nullptr}; te_fun_t current_{NAN}; void next_token(); @@ -520,8 +524,17 @@ double state::term() { auto ret = factor(); while (type_ == TOK_INFIX && (current_ == mul || current_ == divide || current_ == fmod)) { auto fn = current_; + auto tok = next_; 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; } diff --git a/src/tinyexpr.h b/src/tinyexpr.h index 76b930722..c722417af 100644 --- a/src/tinyexpr.h +++ b/src/tinyexpr.h @@ -37,7 +37,8 @@ typedef enum { TE_ERROR_MISSING_OPERATOR = 6, TE_ERROR_UNEXPECTED_TOKEN = 7, TE_ERROR_LOGICAL_OPERATOR = 8, - TE_ERROR_UNKNOWN = 9 + TE_ERROR_DIV_BY_ZERO = 9, + TE_ERROR_UNKNOWN = 10 } te_error_type_t; typedef struct te_error_t { diff --git a/tests/checks/math.fish b/tests/checks/math.fish index a2942969f..17e5010c3 100644 --- a/tests/checks/math.fish +++ b/tests/checks/math.fish @@ -151,9 +151,14 @@ not math -s 12 not math 2^999999 # CHECKERR: math: Error: Result is infinite # CHECKERR: '2^999999' -not math 1 / 0 -# CHECKERR: math: Error: Result is infinite -# CHECKERR: '1 / 0' +printf '<%s>\n' (not math 1 / 0 2>&1) +# CHECK: +# CHECK: <'1 / 0'> +# CHECK: < ^> +printf '<%s>\n' (math 1 % 0 - 5 2>&1) +# CHECK: +# CHECK: <'1 % 0 - 5'> +# CHECK: < ^> # Validate "x" as multiplier math 0x2 # Hex @@ -259,8 +264,9 @@ math pow 2 x cos'(-pi)', 2 # This used to take ages, see #8170. # If this test hangs, that's reintroduced! math 'ncr(0/0, 1)' -# CHECKERR: math: Error: Result is infinite +# CHECKERR: math: Error: Division by zero # CHECKERR: 'ncr(0/0, 1)' +# CHECKERR: ^ # Variadic functions require at least one argument math min