mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 05:13:10 +00:00
Implement support for bare vars by math
This change allows you to type `math x + 3` rather than `math $x + 3`. Another step to resolving issue #3157.
This commit is contained in:
parent
d10decabda
commit
c95b9f06e1
4 changed files with 77 additions and 2 deletions
|
@ -108,22 +108,72 @@ static const wchar_t *math_get_arg_argv(int *argidx, wchar_t **argv) {
|
||||||
/// Get the arguments from argv or stdin based on the execution context. This mimics how builtin
|
/// Get the arguments from argv or stdin based on the execution context. This mimics how builtin
|
||||||
/// `string` does it.
|
/// `string` does it.
|
||||||
static const wchar_t *math_get_arg(int *argidx, wchar_t **argv, wcstring *storage,
|
static const wchar_t *math_get_arg(int *argidx, wchar_t **argv, wcstring *storage,
|
||||||
const io_streams_t &streams) {
|
const io_streams_t &streams) {
|
||||||
if (math_args_from_stdin(streams)) {
|
if (math_args_from_stdin(streams)) {
|
||||||
return math_get_arg_stdin(storage, streams);
|
return math_get_arg_stdin(storage, streams);
|
||||||
}
|
}
|
||||||
return math_get_arg_argv(argidx, argv);
|
return math_get_arg_argv(argidx, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The MuParser mechanism for dynamic lookup of variables requires that we return a unique address
|
||||||
|
// for each variable. The following limit is arbitrary but anyone writing a math expression in fish
|
||||||
|
// that references more than one hundred unique variables is abusing fish.
|
||||||
|
#define MAX_RESULTS 100
|
||||||
|
static double double_results[MAX_RESULTS];
|
||||||
|
static int next_result = 0;
|
||||||
|
|
||||||
|
/// Return a fish var converted to a double. This allows the user to use a bar var name in the
|
||||||
|
/// expression. That is `math a + 1` rather than `math $a + 1`.
|
||||||
|
static double *retrieve_var(const wchar_t *var_name, void *user_data) {
|
||||||
|
UNUSED(user_data);
|
||||||
|
static double zero_result = 0.0;
|
||||||
|
|
||||||
|
env_var_t var = env_get(var_name, ENV_DEFAULT);
|
||||||
|
if (var.missing()) {
|
||||||
|
// We could report an error but we normally don't treat missing vars as a fatal error.
|
||||||
|
// throw mu::ParserError(L"Var '%ls' does not exist.");
|
||||||
|
return &zero_result;
|
||||||
|
}
|
||||||
|
if (var.empty()) {
|
||||||
|
return &zero_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t *first_val = var.as_const_list()[0].c_str();
|
||||||
|
wchar_t *endptr;
|
||||||
|
errno = 0;
|
||||||
|
double result = wcstod(first_val, &endptr);
|
||||||
|
if (*endptr != L'\0' || errno) {
|
||||||
|
wchar_t errmsg[500];
|
||||||
|
swprintf(errmsg, sizeof(errmsg) / sizeof(wchar_t),
|
||||||
|
_(L"Var '%ls' not a valid floating point number: '%ls'."), var_name, first_val);
|
||||||
|
throw mu::ParserError(errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to return a unique address for the var. If we used a `static double` var and returned
|
||||||
|
// it's address then multiple vars in the expression would all refer to the same value.
|
||||||
|
if (next_result == MAX_RESULTS - 1) {
|
||||||
|
wchar_t errmsg[500];
|
||||||
|
swprintf(errmsg, sizeof(errmsg) / sizeof(wchar_t),
|
||||||
|
_(L"More than %d var names in math expression."), MAX_RESULTS);
|
||||||
|
throw mu::ParserError(errmsg);
|
||||||
|
}
|
||||||
|
double_results[next_result++] = result;
|
||||||
|
return double_results + next_result - 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// Implement integer modulo math operator.
|
/// Implement integer modulo math operator.
|
||||||
static double moduloOperator(double v, double w) { return (int)v % std::max(1, (int)w); };
|
static double moduloOperator(double v, double w) { return (int)v % std::max(1, (int)w); };
|
||||||
|
|
||||||
|
/// Evaluate math expressions.
|
||||||
static int evaluate_expression(wchar_t *cmd, parser_t &parser, io_streams_t &streams,
|
static int evaluate_expression(wchar_t *cmd, parser_t &parser, io_streams_t &streams,
|
||||||
math_cmd_opts_t &opts, wcstring &expression) {
|
math_cmd_opts_t &opts, wcstring &expression) {
|
||||||
UNUSED(parser);
|
UNUSED(parser);
|
||||||
|
next_result = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mu::Parser p;
|
mu::Parser p;
|
||||||
|
// Setup callback so variables can be retrieved dynamically.
|
||||||
|
p.SetVarFactory(retrieve_var, nullptr);
|
||||||
// MuParser doesn't implement the modulo operator so we add it ourselves since there are
|
// MuParser doesn't implement the modulo operator so we add it ourselves since there are
|
||||||
// likely users of our old math wrapper around bc that expect it to be available.
|
// likely users of our old math wrapper around bc that expect it to be available.
|
||||||
p.DefineOprtChars(L"%");
|
p.DefineOprtChars(L"%");
|
||||||
|
@ -139,7 +189,6 @@ static int evaluate_expression(wchar_t *cmd, parser_t &parser, io_streams_t &str
|
||||||
streams.out.append_format(L"%.*lf\n", opts.scale, v[i]);
|
streams.out.append_format(L"%.*lf\n", opts.scale, v[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return STATUS_CMD_OK;
|
return STATUS_CMD_OK;
|
||||||
} catch (mu::Parser::exception_type &e) {
|
} catch (mu::Parser::exception_type &e) {
|
||||||
streams.err.append_format(_(L"%ls: Invalid expression: %ls\n"), cmd, e.GetMsg().c_str());
|
streams.err.append_format(_(L"%ls: Invalid expression: %ls\n"), cmd, e.GetMsg().c_str());
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
####################
|
||||||
|
# Validate basic expressions
|
||||||
|
|
||||||
|
####################
|
||||||
|
# Validate how bare variables in an epxression are handled
|
|
@ -1,3 +1,4 @@
|
||||||
|
logmsg Validate basic expressions
|
||||||
math 3 / 2
|
math 3 / 2
|
||||||
math 10/6
|
math 10/6
|
||||||
math -s0 10 / 6
|
math -s0 10 / 6
|
||||||
|
@ -12,3 +13,12 @@ math '-2 * -2'
|
||||||
math 5 \* -2
|
math 5 \* -2
|
||||||
math -- -4 / 2
|
math -- -4 / 2
|
||||||
math -- '-4 * 2'
|
math -- '-4 * 2'
|
||||||
|
|
||||||
|
logmsg Validate how bare variables in an epxression are handled
|
||||||
|
math x + 1
|
||||||
|
set x 1
|
||||||
|
math x + 1
|
||||||
|
set x 3
|
||||||
|
set y 1.5
|
||||||
|
math '-x * y'
|
||||||
|
math -s1 '-x * y'
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
|
||||||
|
####################
|
||||||
|
# Validate basic expressions
|
||||||
1
|
1
|
||||||
1
|
1
|
||||||
1
|
1
|
||||||
|
@ -12,3 +15,10 @@
|
||||||
-10
|
-10
|
||||||
-2
|
-2
|
||||||
-8
|
-8
|
||||||
|
|
||||||
|
####################
|
||||||
|
# Validate how bare variables in an epxression are handled
|
||||||
|
1
|
||||||
|
2
|
||||||
|
-4
|
||||||
|
-4.5
|
||||||
|
|
Loading…
Reference in a new issue