1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-01-01 05:16:05 +01:00

37081: new module zsh/param/private for private-scoped parameters in functions

This commit is contained in:
Barton E. Schaefer 2015-11-08 16:19:37 -08:00
parent 30b90f166e
commit d3d5325d27
6 changed files with 955 additions and 2 deletions

View file

@ -1,5 +1,10 @@
2015-11-08 Barton E. Schaefer <schaefer@brasslantern.com>
* 37081: Doc/Makefile.in, Doc/Zsh/mod_private.yo,
Src/Modules/param_private.c, Src/Modules/param_private.mdd,
Test/V10private.ztst: new module zsh/param/private for
private-scoped parameters in functions
* 37080: Src/builtin.c, Src/params.c: use paramtab abstraction more
consistently, add explanatory comments

View file

@ -62,8 +62,8 @@ Zsh/mod_computil.yo Zsh/mod_curses.yo \
Zsh/mod_datetime.yo Zsh/mod_db_gdbm.yo Zsh/mod_deltochar.yo \
Zsh/mod_example.yo Zsh/mod_files.yo Zsh/mod_langinfo.yo \
Zsh/mod_mapfile.yo Zsh/mod_mathfunc.yo Zsh/mod_newuser.yo \
Zsh/mod_parameter.yo Zsh/mod_pcre.yo Zsh/mod_regex.yo \
Zsh/mod_sched.yo Zsh/mod_socket.yo \
Zsh/mod_parameter.yo Zsh/mod_pcre.yo Zsh/mod_private.yo \
Zsh/mod_regex.yo Zsh/mod_sched.yo Zsh/mod_socket.yo \
Zsh/mod_stat.yo Zsh/mod_system.yo Zsh/mod_tcp.yo \
Zsh/mod_termcap.yo Zsh/mod_terminfo.yo \
Zsh/mod_zftp.yo Zsh/mod_zle.yo Zsh/mod_zleparameter.yo \

89
Doc/Zsh/mod_private.yo Normal file
View file

@ -0,0 +1,89 @@
COMMENT(!MOD!zsh/param/private
Builtins for managing private-scoped parameters in function context.
!MOD!)
The tt(zsh/param/private) module is used to create parameters whose scope
is limited to the current function body, and em(not) to other functions
called by the current function.
This module provides a single autoloaded builtin:
ifnzman()
startitem()
findex(private)
cindex(private parameter, creating)
item(tt(private) [ {tt(PLUS())|tt(-)}tt(AHUahlprtux) ] \
[ {tt(PLUS())|tt(-)}tt(EFLRZi) [ var(n) ] ] [ var(name)[tt(=)var(value)] ... ])(
The tt(private) builtin accepts all the same options and arguments as tt(local)
(ifzman(zmanref(zshbuiltins))ifnzman(noderef(Shell Builtin Commands))) except
for the `tt(-)tt(T)' option. Tied parameters may not be made private.
If used at the top level (outside a function scope), tt(private) creates a
normal parameter in the same manner as tt(declare) or tt(typeset). A
warning about this is printed if tt(WARN_CREATE_GLOBAL) is set
(ifzman(zmanref(zshoptions))ifnzman(noderef(Options))). Used inside a
function scope, tt(private) creates a local parameter similar to one
declared with tt(local), except having special properties noted below.
Special parameters which expose or manipulate internal shell state, such
as tt(ARGC), tt(argv), tt(COLUMNS), tt(LINES), tt(UID), tt(EUID), tt(IFS),
tt(PROMPT), tt(RANDOM), tt(SECONDS), etc., cannot be made private unless
the `tt(-)tt(h)' option is used to hide the special meaning of the
parameter. This may change in the future.
)
enditem()
As with other tt(typeset) equivalents, tt(private) is both a builtin and a
reserved word, so arrays may be assigned with parenthesized word list
var(name)tt(=LPAR())var(value)...tt(RPAR()) syntax. However, the reserved
word `tt(private)' is not available until tt(zsh/param/private) is loaded,
so care must be taken with order of execution and parsing for function
definitions which use tt(private). To compensate for this, the module
also adds the option `tt(-P)' to the `tt(local)' builtin to declare private
parameters.
For example, this construction fails if tt(zsh/param/private) has not yet
been loaded when `tt(failing)' is defined:
example(bad_declaration+LPAR()RPAR() {
zmodload zsh/param/private
private array=LPAR() one two three RPAR()
})
This construction works because tt(local) is already a keyword, and the
module is loaded before the statement is executed:
example(good_declaration+LPAR()RPAR() {
zmodload zsh/param/private
local -P array=LPAR() one two three RPAR()
})
The following is usable in scripts but may have trouble with tt(autoload):
example(zmodload zsh/param/private
iffy_declaration+LPAR()RPAR() {
private array=LPAR() one two three RPAR()
})
The tt(private) builtin may always be used with scalar assignments and
for declarations without assignments.
Parameters declared with tt(private) have the following properties:
ifnzman()
startitemize()
itemiz(Within the function body where it is declared, the parameter
behaves as a local, except as noted above for tied or special parameters.)
itemiz(The type of a parameter declared private cannot be changed in the
scope where it was declared, even if the parameter is unset. Thus an
array cannot be assigned to a private scalar, etc.)
itemiz(Within any other function called by the declaring function, the
private parameter does em(NOT) hide other parameters of the same name, so
for example a global parameter of the same name is visible and may be
assigned or unset. This includes calls to anonymous functions, although
that may also change in the future.)
itemiz(An exported private remains in the environment of inner scopes but
appears unset for the current shell in those scopes. Generally, exporting
private parameters should be avoided.)
enditemize()
Note that this differs from the static scope defined by compiled languages
derived from C, in that the a new call to the same function creates a new
scope, i.e., the parameter is still associated with the call stack rather
than with the function definition. It differs from ksh `tt(typeset -S)'
because the syntax used to define the function has no bearing on whether
the parameter scope is respected.

