From b8751cb9d71d9f8e0ff24c469a8011ff1a58b1a8 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Thu, 31 Jul 2014 19:39:26 +0200 Subject: 32949 (wip 32928, 32937): Add :^ syntax for zipping two arrays --- ChangeLog | 5 ++++ Doc/Zsh/expn.yo | 24 ++++++++++++++++ Src/subst.c | 61 ++++++++++++++++++++++++++++++++++++++++ Test/D04parameter.ztst | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+) diff --git a/ChangeLog b/ChangeLog index cfd8cb6a9..507e222f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2014-08-04 Mikael Magnusson + + * 32949 (wip 32928, 32937): Doc/Zsh/expn.yo, Src/subst.c, + Test/D04parameter.ztst: Add :^ and :^^ for zipping arrays. + 2014-08-03 Peter Stephenson * Doc/Zsh/builtins.yo: 32944: Doc/Zsh/builtins.yo: read -qs is diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 727901346..8394ffc4f 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -636,6 +636,30 @@ Similar to the preceding subsitution, but in the opposite sense, so that entries present in both the original substitution and as elements of var(arrayname) are retained and others removed. ) +xitem(tt(${)var(name)tt(:^)var(arrayname)tt(})) +item(tt(${)var(name)tt(:^^)var(arrayname)tt(}))( +Zips two arrays, such that the output array is twice as long as the +shortest (longest for `tt(:^^)') of tt(name) and tt(arrayname), with +the elements alternatingly being picked from them. For `tt(:^)', if one +of the input arrays is longer, the output will stop when the end of the +shorter array is reached. Thus, + +example(a=(1 2 3 4); b=(a b); print ${a:^b}) + +will output `tt(1 a 2 b)'. For `tt(:^^)', then the input is repeated +until all of the longer array has been used up and the above will output +`tt(1 a 2 b 3 a 4 b)'. + +Either or both inputs may be a scalar, they will be treated as an array +of length 1 with the scalar as the only element. If either array is empty, +the other array is output with no extra elements inserted. + +Currently the following code will output `tt(a b)' and `tt(1)' as two separate +elements, which can be unexpected. The second print provides a workaround which +should continue to work if this is changed. + +example(a=(a b); b=(1 2); print -l "${a:^b}"; print -l "${${a:^b}}") +) xitem(tt(${)var(name)tt(:)var(offset)tt(})) item(tt(${)var(name)tt(:)var(offset)tt(:)var(length)tt(}))( This syntax gives effects similar to parameter subscripting diff --git a/Src/subst.c b/Src/subst.c index d6be2f0ed..fc1be7d54 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -2878,6 +2878,67 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) } break; } + } else if (inbrace && (*s == '^' || *s == Hat)) { + char **zip, **ap, **apsrc; + int shortest = 1; + ++s; + if (*s == '^' || *s == Hat) { + shortest = 0; + ++s; + } + if (*itype_end(s, IIDENT, 0)) { + untokenize(s); + zerr("not an identifier: %s", s); + return NULL; + } + if (vunset) { + if (unset(UNSET)) { + *idend = '\0'; + zerr("%s: parameter not set", idbeg); + return NULL; + } + val = dupstring(""); + } else { + char *sval; + zip = getaparam(s); + if (!zip) { + sval = getsparam(s); + if (sval) + zip = hmkarray(sval); + } + if (!isarr) aval = mkarray(val); + if (zip) { + char **out; + int alen, ziplen, outlen, i = 0; + alen = arrlen(aval); + ziplen = arrlen(zip); + outlen = shortest ^ (alen > ziplen) ? alen : ziplen; + if (!shortest && (alen == 0 || ziplen == 0)) { + if (ziplen) + aval = arrdup(zip); + } else { + out = zhalloc(sizeof(char *) * (2 * outlen + 1)); + while (i < outlen) { + if (copied) + out[i*2] = aval[i % alen]; + else + out[i*2] = dupstring(aval[i % alen]); + out[i*2+1] = dupstring(zip[i % ziplen]); + i++; + } + out[i*2] = NULL; + aval = out; + copied = 1; + isarr = 1; + } + } else { + if (unset(UNSET)) { + zerr("%s: parameter not set", s); + return NULL; + } + val = dupstring(""); + } + } } else if (inbrace && (*s == '|' || *s == Bar || *s == '*' || *s == Star)) { int intersect = (*s == '*' || *s == Star); diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index a8cc93a12..49dcea901 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -1560,3 +1560,79 @@ 0:Intersection and disjunction with empty parameters >0 >0 + + foo=(a b c) + bar=(1 2 3) + print ${foo:^bar} + print ${foo:^^bar} + foo=(a b c d) + bar=(1 2) + print ${foo:^bar} + print ${foo:^^bar} + foo=('a a' b) + bar=(1 '2 2') + print -l "${foo:^bar}" + print -l "${(@)foo:^bar}" +0:Zipping arrays, correct output +>a 1 b 2 c 3 +>a 1 b 2 c 3 +>a 1 b 2 +>a 1 b 2 c 1 d 2 +# maybe this should be changed to output "a a b 1" +>a a b +>1 +>a a +>1 +>b +>2 2 + + foo=(a b c) + bar=() + print ${foo:^bar} + print ${foo:^^bar} + print ${bar:^foo} + print ${bar:^^foo} + print ${bar:^bar} + print ${bar:^^bar} +0:Zipping arrays, one or both inputs empty +> +>a b c +> +>a b c +> +> + + foo=text + bar=() + print ${foo:^bar} + print ${bar:^^foo} + bar=other + print ${foo:^bar} + bar=(array elements) + print ${foo:^bar} + print ${foo:^^bar} + print ${bar:^foo} + print ${bar:^^foo} +0:Zipping arrays, scalar input +> +>text +>text other +>text array +>text array text elements +>array text +>array text elements text + + foo=(a b c) + print ${foo:^^^bar} +1:Zipping arrays, parsing +?(eval):2: not an identifier: ^bar + + (setopt nounset + print ${foo:^noexist}) +1:Zipping arrays, NO_UNSET part 1 +?(eval):2: noexist: parameter not set + + (setopt nounset + print ${noexist:^foo}) +1:Zipping arrays, NO_UNSET part 2 +?(eval):2: noexist: parameter not set -- cgit 1.4.1