Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: 3.1.5 + associative arrays: keys, values, and pattern subscripts



The appended patch improves the implementation of the (kv) parameter flags
and their interaction with the (iIrR) subscripting flags.  This is pretty
much as was discussed in zsh-workers/4656 and its follow-ups, but differs
slightly.  In particular, ordinary arrays do NOT yet return both keys and
values when (kv) are specified together.

Because it was easy (it actually was harder to avoid it) the behavior of
the (IR) flags on AAs has changed; previously they were the same as (ir),
but now (IR) return an array of ALL the matching entries, whereas (ir)
still return a single entry as before.

The complete table is as follows:

                        Associative Array        Ordinary Array
                        -----------------        --------------
  $param[key]           Value in param at key    Value in param at key
                        or empty if none         or empty if none

  ${(k)param[key]}      If key has a value,      If key has a value,
                        then key, else empty     then key, else empty

  $param[(r)pat]        A value in param that    First value in param that
                        matches pattern pat      matches pattern pat

  $param[(R)pat]        All values in param      Last value in param that
                        that match pattern pat   matches pattern pat

  $param[(i)pat]        A key in param that      Index of first value that
                        matches pattern pat      matches pattern pat

  $param[(I)pat]        All keys in param that   Index of last value that
                        match pattern pat        matches pattern pat

  ${(k)param[(r)pat]}   Keys of values that      Same as $param[(i)pat]
  ${(k)param[(R)pat]}   match pattern pat (not   and $param[(I)pat])
                        of keys that match)

  ${(k)param[(i)pat]}   As $param[(i)pat]        As $param[(i)pat]
  ${(k)param[(I)pat]}   and $param[(I)pat]       and $param[(I)pat]

  ${(v)param[(i)pat]}   Values at keys that      As $param[(r)pat]
  ${(v)param[(I)pat]}   match pat                and $param[(R)pat]

  ${(kv)param[(r)pat]}  Key and value pair of    As $param[(r)pat]    
  ${(kv)param[(R)pat]}  value that matches pat   and $param[(R)pat]  

  ${(kv)param[(i)pat]}  Key and value pair of    As $param[(i)pat]
  ${(kv)param[(I)pat]}  key that matches pat     and $param[(I)pat]

The rule for ordinary arrays is that (k) beats (rR) and (v) beats (iI), but
(kv) cancel each other out.  This may be considered interim behavior.

If everybody thinks this is cool so far -- the (IR) behavior being the only
real point of contention that I can see -- I'll produce a documentation
patch sometime next week, before I run off for holiday break.

Implementation notes:

I overloaded the `isarr' field of struct value to hold flag bits for all
this, and backed out the previous hack of overloading both the `a' and `b'
fields.  The difference between $array[*] and $array[@] is still stashed
in the sign bit, mostly to minimize the number of lines changed.  I don't
believe this has broken anything, but keep your eyes open.

I changed scanparamvals() and took advantage of one of the parameters of
scanhashtable() to specify which table entries should be skipped.

The entire body of getvalue() is now in a new function fetchvalue() which
takes an additional parameter for the AA flags; getvalue() simply calls
fetchvalue() with flags == 0 to produce the old behavior.  paramsubst()
calls fetchvalue() directly in order to pass bits for the (kv) flags.

Index: Src/params.c
===================================================================
--- params.c	1998/11/17 16:11:35	1.11
+++ params.c	1998/12/13 22:25:17
@@ -303,7 +303,11 @@
 
 #define SCANPM_WANTVALS   (1<<0)
 #define SCANPM_WANTKEYS   (1<<1)
-#define SCANPM_WANTINDEX  (1<<2)
+#define SCANPM_WANTINDEX  (1<<2)	/* Useful only if nested arrays */
+#define SCANPM_MATCHKEY   (1<<3)
+#define SCANPM_MATCHVAL   (1<<4)
+#define SCANPM_MATCHMANY  (1<<5)
+#define SCANPM_ISVAR_AT   ((-1)<<15)	/* Only sign bit is significant */
 
 static unsigned numparamvals;
 
@@ -311,13 +315,12 @@
 static void
 scancountparams(HashNode hn, int flags)
 {
-    if (!(((Param)hn)->flags & PM_UNSET)) {
+    ++numparamvals;
+    if ((flags & SCANPM_WANTKEYS) && (flags & SCANPM_WANTVALS))
 	++numparamvals;
-	if ((flags & SCANPM_WANTKEYS) && (flags & SCANPM_WANTVALS))
-	    ++numparamvals;
-    }
 }
 
+static Comp scancomp;
 static char **paramvals;
 
 /**/
