Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[PATCH] zsh_eval_context: add math context
- X-seq: zsh-workers 54587
- From: dana <dana@xxxxxxx>
- To: zsh-workers@xxxxxxx
- Subject: [PATCH] zsh_eval_context: add math context
- Date: Thu, 21 May 2026 16:23:37 -0500
- Archived-at: <https://zsh.org/workers/54587>
- Feedback-id: i9be146f9:Fastmail
- List-id: <zsh-workers.zsh.org>
this adds a 'math' context to zsh_eval_context when a math function is
executed. also adds tests promised in w/54575
dana
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 635589be4..790fb9258 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,11 @@ 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,
+or by the tt(let) builtin.
+)
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..811f8a95b 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -1623,7 +1623,13 @@ mathparse(int pc)
push((noeval ? zero_mnumber : getcvar(yylval)), yylval, 0);
break;
case FUNC:
- push((noeval ? zero_mnumber : callmathfunc(yylval)), yylval, 0);
+ if (noeval) {
+ push(zero_mnumber, yylval, 0);
+ } else {
+ zsh_eval_context_push("math");
+ push(callmathfunc(yylval), yylval, 0);
+ zsh_eval_context_pop();
+ }
break;
case M_INPAR:
mathparse(TOPPREC);
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index e192d480d..289512efc 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -1649,11 +1649,60 @@
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
foo="123456789"
print ${foo:3}
Messages sorted by:
Reverse Date,
Date,
Thread,
Author