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

[PATCH]: Revamped (P) expansion flag



Hello all,

In my opinion, the current behavior of the P expansion flag is not
intuitive, and possibly even buggy. For instance:

% FOO='hello!'
% REF='FOO BAR'
% print ${(P)${REF}}
hello!

In this case, ${(P)${REF}} should expand to nothing, because 'FOO BAR'
is not a proper parameter name. However, the current logic takes only
as much as makes sense, and ignores the rest. This creates confusion
with arrays:

% ARRAY=(FOO CLUE SHOE)
% FOO=zsh
% CLUE=is
% SHOE=awesome
% print ${(P)${ARRAY}}
zsh

One would think that each element of the reference ARRAY would
replaced by the value of the variable in that element. However, zsh
will just take the value of FOO, as it's the longest string that makes
sense as a variable name.

On the more buggy-ish side:

% STRING='zsh is awesome!'
% STR='zsh sucks... :-/'
% REF=STRING
% print ${(P)REF[1,3]}
zsh sucks... :-/

As ${(P)REF} is really ${(P)${REF}}, one would think that that
${(P)REF[1,3]} would be expanded as ${(P)${REF}[1,3]}. But as you can
see, it is instead expanded as ${(P)${REF[1,3]}}.

This patch fixes all these problems. The concept of "subexpression" no
longer applies with the P flag. Instead, one should consider whether
or not the "inside" expression (I call it a "reference") expands to
more than one word.

Notably:
1.) A reference can now be a mix of plain text and expansions. For
example, ${(P)${FOO}_BAR}.
2.) A reference can be quoted. Note that ${(P)"REF"} is now possible,
and expands as if it were ${(P)"${REF}"}.
3.) If the reference expands to more than one word, then each element
will be expanded to take on the value of that variable. (See below.)

It is simplest with quotes:

% FOO='zsh.org'
% REF=FOO
% print ${(P)"REF"}
zsh.org
% print ${(P)"${REF}"}
zsh.org

Fairly straight forward. But notice what happens when our reference
expands to a non-valid parameter name:

% FOO='merry xmas!'
% REF='FOO FOO'
% print ${(P)"${REF}"}

% print ${(P)"REF"}


Now try mixing (in quotes):

% FOOBAR='buy champagne for new year'
% REF=FOO
% print ${(P)"${REF}BAR"}
buy champagne for new year

The expansion takes on the value of the variable name that the insides
expand to.

Without quotes, array references are possible.

% REFS=(A B C)
% A=1
% B=2
% C=3
% print -l -- ${(P)${REFS}}
1
2
3

It is also possible to have an array reference with an element that
refers to an array parameter:

% REFS=(A B C)
% A=1
% B=(2 two)
% C=3
% print -l -- ${(P)${REFS}}
1
2
two
3

And finally, mixing, non-quoted.

% REFS=(FOO CLUE SHOE)
% FOOBAR=zsh
% CLUEBAR=is
% SHOEBAR=awesome
% print ${(P)${^REFS}BAR}
zsh is awesome

Note that ${(P)${REFS}BAR} would just expand to "awesome", as
${REFS}BAR expands to the words "FOO" "CLUE" "SHOEBAR", with FOO and
CLUE not being set.

Please test these changes. paramsubst() took a long time to
understand, so I'm sure that I've introduced a bug or two.

Michael Hwang


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