@@ -325,33 +328,45 @@
 scanparamvals(HashNode hn, int flags)
 {
     struct value v;
+    if (numparamvals && (flags & (SCANPM_MATCHVAL|SCANPM_MATCHKEY)) &&
+	!(flags & SCANPM_MATCHMANY))
+	return;
     v.pm = (Param)hn;
-    if (!(v.pm->flags & PM_UNSET)) {
-	if (flags & SCANPM_WANTKEYS) {
-	    paramvals[numparamvals++] = v.pm->nam;
-	    if (!(flags & SCANPM_WANTVALS))
-		return;
-	}
-	v.isarr = (PM_TYPE(v.pm->flags) & (PM_ARRAY|PM_HASHED));
-	v.inv = (flags & SCANPM_WANTINDEX);
-	v.a = 0;
-	v.b = -1;
-	paramvals[numparamvals++] = getstrvalue(&v);
+    if ((flags & SCANPM_MATCHKEY) && !domatch(v.pm->nam, scancomp, 0)) {
+	return;
     }
+    if (flags & SCANPM_WANTKEYS) {
+	paramvals[numparamvals++] = v.pm->nam;
+	if (!(flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)))
+	    return;
+    }
+    v.isarr = (PM_TYPE(v.pm->flags) & (PM_ARRAY|PM_HASHED));
+    v.inv = 0;
+    v.a = 0;
+    v.b = -1;
+    paramvals[numparamvals] = getstrvalue(&v);
+    if (flags & SCANPM_MATCHVAL) {
+	if (domatch(paramvals[numparamvals], scancomp, 0)) {
+	    numparamvals += ((flags & SCANPM_WANTVALS) ? 1 :
+			     !(flags & SCANPM_WANTKEYS));
+	} else if (flags & SCANPM_WANTKEYS)
+	    --numparamvals;	/* Value didn't match, discard key */
+    } else
+	++numparamvals;
 }
 
 /**/
 char **
-paramvalarr(HashTable ht, unsigned flags)
+paramvalarr(HashTable ht, int flags)
 {
     MUSTUSEHEAP("paramvalarr");
     numparamvals = 0;
     if (ht)
-	scanhashtable(ht, 0, 0, 0, scancountparams, flags);
+	scanhashtable(ht, 0, 0, PM_UNSET, scancountparams, flags);
     paramvals = (char **) alloc((numparamvals + 1) * sizeof(char *));
     if (ht) {
 	numparamvals = 0;
-	scanhashtable(ht, 0, 0, 0, scanparamvals, flags);
+	scanhashtable(ht, 0, 0, PM_UNSET, scanparamvals, flags);
     }
     paramvals[numparamvals] = 0;
     return paramvals;
@@ -369,15 +384,10 @@
     else if (PM_TYPE(v->pm->flags) == PM_ARRAY)
 	return v->arr = v->pm->gets.afn(v->pm);
     else if (PM_TYPE(v->pm->flags) == PM_HASHED) {
-	unsigned flags = 0;
-	if (v->a)
-	    flags |= SCANPM_WANTKEYS;
-	if (v->b > v->a)
-	    flags |= SCANPM_WANTVALS;
-	v->arr = paramvalarr(v->pm->gets.hfn(v->pm), flags);
+	v->arr = paramvalarr(v->pm->gets.hfn(v->pm), v->isarr);
 	/* Can't take numeric slices of associative arrays */
 	v->a = 0;
-	v->b = -1;
+	v->b = numparamvals;
 	return v->arr;
     } else
 	return NULL;
@@ -737,7 +747,12 @@
 	down = !down;
 	num = -num;
     }
-    *inv = ind;
+    if (v->isarr & SCANPM_WANTKEYS)
+	*inv = (ind || !(v->isarr & SCANPM_WANTVALS));
+    else if (v->isarr & SCANPM_WANTVALS)
+	*inv = 0;
+    else
+	*inv = ind;
 
     for (t=s, i=0; *t && ((*t != ']' && *t != Outbrack && *t != ',') || i); t++)
 	if (*t == '[' || *t == Inbrack)