587
Src/Modules/param_private.c Normal file
View file

@ -0,0 +1,587 @@
/*
* param_private.c - bindings for private parameter scopes
*
* This file is part of zsh, the Z shell.
*
* Copyright (c) 2015 Barton E. Schaefer
* All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and to distribute modified versions of this software for any
* purpose, provided that the above copyright notice and the following
* two paragraphs appear in all copies of this software.
*
* In no event shall Barton E. Schaefer or the Zsh Development
* Group be liable to any party for direct, indirect, special, incidental, or
* consequential damages arising out of the use of this software and its
* documentation, even if Barton E. Schaefer and the Zsh
* Development Group have been advised of the possibility of such damage.
*
* Barton E. Schaefer and the Zsh Development Group
* specifically disclaim any warranties, including, but not limited to, the
* implied warranties of merchantability and fitness for a particular purpose.
* The software provided hereunder is on an "as is" basis, and
* Barton E. Schaefer and the Zsh Development Group have no
* obligation to provide maintenance, support, updates, enhancements, or
* modifications.
*
*/
#include "param_private.mdh"
#include "param_private.pro"
struct gsu_closure {
union {
struct gsu_scalar s;
struct gsu_integer i;
struct gsu_float f;
struct gsu_array a;
struct gsu_hash h;
} u;
void *g;
};
const struct gsu_scalar scalar_private_gsu =
{ pps_getfn, pps_setfn, pps_unsetfn };
const struct gsu_integer integer_private_gsu =
{ ppi_getfn, ppi_setfn, ppi_unsetfn };
const struct gsu_float float_private_gsu =
{ ppf_getfn, ppf_setfn, ppf_unsetfn };
const struct gsu_array array_private_gsu =
{ ppa_getfn, ppa_setfn, ppa_unsetfn };
const struct gsu_hash hash_private_gsu =
{ pph_getfn, pph_setfn, pph_unsetfn };
/*
* The trick here is:
*
* bin_private() opens a new parameter scope, then calls bin_typeset().
*
* bin_typeset() handles the usual parameter creation and error checks.
*
* makeprivate() then finds all parameters created in the new scope and
* rejects them if they can't be "promoted" to the surrounding scope.
* Otherwise it swaps out their GSU structure and promotes them so they
* will be removed when the surrounding scope ends.
*
* bin_private() then ends the current scope, which discards any of the
* parameters rejected by makeprivate().
*
*/
static int makeprivate_error = 0;
static void
makeprivate(HashNode hn, UNUSED(int flags))
{
Param pm = (Param)hn;
if (pm->level == locallevel) {
if (pm->ename || (pm->node.flags & PM_NORESTORE) ||
(pm->old &&
(pm->old->level == locallevel - 1 ||
((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL &&
/* typeset_single() line 2300 discards PM_REMOVABLE -- why? */
!is_private(pm->old))))) {
zwarnnam("private", "can't change scope of existing param: %s",
pm->node.nam);
makeprivate_error = 1;
return;
}
struct gsu_closure *gsu = zhalloc(sizeof(struct gsu_closure));
switch (PM_TYPE(pm->node.flags)) {
case PM_SCALAR:
gsu->g = (void *)(pm->gsu.s);
gsu->u.s = scalar_private_gsu;
pm->gsu.s = (GsuScalar)gsu;
break;
case PM_INTEGER:
gsu->g = (void *)(pm->gsu.i);
gsu->u.i = integer_private_gsu;
pm->gsu.i = (GsuInteger)gsu;
break;
case PM_EFLOAT:
case PM_FFLOAT:
gsu->g = (void *)(pm->gsu.f);
gsu->u.f = float_private_gsu;
pm->gsu.f = (GsuFloat)gsu;
break;
case PM_ARRAY:
gsu->g = (void *)(pm->gsu.a);
gsu->u.a = array_private_gsu;
pm->gsu.a = (GsuArray)gsu;
break;
case PM_HASHED:
gsu->g = (void *)(pm->gsu.h);
gsu->u.h = hash_private_gsu;
pm->gsu.h = (GsuHash)gsu;
break;
default:
makeprivate_error = 1;
break;
}
/* PM_HIDE so new parameters in deeper scopes do not shadow */
pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE);
pm->level -= 1;
}
}
/**/
static int
is_private(Param pm)
{
switch (PM_TYPE(pm->node.flags)) {
case PM_SCALAR:
if (!pm->gsu.s || pm->gsu.s->unsetfn != pps_unsetfn)
return 0;
break;
case PM_INTEGER:
if (!pm->gsu.i || pm->gsu.i->unsetfn != ppi_unsetfn)
return 0;
break;
case PM_EFLOAT:
case PM_FFLOAT:
if (!pm->gsu.f || pm->gsu.f->unsetfn != ppf_unsetfn)
return 0;
break;
case PM_ARRAY:
if (!pm->gsu.a || pm->gsu.a->unsetfn != ppa_unsetfn)
return 0;
break;
case PM_HASHED:
if (!pm->gsu.h || pm->gsu.h->unsetfn != pph_unsetfn)
return 0;
break;
default:
/* error */
return 0;
}
return 1;
}
static int fakelevel;
/**/
static int
bin_private(char *nam, char **args, LinkList assigns, Options ops, int func)
{
int from_typeset = 1;
makeprivate_error = 0;
if (!OPT_ISSET(ops, 'P'))
return bin_typeset(nam, args, assigns, ops, func);
else if (OPT_ISSET(ops, 'T')) {
zwarn("bad option: -T");
return 1;
}
if (locallevel == 0) {
if (isset(WARNCREATEGLOBAL))
zwarnnam(nam, "invalid local scope, using globals");
return bin_typeset("private", args, assigns, ops, func);
}
ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */
queue_signals();
fakelevel = locallevel;
startparamscope();
from_typeset = bin_typeset("private", args, assigns, ops, func);
scanhashtable(paramtab, 0, 0, 0, makeprivate, 0);
endparamscope();
fakelevel = 0;
unqueue_signals();
return makeprivate_error | from_typeset;
}
static void
setfn_error(Param pm)
{
pm->node.flags |= PM_UNSET;
zerr("%s: attempt to assign private in nested scope", pm->node.nam);
}
/*
* How the GSU functions work:
*
* The getfn and setfn family compare to locallevel and then call through
* to the original getfn or setfn. This means you can't assign at a
* deeper scope to any parameter declared private unless you first declare
* it local again at the new scope. Testing locallevel in getfn is most
* likely unnecessary given the scopeprivate() wrapper installed below.
*
* The unsetfn family compare locallevel and restore the old GSU before
* calling the original unsetfn. This assures that if the old unsetfn
* wants to use its getfn or setfn, they're unconditionally present.
*
*/
/**/
static char *
pps_getfn(Param pm)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s);
GsuScalar gsu = (GsuScalar)(c->g);
if (locallevel >= pm->level)
return gsu->getfn(pm);
else
return (char *) hcalloc(1);
}
/**/
static void
pps_setfn(Param pm, char *x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s);
GsuScalar gsu = (GsuScalar)(c->g);
if (locallevel == pm->level)
gsu->setfn(pm, x);
else
setfn_error(pm);
}
/**/
static void
pps_unsetfn(Param pm, int x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s);
GsuScalar gsu = (GsuScalar)(c->g);
pm->gsu.s = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, x);
}
/**/
static zlong
ppi_getfn(Param pm)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i);
GsuInteger gsu = (GsuInteger)(c->g);
if (locallevel >= pm->level)
return gsu->getfn(pm);
else
return 0;
}
/**/
static void
ppi_setfn(Param pm, zlong x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i);
GsuInteger gsu = (GsuInteger)(c->g);
if (locallevel == pm->level)
gsu->setfn(pm, x);
else
setfn_error(pm);
}
/**/
static void
ppi_unsetfn(Param pm, int x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i);
GsuInteger gsu = (GsuInteger)(c->g);
pm->gsu.i = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, x);
}
/**/
static double
ppf_getfn(Param pm)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f);
GsuFloat gsu = (GsuFloat)(c->g);
if (locallevel >= pm->level)
return gsu->getfn(pm);
else
return 0;
}
/**/
static void
ppf_setfn(Param pm, double x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f);
GsuFloat gsu = (GsuFloat)(c->g);
if (locallevel == pm->level)
gsu->setfn(pm, x);
else
setfn_error(pm);
}
/**/
static void
ppf_unsetfn(Param pm, int x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f);
GsuFloat gsu = (GsuFloat)(c->g);
pm->gsu.f = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, x);
}
/**/
static char **
ppa_getfn(Param pm)
{
static char *nullarray = NULL;
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a);
GsuArray gsu = (GsuArray)(c->g);
if (locallevel >= pm->level)
return gsu->getfn(pm);
else
return &nullarray;
}
/**/
static void
ppa_setfn(Param pm, char **x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a);
GsuArray gsu = (GsuArray)(c->g);
if (locallevel == pm->level)
gsu->setfn(pm, x);
else
setfn_error(pm);
}
/**/
static void
ppa_unsetfn(Param pm, int x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a);
GsuArray gsu = (GsuArray)(c->g);
pm->gsu.a = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, x);
}
static HashTable emptytable;
/**/
static HashTable
pph_getfn(Param pm)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h);
GsuHash gsu = (GsuHash)(c->g);
if (locallevel >= pm->level)
return gsu->getfn(pm);
else
return emptytable;
}
/**/
static void
pph_setfn(Param pm, HashTable x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h);
GsuHash gsu = (GsuHash)(c->g);
if (locallevel == pm->level)
gsu->setfn(pm, x);
else
setfn_error(pm);
}
/**/
static void
pph_unsetfn(Param pm, int x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h);
GsuHash gsu = (GsuHash)(c->g);
pm->gsu.h = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, x);
}
/*
* How visibility works:
*
* Upon entering a new function scope, we find all the private parameters
* at this locallevel. Any that we find are marked PM_UNSET. If they are
* already unset, they are also marked as PM_NORESTORE.
*
* On exit from the scope, we find the same parameters again and remove
* the PM_UNSET and PM_NORESTORE flags as appropriate. We're guaraneed
* by makeprivate() that PM_NORESTORE won't conflict with anything here.
*
*/
static void
scopeprivate(HashNode hn, int onoff)
{
Param pm = (Param)hn;
if (pm->level == locallevel) {
if (!is_private(pm))
return;
if (onoff == PM_UNSET)
if (pm->node.flags & PM_UNSET)
pm->node.flags |= PM_NORESTORE;
else
pm->node.flags |= PM_UNSET;
else if (!(pm->node.flags & PM_NORESTORE))
pm->node.flags &= ~PM_UNSET;
pm->node.flags &= ~PM_NORESTORE;
}
}
static struct funcwrap wrapper[] = {
WRAPDEF(wrap_private)
};
/**/
static int
wrap_private(Eprog prog, FuncWrap w, char *name)
{
static int wraplevel = 0;
if (wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) {
int owl = wraplevel;
wraplevel = locallevel;
scanhashtable(paramtab, 0, 0, 0, scopeprivate, PM_UNSET);
runshfunc(prog, w, name);
scanhashtable(paramtab, 0, 0, 0, scopeprivate, 0);
wraplevel = owl;
return 0;
}
return 1;
}
static HashNode (*getparamnode) _((HashTable, const char *));
/**/
static HashNode
getprivatenode(HashTable ht, const char *nam)
{
HashNode hn = getparamnode(ht, nam);
Param pm = (Param) hn;
while (!fakelevel && pm && locallevel > pm->level && is_private(pm))
pm = pm->old;
return (HashNode)pm;
}
/**/
static HashNode
getprivatenode2(HashTable ht, const char *nam)
{
/* getparamnode() would follow autoloads, we must not do that here */
HashNode hn = gethashnode2(ht, nam);
Param pm = (Param) hn;
while (!fakelevel && pm && locallevel > pm->level && is_private(pm))
pm = pm->old;
return (HashNode)pm;
}
/**/
static void
printprivatenode(HashNode hn, int printflags)
{
Param pm = (Param) hn;
while (pm && (!fakelevel ||
(fakelevel > pm->level && (pm->node.flags & PM_UNSET))) &&
locallevel > pm->level && is_private(pm))
pm = pm->old;
/* Ideally, we'd print the word "private" here instead of "typeset"
* when the parameter is in fact a private, but that would require
* re-implementing the entirety of printparamnode(). */
if (pm)
printparamnode((HashNode)pm, printflags);
}
/*
* Standard module configuration/linkage
*/
static struct builtin bintab[] = {
/* Copied from BUILTIN("local"), "P" added */
BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lprtux", "P")
};
static struct features module_features = {
bintab, sizeof(bintab)/sizeof(*bintab),
NULL, 0,
NULL, 0,
NULL, 0,
0
};
static struct builtin save_local;
static struct reswd reswd_private = {{NULL, "private", 0}, TYPESET};
/**/
int
setup_(UNUSED(Module m))
{
HashNode hn = builtintab->getnode(builtintab, "local");
/* Horrible, horrible hack */
getparamnode = realparamtab->getnode;
realparamtab->getnode = getprivatenode;
realparamtab->getnode2 = getprivatenode2;
realparamtab->printnode = printprivatenode;
/* Even more horrible hack */
save_local = *(Builtin)hn;
((Builtin)hn)->handlerfunc = bintab[0].handlerfunc;
((Builtin)hn)->optstr = bintab[0].optstr;
reswdtab->addnode(reswdtab, reswd_private.node.nam, &reswd_private);
return 0;
}
/**/
int
features_(Module m, char ***features)
{
*features = featuresarray(m, &module_features);
return 0;
}
/**/
int
enables_(Module m, int **enables)
{
return handlefeatures(m, &module_features, enables);
}
/**/
int
boot_(Module m)
{
emptytable = newparamtable(1, "private");
return addwrapper(m, wrapper);
}
/**/
int
cleanup_(Module m)
{
HashNode hn = builtintab->getnode(builtintab, "local");
*(Builtin)hn = save_local;
removehashnode(reswdtab, "private");
realparamtab->getnode = getparamnode;
realparamtab->getnode2 = gethashnode2;
realparamtab->printnode = printparamnode;
deletewrapper(m, wrapper);
return setfeatureenables(m, &module_features, NULL);
}
/**/
int
finish_(UNUSED(Module m))
{
deletehashtable(emptytable);
return 0;
}

