zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: Add :^ syntax for zipping two arrays (v2)
@ 2014-08-01 15:47 Mikael Magnusson
  2014-08-04  8:29 ` Peter Stephenson
  0 siblings, 1 reply; 6+ messages in thread
From: Mikael Magnusson @ 2014-08-01 15:47 UTC (permalink / raw)
  To: zsh-workers

Here's my current version with :^ and :^^ and using heap alloc including hmkarray.

---
 Doc/Zsh/expn.yo | 17 +++++++++++++++++
 Src/subst.c     | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+)

diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index ff9f2b1..4d28d86 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -636,6 +636,23 @@ 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.
+)
 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 944492f..21e1a1a 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -2878,6 +2878,62 @@ 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;
+		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);
-- 
1.9.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: Add :^ syntax for zipping two arrays (v2)
  2014-08-01 15:47 PATCH: Add :^ syntax for zipping two arrays (v2) Mikael Magnusson
@ 2014-08-04  8:29 ` Peter Stephenson
  2014-08-04 16:01   ` Mikael Magnusson
  0 siblings, 1 reply; 6+ messages in thread
From: Peter Stephenson @ 2014-08-04  8:29 UTC (permalink / raw)
  To: zsh-workers

On Fri, 01 Aug 2014 17:47:38 +0200
Mikael Magnusson <mikachu@gmail.com> wrote:
> Here's my current version with :^ and :^^ and using heap alloc
> including hmkarray.

Looks OK to me, if you want to commit it.

pws


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: Add :^ syntax for zipping two arrays (v2)
  2014-08-04  8:29 ` Peter Stephenson
@ 2014-08-04 16:01   ` Mikael Magnusson
  2014-08-04 16:07     ` Peter Stephenson
  0 siblings, 1 reply; 6+ messages in thread
From: Mikael Magnusson @ 2014-08-04 16:01 UTC (permalink / raw)
  To: zsh workers

On 4 August 2014 10:29, Peter Stephenson <p.stephenson@samsung.com> wrote:
> On Fri, 01 Aug 2014 17:47:38 +0200
> Mikael Magnusson <mikachu@gmail.com> wrote:
>> Here's my current version with :^ and :^^ and using heap alloc
>> including hmkarray.
>
> Looks OK to me, if you want to commit it.
>
> pws

What about the issue with "${a:^a}" producing more than one argument?
Just to make sure you're okay with that and didn't just miss it in the
original mail. I'll also add some tests before committing.

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: Add :^ syntax for zipping two arrays (v2)
  2014-08-04 16:01   ` Mikael Magnusson
@ 2014-08-04 16:07     ` Peter Stephenson
  2014-08-04 16:51       ` PATCH: Add :^ syntax for zipping two arrays (v3) Mikael Magnusson
  0 siblings, 1 reply; 6+ messages in thread
From: Peter Stephenson @ 2014-08-04 16:07 UTC (permalink / raw)
  To: zsh workers

On Mon, 04 Aug 2014 18:01:51 +0200
Mikael Magnusson <mikachu@gmail.com> wrote:
> On 4 August 2014 10:29, Peter Stephenson <p.stephenson@samsung.com> wrote:
> > On Fri, 01 Aug 2014 17:47:38 +0200
> > Mikael Magnusson <mikachu@gmail.com> wrote:
> >> Here's my current version with :^ and :^^ and using heap alloc
> >> including hmkarray.
> >
> > Looks OK to me, if you want to commit it.
> >
> > pws
> 
> What about the issue with "${a:^a}" producing more than one argument?
> Just to make sure you're okay with that and didn't just miss it in the
> original mail. I'll also add some tests before committing.

As long as it's documented that's what it does (and if it's likely to
change at some point that should be documented, too) it doesn't seem a
fatal problem.

pws



^ permalink raw reply	[flat|nested] 6+ messages in thread

* PATCH: Add :^ syntax for zipping two arrays (v3)
  2014-08-04 16:07     ` Peter Stephenson
@ 2014-08-04 16:51       ` Mikael Magnusson
  2014-08-04 18:36         ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Mikael Magnusson @ 2014-08-04 16:51 UTC (permalink / raw)
  To: zsh-workers

This includes tests, updated docs to mention the "${a:^a}" thing, and
fixes a crash discovered by adding tests. (When either of the arrays
were empty, the :^^ form attempted to divide by 0).

---
 Doc/Zsh/expn.yo        | 24 ++++++++++++++++
 Src/subst.c            | 61 ++++++++++++++++++++++++++++++++++++++++
 Test/D04parameter.ztst | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 161 insertions(+)

diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 7279013..8394ffc 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 d6be2f0..fc1be7d 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 a8cc93a..49dcea9 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
-- 
1.9.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: Add :^ syntax for zipping two arrays (v3)
  2014-08-04 16:51       ` PATCH: Add :^ syntax for zipping two arrays (v3) Mikael Magnusson
@ 2014-08-04 18:36         ` Bart Schaefer
  0 siblings, 0 replies; 6+ messages in thread
From: Bart Schaefer @ 2014-08-04 18:36 UTC (permalink / raw)
  To: zsh-workers

On Aug 4,  6:51pm, Mikael Magnusson wrote:
} Subject: PATCH: Add :^ syntax for zipping two arrays (v3)
}
} This includes tests, updated docs to mention the "${a:^a}" thing, and
} fixes a crash discovered by adding tests. (When either of the arrays
} were empty, the :^^ form attempted to divide by 0).

Src/subst.c: In function `paramsubst':
Src/subst.c:2882: warning: unused variable `ap'
Src/subst.c:2882: warning: unused variable `apsrc'

diff --git a/Src/subst.c b/Src/subst.c
index fc1be7d..4a5fe3a 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -2879,7 +2879,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
 	    break;
 	}
     } else if (inbrace && (*s == '^' || *s == Hat)) {
-	char **zip, **ap, **apsrc;
+	char **zip;
 	int shortest = 1;
 	++s;
 	if (*s == '^' || *s == Hat) {


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2014-08-04 18:36 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-01 15:47 PATCH: Add :^ syntax for zipping two arrays (v2) Mikael Magnusson
2014-08-04  8:29 ` Peter Stephenson
2014-08-04 16:01   ` Mikael Magnusson
2014-08-04 16:07     ` Peter Stephenson
2014-08-04 16:51       ` PATCH: Add :^ syntax for zipping two arrays (v3) Mikael Magnusson
2014-08-04 18:36         ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).