@@ -829,7 +844,21 @@
 
 	if ((c = parsereg(s))) {
 	    if (v->isarr) {
-		ta = getarrvalue(v);
+		if (PM_TYPE(v->pm->flags) == PM_HASHED) {
+		    scancomp = c;
+		    if (ind)
+			v->isarr |= SCANPM_MATCHKEY;
+		    else
+			v->isarr |= SCANPM_MATCHVAL;
+		    if (down)
+			v->isarr |= SCANPM_MATCHMANY;
+		    if ((ta = getvaluearr(v)) && *ta) {
+			*inv = v->inv;
+			*w = v->b;
+			return 1;
+		    }
+		} else
+		    ta = getarrvalue(v);
 		if (!ta || !*ta)
 		    return 0;
 		if (down)
@@ -920,8 +949,8 @@
     if (*tbrack == Outbrack)
 	*tbrack = ']';
     if ((s[0] == '*' || s[0] == '@') && s[1] == ']') {
-	if (v->isarr)
-	    v->isarr = (s[0] == '*') ? 1 : -1;
+	if (v->isarr && s[0] == '@')
+	    v->isarr |= SCANPM_ISVAR_AT;
 	v->a = 0;
 	v->b = -1;
 	s += 2;
@@ -941,7 +970,7 @@
 		} else
 		    a = -ztrlen(t + a + strlen(t));
 	    }
-	    if (a > 0 && isset(KSHARRAYS))
+	    if (a > 0 && (isset(KSHARRAYS) || (v->pm->flags & PM_HASHED)))
 		a--;
 	    v->inv = 1;
 	    v->isarr = 0;
@@ -985,6 +1014,13 @@
 Value
 getvalue(char **pptr, int bracks)
 {
+  return fetchvalue(pptr, bracks, 0);
+}
+
+/**/
+Value
+fetchvalue(char **pptr, int bracks, int flags)
+{
     char *s, *t;
     char sav;
     Value v;
@@ -1039,8 +1075,10 @@
 	if (!pm || (pm->flags & PM_UNSET))
 	    return NULL;
 	v = (Value) hcalloc(sizeof *v);
-	if (PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED))
-	    v->isarr = isvarat ? -1 : 1;
+	if (PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED)) {
+	    /* Overload v->isarr as the flag bits for hashed arrays. */
+	    v->isarr = flags | (isvarat ? SCANPM_ISVAR_AT : 0);
+	}
 	v->pm = pm;
 	v->inv = 0;
 	v->a = 0;
@@ -1079,7 +1117,7 @@
     if (!v)
 	return hcalloc(1);
     HEAPALLOC {
-	if (v->inv) {
+	if (v->inv && !(v->pm->flags & PM_HASHED)) {
 	    sprintf(buf, "%d", v->a);
 	    s = dupstring(buf);
 	    LASTALLOC_RETURN s;
Index: Src/subst.c
===================================================================
--- subst.c	1998/12/12 07:25:53	1.5
+++ subst.c	1998/12/13 22:22:42
@@ -721,7 +721,7 @@
     int nojoin = 0;
     char inbrace = 0;		/* != 0 means ${...}, otherwise $... */
     char hkeys = 0;		/* 1 means get keys from associative array */
-    char hvals = 1;		/* > hkeys get values of associative array */
+    char hvals = 0;		/* > hkeys get values of associative array */
 
     *s++ = '\0';
     if (!ialnum(*s) && *s != '#' && *s != Pound && *s != '-' &&
@@ -978,8 +978,12 @@
 	copied = 1;
 	*s = sav;
 	v = (Value) NULL;
-    } else if (!(v = getvalue(&s, (unset(KSHARRAYS) || inbrace) ? 1 : -1)))
-	vunset = 1;
+    } else {
+	/* 2 == SCANPM_WANTKEYS, 1 == SCANPM_WANTVALS, see params.c */
+	if (!(v = fetchvalue(&s, (unset(KSHARRAYS) || inbrace) ? 1 : -1,
+			     (hkeys ? 2 : 0) + ((hvals > hkeys) ? 1 : 0))))
+	    vunset = 1;
+    }
     while (v || ((inbrace || (unset(KSHARRAYS) && vunset)) && isbrack(*s))) {
 	if (!v) {
 	    Param pm;
@@ -1004,13 +1008,8 @@
 		break;
 	}
 	if ((isarr = v->isarr)) {
-	    /* No way to reach here with v->inv != 0, so getvaluearr() *
-	     * will definitely be called by getarrvalue().  Slicing of *
-	     * associations isn't done, so use v->a and v->b for flags */
-	    if (PM_TYPE(v->pm->flags) == PM_HASHED) {
-		v->a = hkeys;
-		v->b = hvals;
-	    }
+	    /* No way to get here with v->inv != 0, so getvaluearr() *
+	     * is called by getarrvalue(); needn't test PM_HASHED.   */
 	    aval = getarrvalue(v);
 	} else {
 	    if (v->pm->flags & PM_ARRAY) {

-- 
Bart Schaefer                                 Brass Lantern Enterprises
http://www.well.com/user/barts              http://www.brasslantern.com



Messages sorted by: Reverse Date, Date, Thread, Author