View file

@ -0,0 +1,7 @@
name=zsh/param/private
link=dynamic
load=yes
autofeatures="b:private"
objects="param_private.o"

265
Test/V10private.ztst Normal file
View file

@ -0,0 +1,265 @@
# Tests for the zsh/param/private module
%prep
zmodload zsh/param/private
%test
(zmodload -u zsh/param/private && zmodload zsh/param/private)
0:unload and reload the module without crashing
typeset scalar_test=toplevel
() {
print $scalar_test
private scalar_test
print $+scalar_test
unset scalar_test
print $+scalar_test
}
print $scalar_test
0:basic scope hiding
>toplevel
>1
>0
>toplevel
typeset scalar_test=toplevel
print $scalar_test
() {
private scalar_test=function
print $scalar_test
}
print $scalar_test
0:enter and exit a scope
>toplevel
>function
>toplevel
print $+unset_test
() {
private unset_test
print $+unset_test
unset_test=setme
print $unset_test
}
print $+unset_test
0:variable defined only in scope
>0
>1
>setme
>0
# Depends on zsh-5.0.9 typeset keyword
typeset -a array_test=(top level)
() {
local -Pa array_test=(in function)
() {
private array_test
print $+array_test
}
print $array_test
}
print $array_test
0:nested scope with different type, correctly restored
>1
>in function
>top level
typeset -a array_test=(top level)
() {
private array_test
array_test=(in function)
}
1:type of private may not be changed by assignment
?(anon):2: array_test: attempt to assign array value to non-array
typeset -A hash_test=(top level)
() {
setopt localoptions noglob
private hash_test[top]
}
1:associative array fields may not be private
?(anon):private:2: hash_test[top]: can't create local array elements
() {
private path
}
1:tied params may not be private, part 1
?(anon):private:1: can't change scope of existing param: path
() {
private PATH
}
1:tied params may not be private, part 2
?(anon):private:1: can't change scope of existing param: PATH
() {
private -h path
print X$path
}
0:privates may hide tied paramters
>X
# Deliberate type mismatch here
typeset -a hash_test=(top level)
typeset -p hash_test
inner () {
private -p hash_test
print ${(t)hash_test} ${(kv)hash_test}
}
outer () {
local -PA hash_test=(in function)
typeset -p hash_test
inner
}
outer
print ${(kv)hash_test}
0:private hides value from surrounding scope in nested scope
>typeset -a hash_test
>hash_test=( top level )
>typeset -A hash_test
>hash_test=( in function )
>typeset -a hash_test
>hash_test=( top level )
>array-local top level
>top level
F:note "typeset" rather than "private" in output from outer
() {
private -a array_test
local array_test=scalar
}
1:private cannot be re-declared as local
?(anon):local:2: array_test: inconsistent type for assignment
() {
local hash_test=scalar
private -A hash_test
}
1:local cannot be re-declared as private
?(anon):private:2: can't change scope of existing param: hash_test
inner () {
print $+scalar_test
$ZTST_testdir/../Src/zsh -fc 'print X $scalar_test'
}
() {
private -x scalar_test=whaat
$ZTST_testdir/../Src/zsh -fc 'print X $scalar_test'
inner
print Y $scalar_test
}
0:exported private behaves like a local, part 1
>X whaat
>0
>X whaat
>Y whaat
inner () {
typeset -p array_test
$ZTST_testdir/../Src/zsh -fc 'print X $array_test'
}
() {
local -Pax array_test=(whaat)
print Y $array_test
$ZTST_testdir/../Src/zsh -fc 'print X $array_test'
inner
}
0:exported private behaves like a local, part 2 (arrays do not export)
?inner:typeset:1: no such variable: array_test
>Y whaat
>X
>X
inner () {
print $+scalar_test
$ZTST_testdir/../Src/zsh -fc 'print X $scalar_test'
}
() {
private scalar_test=whaat
export scalar_test
$ZTST_testdir/../Src/zsh -fc 'print X $scalar_test'
inner
() {
print $+scalar_test
$ZTST_testdir/../Src/zsh -fc 'print X $scalar_test'
}
print Y $scalar_test
}
0:exported private behaves like a local, part 3 (export does not change scope)
>X whaat
>0
>X whaat
>0
>X whaat
>Y whaat
typeset -A hash_test=(top level)
() {
local -PA hash_test=(in function)
() {
print X ${(kv)hash_test}
}
print Y ${(kv)hash_test}
}
print ${(kv)hash_test}
0:privates are not visible in anonymous functions, part 1
>X top level
>Y in function
>top level
typeset -A hash_test=(top level)
() {
local -PA hash_test=(in function)
() {
print X ${(kv)hash_test}
hash_test[in]=deeper
}
print Y ${(kv)hash_test}
}
print ${(okv)hash_test}
0:privates are not visible in anonymous functions, part 2
>X top level
>Y in function
>deeper in level top
typeset -A hash_test=(top level)
() {
local -Pa array_test=(in function)
local -PA hash_test=($array_test)
() {
print X ${(kv)hash_test}
hash_test=(even deeper)
array_test+=(${(kv)hash_test})
}
print Y ${(kv)hash_test} Z $array_test
}
print ${(kv)hash_test}
0:privates are not visible in anonymous functions, part 3
>X top level
>Y in function Z in function
>even deeper
typeset -A hash_test=(top level)
() {
local -PA hash_test=(in function)
() {
print X ${(kv)hash_test}
unset hash_test
}
print Y ${(kv)hash_test}
}
print ${(t)hash_test} ${(kv)hash_test}
0:privates are not visible in anonymous functions, part 4
>X top level
>Y in function
>
# Subshell because otherwise this silently dumps core when broken
( () { private SECONDS } )
1:special parameters cannot be made private
?(anon):private: can't change scope of existing param: SECONDS
() { private -h SECONDS }
0:private parameter may hide a special parameter