zsh-workers
 help / color / mirror / code / Atom feed
* Re:  Associative array ordering and selective unset (Re: Example function)
@ 1999-02-02  7:52 Sven Wischnowsky
  1999-02-02 11:10 ` PATCH: 3.1.5-pws-6: unset assoc[key] Bart Schaefer
  0 siblings, 1 reply; 8+ messages in thread
From: Sven Wischnowsky @ 1999-02-02  7:52 UTC (permalink / raw)
  To: zsh-workers


Bart Schaefer wrote:

> ...
> 
> We could go the ksh route and make `noglob unset foo[key]` work.  Like:
> 
> 	function unset() {
> 	    emulate -R zsh
> 	    setopt localoptions extendedglob
> 	    local arg i
> 	    for i
> 	    do
> 		arg=( "${(@s:[:)i}" )
> 		if [[ $#arg > 1 &&
> 		    $(eval print '${(t)'${arg[1]}'}') == association ]]
> 		then
> 		    eval "$arg[1]=( \${(kv)$arg[1][(I)^${(j:[:)arg[2,-1]} )"
> 		else
> 		    builtin unset $i
> 		fi
> 	    done
> 	}

Since even `vared' can now understand the subscripted syntax, I think
adding this to unset (even in C) would be a good thing. (I'm pretty
sure people will want to have a easy way to remove key/value-pairs
from associative arrays).

> Can you think of a better idiom than $(eval print '${(t)'${arg[1]}'}') to
> get the type of the variable whose name is the value of $arg[1] ?  I keep
> thinking there ought to be a way with ${(e)...} but couldn't make it work.

When implementing the new completion example stuff I was somewhat
irritated that there is no easy way to get the value of a parameter of 
which one only has the name in another parameter. Trying to get the
type of that parameter is indeed even more complicated.

Hm, `(e)' is used at the very end and I wouldn't like to change
that. Maybe yet another flag? E.g.: `P' makes the thing after the
flags be used as the name of a parameter. So `${(P)foo}' is the same
as `$foo', but `${(P)${foo}}' will take the value of `foo' as the name 
of a parameter and work on it. So your example would become:

  if [[ $#arg > 1 && ${(Pt)${argv[1]}} == association ]] ...

Looking at the code this doesn't seem too hard to implement (well, I
said `seem'...).

Bye
 Sven


--
Sven Wischnowsky                         wischnow@informatik.hu-berlin.de


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

* PATCH: 3.1.5-pws-6: unset assoc[key]
  1999-02-02  7:52 Associative array ordering and selective unset (Re: Example function) Sven Wischnowsky
@ 1999-02-02 11:10 ` Bart Schaefer
  0 siblings, 0 replies; 8+ messages in thread
From: Bart Schaefer @ 1999-02-02 11:10 UTC (permalink / raw)
  To: zsh-workers

On Feb 2,  8:52am, Sven Wischnowsky wrote:
} Subject: Re:  Associative array ordering and selective unset (Re: Example 
}
} 
} Bart Schaefer wrote:
} 
} > We could go the ksh route and make `noglob unset foo[key]` work.  Like:
} 
} Since even `vared' can now understand the subscripted syntax, I think
} adding this to unset (even in C) would be a good thing.

OK, then, here we go.  I've disallowed

zsh% foo=(x y z p d q)			# Ordinary array
zsh% noglob unset foo[2]
unset: foo: invalid element for unset

