zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: zsh/param/private module
@ 2015-11-08 22:05 Bart Schaefer
  2015-11-09 10:08 ` Jun T.
  2015-11-09 10:58 ` Peter Stephenson
  0 siblings, 2 replies; 4+ messages in thread
From: Bart Schaefer @ 2015-11-08 22:05 UTC (permalink / raw)
  To: zsh-workers

This requires the preceding patch (37080) and replaces zsh/param/static
(posted in 36624 two months ago).  Most of the changes are around the
change of name, including "local -S" becoming "local -P" just in case
we ever want to mess about with ksh "typeset -S" compatibility.

I hope the doc is sufficiently comprehensive / explanatory.

diff --git a/Doc/Makefile.in b/Doc/Makefile.in
index 7645f42..d589991 100644
--- a/Doc/Makefile.in
+++ b/Doc/Makefile.in
@@ -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 \
diff --git a/Doc/Zsh/mod_private.yo b/Doc/Zsh/mod_private.yo
new file mode 100644
index 0000000..c08da67
--- /dev/null
+++ b/Doc/Zsh/mod_private.yo
@@ -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.
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
new file mode 100644
index 0000000..7f9aa79
--- /dev/null
+++ b/Src/Modules/param_private.c
@@ -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;
+}
diff --git a/Src/Modules/param_private.mdd b/Src/Modules/param_private.mdd
new file mode 100644
index 0000000..e6eb322
--- /dev/null
+++ b/Src/Modules/param_private.mdd
@@ -0,0 +1,7 @@
+name=zsh/param/private
+link=dynamic
+load=yes
+
+autofeatures="b:private"
+
+objects="param_private.o"
diff --git a/Test/V10private.ztst b/Test/V10private.ztst
new file mode 100644
index 0000000..444b5b4
--- /dev/null
+++ b/Test/V10private.ztst
@@ -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


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

* Re: PATCH: zsh/param/private module
  2015-11-08 22:05 PATCH: zsh/param/private module Bart Schaefer
@ 2015-11-09 10:08 ` Jun T.
  2015-11-09 10:58 ` Peter Stephenson
  1 sibling, 0 replies; 4+ messages in thread
From: Jun T. @ 2015-11-09 10:08 UTC (permalink / raw)
  To: zsh-workers


On 2015/11/09, at 7:05, Bart Schaefer <schaefer@brasslantern.com> wrote:

> +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()
> +})

failing --> bad_declaration ?


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

* Re: PATCH: zsh/param/private module
  2015-11-08 22:05 PATCH: zsh/param/private module Bart Schaefer
  2015-11-09 10:08 ` Jun T.
@ 2015-11-09 10:58 ` Peter Stephenson
  2015-11-09 18:15   ` Bart Schaefer
  1 sibling, 1 reply; 4+ messages in thread
From: Peter Stephenson @ 2015-11-09 10:58 UTC (permalink / raw)
  To: zsh-workers

This would fit in with standard practice with modules elsewhere..
we can't guarantee how the installer has set up config.modules
so should probably flag it as unimplemented rather than broken.

pws

diff --git a/Test/V10private.ztst b/Test/V10private.ztst
index 444b5b4..6c38e39 100644
--- a/Test/V10private.ztst
+++ b/Test/V10private.ztst
@@ -2,6 +2,9 @@
 
 %prep
 
+ if ! (zmodload zsh/param/private >/dev/null 2>/dev/null); then
+   ZTST_unimplemented="can't load the zsh/param/private module for testing"
+ fi
  zmodload zsh/param/private
 
 %test


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

* Re: PATCH: zsh/param/private module
  2015-11-09 10:58 ` Peter Stephenson
@ 2015-11-09 18:15   ` Bart Schaefer
  0 siblings, 0 replies; 4+ messages in thread
From: Bart Schaefer @ 2015-11-09 18:15 UTC (permalink / raw)
  To: zsh-workers

On Nov 9, 10:58am, Peter Stephenson wrote:
}
} This would fit in with standard practice with modules elsewhere..
} we can't guarantee how the installer has set up config.modules
} so should probably flag it as unimplemented rather than broken.
} 
} + if ! (zmodload zsh/param/private >/dev/null 2>/dev/null); then

Semi-sequitur:

This is zsh.  Why not >& /dev/null ?

Some tests use only 2>/dev/null, others use >/dev/null 2>&1, some use
a subshell and then repeat without one (like this patch) whereas others
simply use "if zmodload ...", etc.  What's actually necessary?


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

end of thread, other threads:[~2015-11-09 18:16 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-08 22:05 PATCH: zsh/param/private module Bart Schaefer
2015-11-09 10:08 ` Jun T.
2015-11-09 10:58 ` Peter Stephenson
2015-11-09 18:15   ` 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).