Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: [PATCH] zsh_eval_context: add math context
On Thu 21 May 2026, at 16:23, dana wrote:
> this adds a 'math' context to zsh_eval_context when a math function is
> executed. also adds tests promised in w/54575
i realised this is annoying sometimes, e.g. (( a(b(c())) )) pushes math
three times for c()
here's a different implementation that pushes it at the start of
evaluation rather than before function execution
i didn't want to do it like this initially because i was worried that
it might cause problems for expressions that reference zsh_eval_context
itself. but i can't actually think of a case where it would:
- (( $zsh_eval_context )) and (( zsh_eval_context )) would only have
worked previously at the very top level and even then it's not a
useful thing to do
- in (( $#zsh_eval_context )) the literal value is substituted via
parameter expansion before the start of evaluation
- in (( #zsh_eval_context )) the result always comes from a preceding
context (the top-most one)
- (( ##zsh_eval_context )) is always invalid
- in (( fn() )), it pushes a new context for the function call either
way
so i think it's fine. but please correct if i'm wrong
dana
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 635589be4..933081b34 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -1082,7 +1082,7 @@ Code specified by the tt(-c) option to the command line that invoked
the shell.
)
item(tt(cmdsubst))(
-Command substitution using of the tt(`)var(...)tt(`),
+Command substitution using the tt(`)var(...)tt(`),
tt($+LPAR())var(...)tt(RPAR()), tt(${{)var(name)tt(}) var(...)tt(}),
tt(${|)var(...)tt(}), or tt(${ )var(...)tt( }) constructs.
)
@@ -1120,6 +1120,12 @@ The tt(>LPAR())var(...)tt(RPAR()) form of process substitution.
item(tt(loadautofunc))(
Code read directly from a file to define an autoloaded function.
)
+item(tt(math))(
+Code executed in the tt($+LPAR()LPAR())var(...)tt(RPAR()RPAR()) or
+tt(LPAR()LPAR())var(...)tt(RPAR()RPAR()) constructs,
+by the tt(let) builtin, in an array subscript, or in
+another arithmetic context.
+)
item(tt(outsubst))(
The tt(<LPAR())var(...)tt(RPAR()) form of process substitution.
)
diff --git a/Src/exec.c b/Src/exec.c
index 2c730b910..22649ae62 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -263,6 +263,11 @@ static int doneps4;
static char *STTYval;
static char *blank_env[] = { NULL };
+/* total allocated elements in zsh_eval_context array */
+static int zsh_eval_context_len;
+/* number of in-use elements in zsh_eval_context array */
+static int zsh_eval_context_alen;
+
/* Execution functions. */
static int (*execfuncs[WC_COUNT-WC_CURSH]) (Estate, int) = {
@@ -1241,30 +1246,44 @@ execstring(char *s, int dont_change_job, int exiting, char *context)
popheap();
}
+/* append a context to the zsh_eval_context array */
+
/**/
mod_export void
-execode(Eprog p, int dont_change_job, int exiting, char *context)
+zsh_eval_context_push(char *context)
{
- struct estate s;
- static int zsh_eval_context_len;
- int alen;
-
if (!zsh_eval_context_len) {
zsh_eval_context_len = 16;
- alen = 0;
+ zsh_eval_context_alen = 0;
zsh_eval_context = (char **)zalloc(zsh_eval_context_len *
sizeof(*zsh_eval_context));
- } else {
- alen = arrlen(zsh_eval_context);
- if (zsh_eval_context_len == alen + 1) {
- zsh_eval_context_len *= 2;
- zsh_eval_context = zrealloc(zsh_eval_context,
- zsh_eval_context_len *
- sizeof(*zsh_eval_context));
- }
- }
- zsh_eval_context[alen] = context;
- zsh_eval_context[alen+1] = NULL;
+ } else if (zsh_eval_context_len == zsh_eval_context_alen + 1) {
+ zsh_eval_context_len *= 2;
+ zsh_eval_context = zrealloc(zsh_eval_context,
+ zsh_eval_context_len *
+ sizeof(*zsh_eval_context));
+ }
+ zsh_eval_context[zsh_eval_context_alen] = context;
+ zsh_eval_context[++zsh_eval_context_alen] = NULL;
+}
+
+/* remove the last context from the zsh_eval_context array */
+
+/**/
+mod_export void
+zsh_eval_context_pop()
+{
+ if (zsh_eval_context_alen)
+ zsh_eval_context[--zsh_eval_context_alen] = NULL;
+}
+
+/**/
+mod_export void
+execode(Eprog p, int dont_change_job, int exiting, char *context)
+{
+ struct estate s;
+
+ zsh_eval_context_push(context);
s.prog = p;
s.pc = p->prog;
@@ -1279,7 +1298,7 @@ execode(Eprog p, int dont_change_job, int exiting, char *context)
* zsh_eval_context may have been altered by a recursive
* call, but that's OK since we're using the global value.
*/
- zsh_eval_context[alen] = NULL;
+ zsh_eval_context_pop();
}
/* Execute a simplified command. This is used to execute things that
diff --git a/Src/math.c b/Src/math.c
index d97dae238..ff0165a87 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -1493,7 +1493,9 @@ matheval(char *s)
x.u.l = 0;
return x;
}
+ zsh_eval_context_push("math");
x = mathevall(s, MPREC_TOP, &junk);
+ zsh_eval_context_pop();
mtok = xmtok;
if (*junk)
zerr("bad math expression: illegal character: %c", *junk);
@@ -1531,7 +1533,9 @@ mathevalarg(char *s, char **ss)
zerr("bad math expression: empty string");
return (zlong)0;
}
+ zsh_eval_context_push("math");
x = mathevall(s, MPREC_ARG, ss);
+ zsh_eval_context_pop();
if (mtok == COMMA)
(*ss)--;
mtok = xmtok;
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index e192d480d..218bca739 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -1649,11 +1649,96 @@
print $zsh_eval_context[1]
[[ $ZSH_EVAL_CONTEXT = ${(j.:.)zsh_eval_context} ]] || print Not equal!
(( icontext = ${#zsh_eval_context} + 1 ))
- contextfn() { print $(print $zsh_eval_context[icontext,-1]); }
+ contextfn() { print -r - $zsh_eval_context[icontext,-1] }
+ functions -M contextfn
contextfn
+ () { contextfn }
+ () { trap contextfn EXIT }
+ eval contextfn
+ print -r - $( contextfn )
+ print -r - ${ contextfn }
+ : ${| contextfn }
+ (( contextfn(0) ))
+ : $(( contextfn(0) ))
+ let 'contextfn(0)'
+ : /(e*contextfn*)
+ : /(o+contextfn)
+ cat <( contextfn )
+ cat < <( contextfn )
+ cat =( contextfn )
+ : >( contextfn )
+ : > >( contextfn )
+ source <( <<< contextfn )
+ $ZTST_testdir/../Src/zsh -fc 'print -r - $zsh_eval_context'
+ () { () { print -r - $( : $(( contextfn(0) )) ) } }
0:$ZSH_EVAL_CONTEXT and $zsh_eval_context
>toplevel
->shfunc cmdsubst
+>shfunc
+>shfunc shfunc
+>trap shfunc
+>eval shfunc
+>cmdsubst shfunc
+>cmdsubst shfunc
+>cmdsubst shfunc
+>math shfunc
+>math shfunc
+>math shfunc
+>globqual shfunc
+>globsort shfunc
+>outsubst shfunc
+>outsubst shfunc
+>equalsubst shfunc
+>insubst shfunc
+>insubst shfunc
+>file shfunc
+>cmdarg
+>shfunc shfunc cmdsubst math shfunc
+
+ (( icontext = ${#zsh_eval_context} + 1 ))
+ contextfn() {
+ print -r - $#zsh_eval_context[icontext,-1] $zsh_eval_context[icontext,-1]
+ }
+ cmd='() { eval contextfn }'
+ repeat 48 cmd="() { $cmd }"
+ eval $cmd
+0:zsh_eval_context resizing
+*>52 eval shfunc * shfunc eval shfunc
+
+ (( icontext = ${#zsh_eval_context} + 1 ))
+ context1() {
+ print -r - $zsh_eval_context[icontext,-1]
+ context2
+ }
+ context2() {
+ print -r - $zsh_eval_context[icontext,-1]
+ }
+ functions -M context1
+ functions -M context2
+ echo '((...))'
+ (( context1(context2()) ))
+ echo '$((...))'
+ : $(( context1(context2()) ))
+ echo let
+ let 'context1(context2())'
+ echo subscript
+ arr=( ) exp='context1()+2'
+ : $arr[$exp]
+0:more math context
+>((...))
+>math shfunc
+>math shfunc
+>math shfunc shfunc
+>$((...))
+>math shfunc
+>math shfunc
+>math shfunc shfunc
+>let
+>math shfunc
+>math shfunc
+>math shfunc shfunc
+>subscript
+>math shfunc
+>math shfunc shfunc
foo="123456789"
print ${foo:3}
Messages sorted by:
Reverse Date,
Date,
Thread,
Author