but it could easily be added if, for example, ksh permits it.  (It would
not, however, be all that easy to add `noglob unset foo[2,4]` -- or at
least I don't see right off how to do it.)

This patch also contains a minor optimization to the "normal" case of unset,
to avoid looking up the parameter name in the hash table twice.

Index: Doc/Zsh/builtins.yo
===================================================================
--- builtins.yo	1999/01/30 18:21:36	1.14
+++ builtins.yo	1999/02/02 11:00:18
@@ -1072,10 +1072,16 @@
 Local parameters remain local even if unset; they appear unset within scope,
 but the previous value will still reappear when the scope ends.
 
-If the tt(-m) flag is specified the
-arguments are taken as patterns (should be quoted) and all parameters
-with matching names are unset.  tt(unset -f) is equivalent to
-tt(unfunction).
+Individual elements of associative array parameters may be unset by using
+subscript syntax on var(name), which should be quoted (or the entire command
+prefixed with tt(noglob)) to protect the subscript from filename generation.
+
+If the tt(-m) flag is specified the arguments are taken as patterns (should
+be quoted) and all parameters with matching names are unset.  Note that this
+cannot be used when unsetting associative array elements, as the subscript
+will be treated as part of the pattern.
+
+tt(unset -f) is equivalent to tt(unfunction).
 )
 findex(unsetopt)
 cindex(options, unsetting)
Index: Src/builtin.c
===================================================================
--- builtin.c	1999/01/29 16:32:36	1.18
+++ builtin.c	1999/02/02 10:46:38
@@ -1928,14 +1928,39 @@
 
     /* do not glob -- unset the given parameter */
     while ((s = *argv++)) {
+	char *ss = strchr(s, '[');
+	char *sse = ss;
+	if (ss) {
+	    if (skipparens('[', ']', &sse) || *sse) {
+		zerrnam(name, "%s: invalid parameter name", s, 0);
+		returnval = 1;
+		continue;
+	    }
+	    *ss = 0;
+	}
 	pm = (Param) paramtab->getnode(paramtab, s);
 	if (!pm)
 	    returnval = 1;
 	else if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
 	    zerrnam(name, "%s: restricted", pm->nam, 0);
 	    returnval = 1;
+	} else if (ss) {
+	    if (PM_TYPE(pm->flags) == PM_HASHED) {
+		HashTable tht = paramtab;
+		if ((paramtab = pm->gets.hfn(pm))) {
+		    *--sse = 0;
+		    unsetparam(ss+1);
+		    *sse = ']';
+		}
+		paramtab = tht;
+	    } else {
+		zerrnam(name, "%s: invalid element for unset", s, 0);
+		returnval = 1;
+	    }
 	} else
-	    unsetparam(s);
+	    unsetparam_pm(pm, 0, 1);
+	if (ss)
+	    *ss = '[';
     }
     return returnval;
 }

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


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

* Re: Associative array ordering and selective unset (Re: Example function)
  1999-02-01 17:02 ` Associative array ordering and selective unset (Re: Example function) Bart Schaefer
  1999-02-01 17:39   ` Bruce Stephens
@ 1999-02-02 20:07   ` Phil Pennock
  1 sibling, 0 replies; 8+ messages in thread
From: Phil Pennock @ 1999-02-02 20:07 UTC (permalink / raw)
  To: zsh-workers

Typing away merrily, Bart Schaefer produced the immortal words:
> On Feb 1, 11:48am, Sven Wischnowsky wrote:
> } I was thinking about this... we could make the code keep a counter in
> } assoc arrays, increment it whenever a new key is added and store the
> } current value in the structure for this new element. Then we can treat 
> } the whole thing as being sorted by `time of addition'.
> } 
> } Hm, does this sound like the right thing?
> 
> Almost.  Something about it doesn't seem quite right to me, but I can't
> put my finger on what different behavior I'd expect.
> 
> I don't like the idea that every parameter table hash would end up with
> another integer of overhead in every entry, but maybe that's not so bad.

A pointer in each to embed a linked list?
-- 
--> Phil Pennock ; GAT d- s+:+ a23 C++(++++) UL++++/I+++/S+++/B++/H+$ P++@$
L+++ E-@ W(+) N>++ o !K w--- O>+ M V !PS PE Y+ PGP+ t-- 5++ X+ R !tv b++>+++
DI+ D+ G+ e+ h* r y?


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

* Re:  Associative array ordering and selective unset (Re: Example function)
@ 1999-02-02 16:58 Sven Wischnowsky
  0 siblings, 0 replies; 8+ messages in thread
From: Sven Wischnowsky @ 1999-02-02 16:58 UTC (permalink / raw)
  To: zsh-workers


I wrote:

> Maybe yet another flag? E.g.: `P' makes the thing after the
> flags be used as the name of a parameter. So `${(P)foo}' is the same
> as `$foo', but `${(P)${foo}}' will take the value of `foo' as the name 
> of a parameter and work on it. So your example would become:
> 
>   if [[ $#arg > 1 && ${(Pt)${argv[1]}} == association ]] ...

The patch below does this (it was quite easy). With it you can do
things like the above or `${(P)+${foo}}' to see if the parameter whose 
name is stored in `foo' is set. If the inner `${...}' produces
multiple words they are joind using IFS which will result in an
invalid parameter name (which is reported as being unset). Maybe we
should make the code test if it is a valid name and barf otherwise. Or 
we could use only the first word. Any other suggestions?

But I don't know if you like this anyway...

The patch also changes the docs and the new-completion-examples.

Bye
 Sven

--- os/subst.c	Mon Feb  1 10:53:56 1999
+++ Src/subst.c	Tue Feb  2 17:27:50 1999
@@ -718,10 +718,12 @@
     int copied = 0;
     int arrasg = 0;
     int eval = 0;
+    int aspar = 0;
     int nojoin = 0;
     char inbrace = 0;		/* != 0 means ${...}, otherwise $... */
     char hkeys = 0;
     char hvals = 0;
+    int subexp;
 
     *s++ = '\0';
     if (!ialnum(*s) && *s != '#' && *s != Pound && *s != '-' &&
@@ -813,6 +815,9 @@
 		case 'e':
 		    eval = 1;
 		    break;
+		case 'P':
+		    aspar = 1;
+		    break;
 
 		case 'c':
 		    whichlen = 1;
@@ -949,7 +954,8 @@
 	    } else
 		globsubst = 1;
 	} else if (*s == '+') {
-	    if (iident(s[1]))
+	    if (iident(s[1]) || (aspar && isstring(s[1]) &&
+				 (s[2] == Inbrace || s[2] == Inpar)))
 		chkset = 1, s++;
 	    else if (!inbrace) {
 		*aptr = '$';
@@ -965,7 +971,8 @@
     globsubst = globsubst && !qt;
 
     idbeg = s;
-    if (s[-1] && isstring(*s) && (s[1] == Inbrace || s[1] == Inpar)) {
+    if ((subexp = (s[-1] && isstring(*s) &&
+		   (s[1] == Inbrace || s[1] == Inpar)))) {
 	int sav;
 	int quoted = *s == Qstring;
 
@@ -973,17 +980,22 @@
 	skipparens(*s, *s == Inpar ? Outpar : Outbrace, &s);
 	sav = *s;
 	*s = 0;
-	if (multsub(&val, &aval, &isarr, NULL) && quoted) {
+	if (multsub(&val, (aspar ? NULL : &aval), &isarr, NULL) && quoted) {
 	    isarr = -1;
 	    aval = alloc(sizeof(char *));
-	}
+	    aspar = 0;
+	} else if (aspar)
+	    idbeg = val;
 	if (isarr)
 	    isarr = -1;
 	copied = 1;
 	*s = sav;
 	v = (Value) NULL;
-    } else {
-	if (!(v = fetchvalue(&s, (wantt ? -1 :
+    }
+    if (!subexp || aspar) {
+	char *ov = val;
+
+	if (!(v = fetchvalue((subexp ? &ov : &s), (wantt ? -1 :
 				  ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
 			     hkeys|hvals)))
 	    vunset = 1;
--- od/Zsh/expn.yo	Mon Feb  1 10:53:36 1999
+++ Doc/Zsh/expn.yo	Tue Feb  2 17:51:35 1999
@@ -477,9 +477,10 @@
 pindex(GLOB_SUBST)
 Turn on the tt(GLOB_SUBST) option for the evaluation of
 var(spec); if the `tt(~)' is doubled, turn it off.  When this option is
-set, any pattern characters resulting
-from parameter expansion are eligible for filename expansion and filename
-generation.
+set, the string resulting from the expansion will be interpreted as a
+pattern thus becoming eligible for filename expansion and filename
+generation and usable as a pattern in pattern-matching contexts like
+the `tt(=)' and `tt(!=)' operators in conditions.
 )
 enditem()
 
@@ -522,6 +523,15 @@
 Perform em(parameter expansion), em(command substitution) and
 em(arithmetic expansion) on the result. Such expansions can be
 nested but too deep recursion may have unpredictable effects.
+)
+item(tt(P))(
+If used with a parameter name, this flag has no effect. But if it is
+used with a tt(${)...tt(}) type parameter expression or a
+tt($LPAR())...tt(RPAR()) type command substitution in place of
+the parameter name this flag makes the result of the expansion be
+taken as a parameter name which is then used. E.g. if you have
+`tt(foo=bar)' and `tt(bar=baz)', the string `tt(${(P)${foo}})' will be 
+expanded to `tt(baz)'.
 )
 item(tt(o))(
 Sort the resulting words in ascending order.
diff -u om/new-completion-examples Misc/new-completion-examples
--- om/new-completion-examples	Mon Feb  1 10:53:27 1999
+++ Misc/new-completion-examples	Tue Feb  2 17:43:34 1999
@@ -75,11 +75,8 @@
 # the arguments from the command line as its arguments.
 
 call-complete() {
-  local var
-
-  eval var\=\$\{\+$1\}
-  if (( var )); then
-    eval complist \$\{${1}\[\@\]\}
+  if [[ ${(P)+${1}} -eq 1 ]] then
+    complist ${(@P)${1}}
   else
     "$@"
   fi
@@ -96,15 +93,6 @@
   setopt localoptions nullglob rcexpandparam globdots
   unsetopt markdirs globsubst shwordsplit nounset
 
-  # We first try the `compctl's. This is without first (-T) and default (-D)
-  # completion. If you want them add `-T' and/or `-D' to this command.
-  # If this produces any matches, we don't try new style completion. If you
-  # want to have that tried anyway, remove the `[[ -nmatches ... ]] ...'
-  # below.
-
-  compcall
-  [[ -nmatches 0 ]] || return
-
   # An entry for `--first--' is the replacement for `compctl -T'
   # The `|| return 1' is used throughout: if a function producing matches
   # returns non-zero this is interpreted as `do not try to produce more matches'
@@ -117,6 +105,15 @@
   # convenience alias `compsub'.
 
   if [[ $CONTEXT == argument || $CONTEXT == command ]] then
+    # We first try the `compctl's. This is without first (-T) and default (-D)
+    # completion. If you want them add `-T' and/or `-D' to this command.
+    # If this produces any matches, we don't try new style completion. If you
+    # want to have that tried anyway, remove the `[[ -nmatches ... ]] ...'
+    # below.
+
+    compcall
+    [[ -nmatches 0 ]] || return
+
     compsub
   else
     # Let's see if we have a special completion definition for the other
@@ -205,7 +202,7 @@
     if [[ "$a[1]" = '(' ]] then
       ppres=( $a[2,-2]/ )
     else
-      eval ppres\=\( \$$a/ \)
+      ppres=( ${(P)${a}} )
       [[ $#ppres -eq 0 ]] && ppres=( $a/ )
     fi
     [[ $#ppres -eq 0 ]] && ppres=( '' )
@@ -329,11 +326,8 @@
 
 defcomp __subscr --subscr--
 __subscr() {
-  local t
-
-  eval t\=\$\{\(t\)$COMMAND\}
   compalso --math-- "$@"
-  [[ $t = assoc* ]] && eval complist -k \"\(\$\{\(k\)$COMMAND\}\)\"
+  [[ ${(Pt)${COMMAND}} = assoc* ]] && complist -k "( ${(kP)${COMMAND}} )"
 }
 
 # Do sub-completion for pre-command modifiers.

--
Sven Wischnowsky                         wischnow@informatik.hu-berlin.de


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

* Re: Associative array ordering and selective unset (Re: Example function)
@ 1999-02-02  8:01 Sven Wischnowsky
  0 siblings, 0 replies; 8+ messages in thread
From: Sven Wischnowsky @ 1999-02-02  8:01 UTC (permalink / raw)
  To: zsh-workers


Bart Schaefer wrote:

> (Note that I moved the (qQ) into the subscript flags, which is probably
> where it really has to be.  And I still hope for a better letter.)
> 
> Now change the assignment a little:
> 
>         map=('*.(gz|Z)' ': 1; zcat
>              '*.bz2' ': 2; bzip2 -dc'
>              '*.bz' ': 3; bzip -dc'
>              '*' ': 4; <')
> 
> Now we can use the (o) substitution flag like so:
> 
> 	eval ${${(o)map[(Q)$argv[i]]}[1]} '$argv[i]'
> 
> That says "find all the values for which the key is a pattern that matches
> $argv[i], sort them in ascending order, take the first one, and evaluate
> it as a command with argument $argv[i]."  By embedding the "preferred"
> ordering in the value as a leading ":" command, we're assured of always
> getting the most-specific match.

Hmm, I like this. For bigger arrays one would have to use `01' and so
on, but this should be acceptible.

Bye
 Sven


--
Sven Wischnowsky                         wischnow@informatik.hu-berlin.de


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

* Re: Associative array ordering and selective unset (Re: Example function)
  1999-02-01 17:39   ` Bruce Stephens
@ 1999-02-01 19:09     ` Bart Schaefer
  0 siblings, 0 replies; 8+ messages in thread
From: Bart Schaefer @ 1999-02-01 19:09 UTC (permalink / raw)
  To: zsh-workers

On Feb 1,  5:39pm, Bruce Stephens wrote:
} Subject: Re: Associative array ordering and selective unset (Re: Example f
}
} It would be a bit tricky to reorder the associative array, wouldn't
} it?  i.e., if I set up some configuration, and then want to insert
} some pattern before some others, then I'd need to recreate the
} associative array.  Maybe that's OK, but it feels a bit awkward.

I just thought of a really clever way to do the specific example that I
gave.  Assume for a moment that the (q) modifier were implemented, i.e.
that (almost repeating the original example) given

        typeset -A map
        map=('*.(gz|Z)' zcat
             '*.bz2' 'bzip2 -dc'
             '*.bz' 'bzip -dc'
             '*' '<')

then ${map[(q)$argv[i]]} returns the value for the first subscript match
found, and ${map[(Q)$argv[i]]} returns the array of values for every key
that matches the subscript.

(Note that I moved the (qQ) into the subscript flags, which is probably
where it really has to be.  And I still hope for a better letter.)

Now change the assignment a little:

        map=('*.(gz|Z)' ': 1; zcat
             '*.bz2' ': 2; bzip2 -dc'
             '*.bz' ': 3; bzip -dc'
             '*' ': 4; <')

Now we can use the (o) substitution flag like so:

	eval ${${(o)map[(Q)$argv[i]]}[1]} '$argv[i]'

That says "find all the values for which the key is a pattern that matches
$argv[i], sort them in ascending order, take the first one, and evaluate
it as a command with argument $argv[i]."  By embedding the "preferred"
ordering in the value as a leading ":" command, we're assured of always
getting the most-specific match.

Now, if only this actually worked ... I'm about to be tied up in a several-
week consulting job, so I probably won't get a chance to do anything with
this anytime soon.

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


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

* Re: Associative array ordering and selective unset  (Re: Example function)
  1999-02-01 17:02 ` Associative array ordering and selective unset (Re: Example function) Bart Schaefer
@ 1999-02-01 17:39   ` Bruce Stephens
  1999-02-01 19:09     ` Bart Schaefer
  1999-02-02 20:07   ` Phil Pennock
  1 sibling, 1 reply; 8+ messages in thread
From: Bruce Stephens @ 1999-02-01 17:39 UTC (permalink / raw)
  To: zsh-workers

"Bart Schaefer" <schaefer@brasslantern.com> writes:

> On Feb 1, 11:48am, Sven Wischnowsky wrote:
> } Subject: Re: Example function
> }
> } I was thinking about this... we could make the code keep a counter in
> } assoc arrays, increment it whenever a new key is added and store the
> } current value in the structure for this new element. Then we can treat 
> } the whole thing as being sorted by `time of addition'.
> } 
> } Hm, does this sound like the right thing?
> 
> Almost.  Something about it doesn't seem quite right to me, but I
> can't put my finger on what different behavior I'd expect.

It would be a bit tricky to reorder the associative array, wouldn't
it?  i.e., if I set up some configuration, and then want to insert
some pattern before some others, then I'd need to recreate the
associative array.  Maybe that's OK, but it feels a bit awkward.

Emacs (and other Lisps) has assoc-lists, which is maybe a better model
for this: they're explicitly lists (so they have ordering), but you
can access them by key.  I've no idea how this would map onto a shell,
but perhaps this is saying that associative arrays don't quite fit
what's wanted.

> I don't like the idea that every parameter table hash would end up
> with another integer of overhead in every entry, but maybe that's
> not so bad.

I agree, the extra integer is probably trivial.


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

* Associative array ordering and selective unset (Re: Example function)
  1999-02-01 10:48 Example function Sven Wischnowsky
@ 1999-02-01 17:02 ` Bart Schaefer
  1999-02-01 17:39   ` Bruce Stephens
  1999-02-02 20:07   ` Phil Pennock
  0 siblings, 2 replies; 8+ messages in thread
From: Bart Schaefer @ 1999-02-01 17:02 UTC (permalink / raw)
  To: zsh-workers

On Feb 1, 11:48am, Sven Wischnowsky wrote:
} Subject: Re: Example function
}
} I was thinking about this... we could make the code keep a counter in
} assoc arrays, increment it whenever a new key is added and store the
} current value in the structure for this new element. Then we can treat 
} the whole thing as being sorted by `time of addition'.
} 
} Hm, does this sound like the right thing?

Almost.  Something about it doesn't seem quite right to me, but I can't
put my finger on what different behavior I'd expect.

I don't like the idea that every parameter table hash would end up with
another integer of overhead in every entry, but maybe that's not so bad.

} P.S.: Is `foo=( ${(kv)foo[(I)^key]} )' the easiest way to remove a
}       key/value-pair from an associative array?

Probably, if you have extendedglob set.  If you don't have extendedglob,
that's a moderately hard way to remove -all- the key/value pairs.

We could go the ksh route and make `noglob unset foo[key]` work.  Like:

	function unset() {
	    emulate -R zsh
	    setopt localoptions extendedglob
	    local arg i
	    for i
	    do
		arg=( "${(@s:[:)i}" )
		if [[ $#arg > 1 &&
		    $(eval print '${(t)'${arg[1]}'}') == association ]]
		then
		    eval "$arg[1]=( \${(kv)$arg[1][(I)^${(j:[:)arg[2,-1]} )"
		else
		    builtin unset $i
		fi
	    done
	}

Can you think of a better idiom than $(eval print '${(t)'${arg[1]}'}') to
get the type of the variable whose name is the value of $arg[1] ?  I keep
thinking there ought to be a way with ${(e)...} but couldn't make it work.

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


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

end of thread, other threads:[~1999-02-02 20:07 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-02-02  7:52 Associative array ordering and selective unset (Re: Example function) Sven Wischnowsky
1999-02-02 11:10 ` PATCH: 3.1.5-pws-6: unset assoc[key] Bart Schaefer
  -- strict thread matches above, loose matches on Subject: below --
1999-02-02 16:58 Associative array ordering and selective unset (Re: Example function) Sven Wischnowsky
1999-02-02  8:01 Sven Wischnowsky
1999-02-01 10:48 Example function Sven Wischnowsky
1999-02-01 17:02 ` Associative array ordering and selective unset (Re: Example function) Bart Schaefer
1999-02-01 17:39   ` Bruce Stephens
1999-02-01 19:09     ` Bart Schaefer
1999-02-02 20:07   ` Phil Pennock

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).