/* A Bison parser, made by GNU Bison 3.8.2.  */

/* Bison implementation for Yacc-like parsers in C

   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
   Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

/* As a special exception, you may create a larger work that contains
   part or all of the Bison parser skeleton and distribute that work
   under terms of your choice, so long as that work isn't itself a
   parser generator using the skeleton or a modified version thereof
   as a parser skeleton.  Alternatively, if you modify or redistribute
   the parser skeleton itself, you may (at your option) remove this
   special exception, which will cause the skeleton and the resulting
   Bison output files to be licensed under the GNU General Public
   License without this special exception.

   This special exception was added by the Free Software Foundation in
   version 2.2 of Bison.  */

/* C LALR(1) parser skeleton written by Richard Stallman, by
   simplifying the original so-called "semantic" parser.  */

/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
   especially those whose name start with YY_ or yy_.  They are
   private implementation details that can be changed or removed.  */

/* All symbols defined below should begin with yy or YY, to avoid
   infringing on user name space.  This should be done even for local
   variables, as they might otherwise be expanded by user macros.
   There are some unavoidable exceptions within include files to
   define necessary library symbols; they are noted "INFRINGES ON
   USER NAME SPACE" below.  */

/* Identify Bison output, and Bison version.  */
#define YYBISON 30802

/* Bison version string.  */
#define YYBISON_VERSION "3.8.2"

/* Skeleton name.  */
#define YYSKELETON_NAME "yacc.c"

/* Pure parsers.  */
#define YYPURE 0

/* Push parsers.  */
#define YYPUSH 0

/* Pull parsers.  */
#define YYPULL 1

/* Substitute the type names.  */
#define YYSTYPE         DERIVE_STYPE
/* Substitute the variable and function names.  */
#define yyparse         derive_parse
#define yylex           derive_lex
#define yyerror         derive_error
#define yydebug         derive_debug
#define yynerrs         derive_nerrs
#define yylval          derive_lval
#define yychar          derive_char

/* First part of user prologue.  */
#line 44 "derive_parser.y"


#include <inttypes.h>
#include <assert.h>
#include <ctype.h>
#include "pmapi.h"
#include "libpcp.h"
#include "internal.h"
#include "fault.h"
#include <sys/stat.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <string.h>
#include "derive.h"

#define YYDEBUG 1

static int		need_init = 2;
static int		in_matchinst = 0;	/* context sensitive / lexing */
static ctl_t		registered = {
    0,			/* nmetric */
    0,			/* nanon */
    NULL,		/* mlist */
    DM_UNLIMITED,	/* limit */
    			/* mutex */
#ifdef PM_MULTI_THREAD
    PTHREAD_MUTEX_INITIALIZER,
#else
    0,
#endif
    0,			/* glob_last -- not used in registered */
    0,			/* fetch_has_dm -- not used in registered */
    0			/* numpmid -- not used in registered */
};

#ifdef PM_MULTI_THREAD
#ifdef HAVE___THREAD
/* using a gcc construct here to make derive_errmsg thread-private */
static __thread char	*derive_errmsg;
#endif
#else
static char		*derive_errmsg;
#endif

#ifdef PM_MULTI_THREAD
pthread_mutex_t	unregister_lock = PTHREAD_MUTEX_INITIALIZER;
#else
void		*unregister_lock;
#endif

/*
 * Control options for derived metrics ... used by
 * pm{Get,Set}DerivedControl(), the $PCP_DERIVED_CONTROL environment
 * variable and #control from a config file
 *
 * This table needs to match the ordinal order of the matching #defines.
 */
#define OPT_NOTSET	0
#define OPT_SET_ENV	1
#define OPT_SET_APP	2
#define OPT_SET_CONTROL	3
static struct {
    int		state;		/* where set, see OPT_SET_ macros */
    const char	*name;		/* #define stripped of leading PCP_DERIVED_ */
} options[] = {
    /* PCP_DERIVED_... starts at 1, so first entry is a no-op */
    { OPT_NOTSET, "" },
    { OPT_NOTSET, "GLOBAL_LIMIT" },
    { OPT_NOTSET, "CONTEXT_LIMIT" },
    { OPT_NOTSET, "DEBUG_SYNTAX" },
    { OPT_NOTSET, "DEBUG_SEMANTICS" },
    { OPT_NOTSET, "DEBUG_EVAL" },
    { OPT_NOTSET, "OPTION_NOVALUE" },
};
static const int n_options = sizeof(options) / sizeof(options[0]);

static int map2novalue;		/* for NOVALUE control option */

/* lexer variables */
static char		*tokbuf;
static int		tokbuflen;
static const char	*lexicon;	/* start of current lexicon */
static int		lexpeek;
static const char	*string;

/* parser type structure */
typedef union {
    char	*s;
    node_t	*n;
    pmUnits	u;
    pmDesc	d;
    int		i;
} YYSTYPE;
#define YYSTYPE_IS_DECLARED 1

static node_t *parse_tree;

int derive_parse(void);
static int derive_lex(void);
static void derive_error(char *);
static int registeranon(int, const char *, int);
static int __dminit_configfile(__pmContext *, const char *);
int derive_debug;

static char *n_type_c(int);
static char *l_type_str(int);

/* strings for error reporting */
static const char follow[]	 = "follow";
static const char bexpr_str[]	 = "Boolean expression";
static const char aexpr_str[]	 = "Arithmetic expression";
static const char op_str[]	 = "Arithmetic or relational or boolean operator";
static const char name_str[]	 = "Metric name";
static const char unexpected_str[]	 = "Unexpected";
static const char initial_str[]	 = "Unexpected initial";

/*
 * bit-field flags for speclist in mkconst() and novalue() to
 * indicate which parameters have been explicitly set ... accumulated in
 * specflags and then stored in flags in the associated expr node
 */
#define META_PMID	1	/* not used, here for completeness */
#define META_TYPE	2
#define META_INDOM	4	/* not used, here for completeness */
#define META_SEM	8
#define META_UNITS	16

static const pmUnits noUnits;
static pmDesc specdesc;		/* used for speclist */
static int specflags;		/* ditto */
static node_t *specmeta;	/* metric name from meta=... */

static int unregister_count;	/* for pmUnregiserDerived */

/*
 * Handle one component of the ':' separated derived config spec.
 * Used for $PCP_DERIVED_CONFIG evaluation and pmLoadDerivedConfig.
 *
 * If descend is 1 and name is a directory then we will process all
 * the files in that directory, otherwise directories are skipped.
 * Note: for the current implementation, descend is always 1.
 *
 * If recover is 1 then we are tolerant of failures like missing or
 * inaccessible files/directories, otherwise we're not and an error
 * is propagated back to the caller.
 */
static int
__dminit_component(__pmContext *ctxp, const char *name, int descend, int recover)
{
    struct stat	sbuf;
    int		sts = 0;
    int		lsts = 0;

    if (stat(name, &sbuf) < 0) {
	sts = -oserror();
	if (pmDebugOptions.derive) {
	    char	errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "Warning: derived metrics path component: %s: %s\n",
		name, pmErrStr_r(sts, errmsg, sizeof(errmsg)));
	}
	goto finish;
    }
    if (S_ISREG(sbuf.st_mode)) {
	/* regular file or symlink to a regular file, load it */
	sts = __dminit_configfile(ctxp, name);
	if (sts < 0 && pmDebugOptions.derive) {
	    char	errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "pmLoadDerivedConfig(%s): %s\n", name, pmErrStr_r(sts, errmsg, sizeof(errmsg)));
	}
	goto finish;
    }
    if (descend && S_ISDIR(sbuf.st_mode)) {
	/* directory, descend to process all files in the directory */
	DIR		*dirp;
	struct dirent	*dp;

	if ((dirp = opendir(name)) == NULL) {
	    sts = -oserror();
	    if (pmDebugOptions.derive) {
		char	errmsg[PM_MAXERRMSGLEN];
		fprintf(stderr, "Warning: derived metrics path directory component: %s: %s\n",
		    name, pmErrStr_r(sts, errmsg, sizeof(errmsg)));
	    }
	    goto finish;
	}
	/* dirp is an on-stack variable, so readdir() is ... */
	/* THREADSAFE */
	while (setoserror(0), (dp = readdir(dirp)) != NULL) {
	    char	path[MAXPATHLEN+1];
	    int		localsts;
	    /*
	     * skip "." and ".." and recursively call __dminit_component()
	     * to process the directory entries ... descend is passed down
	     */
	    if (strcmp(dp->d_name, ".") == 0) continue;
	    if (strcmp(dp->d_name, "..") == 0) continue;
	    pmsprintf(path, sizeof(path), "%s%c%s", name, pmPathSeparator(), dp->d_name);
	    if ((localsts = __dminit_component(ctxp, path, descend, recover)) < 0) {
		sts = localsts;
		closedir(dirp);
		goto finish;
	    }
	    sts += localsts;
	}
	/* error is most unlikely and ignore unless -Dderive specified */
	lsts = -oserror();
	if (lsts != 0 && pmDebugOptions.derive) {
	    char	errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "Warning: %s: readdir failed: %s\n",
		name, pmErrStr_r(lsts, errmsg, sizeof(errmsg)));
	}
	closedir(dirp);
	goto finish;
    }
    /* otherwise not a file or symlink to a real file or a directory */
    if (pmDebugOptions.derive) {
	fprintf(stderr, "Warning: derived metrics path component: %s: unexpected st_mode=%o?\n",
	    name, (unsigned int)sbuf.st_mode);
    }

finish:
    return recover ? 0 : sts;
}

/*
 * Parse, split and process ':' separated components from a
 * derived metrics path specification.
 */
static int
__dminit_parse(__pmContext *ctxp, const char *path, int recover)
{
    const char	*p = path;
    const char	*q;
    int		sts = 0;
    int		lsts;

    while ((q = strchr(p, ':')) != NULL) {
	char	*name = strndup(p, q-p+1);
	name[q-p] = '\0';
	lsts = __dminit_component(ctxp, name, 1, recover);
	if (lsts < 0) {
	    free(name);
	    return lsts;
	}
	sts += lsts;
	free(name);
	p = q+1;
    }
    if (*p != '\0') {
	lsts = __dminit_component(ctxp, p, 1, recover);
	if (lsts < 0)
	    return lsts;
	sts += lsts;
    }
    return sts;
}

/*
 * internal version of pmGetDerivedControl to set control options
 */
static int
getderivedcontrol(__pmContext *ctxp, int what, int *valuep)
{
    int		sts = 0;
    int		value;
    __pmContext	*lctxp;

    switch (what) {
    case PCP_DERIVED_GLOBAL_LIMIT:
		PM_ASSERT_IS_LOCKED(registered.mutex);
		value = registered.limit;
		break;
    case PCP_DERIVED_CONTEXT_LIMIT:
		if (ctxp == NULL) {
		    int		ctx;
		    if ((ctx = pmWhichContext()) < 0) {
			sts = ctx;
			break;
		    }
		    lctxp = __pmHandleToPtr(ctx);
		    if (lctxp == NULL) {
			sts = PM_ERR_NOCONTEXT;
			break;
		    }
		}
		else
		    lctxp = ctxp;
		PM_ASSERT_IS_LOCKED(lctxp->c_lock);
		if (lctxp->c_dm != NULL)
		    value = ((ctl_t *)lctxp->c_dm)->limit;
		else {
		    /* called early before c_dm set up, there can be no value */
		    sts = PM_ERR_NOCONTEXT;
		}
		if (ctxp == NULL)
		    PM_UNLOCK(lctxp->c_lock);
		break;
    case PCP_DERIVED_DEBUG_SYNTAX:
		value = (pmDebugOptions.derive && pmDebugOptions.appl0);
		break;
    case PCP_DERIVED_DEBUG_SEMANTICS:
		value = (pmDebugOptions.derive && pmDebugOptions.appl1);
		break;
    case PCP_DERIVED_DEBUG_EVAL:
		value = (pmDebugOptions.derive && pmDebugOptions.appl2);
		break;
    case PCP_DERIVED_OPTION_NOVALUE:
		value = map2novalue;
		break;
    default:
		/*
		 * this is overloading PM_ERR_NAME a bit, "what" is
		 * the name of a control option, not a performance
		 * metric, but this is the closest error code we
		 * have
		 */
    		sts = PM_ERR_NAME;
		break;
    }

    if (sts >= 0) {
	*valuep = value;
	sts = 0;
    }

    return sts;
}

/*
 * internal version of pmSetDerivedControl to set control options
 *
 * where is one of the OPT_ macros, specifying the context that
 * is trying to set the control option.
 *
 * what is one of the PCP_DERIVED_ macros, but also an index
 * into options[]
 *
 * value is the value to be set
 *
 * Returns
 * PM_ERR_CONTEXT - what == PCP_DERIVED_CONTEXT_LIMIT => need
 * 			a PMAPI context
 * PM_ERR_NAME - what is not a control option
 * PM_ERR_ARG - value is outside the acceptable range
 * PM_ERR_AGAIN - request conflicts with earlier setting
 */
static int
setderivedcontrol(__pmContext *ctxp, int where, int what, int value)
{
    int		oldvalue;
    __pmContext	*lctxp;
    int		sts = 0;

    if (getderivedcontrol(ctxp, what, &oldvalue) == 0) {
	/*
	 * what is valid if old value == requested value
	 * return immediately as there is nothing to do
	 */
	if (oldvalue == value) {
	    if (options[what].state == OPT_NOTSET) {
		/* first setter is the winner, so far */
		options[what].state = where;
	    }
	    return 0;
	}
	if (options[what].state != OPT_NOTSET) {
	    /*
	     * option already set, apply precedence rules
	     * - setting from environment always wins
	     */
	    if (where != OPT_SET_ENV && options[what].state == OPT_SET_ENV) {
		/*
		 * not a winner ... this is overloading PM_ERR_AGAIN,
		 * but there is no obvious better error code
		 */
		return PM_ERR_AGAIN;
	    }
	}
    }

    switch (what) {
    case PCP_DERIVED_GLOBAL_LIMIT:
		if (value == -1 || value > 0) {
		    PM_ASSERT_IS_LOCKED(registered.mutex);
		    registered.limit = value;
		}
		else
		    sts = PM_ERR_ARG;
		break;
    case PCP_DERIVED_CONTEXT_LIMIT:
		if (value == -1 || value > 0) {
		    if (ctxp == NULL) {
			int		ctx;
			if ((ctx = pmWhichContext()) < 0) {
			    sts = ctx;
			    break;
			}
			lctxp = __pmHandleToPtr(ctx);
			if (lctxp == NULL) {
			    sts = PM_ERR_NOCONTEXT;
			    break;
			}
		    }
		    else
			lctxp = ctxp;
		    PM_ASSERT_IS_LOCKED(lctxp->c_lock);
		    if (lctxp->c_dm != NULL)
			((ctl_t *)lctxp->c_dm)->limit = value;
		    else {
			/* called early before c_dm set up, nothing to set */
			sts = PM_ERR_NOCONTEXT;
		    }
		    if (ctxp == NULL)
			PM_UNLOCK(lctxp->c_lock);
		}
		else
		    sts = PM_ERR_ARG;
		break;
    case PCP_DERIVED_DEBUG_SYNTAX:
		if (value == 0 || value == 1) {
		    pmDebugOptions.derive = value;
		    pmDebugOptions.appl0 = value;
		}
		else
		    sts = PM_ERR_ARG;
		break;
    case PCP_DERIVED_DEBUG_SEMANTICS:
		if (value == 0 || value == 1) {
		    pmDebugOptions.derive = value;
		    pmDebugOptions.appl1 = value;
		}
		else
		    sts = PM_ERR_ARG;
		break;
    case PCP_DERIVED_DEBUG_EVAL:
		if (value == 0 || value == 1) {
		    pmDebugOptions.derive = value;
		    pmDebugOptions.appl2 = value;
		}
		else
		    sts = PM_ERR_ARG;
		break;
    case PCP_DERIVED_OPTION_NOVALUE:
		if (value == 0 || value == 1) {
		    map2novalue = value;
		}
		else
		    sts = PM_ERR_ARG;
		break;
    default:
		/*
		 * this is overloading PM_ERR_NAME a bit, "what" is
		 * the name of a control option, not a performance
		 * metric, but this is the closest error code we
		 * have
		 */
    		sts = PM_ERR_NAME;
		break;
    }

    if (sts == 0)
	options[what].state = where;

    return sts;
}

/*
 * Process control options from $PCP_DERIVED_CONTROL or #control
 * lines in a config file.
 *
 * Bad stuff produces Warning: messages and processing the res
 * of the opt string is skipped.
 */
static void
do_control_options(__pmContext *ctxp, const char *fname, int lineno, const char *opt)
{
    const char	*ns, *ne;	/* name start and end */
    const char	*vs;		/* value start ... */
    char	*ve;		/* ... and end */
    int		i;
    int		value;
    int		sts;

    for (ns = opt; ; ) {
	ne = strchr(ns, '=');
	if (ne == NULL) {
	    if (fname == NULL)
		fprintf(stderr, "Warning: $PCP_DERIVED_CONTROL: ");
	    else
		fprintf(stderr, "[%s:%d] Error: ", fname, lineno);
	    fprintf(stderr, "missing '=' in derived metric control (%s)\n",
		    ns);
	    break;
	}
	vs = ne + 1;
	if (*vs == '\0' || *vs == ',') {
	    if (fname == NULL)
		fprintf(stderr, "Warning: $PCP_DERIVED_CONTROL: ");
	    else
		fprintf(stderr, "[%s:%d] Warning: ", fname, lineno);
	    fprintf(stderr, "missing value in derived metric control (%s)\n",
		    ns);
	    break;
	}
	value = (int)strtol(vs, &ve, 10);
	if (*ve != '\0' && *ve != ',') {
	    if (fname == NULL)
		fprintf(stderr, "Warning: $PCP_DERIVED_CONTROL: ");
	    else
		fprintf(stderr, "[%s:%d] Warning: ", fname, lineno);
	    fprintf(stderr, "bad value (%s) for derived metric control (%*.*s)\n",
		    vs, (int)(ne - ns), (int)(ne - ns), ns);
	    break;
	}
	for (i = 1; i < n_options; i++) {
	    if (strncasecmp(ns, options[i].name, ne - ns) == 0) {
		sts = setderivedcontrol(ctxp, fname == NULL ? OPT_SET_ENV : OPT_SET_APP, i, value);
		if (sts == 0) {
		    if (pmDebugOptions.derive) {
			if (fname == NULL)
			    fprintf(stderr, "Derived metric control: $PCP_DERIVED_CONTROL: ");
			else
			    fprintf(stderr, "[%s:%d] derived metric control: ", fname, lineno);
			fprintf(stderr, "%*.*s=%d set\n",
				(int)(ne - ns), (int)(ne - ns), ns, value);
		    }
		}
		else {
		    char	errmsg[PM_MAXERRMSGLEN];
		    if (fname == NULL)
			fprintf(stderr, "Warning: $PCP_DERIVED_CONTROL: ");
		    else
			fprintf(stderr, "[%s:%d] Warning: ", fname, lineno);
		    fprintf(stderr, "failed to set derived metric control %*.*s=%d: %s\n",
			    (int)(ne - ns), (int)(ne - ns), ns, value,
			    pmErrStr_r(sts, errmsg, sizeof(errmsg)));
		}
		break;
	    }
	}
	if (i == n_options) {
	    if (fname == NULL)
		fprintf(stderr, "Warning: $PCP_DERIVED_CONTROL: ");
	    else
		fprintf(stderr, "[%s:%d] Warning: ", fname, lineno);
	    fprintf(stderr, "unknown derived metric control (%*.*s)\n",
		    (int)(ne - ns), (int)(ne - ns), ns);
	}
	if (*ve == '\0')
	    break;
	ns = ve + 1;
    }
}

/*
 * Initialization for Derived Metrics (and Anonymous Metrics for event
 * records) ...
 */
static void
__dminit(__pmContext *ctxp)
{
    char	*opt;		/* $PCP_DERIVED_CONTROL from env */

    if (need_init == 0)
	return;

    /*
     * no derived metrics for PMCD or PMDAs
     */
    if (__pmGetInternalState() == PM_STATE_PMCS) {
	need_init = 0;
	return;
    }

    /*
     * Two-phase initialization ...
     * 1. anon metrics and $PCP_DERIVED_CONTROL
     * 2. $PCP_DERIVED_CONFIG, but only when ctxp != NULL
     */
    if (need_init == 2) {
	int	sts;

	need_init--;

	/* anon metrics for event record unpacking */
PM_FAULT_POINT("libpcp/derive.c:7", PM_FAULT_CALL);
	sts = registeranon(PM_LOCKED, "event.flags", PM_TYPE_U32);
	if (sts < 0) {
	    char	errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "%s: Warning: failed to register event.flags: %s\n",
		    pmGetProgname(), pmErrStr_r(sts, errmsg, sizeof(errmsg)));
	}
	else {
	    registered.mlist[registered.nmetric-1].oneline =
"Flags for event records";
	    registered.mlist[registered.nmetric-1].helptext =
"An anonymous derived metric that is used to encode the event flags\n"
"associated with event records.   See pmUnpackEventRecords(3).";
PM_FAULT_POINT("libpcp/derive.c:8", PM_FAULT_CALL);
	}
	sts = registeranon(PM_LOCKED, "event.missed", PM_TYPE_U32);
	if (sts < 0) {
	    char	errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "%s: Warning: failed to register event.missed: %s\n",
		    pmGetProgname(), pmErrStr_r(sts, errmsg, sizeof(errmsg)));
	}
	else {
	    registered.mlist[registered.nmetric-1].oneline =
"Count of missed event records";
	    registered.mlist[registered.nmetric-1].helptext =
"An anonymous derived metric that is used to encode the number of\n"
"event records missed because either the PMDA could not keep up\n"
"or the PMAPI client did not collect the event records fast\n"
"enough.  See pmUnpackEventRecords(3).";
	}

	/*
	 * Parse and set control options if $PCP_DERIVED_CONTOL is set.
	 * Each specification is of the form option=value (no whitespace
	 * allowed, option is case insensitive)
	 * ... multiple options are separated by commas
	 */
	if ((opt = getenv("PCP_DERIVED_CONTROL")) != NULL) {
	    do_control_options(ctxp, NULL, 0, opt);
	}
    }

    if (need_init == 1 && ctxp != NULL) {
	char	*configpath;
	char	global[MAXPATHLEN+1];

	need_init--;

	/*
	 * If PCP_DERIVED_CONFIG is NOT set, then by default we load global
	 * derived configs from the directory $PCP_VAR_DIR/config/derived.
	 *
	 * If PCP_DERIVED_CONFIG is set to a zero length string, then don't
	 * load any derived metrics definitions.
	 *
	 * Else if PCP_DERIVED_CONFIG is set then load user-defined derived
	 * metrics from one or more files or directories separated by ':'.
	 *
	 */
	if ((configpath = getenv("PCP_DERIVED_CONFIG")) == NULL) {
	    pmsprintf(global, sizeof(global), "%s/config/derived", pmGetConfig("PCP_VAR_DIR"));
	    if (access(global, F_OK) == 0)
		configpath = global;
	}
	if (configpath && configpath[0] != '\0') {
	    if (pmDebugOptions.derive) {
		fprintf(stderr, "Derived metric initialization from %s\n",
		    configpath == global ? global : "$PCP_DERIVED_CONFIG");
	    }
	    __dminit_parse(ctxp, configpath, 1 /*recovering*/);
	}
    }
}

static node_t *
newnode(int type)
{
    node_t	*np;


    if ((np = (node_t *)calloc(1, sizeof(node_t))) == NULL) {
	PM_UNLOCK(registered.mutex);
	pmNoMem("pmRegisterDerived: newnode", sizeof(node_t), PM_FATAL_ERR);
	/*NOTREACHED*/
    }
    np->type = type;
    return np;
}

static void
free_expr(node_t *np)
{
    if (np == NULL) return;
    free_expr(np->left);
    free_expr(np->right);
    np->left = np->right = NULL;
    /* value is only allocated once for the static nodes */
    if (np->type != N_PATTERN && np->data.info == NULL && np->value != NULL) {
	free(np->value);
    }
    else if (np->type == N_DEFINED) {
	/* value is strdup'd, but info is not NULL */
	free(np->value);
    }
    if (np->type == N_PATTERN) {
	if (np->data.pattern->ftype == F_REGEX) {
	    __pmHashNode	*hnp;
	    __pmHashNode	*prior_hnp;
	    /*
	     * free all the instctl_t structs hanging off the hash list
	     */
	    for (hnp = __pmHashWalk(&np->data.pattern->hash, PM_HASH_WALK_START);
		 hnp != NULL; ) {
		free(hnp->data);
		prior_hnp = hnp;
		hnp = __pmHashWalk(&np->data.pattern->hash, PM_HASH_WALK_NEXT);
		free(prior_hnp);
	    }
	    __pmHashClear(&np->data.pattern->hash);
	}
	else {
	    /* F_EXACT */
	    ;			/* nothing extra to be done */
	}
	free(np->data.pattern);
    }
    else if (np->data.info != NULL) {
	/* N_INSTANT nodes copy the left ivlist pointer */
	if (np->data.info->ivlist != NULL && np->type != N_INSTANT) {
	    if (np->desc.type == PM_TYPE_STRING) {
		int	j;
		for (j = 0; j < np->data.info->numval; j++) {
		    if (np->data.info->ivlist[j].value.cp != NULL)
			free(np->data.info->ivlist[j].value.cp);
		}
	    }
	    free(np->data.info->ivlist);
	}
	if (np->data.info->last_ivlist != NULL &&
	    np->type != N_INTEGER && np->type != N_DOUBLE) {
	    if (np->desc.type == PM_TYPE_STRING) {
		int	j;
		for (j = 0; j < np->data.info->last_numval; j++) {
		    if (np->data.info->last_ivlist[j].value.cp != NULL) {
			free(np->data.info->last_ivlist[j].value.cp);
		    }
		}
	    }
	    free(np->data.info->last_ivlist);
	}
    	free(np->data.info);
    }
    free(np);
}

/*
 * variant of free_expr() for per-context derived metrics ... we
 * don't have registered[] backing for parser-allocated memory,
 * so need to be a little more clinical in the cleanup.
 */
static void
free_expr_ctx(node_t *np)
{
    if (np == NULL) return;
    free_expr_ctx(np->left);
    free_expr_ctx(np->right);
    if (np->value != NULL) {
	free(np->value);
	np->value = NULL;
    }
    if (np->type == N_PATTERN && np->data.pattern->ftype == F_REGEX)
	regfree(&np->data.pattern->regex);
    /*
     * free each terminal node here ... recursive descent done in
     * free_expr_ctx() above, don't need it in free_expr()
     */
    np->left = np->right = NULL;
    free_expr(np);
}

/*
 * name is the derived metric name (global or per-context)
 * np is the node where the problem was found
 */
static void
report_sem_error(char *name, node_t *np)
{
    pmprintf("Semantic error: derived metric %s: ", name);
    switch (np->type) {
	case N_PLUS:
	case N_MINUS:
	case N_STAR:
	case N_SLASH:
	case N_LT:
	case N_LEQ:
	case N_EQ:
	case N_GEQ:
	case N_GT:
	case N_NEQ:
	case N_AND:
	case N_OR:
	    assert(np->left != NULL && np->right != NULL);
	    if (np->left->type == N_INTEGER || np->left->type == N_DOUBLE || np->left->type == N_NAME)
		pmprintf("%s ", np->left->value);
	    else
		pmprintf("<expr> ");
	    pmprintf("%s ", n_type_c(np->type));
	    if (np->right->type == N_INTEGER || np->right->type == N_DOUBLE || np->right->type == N_NAME)
		pmprintf("%s", np->right->value);
	    else
		pmprintf("<expr>");
	    break;
	case N_NOT:
	case N_NEG:
	    assert(np->left != NULL);
	    pmprintf("%s ", n_type_c(np->type));
	    if (np->left->type == N_INTEGER || np->left->type == N_DOUBLE || np->left->type == N_NAME)
		pmprintf("%s", np->left->value);
	    else
		pmprintf("<expr>");
	    break;
	case N_AVG:
	case N_COUNT:
	case N_DELTA:
	case N_RATE:
	case N_INSTANT:
	case N_MAX:
	case N_MIN:
	case N_SUM:
	case N_ANON:
	case N_DEFINED:
	case N_SCALAR:
	    assert(np->left != NULL);
	    pmprintf("%s(%s)", __dmnode_type_str(np->type), np->left->value);
	    break;
	case N_QUEST:
	    assert(np->left != NULL && np->right != NULL);
	    if (np->left->type == N_INTEGER || np->left->type == N_DOUBLE || np->left->type == N_NAME)
		pmprintf("%s ? ", np->left->value);
	    else
		pmprintf("<expr> ? ");
	    np = np->right;
	    /* FALLTHROUGH */
	case N_COLON:
	    assert(np->left != NULL && np->right != NULL);
	    if (np->left->type == N_INTEGER || np->left->type == N_DOUBLE || np->left->type == N_NAME)
		pmprintf("%s : ", np->left->value);
	    else
		pmprintf("<expr> : ");
	    if (np->right->type == N_INTEGER || np->right->type == N_DOUBLE || np->right->type == N_NAME)
		pmprintf("%s", np->right->value);
	    else
		pmprintf("<expr>");
	    break;
	case N_RESCALE:
	    assert(np->left != NULL && np->right != NULL);
	    if (np->left->type == N_INTEGER || np->left->type == N_DOUBLE || np->left->type == N_NAME)
		pmprintf("%s ", np->left->value);
	    else
		pmprintf("<expr> ");
	    pmprintf("%s ", __dmnode_type_str(np->type));
	    {
		const char		*units;
		char		strbuf[60];
		units = pmUnitsStr_r(&np->right->desc.units, strbuf, sizeof(strbuf));
		if (*units == '\0')
		    pmprintf("none");
		else
		    pmprintf("%s", units);
	    }
	    break;
	case N_FILTERINST:
	    pmprintf("%s ", __dmnode_type_str(np->type));
	    break;
	case N_NAME:
	    pmprintf("operand %s", np->value);
	    break;
	case N_NOVALUE:
	    pmprintf("%s ", __dmnode_type_str(np->type));
	    break;
	default:
	    /* should never get here ... */
	    pmprintf("botch @ node type %s\n", __dmnode_type_str(np->type));
	    break;
    }
    pmprintf(": %s\n", PM_TPD(derive_errmsg));
    pmflush();
    PM_TPD(derive_errmsg) = NULL;
}

static pmDesc
bind_desc(node_t *np, pmDesc param)
{
    pmDesc	temp;
    char	strbuf[60];

    if (np->left == NULL)
	return param;
    temp = np->left->desc;
    if (pmDebugOptions.derive && pmDebugOptions.appl1 && pmDebugOptions.desperate) {
	fprintf(stderr, "bind_desc: meta: type=%s", pmTypeStr_r(temp.type, strbuf, sizeof(strbuf)));
	fprintf(stderr, ",indom=%s", pmInDomStr_r(temp.indom, strbuf, sizeof(strbuf)));
	fprintf(stderr, ",sem=%s", pmSemStr_r(temp.sem, strbuf, sizeof(strbuf)));
	fprintf(stderr, ",units=%s\n", pmUnitsStr_r(&temp.units, strbuf, sizeof(strbuf)));
	if (np->flags != 0) {
	    fprintf(stderr, "           params:");
	    if (np->flags & META_TYPE)
		fprintf(stderr, " type=%s", pmTypeStr_r(param.type, strbuf, sizeof(strbuf)));
	    if (np->flags & META_INDOM)
		fprintf(stderr, " indom=%s", pmInDomStr_r(param.indom, strbuf, sizeof(strbuf)));
	    if (np->flags & META_SEM)
		fprintf(stderr, " sem=%s", pmSemStr_r(param.sem, strbuf, sizeof(strbuf)));
	    if (np->flags & META_UNITS)
		fprintf(stderr, " units=%s", pmUnitsStr_r(&param.units, strbuf, sizeof(strbuf)));
	    fprintf(stderr, " flags=%x", np->flags);
	    fputc('\n', stderr);
	}
    }

    if (np->flags & META_PMID)
	temp.pmid = param.pmid;
    if (np->flags & META_TYPE)
	temp.type = param.type;
    if (np->flags & META_INDOM)
	temp.indom = param.indom;
    if (np->flags & META_SEM)
	temp.sem = param.sem;
    if (np->flags & META_UNITS)
	temp.units = param.units;

    if (pmDebugOptions.derive && pmDebugOptions.appl1 && pmDebugOptions.desperate && np->flags != 0) {
	fprintf(stderr, "           result: type=%s", pmTypeStr_r(temp.type, strbuf, sizeof(strbuf)));
	fprintf(stderr, ",indom=%s", pmInDomStr_r(temp.indom, strbuf, sizeof(strbuf)));
	fprintf(stderr, ",sem=%s", pmSemStr_r(temp.sem, strbuf, sizeof(strbuf)));
	fprintf(stderr, ",units=%s\n", pmUnitsStr_r(&temp.units, strbuf, sizeof(strbuf)));
    }

    return temp;
}

/*
 * Bind an expression tree in a specific context.
 * For registered metrics this involves copy the expression tree
 * (the intial ctxp->expr points into registered.mlist[n].expr),
 * but for per-context metrics ctxp->expr is already mostly set
 * up.
 * Metadata and the data.info block need to be initialized.
 */
static node_t *
bind_expr(__pmContext *ctxp, int n, node_t *np, node_t *parent, int lookup_mode, int is_global, int async)
{
    node_t	*new;
    int		next_lookup = lookup_mode;
    ctl_t		*cp = NULL;	/* pander to gcc */

    PM_ASSERT_IS_LOCKED(registered.mutex);
    assert(np != NULL);

    if (is_global)
	new = newnode(np->type);
    else {
	new = np;
	cp = (ctl_t *)ctxp->c_dm;
    }

    if (np->type == N_QUEST) {
	/*
	 * <guard> ? <left> : <right> is special because any subtree
	 * below N_QUEST could validly contain references to metrics that
	 * do not exist, so set use QUEST_BIND_LAZY when binding these ones
	 */
	next_lookup = QUEST_BIND_LAZY;
    }
    else if (np->type == N_DEFINED) {
	/*
	 * defined(x) ... failing to lookup x is sort of expected
	 */
	next_lookup = QUEST_BIND_LAZY;
    }
    else {
	/* else follow the NOW|LAZY binding we were called with */
	next_lookup = lookup_mode;
    }

    if (np->left != NULL) {
	new->left = bind_expr(ctxp, n, np->left, np, next_lookup, is_global, async);
	if (new->left == NULL) {
	    /* error, reported deeper in the recursion, clean up */
	    if (is_global)
		free_expr(new);
	    return(NULL);
	}
    }
    if (np->right != NULL) {
	if ((new->right = bind_expr(ctxp, n, np->right, np, next_lookup, is_global, async)) == NULL) {
	    /* error, reported deeper in the recursion, clean up */
	    if (is_global)
		free_expr(new);
	    return(NULL);
	}
    }
    if (is_global && np->type == N_SCALE) {
	new->data.info = NULL;
    }
    if (np->type == N_PATTERN) {
	if (is_global) {
	    if ((new->data.pattern = (pattern_t *)malloc(sizeof(pattern_t))) == NULL) {
		PM_UNLOCK(registered.mutex);
		pmNoMem("bind_expr: pattern block", sizeof(pattern_t), PM_FATAL_ERR);
		/*NOTREACHED*/
	    }
	    new->data.pattern->ftype = np->data.pattern->ftype;
	    if (np->data.pattern->ftype == F_REGEX) {
		new->data.pattern->regex = np->data.pattern->regex;
		new->data.pattern->invert = np->data.pattern->invert;
	      __pmHashInit(&new->data.pattern->hash);
		new->data.pattern->used = 0;
	    }
	    else {
		/* F_EXACT */
		new->data.pattern->inst = np->data.pattern->inst;
	    }
	}
    }
    else {
	/*
	 * need to allocate the data.info block ... this is not done
	 * in the parser for any of the nodes
	 */
	if ((new->data.info = (info_t *)calloc(1, sizeof(info_t))) == NULL) {
	    PM_UNLOCK(registered.mutex);
	    pmNoMem("bind_expr: info block", sizeof(info_t), PM_FATAL_ERR);
	    /*NOTREACHED*/
	}
	new->data.info->pmid = PM_ID_NULL;
	new->data.info->mul_scale = new->data.info->div_scale = 1;
	new->data.info->time_scale = -1; /* one-trip initialization if needed */
    }

    if (is_global) {
	new->value = np->value;		/* value[] does not change */
	new->save_last = np->save_last;
    }
    new->flags = np->flags;

    if (new->type == N_NAME) {
	int	sts;
	/* get pmID and pmDesc from context */
	sts = pmLookupName_ctx(ctxp, PM_LOCKED, 1, (const char **)&new->value, &new->data.info->pmid);
	if (sts < 0) {
	    if (map2novalue) {
		/*
		 * operand metric is undefined, map to "novalues" option
		 * is in effect
		 *
		 * metadata is hard-wired, just like defaults for novalue()
		 */
		if (pmDebugOptions.derive) {
		    if (is_global)
			fprintf(stderr, "Warning: derived metric %s: operand \"%s\" not found, mapped to novalue()\n", registered.mlist[n].name, new->value);
		    else
			fprintf(stderr, "Warning: derived metric %s: operand \"%s\" not found, mapped to novalue()\n", cp->mlist[n].name, new->value);
		}
		/* expect this is a leaf node, just being cautious here */
		if (new->left != NULL) {
		    free_expr(new->left);
		    new->left = NULL;
		}
		if (new->right != NULL) {
		    free_expr(new->right);
		    new->right = NULL;
		}
		/* whack the node */
		new->type = N_NOVALUE;
		new->desc.pmid = PM_ID_NULL;
		new->desc.type = PM_TYPE_32;
		new->desc.indom = PM_INDOM_NULL;
		new->desc.sem = PM_SEM_DISCRETE;
		new->desc.units = noUnits;
		new->save_last = 0;
		new->flags = 0;
		new->data.info->numval = 0;
		return new;
	    }
	    else {
		/*
		 * operand metric is undefined, the classical behaviour ...
		 */
		if (lookup_mode == QUEST_BIND_LAZY) {
		    /*
		     * undefined metrics maybe OK here ...
		     * set pmid for later testing
		     */
		    new->desc.pmid = new->data.info->pmid = PM_ID_NULL;
		    return new;
		}
		if (async) {
		    /*
		     * async reporting considerations ...
		     *
		     * We've tossed and turned here between making this
		     * reporting unconditional vs conditional on -Dderive.
		     * Conditional was motivated by formulas in the system
		     * derived metrics configurations that use metrics that are
		     * not universally available, but the associated derived
		     * metric may never be user, leading to distracting error
		     * messages for every PMAPI client ... the addition of the
		     * defined(x) function for derived metrics means these
		     * cases can be handled in a more elegant manner, hence we
		     * now warn unconditionally here.
		     */
		    PM_TPD(derive_errmsg) = "Unknown operand metric";
		    if (is_global)
			report_sem_error(registered.mlist[n].name, np);
		    else
			report_sem_error(cp->mlist[n].name, np);
		}
		else {
		    PM_TPD(derive_errmsg) = "Unknown operand metric";
		}
		if (is_global)
		    free_expr(new);
		return NULL;
	    }
	}

	/*
	 * nested definitions are not allowed ... if this N_NAME node
	 * has a derived metric PMID, you lose ... but there are
	 * exceptions:
	 * - N_NOVALUE - this comes from novalue(meta=metric)
	 * - N_INTEGER or N_DOUBLE - this comes from mkconst(...,meta=metric)
	 * ... in both cases the metadata is all we need, and this is
	 * available provided metric is already defined, and we never
	 * evaluate these nodes, so there is no nested evaluation
	 */
	if (IS_DERIVED(new->data.info->pmid) &&
	    parent != NULL && parent->type != N_NOVALUE &&
	    parent->type != N_INTEGER && parent->type != N_DOUBLE) {
		PM_TPD(derive_errmsg) = "Illegal nested derived metric";
		if (async) {
		    if (is_global)
			report_sem_error(registered.mlist[n].name, np);
		    else
			report_sem_error(cp->mlist[n].name, np);
		}
	    if (is_global)
		free_expr(new);
	    return NULL;
	}


	sts = pmLookupDesc_ctx(ctxp, PM_LOCKED, new->data.info->pmid, &new->desc);
	if (sts < 0) {
	    if (async) {
		/*
		 * async reporting considerations ...
		 *
		 * See comments above.
		 */
		 PM_TPD(derive_errmsg) = "Metadata for operand metric not available";
		if (is_global)
		    report_sem_error(registered.mlist[n].name, np);
		else
		    report_sem_error(cp->mlist[n].name, np);
	    }
	    else {
		 PM_TPD(derive_errmsg) = "Metadata for operand metric not available";
	    }
	    if (is_global)
		free_expr(new);
	    return NULL;
	}
    }
    else if (new->type == N_SCALE) {
	if (is_global)
	    new->desc = np->desc;
    }
    else if (new->type == N_INTEGER || new->type == N_DOUBLE) {
	if (is_global)
	    new->desc = bind_desc(new, np->desc);
    }
    else if (new->type == N_NOVALUE) {
	if (is_global) {
	    new->desc = bind_desc(new, np->desc);
	    new->data.info->numval = 0;
	}
    }

    return new;
}

/* type promotion */
const int promote[6][6] = {
    { PM_TYPE_32, PM_TYPE_U32, PM_TYPE_64, PM_TYPE_U64, PM_TYPE_FLOAT, PM_TYPE_DOUBLE },
    { PM_TYPE_U32, PM_TYPE_U32, PM_TYPE_64, PM_TYPE_U64, PM_TYPE_FLOAT, PM_TYPE_DOUBLE },
    { PM_TYPE_64, PM_TYPE_64, PM_TYPE_64, PM_TYPE_U64, PM_TYPE_FLOAT, PM_TYPE_DOUBLE },
    { PM_TYPE_U64, PM_TYPE_U64, PM_TYPE_U64, PM_TYPE_U64, PM_TYPE_FLOAT, PM_TYPE_DOUBLE },
    { PM_TYPE_FLOAT, PM_TYPE_FLOAT, PM_TYPE_FLOAT, PM_TYPE_FLOAT, PM_TYPE_FLOAT, PM_TYPE_DOUBLE },
    { PM_TYPE_DOUBLE, PM_TYPE_DOUBLE, PM_TYPE_DOUBLE, PM_TYPE_DOUBLE, PM_TYPE_DOUBLE, PM_TYPE_DOUBLE }
};

/* time scale conversion factors */
static const int timefactor[] = {
    1000,		/* NSEC -> USEC */
    1000,		/* USEC -> MSEC */
    1000,		/* MSEC -> SEC */
    60,			/* SEC -> MIN */
    60,			/* MIN -> HOUR */
};

/*
 * mapping pmUnits for the result, and refining pmDesc as we go ...
 * we start with the pmDesc from the left operand and adjust as
 * necessary
 *
 * scale conversion rules ...
 * Count - choose larger, divide/multiply smaller by 10^(difference)
 * Time - choose larger, divide/multiply smaller by appropriate scale
 * Space - choose larger, divide/multiply smaller by 1024^(difference)
 * and result is of type PM_TYPE_DOUBLE
 *
 * Need inverted logic to deal with numerator (dimension > 0) and
 * denominator (dimension < 0) cases.
 */
static void
map_units(node_t *np)
{
    pmDesc	*right = &np->right->desc;
    pmDesc	*left = &np->left->desc;
    int		diff;
    int		i;

    if (left->units.dimCount != 0 && right->units.dimCount != 0) {
	diff = left->units.scaleCount - right->units.scaleCount;
	if (diff > 0) {
	    /* use the left scaleCount, scale the right operand */
	    for (i = 0; i < diff; i++) {
		if (right->units.dimCount > 0)
		    np->right->data.info->div_scale *= 10;
		else
		    np->right->data.info->mul_scale *= 10;
	    }
	    np->desc.type = PM_TYPE_DOUBLE;
	}
	else if (diff < 0) {
	    /* use the right scaleCount, scale the left operand */
	    np->desc.units.scaleCount = right->units.scaleCount;
	    for (i = diff; i < 0; i++) {
		if (left->units.dimCount > 0)
		    np->left->data.info->div_scale *= 10;
		else
		    np->left->data.info->mul_scale *= 10;
	    }
	    np->desc.type = PM_TYPE_DOUBLE;
	}
    }
    if (left->units.dimTime != 0 && right->units.dimTime != 0) {
	diff = left->units.scaleTime - right->units.scaleTime;
	if (diff > 0) {
	    /* use the left scaleTime, scale the right operand */
	    for (i = right->units.scaleTime; i < left->units.scaleTime; i++) {
		if (right->units.dimTime > 0)
		    np->right->data.info->div_scale *= timefactor[i];
		else
		    np->right->data.info->mul_scale *= timefactor[i];
	    }
	    np->desc.type = PM_TYPE_DOUBLE;
	}
	else if (diff < 0) {
	    /* use the right scaleTime, scale the left operand */
	    np->desc.units.scaleTime = right->units.scaleTime;
	    for (i = left->units.scaleTime; i < right->units.scaleTime; i++) {
		if (right->units.dimTime > 0)
		    np->left->data.info->div_scale *= timefactor[i];
		else
		    np->left->data.info->mul_scale *= timefactor[i];
	    }
	    np->desc.type = PM_TYPE_DOUBLE;
	}
    }
    if (left->units.dimSpace != 0 && right->units.dimSpace != 0) {
	diff = left->units.scaleSpace - right->units.scaleSpace;
	if (diff > 0) {
	    /* use the left scaleSpace, scale the right operand */
	    for (i = 0; i < diff; i++) {
		if (right->units.dimSpace > 0)
		    np->right->data.info->div_scale *= 1024;
		else
		    np->right->data.info->mul_scale *= 1024;
	    }
	    np->desc.type = PM_TYPE_DOUBLE;
	}
	else if (diff < 0) {
	    /* use the right scaleSpace, scale the left operand */
	    np->desc.units.scaleSpace = right->units.scaleSpace;
	    for (i = diff; i < 0; i++) {
		if (right->units.dimSpace > 0)
		    np->left->data.info->div_scale *= 1024;
		else
		    np->left->data.info->mul_scale *= 1024;
	    }
	    np->desc.type = PM_TYPE_DOUBLE;
	}
    }

    if (np->type == N_STAR) {
	np->desc.units.dimCount = left->units.dimCount + right->units.dimCount;
	np->desc.units.dimTime = left->units.dimTime + right->units.dimTime;
	np->desc.units.dimSpace = left->units.dimSpace + right->units.dimSpace;
	/*
	 * for extra units + ! extra units, propagate the
	 * extrta units to the result
	 */
	if (left->units.extraUnit == 0 && right->units.extraUnit != 0) {
	    /* extra units in the right operand */
	    np->desc.units.extraUnit = right->units.extraUnit;
	    np->desc.units.extraScale = right->units.extraScale;
	}
	if (left->units.extraUnit != 0 && right->units.extraUnit == 0) {
	    /* extra units in the left operand */
	    np->desc.units.extraUnit = left->units.extraUnit;
	    np->desc.units.extraScale = left->units.extraScale;
	}
    }
    else if (np->type == N_SLASH) {
	np->desc.units.dimCount = left->units.dimCount - right->units.dimCount;
	np->desc.units.dimTime = left->units.dimTime - right->units.dimTime;
	np->desc.units.dimSpace = left->units.dimSpace - right->units.dimSpace;
	if (left->units.extraUnit != 0 && right->units.extraUnit != 0) {
	    /*
	     * if both operands have extra units then prior checks ensure
	     * extraUnit and extraScale are identical for both operands of
	     * division
	     */
	    np->desc.units.extraUnit = 0;
	    np->desc.units.extraScale = 0;
	}
	else if (left->units.extraUnit) {
	    /* extra units in the dividend */
	    np->desc.units.extraUnit = left->units.extraUnit;
	    np->desc.units.extraScale = left->units.extraScale;
	}
	else if (right->units.extraUnit) {
	    /* extra units in the divisor */
	    np->desc.units.extraUnit = -right->units.extraUnit;
	    np->desc.units.extraScale = right->units.extraScale;
	}
    }
    
    /*
     * for division and multiplication, dimension may have come from
     * right operand, need to pick up scale from there also
     */
    if (np->desc.units.dimCount != 0 && left->units.dimCount == 0)
	np->desc.units.scaleCount = right->units.scaleCount;
    if (np->desc.units.dimTime != 0 && left->units.dimTime == 0)
	np->desc.units.scaleTime = right->units.scaleTime;
    if (np->desc.units.dimSpace != 0 && left->units.dimSpace == 0)
	np->desc.units.scaleSpace = right->units.scaleSpace;

    /*
     * and if the resulting dimension is zero, make the scale 0
     * ... for most cases, if the dimension is zero we do not
     * look at the scale, but the ternary operator semantic check
     * is one exception
     */
    if (np->desc.units.dimCount == 0)
	np->desc.units.scaleCount = 0;
    if (np->desc.units.dimTime == 0)
	np->desc.units.scaleTime = 0;
    if (np->desc.units.dimSpace == 0)
	np->desc.units.scaleSpace = 0;
    if (np->desc.units.extraUnit == 0)
	np->desc.units.extraScale = 0;
}

/*
 * see __dmbind() for semantics of async parameter
 */
static int
map_desc(dm_t *dmp, node_t *np, int async)
{
    /*
     * pmDesc mapping for binary operators ...
     *
     * semantics		acceptable operators
     * counter, counter		+ - <relational>
     * non-counter, non-counter	+ - * / <relational>
     * counter, non-counter	* / <relational>
     * non-counter, counter	* <relational>
     *
     * in the non-counter and non-counter case, the semantics for the
     * result are PM_SEM_INSTANT, unless both operands are
     * PM_SEM_DISCRETE in which case the result is also PM_SEM_DISCRETE
     *
     * type promotion (similar to ANSI C)
     * PM_TYPE_STRING, PM_TYPE_AGGREGATE, PM_TYPE_AGGREGATE_STATIC,
     * PM_TYPE_EVENT and PM_TYPE_HIGHRES_EVENT are illegal operands
     * except for renaming (where no operator is involved)
     * for all operands, division => PM_TYPE_DOUBLE
     * else PM_TYPE_DOUBLE & any type => PM_TYPE_DOUBLE
     * else PM_TYPE_FLOAT & any type => PM_TYPE_FLOAT
     * else PM_TYPE_U64 & any type => PM_TYPE_U64
     * else PM_TYPE_64 & any type => PM_TYPE_64
     * else PM_TYPE_U32 & any type => PM_TYPE_U32
     * else PM_TYPE_32 & any type => PM_TYPE_32
     *
     * units mapping
     * operator			checks
     * +, -			same dimension
     * *, /			if only one is a counter, non-counter must
     *				have pmUnits of "none"
     * <relational>             same dimension
     */
    pmDesc	*right = &np->right->desc;
    pmDesc	*left = &np->left->desc;

    /* counter and non-counter checks */
    if (np->type == N_LT || np->type == N_LEQ || np->type == N_EQ ||
	np->type == N_GEQ || np->type == N_GT || np->type == N_NEQ ||
	np->type == N_AND || np->type == N_OR) {
	/*
	 * No restrictions on relational or boolean operators ... since
	 * evaluation will only ever use the current value and the
	 * result is not a counter, so the difference between counter
	 * and non-counter semantics for the operands is immaterial.
	 */
	;
    }
    else if (np->type == N_RESCALE) {
	/*
	 * Use metadata from np->left as base, check scale conversion
	 * is possible, then use units from np->right
	 */
	pmAtomValue	in;
	pmAtomValue	out;
	int		sts;
	np->desc = *left;
	switch (np->desc.type) {
	    case PM_TYPE_32:
		in.l = 1;
		break;
	    case PM_TYPE_U32:
		in.ul = 1;
		break;
	    case PM_TYPE_64:
		in.ll = 1;
		break;
	    case PM_TYPE_U64:
		in.ull = 1;
		break;
	    case PM_TYPE_FLOAT:
		in.f = 1;
		break;
	    case PM_TYPE_DOUBLE:
		in.d = 1;
		break;
	    default:
		PM_TPD(derive_errmsg) = "Non-arithmetic operand for function";
		if (async)
		    report_sem_error(dmp->name, np);
		return -1;
	}
	sts = pmConvScale(np->desc.type, &in, &np->left->desc.units,
					 &out, &np->right->desc.units);
	if (sts < 0) {
	    PM_TPD(derive_errmsg) = "Incompatible dimensions";
	    goto bad;
	}
	np->desc.units = np->right->desc.units;		/* struct copy */
	return 0;
    }
    else if (np->type == N_PATTERN) {
	/* nothing to see or test here */
	return 0;
    }
    else {
	if (left->sem == PM_SEM_COUNTER) {
	    if (right->sem == PM_SEM_COUNTER) {
		if (np->type != N_PLUS && np->type != N_MINUS) {
		    PM_TPD(derive_errmsg) = "Illegal operator for counters";
		    goto bad;
		}
	    }
	    else {
		if (np->type != N_STAR && np->type != N_SLASH) {
		    PM_TPD(derive_errmsg) = "Illegal operator for counter and non-counter";
		    goto bad;
		}
	    }
	}
	else {
	    if (right->sem == PM_SEM_COUNTER) {
		if (np->type != N_STAR) {
		    PM_TPD(derive_errmsg) = "Illegal operator for non-counter and counter";
		    goto bad;
		}
	    }
	    else {
		if (np->type != N_PLUS && np->type != N_MINUS &&
		    np->type != N_STAR && np->type != N_SLASH) {
		    /*
		     * this is not possible at the present since only
		     * arithmetic operators are supported and all are
		     * acceptable here ... check added for completeness
		     */
		    PM_TPD(derive_errmsg) = "Illegal operator for non-counters";
		    goto bad;
		}
	    }
	}
    }

    /*
     * Choose candidate descriptor ... prefer metric or expression
     * over constant
     */
    if (np->left->type != N_INTEGER && np->left->type != N_DOUBLE)
	np->desc = *left;	/* struct copy */
    else
	np->desc = *right;	/* struct copy */

    /*
     * most non-counter expressions produce PM_SEM_INSTANT results
     */
    if (left->sem != PM_SEM_COUNTER && right->sem != PM_SEM_COUNTER) {
	if (left->sem == PM_SEM_DISCRETE && right->sem == PM_SEM_DISCRETE)
	    np->desc.sem = PM_SEM_DISCRETE;
	else
	    np->desc.sem = PM_SEM_INSTANT;
    }

    /*
     * type checking and promotion
     */
    switch (left->type) {
	case PM_TYPE_32:
	case PM_TYPE_U32:
	case PM_TYPE_64:
	case PM_TYPE_U64:
	case PM_TYPE_FLOAT:
	case PM_TYPE_DOUBLE:
	    break;
	default:
	    PM_TPD(derive_errmsg) = "Non-arithmetic type for left operand";
	    goto bad;
    }
    switch (right->type) {
	case PM_TYPE_32:
	case PM_TYPE_U32:
	case PM_TYPE_64:
	case PM_TYPE_U64:
	case PM_TYPE_FLOAT:
	case PM_TYPE_DOUBLE:
	    break;
	default:
	    PM_TPD(derive_errmsg) = "Non-arithmetic type for right operand";
	    goto bad;
    }
    if (np->type == N_SLASH) {
	/* for division result is real number */
	np->desc.type = PM_TYPE_DOUBLE;
    }
    else if (np->type == N_LT || np->type == N_LEQ || np->type == N_EQ ||
	     np->type == N_GEQ || np->type == N_GT || np->type == N_NEQ ||
	     np->type == N_AND || np->type == N_OR) {
	/*
	 * logical and boolean operators return a U32 result, independent
	 * of the operands' type
	 */
	np->desc.type = PM_TYPE_U32;
    }
    else {
	/*
	 * for other operators, the operands' type determine the type of
	 * the result
	 */
	np->desc.type = promote[left->type][right->type];
    }

    if (np->type == N_PLUS || np->type == N_MINUS) {
	/*
	 * unit dimensions have to be identical
	 */
	if (left->units.dimCount != right->units.dimCount ||
	    left->units.dimTime != right->units.dimTime ||
	    left->units.dimSpace != right->units.dimSpace ||
	    left->units.extraUnit != right->units.extraUnit) {
	    /*
	     * or one of them is allowed to be "none" provided the
	     * other is "count" ... this matches the special case
	     * documented for pmConvScale(3)
	     */
	    if (left->units.dimTime == 0 && right->units.dimTime == 0 &&
	        left->units.dimSpace == 0 && right->units.dimSpace == 0 &&
	        left->units.extraUnit == 0 && right->units.extraUnit == 0 &&
		((left->units.dimCount == 0 && right->units.dimCount == 1) ||
		 (left->units.dimCount == 1 && right->units.dimCount == 0)))
		    ;
	    else {
		PM_TPD(derive_errmsg) = "Dimensions are not the same";
		goto bad;
	    }
	}
    }

    if (np->type == N_LT || np->type == N_LEQ || np->type == N_EQ ||
	np->type == N_GEQ || np->type == N_GT || np->type == N_NEQ) {
	/*
	 * unit dimensions have to be identical, unless one of
	 * the operands is numeric constant, e.g. > 0
	 */
	if ((left->type != N_INTEGER && left->type != N_DOUBLE &&
	     right->type != N_INTEGER && right->type != N_DOUBLE) &&
	    (left->units.dimCount != right->units.dimCount ||
	     left->units.dimTime != right->units.dimTime ||
	     left->units.dimSpace != right->units.dimSpace ||
	     left->units.extraUnit != right->units.extraUnit)) {
	    /*
	     * or one of them is allowed to be "none" provided the
	     * other is "count" ... this matches the special case
	     * documented for pmConvScale(3)
	     */
	    if (left->units.dimTime == 0 && right->units.dimTime == 0 &&
	        left->units.dimSpace == 0 && right->units.dimSpace == 0 &&
	        left->units.extraUnit == 0 && right->units.extraUnit == 0 &&
		((left->units.dimCount == 0 && right->units.dimCount == 1) ||
		 (left->units.dimCount == 1 && right->units.dimCount == 0)))
		    ;
	    else {
		PM_TPD(derive_errmsg) = "Dimensions are not the same";
		goto bad;
	    }
	}
    }

    if (np->type == N_AND || np->type == N_OR) {
	/*
	 * unit dimensions have to be none
	 */
	if (left->units.dimCount != 0 || right->units.dimCount != 0 ||
	    left->units.dimTime != 0 ||  right->units.dimTime != 0 ||
	    left->units.dimSpace != 0 || right->units.dimSpace != 0 ||
	    left->units.extraUnit != 0 || right->units.extraUnit != 0) {
	    PM_TPD(derive_errmsg) = "Dimensions are not the same";
	    goto bad;
	}
    }

    if (np->type == N_STAR || np->type == N_SLASH ||
	np->type == N_LT || np->type == N_LEQ || np->type == N_EQ ||
	np->type == N_GEQ || np->type == N_GT || np->type == N_NEQ) {
	/*
	 * if multiply or divide or relational operator, and operands
	 * are a counter and a non-counter, then non-counter needs to
	 * be dimensionless
	 */
	if (left->sem == PM_SEM_COUNTER && right->sem != PM_SEM_COUNTER) {
	    if (right->units.dimCount != 0 ||
	        right->units.dimTime != 0 ||
	        right->units.dimSpace != 0 ||
		right->units.extraUnit != 0) {
		PM_TPD(derive_errmsg) = "Non-counter and not dimensionless for right operand";
		goto bad;
	    }
	}
	if (left->sem != PM_SEM_COUNTER && right->sem == PM_SEM_COUNTER) {
	    if (left->units.dimCount != 0 ||
	        left->units.dimTime != 0 ||
	        left->units.dimSpace != 0 ||
		left->units.extraUnit != 0) {
		PM_TPD(derive_errmsg) = "Non-counter and not dimensionless for left operand";
		goto bad;
	    }
	}
    }

    if (np->type == N_STAR) {
	/*
	 * don't allow extra units in both operands for multiplication
	 */
	if (left->units.extraUnit != 0 && right->units.extraUnit != 0) {
	    PM_TPD(derive_errmsg) = "Extra units not allowed for both operands of *";
	    goto bad;
	}
    }

    if (np->type == N_SLASH) {
	/*
	 * don't allow extra units in both operands for division unless
	 * they are identical
	 */
	if ((left->units.extraUnit != 0 && right->units.extraUnit != 0) &&
	    ((left->units.extraUnit != right->units.extraUnit) ||
	     (left->units.extraScale != right->units.extraScale))) {
	    PM_TPD(derive_errmsg) = "Different extra units not allowed for both operands of /";
	    goto bad;
	}
    }

    if (left->units.extraUnit != 0 && right->units.extraUnit != 0) {
	/*
	 * if both operands use extra units then they must have
	 * the same unit and scale
	 */
	if (left->units.extraUnit != right->units.extraUnit ||
	    left->units.extraScale != right->units.extraScale) {
	    PM_TPD(derive_errmsg) = "Operands have incompatible extra units or scale";
	    goto bad;
	}
    }

    /* do pmUnits mapping and scale conversion */
    if (np->type == N_PLUS || np->type == N_MINUS ||
	np->type == N_STAR || np->type == N_SLASH) {
	map_units(np);
    }
    else if (np->type == N_LT || np->type == N_LEQ || np->type == N_EQ ||
	     np->type == N_GEQ || np->type == N_GT || np->type == N_NEQ ||
	     np->type == N_AND || np->type == N_OR || np->type == N_NOT) {
	/* no units for relational or boolean expressions */
	np->desc.units = noUnits;	/* struct copy */
    }

    /*
     * if neither singular, then both operands must have the same
     * instance domain.
     * if one is singular but the other is not, result indom must
     * not be singular.
     */
    if (left->indom != PM_INDOM_NULL && right->indom != PM_INDOM_NULL && left->indom != right->indom && !__pmEquivInDom(left->indom, right->indom)) {
	PM_TPD(derive_errmsg) = "Operands should have the same instance domain";
	goto bad;
    }
    else if (left->indom != PM_INDOM_NULL && right->indom == PM_INDOM_NULL)
	np->desc.indom = left->indom;
    else if (right->indom != PM_INDOM_NULL && left->indom == PM_INDOM_NULL)
	np->desc.indom = right->indom;

    return 0;

bad:
    if (async)
	report_sem_error(dmp->name, np);
    return -1;
}

/*
 * recursive descent from a COLON node to check if all operand metrics
 * are defined (needed after QUEST_BIND_LAZY has been used) ... returns
 * the node_t of the first metric that is not defined, else NULL
 *
 * be careful with nested COLON nodes, need to honour their lazy
 * binding as well
 *
 * hand is "left" or "right" of the initial COLON node for error
 * messages (for nested COLON expressions we may find the missing
 * metric down the hand-><left-or-right>-><left-or-right> branches
 * so there is some ambiguity here that cannot be avoided
 */
static node_t *
check_metrics(node_t *np, char *hand)
{
    node_t	*bad = NULL;

    assert(np != NULL);
    if (np->left != NULL) {
	if (np->type != N_COLON || np->data.info->bind & QUEST_BIND_LEFT)
	    bad = check_metrics(np->left, hand);
    }
    if (bad != NULL)
	return bad;
    if (np->type == N_NAME && np->desc.pmid == PM_ID_NULL) {
	if (pmDebugOptions.derive && pmDebugOptions.appl1) {
	    fprintf(stderr, "Semantic error: ternary op: metric \"%s\" not found for <%s-expr>\n", np->value, hand);
	}
	PM_TPD(derive_errmsg) = "Unknown metric for ternary expression";
	return np;
    }
    if (np->right != NULL) {
	if (np->type != N_COLON || np->data.info->bind & QUEST_BIND_RIGHT)
	    bad = check_metrics(np->right, hand);
    }

    return bad;
}

/*
 * evaluate a ? guard @ bind-time (not eval-time) ... if it
 * is a constant expression we can return 1 (true) or 0 (false)
 * else -1 (unknown)
 */
static int
eval_guard(node_t *np)
{
    int		sts;

    if (np->type == N_DEFINED) {
	sts = np->data.info->ivlist[0].value.ul;
    }
    else if (np->type == N_INTEGER) {
	if (atoi(np->value) == 0)
	    sts = 0;
	else
	    sts = 1;
    }
    else if (np->type == N_NOT) {
	sts = eval_guard(np->left);
	if (sts != -1)
	    sts = 1 - sts;
    }
    else if (np->type == N_OR) {
	sts = eval_guard(np->left);
	if (sts == 0)
	    sts = eval_guard(np->right);
    }
    else if (np->type == N_AND) {
	sts = eval_guard(np->left);
	if (sts == 1)
	    sts = eval_guard(np->right);
    }
    else
	sts = -1;

    return sts;
}

/*
 * see __dmbind() for semantics of async parameter
 */
static int
check_expr(dm_t *dmp, node_t *np, node_t *parent, int async)
{
    int		sts;
    node_t	*bad;

    assert(np != NULL);

    if (np->type == N_INTEGER || np->type == N_DOUBLE || np->type == N_NAME ||
        np->type == N_SCALE || np->type == N_PATTERN)
	return 0;

    if (np->type == N_DEFINED) {
	if ((np->data.info->ivlist = (val_t *)malloc(sizeof(val_t))) == NULL) {
	    pmNoMem("check_expr: defined ivlist", sizeof(val_t), PM_FATAL_ERR);
	    /*NOTREACHED*/
	}
	np->data.info->numval = 1;
	np->data.info->ivlist[0].inst = PM_IN_NULL;
	if (np->left->data.info->pmid == PM_ID_NULL) {
	    /* defined(x) is false */
	    np->data.info->ivlist[0].value.ul = 0;
	}
	else {
	    /* defined(x) is true */
	    np->data.info->ivlist[0].value.ul = 1;
	}
	np->data.info->pmid = np->desc.pmid = PM_ID_NULL;
	np->desc.type = PM_TYPE_U32;
	np->desc.indom = PM_INDOM_NULL;
	np->desc.sem = PM_SEM_DISCRETE;
	np->desc.units = noUnits;
	np->value = strdup(np->left->value);
	/*
	 * don't need metric name any more and do not need to fetch it
	 * again ...
	 */
	if ((dmp->flags & DM_GLOBAL) == 0) {
	    free(np->left->value);
	    np->left->value = NULL;
	}
	free_expr(np->left);
	np->left = NULL;
	return 0;
    }
    else if (np->type == N_NOVALUE) {
	return 0;
    }

    /* otherwise, np->left is never NULL ... */
    assert(np->left != NULL);

    if (np->type == N_COLON) {
	/*
	 * <guard> ? <left-expr> : <right-expr>
	 *
	 * the default strategy is to ensure pmDesc for the <left-expr>
	 * and the <right-expr> are the same, and choose pmDesc from
	 * <left-expr> ... then for the indom start assuming they are
	 * the same, may need to adjust if one <expr> has an indom and
	 * the other does not (see below)
	 *
	 * but first, some special case exceptions to limit the
	 * binding and checking ...
	 */
	np->data.info->bind = QUEST_BIND_BOTH;
	if (parent->type == N_QUEST) {
	    /*
	     * special handling for <guard> ? <left> : <right>
	     * depending on the result of <guard> choose do lazy
	     * checking and binding of only <left> (true) or only
	     * <right> (false) or both <left> and <right> (unknown)
	     */
	    int	guard = eval_guard(parent->left);
	    if (pmDebugOptions.derive && pmDebugOptions.appl1 && pmDebugOptions.desperate) {
		fprintf(stderr, "check_expr: guard(%s) = %d [bind %s @ " PRINTF_P_PFX "%p(%s)]\n",
		    __dmnode_type_str(parent->left->type), guard,
		    guard == 1 ? "left" : (guard == 0 ? "right" : "both"),
		    np, __dmnode_type_str(np->type));
	    }
	    if (guard == 1)
		np->data.info->bind = QUEST_BIND_LEFT;
	    else if (guard == 0)
		np->data.info->bind = QUEST_BIND_RIGHT;
	}
    }

    if (np->type != N_COLON || (np->data.info->bind & QUEST_BIND_LEFT)) {
	if ((sts = check_expr(dmp, np->left, np, async)) < 0)
	    return sts;
    }

    if (np->right != NULL) {
	if (np->type != N_COLON || (np->data.info->bind & QUEST_BIND_RIGHT)) {
	    if ((sts = check_expr(dmp, np->right, np, async)) < 0)
		return sts;
	}

	switch (np->type) {

	case N_COLON:
	    /* update metadata */
	    if (np->data.info->bind == QUEST_BIND_BOTH) {
		/* prefer other expr to novalue() */
		if (np->left->type == N_NOVALUE)
		    np->desc = np->right->desc;		/* struct copy */
		else
		    np->desc = np->left->desc;		/* struct copy */
		/* prefer indom to no indom */
		if (np->left->desc.indom == PM_INDOM_NULL)
		    np->desc.indom = np->right->desc.indom;
		else
		    np->desc.indom = np->left->desc.indom;
	    }
	    else if (np->data.info->bind & QUEST_BIND_LEFT)
		np->desc = np->left->desc;		/* struct copy */
	    else
		np->desc = np->right->desc;		/* struct copy */

	    /*
	     * recursive descent looking for undefined metrics
	     * in left and/or right expr ... use info->bind to guide
	     * note all errors are reported in check_metrics()
	     */
	    bad = NULL;
	    if (np->data.info->bind & QUEST_BIND_LEFT)
		bad = check_metrics(np->left,"left");
	    if (bad == NULL && (np->data.info->bind & QUEST_BIND_RIGHT))
		bad = check_metrics(np->right, "right");
	    /* if either of these fails, no point in trying other checks */
	    if (bad != NULL) {
		if (async)
		    report_sem_error(dmp->name, bad);
		return -1;
	    }

	    /*
	     * special case when left or right operand is novalue()
	     * (grammer ensures it cannot be both), then skip semantic
	     * checks as these will fail comparing novalue() to the other
	     * <expr>, and even worse the other <expr> may contain undefined
	     * metrics with no metadata available
	     */
	    if (np->left->type == N_NOVALUE || np->right->type == N_NOVALUE)
		return 0;

	    /*
	     * if QUEST_BIND_LEFT or QUEST_BIND_RIGHT (and not
	     * QUEST_BIND_BOTH), then skip remaining checks, they are
	     * doomed to fail but the *unselected* expression tree is
	     * never evaluated, so it is OK
	     */
	    if (np->data.info->bind == QUEST_BIND_LEFT || np->data.info->bind == QUEST_BIND_RIGHT)
		return 0;


	    /* semantic checks for left-expr cf right-expr */
	    if (np->left->desc.type != np->right->desc.type) {
		if (pmDebugOptions.derive && pmDebugOptions.appl1) {
		    char	strbuf[20];

		    pmTypeStr_r(np->left->desc.type, strbuf, sizeof(strbuf));
		    fprintf(stderr, "Semantic error: ternary op type: left=%s", strbuf);
		    pmTypeStr_r(np->right->desc.type, strbuf, sizeof(strbuf));
		    fprintf(stderr, " right=%s\n", strbuf);
		}
		PM_TPD(derive_errmsg) = "Different types for ternary operands";
		if (async)
		    report_sem_error(dmp->name, np);
		return -1;
	    }
	    if (np->left->desc.indom != np->right->desc.indom &&
	        np->left->desc.indom != PM_INDOM_NULL &&
	        np->right->desc.indom != PM_INDOM_NULL) {
		if (pmDebugOptions.derive && pmDebugOptions.appl1) {
		    char	strbuf[20];

		    pmInDomStr_r(np->left->desc.indom, strbuf, sizeof(strbuf));
		    fprintf(stderr, "Semantic error: ternary op indom: left=%s", strbuf);
		    pmInDomStr_r(np->right->desc.indom, strbuf, sizeof(strbuf));
		    fprintf(stderr, " right=%s\n", strbuf);
		}
		PM_TPD(derive_errmsg) = "Different instance domains for ternary operands";
		if (async)
		    report_sem_error(dmp->name, np);
		return -1;
	    }
	    if (np->left->desc.sem != np->right->desc.sem) {
		if (pmDebugOptions.derive && pmDebugOptions.appl1) {
		    char	strbuf[20];

		    pmSemStr_r(np->left->desc.sem, strbuf, sizeof(strbuf));
		    fprintf(stderr, "Semantic error: ternary op semantics: left=%s", strbuf);
		    pmSemStr_r(np->right->desc.sem, strbuf, sizeof(strbuf));
		    fprintf(stderr, " right=%s\n", strbuf);
		}
		PM_TPD(derive_errmsg) = "Different semantics for ternary operands";
		if (async)
		    report_sem_error(dmp->name, np);
		return -1;
	    }
	    if (np->left->desc.units.dimSpace != np->right->desc.units.dimSpace ||
	        np->left->desc.units.scaleSpace != np->right->desc.units.scaleSpace)
		{
		if (pmDebugOptions.derive && pmDebugOptions.appl1) {
		    char	strbuf[60];

		    pmUnitsStr_r(&np->left->desc.units, strbuf, sizeof(strbuf));
		    fprintf(stderr, "Semantic error: ternary op units (space): left=%s (dim=%d scale=%d)", strbuf, np->left->desc.units.dimSpace, np->left->desc.units.scaleSpace);
		    pmUnitsStr_r(&np->right->desc.units, strbuf, sizeof(strbuf));
		    fprintf(stderr, " right=%s (dim=%d scale=%d)\n", strbuf, np->right->desc.units.dimSpace, np->right->desc.units.scaleSpace);
		}
		PM_TPD(derive_errmsg) = "Different units or scale (space) for ternary operands";
		if (async)
		    report_sem_error(dmp->name, np);
		return -1;
	    }
	    if (np->left->desc.units.dimTime != np->right->desc.units.dimTime ||
	        np->left->desc.units.scaleTime != np->right->desc.units.scaleTime)
		{
		if (pmDebugOptions.derive && pmDebugOptions.appl1) {
		    char	strbuf[60];

		    pmUnitsStr_r(&np->left->desc.units, strbuf, sizeof(strbuf));
		    fprintf(stderr, "Semantic error: ternary op units (time): left=%s", strbuf);
		    pmUnitsStr_r(&np->right->desc.units, strbuf, sizeof(strbuf));
		    fprintf(stderr, " right=%s\n", strbuf);
		}
		PM_TPD(derive_errmsg) = "Different units or scale (time) for ternary operands";
		if (async)
		    report_sem_error(dmp->name, np);
		return -1;
	    }
	    if (np->left->desc.units.dimCount != np->right->desc.units.dimCount ||
	        np->left->desc.units.scaleCount != np->right->desc.units.scaleCount)
		{
		if (pmDebugOptions.derive && pmDebugOptions.appl1) {
		    char	strbuf[60];

		    pmUnitsStr_r(&np->left->desc.units, strbuf, sizeof(strbuf));
		    fprintf(stderr, "Semantic error: ternary op units (count): left=%s", strbuf);
		    pmUnitsStr_r(&np->right->desc.units, strbuf, sizeof(strbuf));
		    fprintf(stderr, " right=%s\n", strbuf);
		}
		PM_TPD(derive_errmsg) = "Different units or scale (count) for ternary operands";
		if (async)
		    report_sem_error(dmp->name, np);
		return -1;
	    }
	    if (np->left->desc.units.extraUnit != np->right->desc.units.extraUnit ||
	        np->left->desc.units.extraScale != np->right->desc.units.extraScale)
		{
		if (pmDebugOptions.derive && pmDebugOptions.appl1) {
		    char	strbuf[60];

		    pmUnitsStr_r(&np->left->desc.units, strbuf, sizeof(strbuf));
		    fprintf(stderr, "Semantic error: ternary op units (extra): left=%s", strbuf);
		    pmUnitsStr_r(&np->right->desc.units, strbuf, sizeof(strbuf));
		    fprintf(stderr, " right=%s\n", strbuf);
		}
		PM_TPD(derive_errmsg) = "Different units or scale (extra) for ternary operands";
		if (async)
		    report_sem_error(dmp->name, np);
		return -1;
	    }
	    return 0;

	case N_QUEST:
	    switch (np->left->desc.type) {
		case PM_TYPE_32:
		case PM_TYPE_U32:
		case PM_TYPE_64:
		case PM_TYPE_U64:
		case PM_TYPE_FLOAT:
		case PM_TYPE_DOUBLE:
		    break;
		default:
		    PM_TPD(derive_errmsg) = "Non-arithmetic operand for ternary guard";
		    if (async)
			report_sem_error(dmp->name, np);
		    return -1;
	    }
	    if (np->right->left->desc.indom == PM_INDOM_NULL &&
	        np->right->right->desc.indom == PM_INDOM_NULL &&
		np->left->desc.indom != PM_INDOM_NULL) {
		PM_TPD(derive_errmsg) = "Non-scalar ternary guard with scalar expressions";
		if (async)
		    report_sem_error(dmp->name, np);
		return -1;
	    }
	    /* correct pmDesc promoted through COLON node at the right */
	    np->desc = np->right->desc;
	    return 0;

	case N_FILTERINST:
	    /* use descriptor from right operand (left is pattern) */
	    np->desc = np->right->desc;
	    return 0;
	
	default:
	    /* build pmDesc from pmDesc of both operands */
	    return map_desc(dmp, np, async);
	}
    }
    np->desc = np->left->desc;	/* struct copy */

    /*
     * special cases for functions ...
     * count()		u32 and instantaneous
     * instant()	result is instantaneous or discrete
     * delta()		expect numeric operand, result is instantaneous
     * rate()		expect numeric operand, dimension of time must be
     * 			0 or 1, result is instantaneous
     * scalar()		remove indom
     * aggr funcs	most expect numeric operand, result is instantaneous
     *			and singular
     * unary -		expect numeric operand, result is signed
     */
    switch (np->type) {

	case N_COUNT:
	    /* count() has its own type and units */
	    np->desc.type = PM_TYPE_U32;
	    memset((void *)&np->desc.units, 0, sizeof(np->desc.units));
	    np->desc.units.dimCount = 1;
	    np->desc.units.scaleCount = PM_COUNT_ONE;
	    np->desc.sem = PM_SEM_INSTANT;
	    np->desc.indom = PM_INDOM_NULL;
	    break;

	case N_INSTANT:
	    /*
	     * semantics are INSTANT if operand is COUNTER, else
	     * inherit the semantics of the operand
	     */
	    if (np->left->desc.sem == PM_SEM_COUNTER)
		np->desc.sem = PM_SEM_INSTANT;
	    else
		np->desc.sem = np->left->desc.sem;
	    break;

	case N_SCALAR:
	    np->desc.indom = PM_INDOM_NULL;
	    break;

	case N_AVG:
	case N_SUM:
	case N_MAX:
	case N_MIN:
	case N_DELTA:
	case N_RATE:
	case N_NEG:
	    /* others inherit, but need arithmetic operand */
	    switch (np->left->desc.type) {
		case PM_TYPE_32:
		case PM_TYPE_U32:
		case PM_TYPE_64:
		case PM_TYPE_U64:
		case PM_TYPE_FLOAT:
		case PM_TYPE_DOUBLE:
		    break;
		default:
		    if (np->type == N_NEG)
			PM_TPD(derive_errmsg) = "Non-arithmetic operand for unary negation";
		    else
			PM_TPD(derive_errmsg) = "Non-arithmetic operand for function";
		    if (async)
			report_sem_error(dmp->name, np);
		    return -1;
	    }
	    if (np->type != N_SUM)
		np->desc.sem = PM_SEM_INSTANT;
	    if (np->type == N_DELTA || np->type == N_RATE || np->type == N_INSTANT || np->type == N_NEG) {
		/* inherit indom */
		if (np->type == N_RATE) {
		    /*
		     * further restriction for rate() that dimension
		     * for time must be 0 (->counter/sec) or 1
		     * (->time utilization)
		     */
		    if (np->left->desc.units.dimTime != 0 && np->left->desc.units.dimTime != 1) {
			PM_TPD(derive_errmsg) = "Incorrect time dimension for operand";
			if (async)
			    report_sem_error(dmp->name, np);
			return -1;
		    }
		}
	    }
	    else {
		/* all the others are aggregate funcs with a singular value */
		np->desc.indom = PM_INDOM_NULL;
	    }
	    if (np->type == N_AVG) {
		/* avg() returns float result */
		np->desc.type = PM_TYPE_FLOAT;
	    }
	    else if (np->type == N_RATE) {
		/* rate() returns double result and time dimension is
		 * reduced by one ... if time dimension is then 0, set
		 * the scale to be none (this is time utilization)
		 */
		np->desc.type = PM_TYPE_DOUBLE;
		np->desc.units.dimTime--;
		if (np->desc.units.dimTime == 0)
		    np->desc.units.scaleTime = 0;
		else
		    np->desc.units.scaleTime = PM_TIME_SEC;
	    }
	    else if (np->type == N_NEG) {
		/* make sure result is signed */
		if (np->left->desc.type == PM_TYPE_U32)
		    np->desc.type = PM_TYPE_32;
		else if (np->left->desc.type == PM_TYPE_U64)
		    np->desc.type = PM_TYPE_64;
	    }
	    else if (np->type == N_DELTA) {
		/* make sure result is signed and has sufficient precision */
		if (np->left->desc.type == PM_TYPE_U32)
		    np->desc.type = PM_TYPE_64;
		else if (np->left->desc.type == PM_TYPE_U64)
		    np->desc.type = PM_TYPE_DOUBLE;
	    }
	    break;

	case N_ANON:
	    /* do nothing, pmDesc inherited "as is" from left node */
	    break;
    }
    return 0;
}

static void
dump_value(int type, pmAtomValue *avp)
{
    switch (type) {
	case PM_TYPE_32:
	    fprintf(stderr, "%i", avp->l);
	    break;

	case PM_TYPE_U32:
	    fprintf(stderr, "%u", avp->ul);
	    break;

	case PM_TYPE_64:
	    fprintf(stderr, "%" PRId64, avp->ll);
	    break;

	case PM_TYPE_U64:
	    fprintf(stderr, "%" PRIu64, avp->ull);
	    break;

	case PM_TYPE_FLOAT:
	    fprintf(stderr, "%g", (double)avp->f);
	    break;

	case PM_TYPE_DOUBLE:
	    fprintf(stderr, "%g", avp->d);
	    break;

	case PM_TYPE_STRING:
	    fprintf(stderr, "%s", avp->cp);
	    break;

	case PM_TYPE_AGGREGATE:
	case PM_TYPE_AGGREGATE_STATIC:
	case PM_TYPE_EVENT:
	case PM_TYPE_HIGHRES_EVENT:
	case PM_TYPE_UNKNOWN:
	    fprintf(stderr, "[blob]");
	    break;

	case PM_TYPE_NOSUPPORT:
	    fprintf(stderr, "dump_value: bogus value, metric Not Supported\n");
	    break;

	default:
	    fprintf(stderr, "dump_value: unknown value type=%d\n", type);
    }
}

void
__dmdumpexpr(node_t *np, int level)
{
    char	strbuf[60];

    if (level == 0) fprintf(stderr, "Derived metric expr dump from " PRINTF_P_PFX "%p...\n", np);
    if (np == NULL) return;
    fprintf(stderr, "expr node " PRINTF_P_PFX "%p type=%s", np, __dmnode_type_str(np->type));
    if (np->type == N_COLON && np->data.info != NULL) {
	if (np->data.info->bind == QUEST_BIND_LEFT)
	    fprintf(stderr, " [LEFT]");
	else if (np->data.info->bind == QUEST_BIND_RIGHT)
	    fprintf(stderr, " [RIGHT]");
    }
    if (np->type == N_DEFINED) {
	if (np->left != NULL) {
	    /* has not been evaluated, name in N_NAME node */
	    fprintf(stderr, "(%s)", np->left->value);
	}
	else {
	    /* np->left has been free'd */
	    fprintf(stderr, "(%s)", np->value);
	}
    }
    if (np->type == N_PATTERN) {
	__pmHashNode	*hnp;
	instctl_t	*icp;
	int		numinst = 0;
	int		nummatch = 0;;
	fprintf(stderr, " pattern: %s type: ", np->value);
	if (np->data.pattern->ftype == F_REGEX) {
	    /* regular expression from matchinst() */
	    fprintf(stderr, "%sregex used=%d",
		np->data.pattern->invert ? "inverted " : "", np->data.pattern->used);
	    if (np->data.pattern->hash.hash != NULL) {
		for (hnp = __pmHashWalk(&np->data.pattern->hash, PM_HASH_WALK_START);
		     hnp != NULL;
		     hnp = __pmHashWalk(&np->data.pattern->hash, PM_HASH_WALK_NEXT)) {
		    numinst++;
		    icp = (instctl_t *)hnp->data;
		    if (icp->match) nummatch++;
		}
		fprintf(stderr, " (%d instances, %d matches)", numinst, nummatch);
	    }
	}
	else {
	    /* F_EXACT */
	    fprintf(stderr, "exact match inst=%d", np->data.pattern->inst);
	}
	fputc('\n', stderr);
    }
    else {
	fprintf(stderr, " left=" PRINTF_P_PFX "%p right=" PRINTF_P_PFX "%p save_last=%d", np->left, np->right, np->save_last);
	if (np->type == N_NAME || np->type == N_INTEGER || np->type == N_DOUBLE)
	    fprintf(stderr, " [%s] primary=%d", np->value, np->data.info == NULL ? 1 : 0);
	if (np->type == N_SCALE) {
	    if (pmUnitsStr_r(&np->desc.units, strbuf, sizeof(strbuf)) == NULL) {
		fprintf(stderr, " [bad units: %d %d %d %u %u %d %d %u]", 
		    np->desc.units.scaleCount, np->desc.units.scaleTime,
		    np->desc.units.scaleSpace,
		    np->desc.units.dimCount, np->desc.units.dimTime,
		    np->desc.units.dimSpace,
		    np->desc.units.extraUnit, np->desc.units.extraScale);
	    }
	    else
		fprintf(stderr, " [%s]", strbuf);
	    fputc('\n', stderr);
	}
	else {
	    fputc('\n', stderr);
	    if (np->data.info) {
		fprintf(stderr, "    PMID: %s ", pmIDStr_r(np->data.info->pmid, strbuf, sizeof(strbuf)));
		fprintf(stderr, "(%s from pmDesc) numval: %d", pmIDStr_r(np->desc.pmid, strbuf, sizeof(strbuf)), np->data.info->numval);
		if (np->data.info->div_scale != 1)
		    fprintf(stderr, " div_scale: %d", np->data.info->div_scale);
		if (np->data.info->mul_scale != 1)
		    fprintf(stderr, " mul_scale: %d", np->data.info->mul_scale);
		fputc('\n', stderr);
		pmPrintDesc(stderr, &np->desc);
		if (np->data.info->ivlist) {
		    int		j;
		    int		max;

		    max = np->data.info->numval > np->data.info->last_numval ? np->data.info->numval : np->data.info->last_numval;

		    for (j = 0; j < max; j++) {
			fprintf(stderr, "[%d]", j);
			if (j < np->data.info->numval) {
			    fprintf(stderr, " inst=%d, val=", np->data.info->ivlist[j].inst);
			    dump_value(np->desc.type, &np->data.info->ivlist[j].value);
			}
			if (j < np->data.info->last_numval) {
			    fprintf(stderr, " (last inst=%d, val=", np->data.info->last_ivlist[j].inst);
			    dump_value(np->desc.type, &np->data.info->last_ivlist[j].value);
			    fputc(')', stderr);
			}
			fputc('\n', stderr);
		    }
		}
	    }
	}
    }

    if (np->left != NULL) __dmdumpexpr(np->left, level+1);
    if (np->right != NULL) __dmdumpexpr(np->right, level+1);

}

static int
checkname(char *p)
{
    int	firstch = 1;

    for ( ; *p; p++) {
	if (firstch) {
	    firstch = 0;
	    if (isalpha((int)*p)) continue;
	    return -1;
	}
	else {
	    if (isalpha((int)*p) || isdigit((int)*p) || *p == '_') continue;
	    if (*p == '.') {
		firstch = 1;
		continue;
	    }
	    return -1;
	}
    }
    return 0;
}

static char *
registerderived(int derive_locked, const char *name, const char *expr, int isanon)
{
    static __pmID_int	pmid;
    int			i;
    dm_t		*tmp_mlist;

    if (derive_locked == PM_NOT_LOCKED) {
	PM_INIT_LOCKS();
	PM_LOCK(registered.mutex);
	__dminit(NULL);
    }
    else
	PM_ASSERT_IS_LOCKED(registered.mutex);

    if (pmDebugOptions.derive && pmDebugOptions.appl0) {
	fprintf(stderr, "pmRegisterDerived: name=\"%s\" expr=\"%s\"\n", name, expr);
    }

    if (registered.limit != -1 && registered.nmetric - registered.nanon >= registered.limit) {
	/* DoS prevention limit exceeded ... */
	PM_TPD(derive_errmsg) = "Too many global derived metrics";
	if (derive_locked == PM_NOT_LOCKED)
	    PM_UNLOCK(registered.mutex);
	return (char *)expr;
    }

    for (i = 0; i < registered.nmetric; i++) {
	if (!(registered.mlist[i].flags & DM_FREE) &&
	    strcmp(name, registered.mlist[i].name) == 0) {
	    /* oops, duplicate name ... */
	    PM_TPD(derive_errmsg) = "Duplicate global derived metric name";
	    if (derive_locked == PM_NOT_LOCKED)
		PM_UNLOCK(registered.mutex);
	    return (char *)expr;
	}
    }
    if (pmDebugOptions.derive && pmDebugOptions.appl0 &&
        pmDebugOptions.desperate) {
	/* turn on bison diagnostics */
	derive_debug = 1;
    }

    PM_TPD(derive_errmsg) = NULL;
    string = expr;
    /* reset lexer lookahead in case of error in previous derive_parse() call */
    lexpeek = 0;
    derive_parse();
    if (parse_tree == NULL) {
	/* parser error */
	char	*sts = (char *)lexicon;
	if (derive_locked == PM_NOT_LOCKED)
	    PM_UNLOCK(registered.mutex);
	return sts;
    }

    if (registered.nmetric == 0) {
	/* global derived metrics start at the minimum possible pmID */
	pmid.flag = 0;
	pmid.domain = DYNAMIC_PMID;
	pmid.cluster = 0;
	pmid.item = 1;
    }
    else {
	/* begin fault-injection block */
PM_FAULT_POINT("libpcp/derive.c:1", PM_FAULT_MISC);
	if (PM_FAULT_CHECK) {
	    PM_FAULT_CLEAR;
	    pmid.item = 1024 - 1;
	    pmid.cluster = 2048 - 1;
	}
	/* end fault-injection block */

	/* increment the pmID for this one */
	if (pmid.item < 1024 - 1)
	    pmid.item++;
	else if (pmid.cluster < 2048 - 1) {
	    pmid.cluster++;
	    pmid.item = 1;		/* item field == 0 is magic */
	}
	else {
	    /* run out of pmIDs ... */
	    PM_TPD(derive_errmsg) = "global PMID space exhausted";
	    if (derive_locked == PM_NOT_LOCKED)
		PM_UNLOCK(registered.mutex);
	    return (char *)expr;
	}
    }

    registered.glob_last = registered.nmetric++;
    if (isanon)
	registered.nanon++;
    tmp_mlist = (dm_t *)realloc(registered.mlist, registered.nmetric*sizeof(dm_t));
    if (tmp_mlist == NULL) {
	if (derive_locked == PM_NOT_LOCKED)
	    PM_UNLOCK(registered.mutex);
	pmNoMem("pmRegisterDerived: mlist", registered.nmetric*sizeof(dm_t), PM_FATAL_ERR);
	/*NOTREACHED*/
    }
    registered.mlist = tmp_mlist;

    registered.mlist[registered.nmetric-1].name = strdup(name);
    registered.mlist[registered.nmetric-1].anon = isanon;
    registered.mlist[registered.nmetric-1].pmid = *((pmID *)&pmid);
    registered.mlist[registered.nmetric-1].expr = parse_tree;
    registered.mlist[registered.nmetric-1].flags = DM_GLOBAL;
    registered.mlist[registered.nmetric-1].oneline = NULL;
    registered.mlist[registered.nmetric-1].helptext = NULL;

    if (pmDebugOptions.derive) {
	fprintf(stderr, "pmRegisterDerived: global metric[%d] %s = %s\n", registered.nmetric-1, name, expr);
	if (pmDebugOptions.appl0)
	    __dmdumpexpr(parse_tree, 0);
    }

    if (derive_locked == PM_NOT_LOCKED)
	PM_UNLOCK(registered.mutex);
    return NULL;
}

#define ATTR_BAD_METRIC	-1
#define ATTR_BAD_ATTR	-2
#define ATTR_DUP_VALUE	-3

/*
 * set attribute values for derived metrics
 */
static int
setattr(int derive_locked, const char *name, const char *attr, const char *value)
{
    int			i;
    int			sts = 0;

    if (derive_locked == PM_NOT_LOCKED) {
	PM_INIT_LOCKS();
	PM_LOCK(registered.mutex);
    }
    else {
	PM_ASSERT_IS_LOCKED(registered.mutex);
    }

    if (pmDebugOptions.derive && pmDebugOptions.appl0) {
	fprintf(stderr, "setattr: name=\"%s\" attr=\"%s\" value=\"%s\"\n", name, attr, value);
    }

    for (i = 0; i < registered.nmetric; i++) {
	if (!(registered.mlist[i].flags & DM_FREE) &&
	    strcmp(name, registered.mlist[i].name) == 0) {
	    /* found it! */
	    break;
	}
    }

    if (i == registered.nmetric) {
	/* oops, derived metric name not defined (yet) */
	sts = ATTR_BAD_METRIC;
	goto done;
    }

    if (strcmp(attr, "oneline") == 0) {
	if (registered.mlist[i].oneline == NULL)
	    registered.mlist[i].oneline = value;
	else
	    sts = ATTR_DUP_VALUE;
    }
    else if (strcmp(attr, "helptext") == 0) {
	if (registered.mlist[i].helptext == NULL)
	    registered.mlist[i].helptext = value;
	else
	    sts = ATTR_DUP_VALUE;
    }
    else {
	/* not one of the attributes we recognize */
	sts = ATTR_BAD_ATTR;
	goto done;
    }

done:
    if (derive_locked == PM_NOT_LOCKED)
	PM_UNLOCK(registered.mutex);
    return sts;
}

/* Global derived metrics - the original, and still the best. */
char *
pmRegisterDerived(const char *name, const char *expr)
{
    char	*c_sts;

    if (pmDebugOptions.pmapi)
	fprintf(stderr, "pmRegisterDerived(%s, %s) <:", name, expr);

    c_sts = registerderived(PM_NOT_LOCKED, name, expr, 0);

    if (pmDebugOptions.pmapi) {
	fprintf(stderr, ":> returns ");
	if (c_sts == NULL)
	    fprintf(stderr, "NULL\n");
	else {
	    fprintf(stderr, "%s\n", c_sts);
	}
    }

    return c_sts;
}

/* Global derived metrics - variant including error handling. */
int
pmRegisterDerivedMetric(const char *name, const char *expr, char **errmsg)
{
    size_t	length;
    char	*offset;
    char	*error;
    char	*dmsg;
    int		sts;

    static const char	fmt[] = \
	"Error: pmRegisterDerivedMetric(\"%s\", ...)\n%s\n%*s^\n";
    static const char	fmt_nopos[] = \
	"Error: pmRegisterDerivedMetric(\"%s\", ...)\n%s\n^\n";

    if (pmDebugOptions.pmapi)
	fprintf(stderr, "pmRegisterDerivedMetric(%s, %s, ...) <:", name, expr);

    *errmsg = NULL;
    if ((offset = registerderived(PM_NOT_LOCKED, name, expr, 0)) == NULL) {
	sts = 0;
	goto pmapi_return;
    }

    /* failed to register name/expr - build an error string to pass back */
    length = strlen(fmt);
    length += strlen(name);
    length += strlen(expr);
    length += (offset - expr);
    if ((dmsg = PM_TPD(derive_errmsg)) != NULL)
	length += strlen(dmsg) + 2;

    if ((error = malloc(length)) == NULL)
	pmNoMem("pmRegisterDerivedMetric", length, PM_FATAL_ERR);
    if (offset == expr)
	pmsprintf(error, length, fmt_nopos, name, expr);
    else
	pmsprintf(error, length, fmt, name, expr, (int)(expr - offset), " ");
    if (dmsg) {
	pmstrncat(error, length, dmsg);
	pmstrncat(error, length, "\n");
    }
    error[length-1] = '\0';

    *errmsg = error;
    sts = -1;

pmapi_return:

    if (pmDebugOptions.pmapi) {
	fprintf(stderr, ":> returns ");
	if (sts >= 0)
	    fprintf(stderr, "%d\n", sts);
	else {
	    char	l_errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "%s\n", pmErrStr_r(sts, l_errmsg, sizeof(l_errmsg)));
	}
    }

    return sts;

}

/* Register an anonymous metric */
int
__pmRegisterAnon(const char *name, int type)
{
    return registeranon(PM_NOT_LOCKED, name, type);
}

/* Internal version of register an anonymous metric */
static int
registeranon(int derive_locked, const char *name, int type)
{
    char	*msg;
    char	buf[21];	/* anon(PM_TYPE_XXXXXX) */

PM_FAULT_RETURN(PM_ERR_FAULT);
    switch (type) {
	case PM_TYPE_32:
	    pmsprintf(buf, sizeof(buf), "anon(PM_TYPE_32)");
	    break;
	case PM_TYPE_U32:
	    pmsprintf(buf, sizeof(buf), "anon(PM_TYPE_U32)");
	    break;
	case PM_TYPE_64:
	    pmsprintf(buf, sizeof(buf), "anon(PM_TYPE_64)");
	    break;
	case PM_TYPE_U64:
	    pmsprintf(buf, sizeof(buf), "anon(PM_TYPE_U64)");
	    break;
	case PM_TYPE_FLOAT:
	    pmsprintf(buf, sizeof(buf), "anon(PM_TYPE_FLOAT)");
	    break;
	case PM_TYPE_DOUBLE:
	    pmsprintf(buf, sizeof(buf), "anon(PM_TYPE_DOUBLE)");
	    break;
	default:
	    return PM_ERR_TYPE;
    }
    if ((msg = registerderived(derive_locked, name, buf, 1)) != NULL) {
	pmprintf("__pmRegisterAnon(%s, %d): @ \"%s\" Error: %s\n", name, type, msg, pmDerivedErrStr());
	pmflush();
	return PM_ERR_GENERIC;
    }
    return 0;
}

/*
 * called from pmUnRegisterDerived() once for each active
 * context
 */
static void
context_walk_cb(void *handle, __pmContext *ctxp)
{
    char	*name = (char *)handle;
    ctl_t	*cp = (ctl_t *)ctxp->c_dm;
    int		i;

    if (cp == NULL)
	return;

    for (i = 0; i < cp->nmetric; i++) {
	if (!(cp->mlist[i].flags & DM_FREE) &&
	    strcmp(name, cp->mlist[i].name) == 0) {
	    /* bingo! */
	    unregister_count++;
	    cp->mlist[i].flags |= DM_FREE;
	    if (cp->mlist[i].flags & DM_GLOBAL)
		;
	    else {
		/*
		 * per-context derived metrics (via pmAddDerivedMetric())
		 * have their own storage, others have pointers into the
		 * global metrics (registered.mlist[]) and these have
		 * already been freed
		 */
		free(cp->mlist[i].name);
		if (cp->mlist[i].oneline != NULL)
		    free((void *)cp->mlist[i].oneline);
		if (cp->mlist[i].helptext != NULL)
		    free((void *)cp->mlist[i].helptext);
	    }
	    cp->mlist[i].name = NULL;
	    cp->mlist[i].pmid = PM_ID_NULL;
	    /* mimic logic from __dmclosecontext() */
	    if (cp->mlist[i].expr != NULL) {
		if (cp->mlist[i].flags & DM_GLOBAL) {
		    if (cp->mlist[i].flags & DM_BIND)
			free_expr(cp->mlist[i].expr); 
		}
		else {
		    free_expr_ctx(cp->mlist[i].expr); 
		}
		cp->mlist[i].expr = NULL;
	    }
	    cp->mlist[i].oneline = NULL;
	    cp->mlist[i].helptext = NULL;
	    /* no break, see comment above in pmUnregisterDerived() */
	}
    }

    return;
}

int
pmUnregisterDerived(const char *name)
{
    int		i;
    int		sts;
    int		seen_in_pass1 = 0;	/* per-context derived metrics from */
    					/* pmAddDerived() are only seen in pass 2 */

    if (pmDebugOptions.pmapi)
	fprintf(stderr, "pmUnregisterDerived(%s) <:", name);

    PM_INIT_LOCKS();
    PM_LOCK(unregister_lock);
    unregister_count = 0;

    PM_LOCK(registered.mutex);
    /* 
     * pass 1, mark global metric (if any) as DM_FREE
     * => being unregistered
     */
    for (i = 0; i < registered.nmetric; i++) {
	if (!(registered.mlist[i].flags & DM_FREE) &&
	    strcmp(name, registered.mlist[i].name) == 0) {
	    /*
	     * bingo!
	     * -- but masked per-context metrics means there
	     * may be more, so keep going
	     */
	    registered.mlist[i].flags |= DM_FREE;
	    seen_in_pass1 = 1;
	}
    }
    PM_UNLOCK(registered.mutex);

    /* pass 2, unregister the metric in every context */
    __pmContextWalk((void *)name, context_walk_cb);

    PM_LOCK(registered.mutex);
    /* pass 3, clean up global metric definition */
    for (i = 0; i < registered.nmetric; i++) {
	/* flagged as DM_FREE in pass 1, so need a slightly different guard here */
	if (registered.mlist[i].name != NULL &&
	    strcmp(name, registered.mlist[i].name) == 0) {
	    /* bingo! */
	    free(registered.mlist[i].name);
	    registered.mlist[i].name = NULL;
	    registered.mlist[i].pmid = PM_ID_NULL;
	    free_expr(registered.mlist[i].expr);
	    registered.mlist[i].expr = NULL;
	    if (registered.mlist[i].oneline != NULL) {
		free((void *)registered.mlist[i].oneline);
		registered.mlist[i].oneline = NULL;
	    }
	    if (registered.mlist[i].helptext != NULL) {
		free((void *)registered.mlist[i].helptext);
		registered.mlist[i].helptext = NULL;
	    }
	    /* no break, see comment above */
	}
    }

    PM_UNLOCK(registered.mutex);

    sts = (unregister_count == 0 && !seen_in_pass1) ? PM_ERR_NAME : unregister_count;

    PM_UNLOCK(unregister_lock);

    if (pmDebugOptions.pmapi) {
	fprintf(stderr, ":> returns ");
	if (sts >= 0)
	    fprintf(stderr, "%d\n", sts);
	else {
	    char	errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "%s\n", pmErrStr_r(sts, errmsg, sizeof(errmsg)));
	}
    }

    return sts;
}

int
pmLoadDerivedConfig(const char *fname)
{

    int		sts;

    if (pmDebugOptions.pmapi)
	fprintf(stderr, "pmLoadDerivedConfig(%s) <:", fname);

    PM_INIT_LOCKS();
    PM_LOCK(registered.mutex);
    __dminit(NULL);
    sts = __dminit_parse(NULL, fname, 0 /*non-recovering*/);
    PM_UNLOCK(registered.mutex);

    if (pmDebugOptions.pmapi) {
	fprintf(stderr, ":> returns ");
	if (sts >= 0)
	    fprintf(stderr, "%d\n", sts);
	else {
	    char	errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "%s\n", pmErrStr_r(sts, errmsg, sizeof(errmsg)));
	}
    }

    return sts;
}

/*
 * process a derived metric configuration file
 * - skip comments (leading #) and blank lines
 * - split each line into either
 *   <name> = <expr>
 *   or
 *   <name>(<attr>) = <string>
 * - remove white space around name and (optionally) attr
 * - build <string> which includes \x handling and multi-line <string>s
 */  
static int
__dminit_configfile(__pmContext *ctxp, const char *fname)
{
    FILE	*fp;
    int		buflen;
    char	*buf;
    char	*p;
    int		c;
    int		lastc = '\0';
    int		sts = 0;
    int		eq = -1;	/* offset to =, start of <expr> or <value> */
    int		lp = -1;	/* offset to (, start of <attr> */
    int		rp = -1;	/* offset to ), end of <attr> */
    int		lineno = 1;
    int		clineno = 0;	/* continuation line count */

    if (pmDebugOptions.derive) {
	fprintf(stderr, "pmLoadDerivedConfig(\"%s\")\n", fname);
    }

    if ((fp = fopen(fname, "r")) == NULL) {
	return -oserror();
    }
    buflen = 128;
    if ((buf = (char *)malloc(buflen)) == NULL) {
	/* registered.mutex not locked in this case */
	pmNoMem("pmLoadDerivedConfig: alloc buf", buflen, PM_FATAL_ERR);
	/*NOTREACHED*/
    }
    p = buf;
    while ((c = fgetc(fp)) != EOF) {
	if (p == &buf[buflen]) {
	    if ((buf = (char *)realloc(buf, 2*buflen)) == NULL) {
		/* registered.mutex not locked in this case */
		pmNoMem("pmLoadDerivedConfig: expand buf", 2*buflen, PM_FATAL_ERR);
		/*NOTREACHED*/
	    }
	    p = &buf[buflen];
	    buflen *= 2;
	}
	if (eq == -1) {
	    /* no = yet ... */
	    if (c == '=') {
		/*
		 * mark first = in line ... <name> or <name>(<attr>) to the
		 * left and <expr> or <value> to the right
		 */
		eq = p - buf;
	    }
	    else if (c == '(' && lp == -1) {
		/*
		 * mark first ( in line before = ... <name> to the
		 * left and <attr> to the right
		 */
		lp = p - buf;
	    }
	    else if (c == ')' && rp == -1) {
		/*
		 * mark first ) in line before = ... <attr> to the
		 * left and = <value> to the right
		 */
		rp = p - buf;
	    }
	}
	if (c == '\n' && lastc == '\\') {
	    /* \ continuation at the end of a line */
	    clineno++;
	    p--;
	    lastc = c;
	    continue;
	}
	if (c == '\n' && lastc != '\\') {
	    if (p == buf || buf[0] == '#') {
		/*
		 * pragma-like things handled here and never get to the parser
		 * - #control option=value[,option=value]...
		 */
		*p = '\0';
		if (strncasecmp(buf, "#control", 8) == 0) {
		    /*
		     * skip #control + <space> ... option=value starts at
		     * buf[9]
		     */
		    do_control_options(ctxp, fname, lineno, (const char *)&buf[9]);
		}
		/* otherwise comment or empty line, skip it ... */
		goto next_line;
	    }
	    *p = '\0';
	    if (eq != -1) {
		char	*dupbuf;	/* copy of <name> */
		char	*np;		/* start of <name> or <name>(<attr>) */
		char	*q;
		char	*errp;
		buf[eq] = '\0';
		if ((dupbuf = strdup(buf)) == NULL) {
		    /* registered.mutex not locked in this case */
		    pmNoMem("pmLoadDerivedConfig: dupname", strlen(buf), PM_FATAL_ERR);
		    /*NOTREACHED*/
		}
		buf[eq] = '=';
		/* trim white space from tail of <name> */
		if (lp != -1) {
		    /* <name> ends at or before ( */
		    q = &dupbuf[lp];
		    *q-- = '\0';
		}
		else {
		    /* <name> ends at or before = */
		    q = &dupbuf[eq-1];
		}
		while (q >= dupbuf && isspace((int)*q))
		    *q-- = '\0';
		/* trim white space from head of <name> */
		np = dupbuf;
		while (*np && isspace((int)*np))
		    np++;
		if (*np == '\0') {
		    pmprintf("[%s:%d] Error: pmLoadDerivedConfig: derived metric name missing\n%s\n", fname, lineno, buf);
		    pmflush();
		    free(dupbuf);
		    goto next_line;
		}
		if (checkname(np) < 0) {
		    pmprintf("[%s:%d] Error: pmLoadDerivedConfig: illegal derived metric name (%s)\n", fname, lineno, np);
		    pmflush();
		    free(dupbuf);
		    goto next_line;
		}
		if (lp >= 0 && rp == -1) {
		    pmprintf("[%s:%d] Error: pmLoadDerivedConfig: missing ')' after attribute\n%s\n", fname, lineno, buf);
		    pmflush();
		    free(dupbuf);
		    goto next_line;
		}
		if (rp >= 0 && lp == -1) {
		    pmprintf("[%s:%d] Error: pmLoadDerivedConfig: missing '(' before attribute\n%s\n", fname, lineno, buf);
		    pmflush();
		    free(dupbuf);
		    goto next_line;
		}
		if (lp == -1 && rp == -1) {
		    /* <name> = ... */
		    char	*ep;	/* start of <expr> */
		    ep = &buf[eq+1];
		    while (*ep != '\0' && isspace((int)*ep))
			ep++;
		    if (*ep == '\0') {
			pmprintf("[%s:%d] Error: pmLoadDerivedConfig: expression missing\n%s\n", fname, lineno, buf);
			pmflush();
			free(dupbuf);
			goto next_line;
		    }
		    errp = registerderived(PM_LOCKED, np, ep, 0);
		    if (errp != NULL) {
			pmprintf("[%s:%d] Error: pmRegisterDerived(%s, ...) syntax error\n", fname, lineno, np);
			pmprintf("%s\n", &buf[eq+1]);
			for (q = &buf[eq+1]; *q; q++) {
			    if (q == errp) *q = '^';
			    else if (!isspace((int)*q)) *q = ' ';
			}
			pmprintf("%s\n", &buf[eq+1]);
			q = pmDerivedErrStr();
			if (q != NULL) pmprintf("%s\n", q);
			pmflush();
		    }
		    else
			sts++;
		}
		else {
		    /* <name>(<attr>) = ... */
		    char	*ap;		/* start of <attr> */
		    char	*vp;		/* start of <value> */
		    char	*value;		/* accumulated <value> */
		    int		vlen = 0;	/* length of value */
		    int		lsts;
		    char	delim;		/* string delimiter, if any */
		    /* trim white space from tail of <attr> */
		    q = &dupbuf[rp];
		    *q-- = '\0';
		    while (q >= &dupbuf[lp] && isspace((int)*q))
			*q-- = '\0';
		    /* trim white space from head of <attr> */
		    ap = &dupbuf[lp+1];
		    while (*ap && isspace((int)*ap))
			ap++;
		    if (*ap == '\0') {
			pmprintf("[%s:%d] Error: pmLoadDerivedConfig: attribute name missing\n%s\n", fname, lineno, buf);
			pmflush();
			free(dupbuf);
			goto next_line;
		    }
		    /* trim white space from head of <value> */
		    vp = &buf[eq+1];
		    while (*vp != '\0' && isspace((int)*vp))
			vp++;
		    if (*vp == '"' || *vp == '\'') {
			/* string delimiter found */
			delim = *vp;
			vp++;
		    }
		    else
			delim = '\0';
		    if (*vp == '\0') {
			pmprintf("[%s:%d] Error: pmLoadDerivedConfig: attribute value missing\n%s\n", fname, lineno, buf);
			pmflush();
			free(dupbuf);
			goto next_line;
		    }
		    vlen = strlen(vp);
		    if ((value = strdup(vp)) == NULL) {
			/* registered.mutex not locked in this case */
			pmNoMem("pmLoadDerivedConfig: value", vlen, PM_FATAL_ERR);
			/*NOTREACHED*/
		    }
		    if (delim == '\0') {
			/* no string delimiter, so <value> is complete */
			;
		    }
		    else {
			/*
			 * process the <value>, handling any \x escape
			 * sequences ... may need more input for multi-line
			 * <value>s
			 */
			char	*v = value;
			int	esc = 0;
			int	xtralines = 0;
			for ( ; ; ) {
			    if (*vp)
				c = *vp++;
			    else if (xtralines == 0)
				c = '\n';
			    else {
				c = fgetc(fp);
				if (c == EOF) {
				    /* string value not terminated */
				    break;
				}
			    }
			    if (c == '\n')
				xtralines++;
			    if (v >= &value[vlen]) {
				/*
				 * out the end of the value buffer ... use
				 * a "doubling" algorithm
				 */
				char	*tmp_value;
				size_t	tmp_v;

				tmp_v = v - value; /* beware realloc'd ptrs */
				tmp_value = realloc(value, 2*vlen);
				if (tmp_value == NULL) {
				    /* registered.mutex not locked in this case */
				    pmNoMem("pmLoadDerivedConfig: expand value", 2*vlen, PM_FATAL_ERR);
				    /*NOTREACHED*/
				}
				v = &tmp_value[tmp_v];
				value = tmp_value;
				vlen *= 2;
			    }
			    if (esc) {
				esc = 0;
				/*
				 * \ at the end of the line is a special case,
				 * ... don't emit the newline (so treat as
				 * "continuation"
				 */
				if (c != '\n')
				    *v++ = c;
				continue;
			    }
			    if (c == '\\') {
				esc = 1;
				continue;
			    }
			    if (c == delim) {
				*v++ = '\0';
				break;
			    }
			    *v++ = c;
			}
			if (c == EOF) {
			    pmprintf("[%s:%d] Error: pmLoadDerivedConfig: missing termination (%c) for value\n", fname, lineno, delim);
			    pmflush();
			    free(value);
			    vp = NULL;
			}
			else {
			    /*
			     * expect no text on same line after string
			     * terminator
			     */
			    if (*vp) {
				while (*vp && isspace((int)*vp))
				    vp++;
				if (*vp) {
				    pmprintf("[%s:%d] Warning: pmLoadDerivedConfig: extra text (%s) after attribute value will be ignored\n", fname, lineno, vp);
				    pmflush();
				}
			    }
			    else if (xtralines == 0) {
				/* all ok, <value> is end of one line of input */
				;
			    }
			    else {
				c = fgetc(fp);
				while (c != EOF && c != '\n') {
				    c = fgetc(fp);
				    if (!isspace(c))
					break;
				}
				if (c != EOF && c != '\n') {
				    pmprintf("[%s:%d] Warning: pmLoadDerivedConfig: extra text (%c", fname, lineno, c);
				    while ((c = fgetc(fp)) != EOF && c != '\n')
					pmprintf("%c", c);
				    pmprintf(") after attribute value will be ignored\n");
				    pmflush();
				}
				if (c == '\n')
				    xtralines++;
			    }
			    vp = value;
			}
			if (xtralines > 1)
			    clineno += xtralines - 1;
		    }

		    if (vp != NULL) {
			/*
			 * value[] is guaranteed to be a malloc'd array at
			 * this point ... settattr() will permanently hold
			 * a pointer to this memory on succes (so nothing to
			 * do there), but we need to free on error
			 */
			if ((lsts = setattr(PM_LOCKED, np, ap, value)) < 0) {
			    free(value);
			    if (lsts == ATTR_BAD_METRIC)
				pmprintf("[%s:%d] Error: pmLoadDerivedConfig: cannot set attribute, derived metric (%s) not defined\n", fname, lineno, np);
			    else if (lsts == ATTR_BAD_ATTR)
				pmprintf("[%s:%d] Error: pmLoadDerivedConfig: attribute (%s) is unknown for metric (%s)\n", fname, lineno, ap, np);
			    else if (lsts == ATTR_DUP_VALUE)
				pmprintf("[%s:%d] Error: pmLoadDerivedConfig: duplicate value for attribute (%s) of metric (%s)\n", fname, lineno, ap, np);
			    else
				pmprintf("[%s:%d] Error: pmLoadDerivedConfig: cannot set attribute, reason unknown\n", fname, lineno);
			    pmprintf("%s\n", buf);
			    pmflush();
			}
		    }
		}
		free(dupbuf);
	    }
	    else {
		/*
		 * error ... no = in the line, so no derived metric name
		 */
		pmprintf("[%s:%d] Error: pmLoadDerivedConfig: missing ``='' after derived metric name\n%s\n", fname, lineno, buf);
		pmflush();
	    }
next_line:
	    lineno++;
	    if (clineno) {
		lineno += clineno;
		clineno = 0;
	    }
	    p = buf;
	    eq = lp = rp = -1;
	}
	else {
	    lastc = c;
	    *p++ = c;
	}
    }
    fclose(fp);
    free(buf);
    return sts;
}

char *
pmDerivedErrStr(void)
{
    PM_INIT_LOCKS();
    return PM_TPD(derive_errmsg);
}

#undef NO_ERROR
#define NO_ERROR 0
#define SYNTAX_ERROR 1
#define SEMANTIC_ERROR 2

static int
addderived(const char *name, const char *expr, char **p)
{
    int			ctx;
    __pmContext		*ctxp;
    ctl_t		*cp;
    static __pmID_int	pmid;
    dm_t		*tmp_mlist;
    int			i;

    *p = (char *)expr;		/* safest, especially if errors encountered */

    if ((ctx = pmWhichContext()) < 0) {
	PM_TPD(derive_errmsg) = "Attempt to use an illegal context";
	return PM_ERR_NOCONTEXT;
    }
    if ((ctxp = __pmHandleToPtr(ctx)) == NULL) {
	PM_TPD(derive_errmsg) = "Attempt to use an illegal context handle";
	return PM_ERR_NOCONTEXT;
    }
    PM_ASSERT_IS_LOCKED(ctxp->c_lock);
    cp = (ctl_t *)ctxp->c_dm;

    if (pmDebugOptions.derive && pmDebugOptions.appl0) {
	fprintf(stderr, "pmAddDerived(ctx->%d): name=\"%s\" expr=\"%s\"\n", ctxp->c_handle, name, expr);
    }

    if (cp->limit != -1 && cp->nmetric - cp->glob_last >= cp->limit) {
	/* DoS prevention limit exceeded ... */
	PM_TPD(derive_errmsg) = "Too many per-context derived metrics";
	PM_UNLOCK(ctxp->c_lock);
	return PM_ERR_BADDERIVE;
    }

    for (i = 0; i < cp->nmetric; i++) {
	if (!(cp->mlist[i].flags & DM_FREE) &&
	    strcmp(name, cp->mlist[i].name) == 0) {
	    if ((cp->mlist[i].flags & DM_GLOBAL) == 0) {
		/* oops, duplicate per-context name ... */
		PM_TPD(derive_errmsg) = "Duplicate per-context derived metric name";
		PM_UNLOCK(ctxp->c_lock);
		return PM_ERR_BADDERIVE;
	    }
	}
    }

    if (pmDebugOptions.derive && pmDebugOptions.appl0 &&
        pmDebugOptions.desperate) {
	/* turn on bison diagnostics */
	derive_debug = 1;
    }

    PM_TPD(derive_errmsg) = NULL;
    string = expr;
    /* reset lexer lookahead in case of error in previous derive_parse() call */
    lexpeek = 0;
    derive_parse();
    if (parse_tree == NULL) {
	/* parser error */
	*p = (char *)lexicon;
	PM_UNLOCK(ctxp->c_lock);
	return SYNTAX_ERROR;
    }
    if (parse_tree == NULL) {
	/* post-parser error, can't help with location in expr */
	PM_UNLOCK(ctxp->c_lock);
	*p = (char *)expr;
	return SYNTAX_ERROR;
    }

    if (cp->nmetric - cp->glob_last == 0) {
	/* per-context derived metrics start at the maximum possible pmID */
	pmid.flag = 1;			/* item field == 0 is magic */
	pmid.domain = DYNAMIC_PMID;
	pmid.cluster = 2048 - 1;	/* top bit in cluster field is magic */
	pmid.item = 1024 - 1;		/* max item */
    }
    else {
	/* begin fault-injection block */
PM_FAULT_POINT("libpcp/derive.c:2", PM_FAULT_MISC);
	if (PM_FAULT_CHECK) {
	    PM_FAULT_CLEAR;
	    pmid.item = 1;
	    pmid.cluster = 0;
	}
	/* end fault-injection block */

	/* decrement the pmID for this one */
	if (pmid.item > 1)
	    pmid.item--;
	else if (pmid.cluster > 0) {
	    pmid.cluster--;
	    pmid.item = 1024 - 1;
	}
	else {
	    /* run out of pmIDs ... */
	    PM_TPD(derive_errmsg) = "per-context PMID space exhausted";
	    PM_UNLOCK(ctxp->c_lock);
	    return PM_ERR_BADDERIVE;
	}
    }

    /*
     * check for masking ...
     */
    for (i = 0; i < cp->nmetric; i++) {
	if (!(cp->mlist[i].flags & DM_FREE) &&
	    strcmp(name, cp->mlist[i].name) == 0) {
	    /*
	     * global name same as per-context name, the latter wins
	     * and the former is "masked"
	     */
	    cp->mlist[i].flags |= DM_MASKED;
	    if (pmDebugOptions.derive && pmDebugOptions.appl0) {
		char	strbuf[20];
		fprintf(stderr, "pmAddDerived(ctx->%d): per-context metric \"%s\" [%s]",
		    ctxp->c_handle, name,
		    pmIDStr_r(*((pmID *)&pmid), strbuf, sizeof(strbuf)));
		fprintf(stderr, " masks global derived metric of the same name [%s]\n",
		    pmIDStr_r(cp->mlist[i].pmid, strbuf, sizeof(strbuf)));
	    }
	}
    }

    cp->nmetric++;
    tmp_mlist = (dm_t *)realloc(cp->mlist, cp->nmetric*sizeof(dm_t));
    if (tmp_mlist == NULL) {
	PM_UNLOCK(ctxp->c_lock);
	pmNoMem("pmAddDerived: mlist", cp->nmetric*sizeof(dm_t), PM_FATAL_ERR);
	/*NOTREACHED*/
    }

    cp->mlist = tmp_mlist;
    cp->mlist[cp->nmetric-1].name = strdup(name);
    cp->mlist[cp->nmetric-1].anon = 0;
    cp->mlist[cp->nmetric-1].pmid = *((pmID *)&pmid);
    cp->mlist[cp->nmetric-1].expr = parse_tree;
    cp->mlist[cp->nmetric-1].flags = 0;
    cp->mlist[cp->nmetric-1].oneline = NULL;
    cp->mlist[cp->nmetric-1].helptext = NULL;

    /*
     * we must be in a context, and this derived metric is private to
     * that context, so we can bind synchronously
     */
    __dmbind(PM_NOT_LOCKED, ctxp, cp->nmetric-1, 0);
    if (PM_TPD(derive_errmsg) != NULL) {
	/* oops, semantic error(s) ... */
	if (pmDebugOptions.derive) {
	    fprintf(stderr, "pmAddDerived(ctx->%d): %s: bind failed: %s\n", ctxp->c_handle, name, PM_TPD(derive_errmsg));
	}
	free_expr(cp->mlist[cp->nmetric-1].expr);
	cp->mlist[cp->nmetric-1].expr = NULL;
	PM_UNLOCK(ctxp->c_lock);
	return SEMANTIC_ERROR;
    }

    if (pmDebugOptions.derive) {
	fprintf(stderr, "pmAddDerived(ctx->%d): per-context metric[%d] %s = %s\n", ctxp->c_handle, cp->nmetric-1, name, expr);
	if (pmDebugOptions.appl0)
	    __dmdumpexpr(parse_tree, 0);
    }

    PM_UNLOCK(ctxp->c_lock);
    return NO_ERROR;
}

/* Global derived metrics - the original, and still the best. */
char *
pmAddDerived(const char *name, const char *expr)
{
    char	*p;
    int		sts;
    char	*c_sts;

    if (pmDebugOptions.pmapi)
	fprintf(stderr, "pmAddDerived(%s, %s) <:", name, expr);

    sts = addderived(name, expr, &p);
    if (sts == NO_ERROR)
	c_sts = NULL;
    else
	c_sts = p;

    if (pmDebugOptions.pmapi) {
	fprintf(stderr, ":> returns ");
	if (c_sts == NULL)
	    fprintf(stderr, "NULL\n");
	else {
	    fprintf(stderr, "%s\n", c_sts);
	}
    }

    return c_sts;
}

/* Global derived metrics - variant including error handling. */
int
pmAddDerivedMetric(const char *name, const char *expr, char **errmsg)
{
    size_t	length;
    char	*offset;
    char	*error;
    char	*dmsg;
    int		sts;

    static const char	fmt[] = \
	"%s Error: pmAddDerivedMetric(\"%s\", ...)\n%s\n%*s^\n";
    static const char	fmt_nopos[] = \
	"%s Error: pmAddDerivedMetric(\"%s\", ...)\n%s\n^\n";
    static const char	fmt_pmapi[] = \
	"Error: pmAddDerivedMetric(\"%s\", ...): returns %d\n";

    if (pmDebugOptions.pmapi)
	fprintf(stderr, "pmAddDerivedMetric(%s, %s, ...) <:", name, expr);

    *errmsg = NULL;
    if ((sts = addderived(name, expr, &offset)) == NO_ERROR) {
	sts = 0;
	goto pmapi_return;
    }

    /*
     * failed to add name/expr - build an error string to pass back
     * ... length is maximum
     */
    if (sts < 0) {
	/* PMAPI error */
	length = strlen(fmt_pmapi) + strlen(name);
	length += 6;	/* decimal PMAPI error code */
    }
    else {
	/* Syntax or semantic error */
	length = strlen(fmt) + strlen("Semantic") + strlen(name) + strlen(expr);
	length += (offset - expr);
    }
    if ((dmsg = PM_TPD(derive_errmsg)) != NULL)
	length += strlen(dmsg) + 2;

    if ((error = malloc(length)) == NULL)
	pmNoMem("pmAddDerivedMetric", length, PM_FATAL_ERR);
    if (sts < 0)
	pmsprintf(error, length, fmt_pmapi, name, sts);
    else {
	if (offset == expr)
	    pmsprintf(error, length, fmt_nopos, sts == SYNTAX_ERROR ? "Syntax" : "Semantic", name, expr);
	else
	    pmsprintf(error, length, fmt, sts == SYNTAX_ERROR ? "Syntax" : "Semantic", name, expr, (int)(expr - offset), " ");
	sts = PM_ERR_BADDERIVE;
    }
    if (dmsg != NULL) {
	pmstrncat(error, length, dmsg);
	pmstrncat(error, length, "\n");
    }

    *errmsg = error;

pmapi_return:

    if (pmDebugOptions.pmapi) {
	fprintf(stderr, ":> returns ");
	if (sts >= 0)
	    fprintf(stderr, "%d\n", sts);
	else {
	    char	l_errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "%s\n", pmErrStr_r(sts, l_errmsg, sizeof(l_errmsg)));
	}
    }

    return sts;
}

int
pmAddDerivedText(const char *name, int type, const char *text)
{
    int		i;
    int		sts = 0;
    dm_t	*dmp = NULL;
    int		ctx;
    __pmContext	*ctxp = NULL;

    if (pmDebugOptions.pmapi)
	fprintf(stderr, "pmAddDerivedText(%s, %d, %s) <:", name, type, text);

    if ((ctx = pmWhichContext()) >= 0) {
	ctxp = __pmHandleToPtr(ctx);
	/* ensures we have context lock if there is a valid context */
    }

    PM_LOCK(registered.mutex);
    __dminit(ctxp);

    for (i = 0; i < registered.nmetric; i++) {
	if (!(registered.mlist[i].flags & DM_FREE) &&
	    strcmp(name, registered.mlist[i].name) == 0) {
	    /* found it! */
	    break;
	}
    }

    if (i < registered.nmetric) {
	/* matched global derived metric name, all good */
	dmp = &registered.mlist[i];
    }
    else {
	/*
	 * oops, global derived metric name not defined ...
	 * try for per-context derived metrics ...
	 */
	if (ctxp != NULL) {
	    ctl_t		*cp;
	    cp = (ctl_t *)ctxp->c_dm;
	    for (i = 0; i < cp->nmetric; i++) {
		if (!(cp->mlist[i].flags & DM_FREE) &&
		    strcmp(name, cp->mlist[i].name) == 0) {
		    /* found it! */
		    break;
		}
	    }
	    if (i < cp->nmetric) {
		/* matched per-context derived metric name, all good */
		dmp = &cp->mlist[i];
	    }
	}
    }

    if (dmp == NULL) {
	sts = PM_ERR_NAME;
	goto done;
    }

    if (type == PM_TEXT_ONELINE) {
	if (dmp->oneline != NULL) {
	    sts = PM_ERR_TEXT;
	    goto done;
	}
	if ((dmp->oneline = strdup(text)) == NULL) {
	    pmNoMem("pmAddDerivedText: oneline", strlen(text), PM_RECOV_ERR);
	    sts = -oserror();
	    goto done;
	}
    }
    else if (type == PM_TEXT_HELP) {
	if (dmp->helptext != NULL) {
	    sts = PM_ERR_TEXT;
	    goto done;
	}
	if ((dmp->helptext = strdup(text)) == NULL) {
	    pmNoMem("pmAddDerivedText: helptext", strlen(text), PM_RECOV_ERR);
	    sts = -oserror();
	    goto done;
	}
    }
    else
	sts = PM_ERR_ARG;

done:
    PM_UNLOCK(registered.mutex);
    if (ctx >= 0)
	PM_UNLOCK(ctxp->c_lock);

    if (pmDebugOptions.pmapi) {
	fprintf(stderr, ":> returns ");
	if (sts >= 0)
	    fprintf(stderr, "%d\n", sts);
	else {
	    char	errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "%s\n", pmErrStr_r(sts, errmsg, sizeof(errmsg)));
	}
    }

    return sts;
}

int
pmGetDerivedControl(int what, int *valuep)
{
    int		sts;

    if (pmDebugOptions.pmapi)
	fprintf(stderr, "pmGetDerivedControl(what=%d, ...) <:", what);
	
    PM_LOCK(registered.mutex);
    __dminit(NULL);
    sts = getderivedcontrol(NULL, what, valuep);
    PM_UNLOCK(registered.mutex);

    if (pmDebugOptions.pmapi) {
	fprintf(stderr, ":> returns ");
	if (sts >= 0)
	    fprintf(stderr, "%d\n", sts);
	else {
	    char	errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "%s\n", pmErrStr_r(sts, errmsg, sizeof(errmsg)));
	}
    }

    return sts;
}

int
pmSetDerivedControl(int what, int value)
{
    int		sts = 0;

    if (pmDebugOptions.pmapi)
	fprintf(stderr, "pmSetDerivedControl(what=%d, value=%d) <:", what, value);

    PM_LOCK(registered.mutex);
    __dminit(NULL);
    sts = setderivedcontrol(NULL, OPT_SET_APP, what, value);
    PM_UNLOCK(registered.mutex);

    if (pmDebugOptions.pmapi) {
	fprintf(stderr, ":> returns ");
	if (sts >= 0)
	    fprintf(stderr, "%d\n", sts);
	else {
	    char	errmsg[PM_MAXERRMSGLEN];
	    fprintf(stderr, "%s\n", pmErrStr_r(sts, errmsg, sizeof(errmsg)));
	}
    }

    return sts;
}

/*
 * callbacks
 */

static ctl_t *
getctl(__pmContext *ctxp)
{
    ctl_t *cp;


    if (__pmGetInternalState() == PM_STATE_PMCS) {
	/* no derived metrics below PMCS, not even anon */
    	cp = NULL;
    }
    else if (ctxp == NULL) {
	/*
	 * No context, but we still need to traverse globally registered anon
	 * derived metrics using local pmns (but *only* anon, e.g. event.*).
	 */
	PM_ASSERT_IS_LOCKED(registered.mutex);
    	cp = &registered;
    }
    else {
        /*
         * Else use the per-context control structure. Invalid derived metrics,
         * e.g. with missing operands, have cp->mlist[i].expr == NULL, which we
         * can check to effectively exclude them from the pmns for this context.
         * Note that anon derived metrics are assumed to always be valid, so we
         * can use the per-context control structure *or* the registered global
         * control structure (as above) for anon derived metrics.
         */
	PM_ASSERT_IS_LOCKED(ctxp->c_lock);
	cp = (ctl_t *)ctxp->c_dm;
    }

    return cp;
}

/*
 * update the context's version of the available derived metrics
 * - only ever called with the context locked and registered.mutex
 * locked
 */
static void
refresh(__pmContext *ctxp)
{
    ctl_t       *cp;
    int		i;
    int		j;
    dm_t	*tmp_mlist;
    int		need;

    if (ctxp == NULL) {
	/* no current context, nothing to refresh yet */
	return;
    }
    cp = (ctl_t *)ctxp->c_dm;

    need = cp->nmetric + registered.nmetric - cp->glob_last;

    if ((tmp_mlist = (dm_t *)realloc(cp->mlist, need*sizeof(dm_t))) == NULL) {
	/*
	 * tough luck, recently added derived metrics will not be
	 * available in this context
	 */
	pmNoMem("refresh: derived metrics (mlist)", need*sizeof(dm_t), PM_RECOV_ERR);
	return;
    }
    cp->mlist = tmp_mlist;
    for (i = cp->nmetric, j = cp->glob_last; j < registered.nmetric; j++) {
	if (registered.mlist[j].flags & DM_FREE) {
	    /* this has been unregistered, so skip it */
	    continue;
	}
	cp->mlist[i].name = registered.mlist[j].name;
	cp->mlist[i].pmid = registered.mlist[j].pmid;
	cp->mlist[i].anon = registered.mlist[j].anon;
	assert(registered.mlist[j].expr != NULL);
	cp->mlist[i].expr = registered.mlist[i].expr;
	cp->mlist[i].flags = DM_GLOBAL;
	cp->mlist[i].oneline = registered.mlist[j].oneline;
	cp->mlist[i].helptext = registered.mlist[j].helptext;
	if (pmDebugOptions.derive && pmDebugOptions.appl1) {
	    fprintf(stderr, "refresh: append metric \"%s\" for ctx %d\n",
	    	cp->mlist[i].name, ctxp->c_handle);
	}
	i++;
	cp->nmetric++;
    }
    cp->glob_last = registered.nmetric;
}

int
__dmtraverse(__pmContext *ctxp, const char *name, char ***namelist)
{
    int		sts = 0;
    int		i;
    char	**list = NULL;
    int		matchlen = strlen(name);
    ctl_t       *cp;

    if (ctxp != NULL)
	PM_LOCK(ctxp->c_lock);

    if (pmDebugOptions.derive) {
	if (ctxp != NULL)
	    fprintf(stderr, "__dmtraverse(ctx=%d, name=\"%s\", ...)\n", ctxp->c_handle, name);
	else
	    fprintf(stderr, "__dmtraverse(ctx=NULL, name=\"%s\", ...)\n", name);
    }

    PM_LOCK(registered.mutex);
    __dminit(ctxp);

    if ((cp = getctl(ctxp)) == NULL)
	/* PMNS operations are safe, even below PMCS */
    	cp = &registered;

    if (cp != &registered && cp->glob_last < registered.nmetric)
	refresh(ctxp);

    for (i = 0; i < cp->nmetric; i++) {
	/*
	 * skip global derived metrics masked by per-context derived
	 * metrics with the same name and "free" entries
	 */
	if ((cp->mlist[i].flags & DM_MASKED) || (cp->mlist[i].flags & DM_FREE))
	    continue;

	/*
	 * prefix match ... if name is "", then all names match
	 */
	if (matchlen == 0 ||
	    (strncmp(name, cp->mlist[i].name, matchlen) == 0 &&
	     (cp->mlist[i].name[matchlen] == '.' ||
	      cp->mlist[i].name[matchlen] == '\0'))) {
	    /*
	     * we have a match ... try binding the first time we see
	     * this one in the current context ... if this fails we
	     * will skip the metric
	     */
	    if ((cp->mlist[i].flags & DM_BIND) == 0) {
		__dmbind(PM_LOCKED, ctxp, i, 1);
	    }
	    /* skip invalid derived metrics, e.g. due to missing operands */
	    if (!cp->mlist[i].anon) {
		if ((cp->mlist[i].flags & DM_BIND) && cp->mlist[i].expr == NULL) {
		    if (pmDebugOptions.derive) {
			char	strbuf[20];
			fprintf(stderr, "__dmtraverse: name=\"%s\", omitting %s child %s [%s]\n",
			    name,
			    cp->mlist[i].flags & DM_MASKED ? "masked" : "invalid",
			    cp->mlist[i].name,
			    pmIDStr_r(cp->mlist[i].pmid, strbuf, sizeof(strbuf)));
		    }
		    continue;
		}
	    }
	    sts++;
	    if ((list = (char **)realloc(list, sts*sizeof(list[0]))) == NULL) {
		PM_UNLOCK(registered.mutex);
		pmNoMem("__dmtraverse: list", sts*sizeof(list[0]), PM_FATAL_ERR);
		/*NOTREACHED*/
	    }
	    list[sts-1] = cp->mlist[i].name;
	    if (pmDebugOptions.derive)
	    	fprintf(stderr, "__dmtraverse: name=\"%s\" added \"%s\"\n", name, list[sts-1]);
	}
    }
    *namelist = list;

    PM_UNLOCK(registered.mutex);
    if (ctxp != NULL)
	PM_UNLOCK(ctxp->c_lock);
    return sts;
}

int
__dmchildren(__pmContext *ctxp, int derive_locked, const char *name, char ***offspring, int **statuslist)
{
    int		i;
    int		j;
    char	**children = NULL;
    int		*status = NULL;
    char	**n_children;
    char	*q;
    int		matchlen = strlen(name);
    int		start;
    int		len;
    int		num_chn = 0;
    size_t	need = 0;
    ctl_t       *cp;

    if (pmDebugOptions.derive) {
	if (ctxp != NULL)
	    fprintf(stderr, "__dmchildren(ctx=%d", ctxp->c_handle);
	else
	    fprintf(stderr, "__dmchildren(ctx=NULL");
	fprintf(stderr, ", derive_locked=%d, name=\"%s\", ...)\n", derive_locked, name);
    }

    if (derive_locked == PM_NOT_LOCKED) {
	PM_LOCK(registered.mutex);
	__dminit(ctxp);
    }
    else {
	PM_ASSERT_IS_LOCKED(registered.mutex);
    }

    if ((cp = getctl(ctxp)) == NULL)
	/* PMNS operations are safe, even below PMCS */
    	cp = &registered;

    if (cp != &registered && cp->glob_last < registered.nmetric)
	refresh(ctxp);

    for (i = 0; i < cp->nmetric; i++) {
	/*
	 * skip global derived metrics masked by per-context derived
	 * metrics with the same name, and "free" entries
	 */
	if ((cp->mlist[i].flags & DM_MASKED) || (cp->mlist[i].flags & DM_FREE))
	    continue;

	/*
	 * prefix match ... pick off the unique next level names on match
	 */
	if (name[0] == '\0' ||
	    (strncmp(name, cp->mlist[i].name, matchlen) == 0 &&
	     (cp->mlist[i].name[matchlen] == '.' ||
	      cp->mlist[i].name[matchlen] == '\0'))) {
	    /*
	     * we have a match ... try binding the first time we see
	     * this one in the current context (if we have one) ... if
	     * binding this fails we will skip the metric
	     */
	    if ((cp->mlist[i].flags & DM_BIND) == 0) {
		__pmContext	*l_ctxp = ctxp;

		if (ctxp == NULL) {
		    int		ctx = pmWhichContext();
		    if (ctx >= 0)
			l_ctxp = __pmHandleToPtr(ctx);
		}
		if (l_ctxp != NULL) {
		    __dmbind(PM_LOCKED, l_ctxp, i, 1);
		    if (ctxp == NULL && l_ctxp != ctxp)
			/* context locked in __pmHandleToPtr() above) */
			PM_UNLOCK(ctxp->c_lock);
		}
	    }
	    /* skip invalid derived metrics, e.g. due to missing operands */
	    if (!cp->mlist[i].anon) {
		if ((cp->mlist[i].flags & DM_BIND) && cp->mlist[i].expr == NULL) {
		    if (pmDebugOptions.derive) {
			fprintf(stderr, "__dmchildren: name=\"%s\", omitting invalid child \"%s\"\n",
			    name, cp->mlist[i].name);
		    }
		    continue;
		}
	    }
	    if (cp->mlist[i].name[matchlen] == '\0') {
		/*
		 * leaf node
		 * assert is for coverity, name uniqueness means we
		 * should only ever come here after zero passes through
		 * the block below where num_chn is incremented and children[]
		 * and status[] are realloc'd
		 */
		assert(num_chn == 0 && children == NULL && status == NULL);
		if (derive_locked == PM_NOT_LOCKED)
		    PM_UNLOCK(registered.mutex);
		return 0;
	    }
	    start = matchlen > 0 ? matchlen + 1 : 0;
	    for (j = 0; j < num_chn; j++) {
		len = strlen(children[j]);
		if (strncmp(&cp->mlist[i].name[start], children[j], len) == 0 &&
		    cp->mlist[i].name[start+len] == '.')
		    break;
	    }
	    if (j == num_chn) {
		/* first time for this one */
		num_chn++;
		if ((children = (char **)realloc(children, num_chn*sizeof(children[0]))) == NULL) {
		    if (derive_locked == PM_NOT_LOCKED)
			PM_UNLOCK(registered.mutex);
		    pmNoMem("__dmchildren: children", num_chn*sizeof(children[0]), PM_FATAL_ERR);
		    /*NOTREACHED*/
		}
		for (len = 0; cp->mlist[i].name[start+len] != '\0' && cp->mlist[i].name[start+len] != '.'; len++)
		    ;
		if ((children[num_chn-1] = (char *)malloc(len+1)) == NULL) {
		    if (derive_locked == PM_NOT_LOCKED)
			PM_UNLOCK(registered.mutex);
		    pmNoMem("__dmchildren: name", len+1, PM_FATAL_ERR);
		    /*NOTREACHED*/
		}
		pmstrncpy(children[num_chn-1], len+1, &cp->mlist[i].name[start]);
		need += len+1;
		if (pmDebugOptions.derive && pmDebugOptions.appl2) {
		    fprintf(stderr, "__dmchildren: offspring[%d] %s", num_chn-1, children[num_chn-1]);
		}

		if (statuslist != NULL) {
		    if ((status = (int *)realloc(status, num_chn*sizeof(status[0]))) == NULL) {
			if (derive_locked == PM_NOT_LOCKED)
			    PM_UNLOCK(registered.mutex);
			pmNoMem("__dmchildren: status", num_chn*sizeof(status[0]), PM_FATAL_ERR);
			/*NOTREACHED*/
		    }
		    status[num_chn-1] = cp->mlist[i].name[start+len] == '\0' ? PMNS_LEAF_STATUS : PMNS_NONLEAF_STATUS;
		    if (pmDebugOptions.derive && pmDebugOptions.appl2) {
			fprintf(stderr, " (status=%d)", status[num_chn-1]);
		    }
		}
		if (pmDebugOptions.derive && pmDebugOptions.appl2) {
		    fputc('\n', stderr);
		}
	    }
	}
    }

    if (num_chn == 0) {
	if (derive_locked == PM_NOT_LOCKED)
	    PM_UNLOCK(registered.mutex);
	return PM_ERR_NAME;
    }

    /*
     * children[] is complete, but to ensure correct free()ing of
     * allocated space, we need to restructure this so that
     * n_children[] and all the names are allocated in a single
     * block, as per the pmGetChildren() semantics ... even though
     * n_children[] is never handed back to the caller, the stitch
     * and cleanup logic in pmGetChildrenStatus() assumes that
     * free(n_children) is all that is needed.
     */
    need += num_chn * sizeof(char *);
    if ((n_children = (char **)malloc(need)) == NULL) {
	pmNoMem("__dmchildren: n_children", need, PM_FATAL_ERR);
	/*NOTREACHED*/
    }
    q = (char *)&n_children[num_chn];
    for (j = 0; j < num_chn; j++) {
	n_children[j] = q;
	strcpy(q, children[j]);
	q += strlen(children[j]) + 1;
	free(children[j]);
    }
    free(children);

    *offspring = n_children;
    if (statuslist != NULL)
	*statuslist = status;

    if (derive_locked == PM_NOT_LOCKED)
	PM_UNLOCK(registered.mutex);
    return num_chn;
}

int
__dmgetpmid(__pmContext *ctxp, int derive_locked, const char *name, pmID *dp)
{
    int		i;
    ctl_t	*cp;

    if (derive_locked == PM_NOT_LOCKED) {
	PM_LOCK(registered.mutex);
	__dminit(ctxp);
    }
    else {
	PM_ASSERT_IS_LOCKED(registered.mutex);
    }

    if ((cp = getctl(ctxp)) == NULL)
	/* PMNS operations are safe, even below PMCS */
    	cp = &registered;

    if (cp == &registered) {
	if (pmDebugOptions.derive && pmDebugOptions.appl2)
	    fprintf(stderr, "__dmgetpmid: using registered for metric \"%s\"\n", name);
    }
    else if (cp->glob_last < registered.nmetric)
	refresh(ctxp);

    for (i = 0; i < cp->nmetric; i++) {
	/*
	 * skip global derived metrics masked by per-context derived
	 * metrics with the same name and "free" entries
	 */
	if ((cp->mlist[i].flags & DM_MASKED) || (cp->mlist[i].flags & DM_FREE))
	    continue;
	if (strcmp(name, cp->mlist[i].name) == 0) {
	    *dp = cp->mlist[i].pmid;
	    if (derive_locked == PM_NOT_LOCKED)
		PM_UNLOCK(registered.mutex);
	    return 0;
	}
    }
    if (derive_locked == PM_NOT_LOCKED)
	PM_UNLOCK(registered.mutex);

    return PM_ERR_NAME;
}

int
__dmgetname(__pmContext *ctxp, pmID pmid, char ** name)
{
    int		i;
    ctl_t	*cp;

    PM_LOCK(registered.mutex);
    __dminit(ctxp);

    if ((cp = getctl(ctxp)) == NULL)
	/* PMNS operations are safe, even below PMCS */
    	cp = &registered;

    if (cp == &registered) {
	if (pmDebugOptions.derive && pmDebugOptions.appl2) {
	    char	strbuf[20];
	    fprintf(stderr, "__dmgetname: using registered for PMID %s\n", pmIDStr_r(pmid, strbuf, sizeof(strbuf)));
	}
    }
    else if (cp->glob_last < registered.nmetric)
	refresh(ctxp);

    for (i = 0; i < cp->nmetric; i++) {
	/*
	 * skip global derived metrics masked by per-context derived
	 * metrics with the same name and "free" entries
	 */
	if ((cp->mlist[i].flags & DM_MASKED) || (cp->mlist[i].flags & DM_FREE))
	    continue;
	if (pmid == cp->mlist[i].pmid) {
	    *name = strdup(cp->mlist[i].name);
	    if (*name == NULL) {
		PM_UNLOCK(registered.mutex);
		return -oserror();
	    }
	    else {
		PM_UNLOCK(registered.mutex);
		return 0;
	    }
	}
    }
    PM_UNLOCK(registered.mutex);
    return PM_ERR_PMID;
}

/*
 * Look for name masking ... this looks a lot like __dmgetpmid() only
 * we keep iterating and don't skip masked global drived metrics.
 *
 * Only called if -Dderive, so can skip that guard
 */
void
__dmcheckname(__pmContext *ctxp, int derive_locked, const char *name, pmID pmid)
{
    int		i;
    ctl_t	*cp;
    char	strbuf[20];

    if (derive_locked == PM_NOT_LOCKED) {
	PM_LOCK(registered.mutex);
	__dminit(ctxp);
    }
    else {
	PM_ASSERT_IS_LOCKED(registered.mutex);
    }

    if ((cp = getctl(ctxp)) == NULL)
	/* PMNS operations are safe, even below PMCS */
    	cp = &registered;

    if (cp == &registered) {
	if (pmDebugOptions.derive && pmDebugOptions.appl2)
	    fprintf(stderr, "__dmcheckname: using registered for metric \"%s\"\n", name);
    }
    else if (cp->glob_last < registered.nmetric)
	refresh(ctxp);

    for (i = 0; i < cp->nmetric; i++) {
	if (!(cp->mlist[i].flags & DM_FREE) &&
	    strcmp(name, cp->mlist[i].name) == 0) {
	    if (cp->mlist[i].pmid == pmid)
		continue;
	    fprintf(stderr, "Warning: %s [%s",
		name, pmIDStr_r(pmid, strbuf, sizeof(strbuf)));
	    if (IS_DERIVED(pmid)) {
		fprintf(stderr, " derived");
	    }
	    else {
		switch (ctxp->c_type) {
		    case PM_CONTEXT_HOST:
			fprintf(stderr, " host");
			break;
		    case PM_CONTEXT_ARCHIVE:
			fprintf(stderr, " archive");
			break;
		    case PM_CONTEXT_LOCAL:
			fprintf(stderr, " local");
			break;
		    default:
			fprintf(stderr, " unknown");
			break;
		}
		fprintf(stderr, " context");
	    }
	    if (IS_DERIVED_LOGGED(pmid))
		fprintf(stderr, ", remapped");
	    fprintf(stderr, "] masks %s derived metric [%s] with the same name\n",
		    cp->mlist[i].flags & DM_GLOBAL ? "global" : "per-context",
		    pmIDStr_r(cp->mlist[i].pmid, strbuf, sizeof(strbuf)));
	}
    }
    if (derive_locked == PM_NOT_LOCKED)
	PM_UNLOCK(registered.mutex);
}

/*
 * bind the ith derived metric expression in the current context ...
 * sets cp->mlist[i].expr as return value (NULL for error)
 *
 * async == 1 => bind is asynchronous and error reporting via pmprintf()
 *               is needed
 *       == 0 => bind is part of pmAddDerived*() and error code can be
 *               returned directly to the caller
 */
void
__dmbind(int derive_locked, __pmContext *ctxp, int i, int async)
{
    int		sts = 0;
    pmID	pmid;
    ctl_t	*cp;

    PM_ASSERT_IS_LOCKED(ctxp->c_lock);

    if (derive_locked == PM_NOT_LOCKED) {
	PM_INIT_LOCKS();
	PM_LOCK(registered.mutex);
    }
    else {
	PM_ASSERT_IS_LOCKED(registered.mutex);
    }

    cp = (ctl_t *)ctxp->c_dm;

    if (pmDebugOptions.derive) {
	fprintf(stderr, "__dmbind(derive_locked=%d, ctx=%d, metric[%d] %s, async= %d)\n", derive_locked, ctxp->c_handle, i, cp->mlist[i].name, async);
    }

    if (!cp->mlist[i].anon) {
	/*
	 * Assume anonymous derived metric names are unique, but otherwise
	 * derived metric names must not clash with real metric names ...
	 * and if this happens, the real metric wins!
	 * Logic here depends on pmLookupName() returning before any
	 * derived metric searching is performed if the name is valid
	 * for a real metric in the current context.
	 */
	sts = pmLookupName_ctx(ctxp, PM_LOCKED, 1, (const char **)&cp->mlist[i].name, &pmid);
	if (sts >= 0 && !IS_DERIVED(pmid)) {
	    /* use metric of same name from context, not the derived one */
	    cp->mlist[i].expr = NULL;
	    goto done;
	}
    }
    if (pmDebugOptions.derive && pmDebugOptions.appl1 && pmDebugOptions.desperate) {
	fprintf(stderr, "__dmbind: metric[%d] %s: before bind_expr ...\n", i, cp->mlist[i].name);
	__dmdumpexpr(cp->mlist[i].expr, 0);
    }
    /* failures must be reported in bind_expr() or below */
    cp->mlist[i].expr = bind_expr(ctxp, i, cp->mlist[i].expr, NULL, QUEST_BIND_NOW, cp->mlist[i].flags & DM_GLOBAL, async);
    if (cp->mlist[i].expr != NULL) {
	if (pmDebugOptions.derive && pmDebugOptions.appl1 && pmDebugOptions.desperate) {
	    fprintf(stderr, "__dmbind: metric[%d] %s: after bind_expr and before check_expr ...\n", i, cp->mlist[i].name);
	    __dmdumpexpr(cp->mlist[i].expr, 0);
	}
	/* failures must be reported in check_expr() or below */
	sts = check_expr(&cp->mlist[i], cp->mlist[i].expr, NULL, async);
	if (sts >= 0) {
	    /* set correct PMID in pmDesc at the top level */
	    cp->mlist[i].expr->desc.pmid = cp->mlist[i].pmid;
	}
    }
    if (pmDebugOptions.derive && (cp->mlist[i].expr == NULL || sts < 0)) {
	if (cp->mlist[i].expr == NULL)
	    fprintf(stderr, "__dmbind: bind_expr failed for metric[%d] %s\n", i, cp->mlist[i].name);
	else {
	    fprintf(stderr, "__dmbind: check_expr failed for metric[%d] %s\n", i, cp->mlist[i].name);
	    if (pmDebugOptions.appl1)
		__dmdumpexpr(cp->mlist[i].expr, 0);
	}
    }
    else if (pmDebugOptions.derive && pmDebugOptions.appl1 && pmDebugOptions.desperate) {
	fprintf(stderr, "__dmbind: metric[%d] %s: after check_expr ...\n", i, cp->mlist[i].name);
	__dmdumpexpr(cp->mlist[i].expr, 0);
    }
    if (sts < 0 && cp->mlist[i].expr != NULL) {
	free_expr(cp->mlist[i].expr);
	cp->mlist[i].expr = NULL;
    }

done:
    cp->mlist[i].flags |= DM_BIND;
    if (derive_locked == PM_NOT_LOCKED)
	PM_UNLOCK(registered.mutex);
    return;
}

/*
 * called when new context established to clone the derived
 * metrics control structures
 */
void
__dmopencontext(__pmContext *ctxp)
{
    int		i;
    int		j;
    ctl_t	*cp;

    PM_LOCK(registered.mutex);
    __dminit(ctxp);

    if (pmDebugOptions.derive && pmDebugOptions.appl1) {
	fprintf(stderr, "__dmopencontext(->ctx %d) called, %d derived metrics\n", ctxp->c_handle, registered.nmetric);
    }
    if (registered.nmetric == 0) {
	ctxp->c_dm = NULL;
	PM_UNLOCK(registered.mutex);
	return;
    }
    if ((cp = (void *)malloc(sizeof(ctl_t))) == NULL) {
	PM_UNLOCK(registered.mutex);
	pmNoMem("pmNewContext: derived metrics (ctl)", sizeof(ctl_t), PM_FATAL_ERR);
	/* NOTREACHED */
    }
    ctxp->c_dm = (void *)cp;
    cp->glob_last = cp->nmetric = registered.nmetric;
    cp->limit = registered.limit;
    if ((cp->mlist = (dm_t *)calloc(cp->nmetric, sizeof(dm_t))) == NULL) {
	PM_UNLOCK(registered.mutex);
	pmNoMem("pmNewContext: derived metrics (mlist)", cp->nmetric*sizeof(dm_t), PM_FATAL_ERR);
	/* NOTREACHED */
    }
    for (j = i = 0; i < cp->nmetric; i++) {
	if (registered.mlist[i].flags & DM_FREE) {
	    /* this has been unregistered, so skip it */
	    continue;
	}
	cp->mlist[j].name = registered.mlist[i].name;
	cp->mlist[j].pmid = registered.mlist[i].pmid;
	cp->mlist[j].anon = registered.mlist[i].anon;
	assert(registered.mlist[i].expr != NULL);
	cp->mlist[j].expr = registered.mlist[i].expr;
	cp->mlist[j].flags = registered.mlist[i].flags;
	cp->mlist[j].flags &= ~DM_BIND;
	cp->mlist[j].oneline = registered.mlist[i].oneline;
	cp->mlist[j].helptext = registered.mlist[i].helptext;
	j++;
    }
    cp->nmetric = j;
    PM_UNLOCK(registered.mutex);
}

void
__dmclosecontext(__pmContext *ctxp)
{
    int		i;
    ctl_t	*cp = (ctl_t *)ctxp->c_dm;

    /* if needed, __dminit() called in __dmopencontext beforehand */

    if (pmDebugOptions.derive) {
	fprintf(stderr, "__dmclosecontext(->ctx %d) called dm->" PRINTF_P_PFX "%p %d metrics\n", ctxp->c_handle, cp, cp == NULL ? -1 : cp->nmetric);
    }
    if (cp == NULL) return;
    for (i = 0; i < cp->nmetric; i++) {
	if (cp->mlist[i].expr != NULL) {
	    if (cp->mlist[i].flags & DM_GLOBAL) {
		/* only free expr tree for global derived metrics if
		 * __dmbind() has been called, otherwise expr -> registered[]
		 * and we don't want to free that!
		 */
		if (cp->mlist[i].flags & DM_BIND)
		    free_expr(cp->mlist[i].expr); 
	    }
	    else {
		/* for per-context derived metrics, always safe to free */
		free_expr_ctx(cp->mlist[i].expr); 
	    }
	}
	if ((cp->mlist[i].flags & DM_GLOBAL) == 0) {
	    free(cp->mlist[i].name);
	}
    }
    free(cp->mlist);
    free(cp);
    ctxp->c_dm = NULL;
}

/* for dbpmda */
void
__pmFreeDerived(__pmContext *ctxp)
{
    __dmclosecontext(ctxp);
}

int
__dmdesc(__pmContext *ctxp, int derive_locked, pmID pmid, pmDesc *desc)
{
    int		i;
    ctl_t	*cp = (ctl_t *)ctxp->c_dm;

    /*
     * if needed, __dminit() has been called in __dmopencontext beforehand
     * which means cp != NULL
     */
    if (cp == NULL) return PM_ERR_PMID;

    if (derive_locked == PM_NOT_LOCKED)
	PM_LOCK(registered.mutex);
    if (cp->glob_last < registered.nmetric)
	refresh(ctxp);
    if (derive_locked == PM_NOT_LOCKED)
	PM_UNLOCK(registered.mutex);

    for (i = 0; i < cp->nmetric; i++) {
	/*
	 * skip global derived metrics masked by per-context derived
	 * metrics with the same name and "free" entries
	 */
	if ((cp->mlist[i].flags & DM_MASKED) || (cp->mlist[i].flags & DM_FREE))
	    continue;
	if (cp->mlist[i].pmid == pmid) {
	    if ((cp->mlist[i].flags & DM_BIND) == 0) {
		/* ctxp->c_lock already locked at this point */
		__dmbind(derive_locked, ctxp, i, 1);
	    }
	    if (cp->mlist[i].expr == NULL)
		/* bind failed for some reason, reported earlier */
		return PM_ERR_BADDERIVE;
	    *desc = cp->mlist[i].expr->desc;
	    return 0;
	}
    }

    return PM_ERR_PMID;
}

int
__dmhelptext(pmID pmid, int level, char **buffer)
{
    int		i;
    int		sts = 0;
    const char	*text;
    dm_t	*dmp = NULL;
    int		ctx;
    __pmContext	*ctxp = NULL;

    if ((ctx = pmWhichContext()) >= 0) {
	ctxp = __pmHandleToPtr(ctx);
	/* ensures we have context lock if there is a valid context */
    }

    PM_LOCK(registered.mutex);
    __dminit(ctxp);

    for (i = 0; i < registered.nmetric; i++) {
	if (pmid == registered.mlist[i].pmid) {
	    /* matched global derived metric ... */
	    dmp = &registered.mlist[i];
	    break;
	}
    }
    
    if (i == registered.nmetric) {
	if (ctxp != NULL) {
	    /* try per-context derived metrics ... */
	    ctl_t		*cp;
	    cp = (ctl_t *)ctxp->c_dm;
	    for (i = 0; i < cp->nmetric; i++) {
		if (pmid == cp->mlist[i].pmid) {
		    /* found it! */
		    dmp = &cp->mlist[i];
		    break;
		}
	    }
	}
    }

    if (dmp == NULL) {
	sts = PM_ERR_PMID;
	goto done;
    }

    text = NULL;
    if (level & PM_TEXT_ONELINE) {
	if ((text = dmp->oneline) == NULL)
	    sts = PM_ERR_TEXT;
    } else if (level & PM_TEXT_HELP) {
	text = dmp->helptext;
	if (text == NULL && !(level & PM_TEXT_DIRECT))
	    text = dmp->oneline;
	if (text == NULL)
	    sts = PM_ERR_TEXT;
    }
    if (text && (*buffer = strdup(text)) == NULL) {
	pmNoMem("__dmhelptext", strlen(text) + 1, PM_RECOV_ERR);
	sts = -oserror();
    }

done:
    PM_UNLOCK(registered.mutex);
    if (ctx >= 0)
	PM_UNLOCK(ctxp->c_lock);

    return sts;
}

#if defined(PM_MULTI_THREAD) && defined(PM_MULTI_THREAD_DEBUG)
/*
 * return true if lock == registered.mutex ... no locking here to avoid
 * recursion ad nauseum
 */
int
__pmIsDeriveLock(void *lock)
{
    return lock == (void *)&registered.mutex;
}

int
__pmIsUnregisterLock(void *lock)
{
    return lock == (void *)&unregister_lock;
}
#endif

/* report grammatical error */
static void
gramerr(const char *phrase, const char *pos, char *arg)
{
    static char errmsg[256];
    /* unless lexer has already found something amiss ... */
    if (PM_TPD(derive_errmsg) == NULL) {
	if (pos == NULL)
	    pmsprintf(errmsg, sizeof(errmsg), "%s '%s'", phrase, arg);
	else
	    pmsprintf(errmsg, sizeof(errmsg), "%s expected to %s %s", phrase, pos, arg);
	PM_TPD(derive_errmsg) = errmsg;
    }
}

static node_t *np;


#line 4671 "derive_parser.tab.c"

# ifndef YY_CAST
#  ifdef __cplusplus
#   define YY_CAST(Type, Val) static_cast<Type> (Val)
#   define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val)
#  else
#   define YY_CAST(Type, Val) ((Type) (Val))
#   define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val))
#  endif
# endif
# ifndef YY_NULLPTR
#  if defined __cplusplus
#   if 201103L <= __cplusplus
#    define YY_NULLPTR nullptr
#   else
#    define YY_NULLPTR 0
#   endif
#  else
#   define YY_NULLPTR ((void*)0)
#  endif
# endif

#include "derive_parser.tab.h"
/* Symbol kind.  */
enum yysymbol_kind_t
{
  YYSYMBOL_YYEMPTY = -2,
  YYSYMBOL_YYEOF = 0,                      /* "end of file"  */
  YYSYMBOL_YYerror = 1,                    /* error  */
  YYSYMBOL_YYUNDEF = 2,                    /* "invalid token"  */
  YYSYMBOL_L_UNDEF = 3,                    /* L_UNDEF  */
  YYSYMBOL_L_ERROR = 4,                    /* L_ERROR  */
  YYSYMBOL_L_EOS = 5,                      /* L_EOS  */
  YYSYMBOL_L_PLUS = 6,                     /* L_PLUS  */
  YYSYMBOL_L_MINUS = 7,                    /* L_MINUS  */
  YYSYMBOL_L_STAR = 8,                     /* L_STAR  */
  YYSYMBOL_L_SLASH = 9,                    /* L_SLASH  */
  YYSYMBOL_L_QUEST = 10,                   /* L_QUEST  */
  YYSYMBOL_L_COLON = 11,                   /* L_COLON  */
  YYSYMBOL_L_LPAREN = 12,                  /* L_LPAREN  */
  YYSYMBOL_L_RPAREN = 13,                  /* L_RPAREN  */
  YYSYMBOL_L_AVG = 14,                     /* L_AVG  */
  YYSYMBOL_L_COUNT = 15,                   /* L_COUNT  */
  YYSYMBOL_L_DELTA = 16,                   /* L_DELTA  */
  YYSYMBOL_L_MAX = 17,                     /* L_MAX  */
  YYSYMBOL_L_MIN = 18,                     /* L_MIN  */
  YYSYMBOL_L_SUM = 19,                     /* L_SUM  */
  YYSYMBOL_L_ANON = 20,                    /* L_ANON  */
  YYSYMBOL_L_RATE = 21,                    /* L_RATE  */
  YYSYMBOL_L_INSTANT = 22,                 /* L_INSTANT  */
  YYSYMBOL_L_LT = 23,                      /* L_LT  */
  YYSYMBOL_L_LEQ = 24,                     /* L_LEQ  */
  YYSYMBOL_L_EQ = 25,                      /* L_EQ  */
  YYSYMBOL_L_GEQ = 26,                     /* L_GEQ  */
  YYSYMBOL_L_GT = 27,                      /* L_GT  */
  YYSYMBOL_L_NEQ = 28,                     /* L_NEQ  */
  YYSYMBOL_L_AND = 29,                     /* L_AND  */
  YYSYMBOL_L_OR = 30,                      /* L_OR  */
  YYSYMBOL_L_MKCONST = 31,                 /* L_MKCONST  */
  YYSYMBOL_L_META = 32,                    /* L_META  */
  YYSYMBOL_L_NOVALUE = 33,                 /* L_NOVALUE  */
  YYSYMBOL_L_RESCALE = 34,                 /* L_RESCALE  */
  YYSYMBOL_L_DEFINED = 35,                 /* L_DEFINED  */
  YYSYMBOL_L_MATCHINST = 36,               /* L_MATCHINST  */
  YYSYMBOL_L_SCALAR = 37,                  /* L_SCALAR  */
  YYSYMBOL_L_TYPE = 38,                    /* L_TYPE  */
  YYSYMBOL_L_SEMANTICS = 39,               /* L_SEMANTICS  */
  YYSYMBOL_L_UNITS = 40,                   /* L_UNITS  */
  YYSYMBOL_L_INDOM = 41,                   /* L_INDOM  */
  YYSYMBOL_L_ASSIGN = 42,                  /* L_ASSIGN  */
  YYSYMBOL_L_COMMA = 43,                   /* L_COMMA  */
  YYSYMBOL_EVENT_UNIT = 44,                /* EVENT_UNIT  */
  YYSYMBOL_TIME_UNIT = 45,                 /* TIME_UNIT  */
  YYSYMBOL_SPACE_UNIT = 46,                /* SPACE_UNIT  */
  YYSYMBOL_L_INTEGER = 47,                 /* L_INTEGER  */
  YYSYMBOL_L_DOUBLE = 48,                  /* L_DOUBLE  */
  YYSYMBOL_L_NAME = 49,                    /* L_NAME  */
  YYSYMBOL_L_STRING = 50,                  /* L_STRING  */
  YYSYMBOL_L_INSTNAME = 51,                /* L_INSTNAME  */
  YYSYMBOL_L_PATTERN = 52,                 /* L_PATTERN  */
  YYSYMBOL_L_NOT = 53,                     /* L_NOT  */
  YYSYMBOL_YYACCEPT = 54,                  /* $accept  */
  YYSYMBOL_defn = 55,                      /* defn  */
  YYSYMBOL_expr = 56,                      /* expr  */
  YYSYMBOL_novalue = 57,                   /* novalue  */
  YYSYMBOL_58_1 = 58,                      /* $@1  */
  YYSYMBOL_novalueparam = 59,              /* novalueparam  */
  YYSYMBOL_regexpr = 60,                   /* regexpr  */
  YYSYMBOL_opt_bang = 61,                  /* opt_bang  */
  YYSYMBOL_num = 62,                       /* num  */
  YYSYMBOL_63_2 = 63,                      /* $@2  */
  YYSYMBOL_64_3 = 64,                      /* $@3  */
  YYSYMBOL_speclist = 65,                  /* speclist  */
  YYSYMBOL_spec = 66,                      /* spec  */
  YYSYMBOL_specval = 67,                   /* specval  */
  YYSYMBOL_func = 68                       /* func  */
};
typedef enum yysymbol_kind_t yysymbol_kind_t;




#ifdef short
# undef short
#endif

/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure
   <limits.h> and (if available) <stdint.h> are included
   so that the code can choose integer types of a good width.  */

#ifndef __PTRDIFF_MAX__
# include <limits.h> /* INFRINGES ON USER NAME SPACE */
# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
#  include <stdint.h> /* INFRINGES ON USER NAME SPACE */
#  define YY_STDINT_H
# endif
#endif

/* Narrow types that promote to a signed type and that can represent a
   signed or unsigned integer of at least N bits.  In tables they can
   save space and decrease cache pressure.  Promoting to a signed type
   helps avoid bugs in integer arithmetic.  */

#ifdef __INT_LEAST8_MAX__
typedef __INT_LEAST8_TYPE__ yytype_int8;
#elif defined YY_STDINT_H
typedef int_least8_t yytype_int8;
#else
typedef signed char yytype_int8;
#endif

#ifdef __INT_LEAST16_MAX__
typedef __INT_LEAST16_TYPE__ yytype_int16;
#elif defined YY_STDINT_H
typedef int_least16_t yytype_int16;
#else
typedef short yytype_int16;
#endif

/* Work around bug in HP-UX 11.23, which defines these macros
   incorrectly for preprocessor constants.  This workaround can likely
   be removed in 2023, as HPE has promised support for HP-UX 11.23
   (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of
   <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>.  */
#ifdef __hpux
# undef UINT_LEAST8_MAX
# undef UINT_LEAST16_MAX
# define UINT_LEAST8_MAX 255
# define UINT_LEAST16_MAX 65535
#endif

#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__
typedef __UINT_LEAST8_TYPE__ yytype_uint8;
#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \
       && UINT_LEAST8_MAX <= INT_MAX)
typedef uint_least8_t yytype_uint8;
#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX
typedef unsigned char yytype_uint8;
#else
typedef short yytype_uint8;
#endif

#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__
typedef __UINT_LEAST16_TYPE__ yytype_uint16;
#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \
       && UINT_LEAST16_MAX <= INT_MAX)
typedef uint_least16_t yytype_uint16;
#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX
typedef unsigned short yytype_uint16;
#else
typedef int yytype_uint16;
#endif

#ifndef YYPTRDIFF_T
# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__
#  define YYPTRDIFF_T __PTRDIFF_TYPE__
#  define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__
# elif defined PTRDIFF_MAX
#  ifndef ptrdiff_t
#   include <stddef.h> /* INFRINGES ON USER NAME SPACE */
#  endif
#  define YYPTRDIFF_T ptrdiff_t
#  define YYPTRDIFF_MAXIMUM PTRDIFF_MAX
# else
#  define YYPTRDIFF_T long
#  define YYPTRDIFF_MAXIMUM LONG_MAX
# endif
#endif

#ifndef YYSIZE_T
# ifdef __SIZE_TYPE__
#  define YYSIZE_T __SIZE_TYPE__
# elif defined size_t
#  define YYSIZE_T size_t
# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
#  define YYSIZE_T size_t
# else
#  define YYSIZE_T unsigned
# endif
#endif

#define YYSIZE_MAXIMUM                                  \
  YY_CAST (YYPTRDIFF_T,                                 \
           (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1)  \
            ? YYPTRDIFF_MAXIMUM                         \
            : YY_CAST (YYSIZE_T, -1)))

#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X))


/* Stored state numbers (used for stacks). */
typedef yytype_uint8 yy_state_t;

/* State numbers in computations.  */
typedef int yy_state_fast_t;

#ifndef YY_
# if defined YYENABLE_NLS && YYENABLE_NLS
#  if ENABLE_NLS
#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
#   define YY_(Msgid) dgettext ("bison-runtime", Msgid)
#  endif
# endif
# ifndef YY_
#  define YY_(Msgid) Msgid
# endif
#endif


#ifndef YY_ATTRIBUTE_PURE
# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
#  define YY_ATTRIBUTE_PURE __attribute__ ((__pure__))
# else
#  define YY_ATTRIBUTE_PURE
# endif
#endif

#ifndef YY_ATTRIBUTE_UNUSED
# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
#  define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
# else
#  define YY_ATTRIBUTE_UNUSED
# endif
#endif

/* Suppress unused-variable warnings by "using" E.  */
#if ! defined lint || defined __GNUC__
# define YY_USE(E) ((void) (E))
#else
# define YY_USE(E) /* empty */
#endif

/* Suppress an incorrect diagnostic about yylval being uninitialized.  */
#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__
# if __GNUC__ * 100 + __GNUC_MINOR__ < 407
#  define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                           \
    _Pragma ("GCC diagnostic push")                                     \
    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")
# else
#  define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                           \
    _Pragma ("GCC diagnostic push")                                     \
    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")              \
    _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
# endif
# define YY_IGNORE_MAYBE_UNINITIALIZED_END      \
    _Pragma ("GCC diagnostic pop")
#else
# define YY_INITIAL_VALUE(Value) Value
#endif
#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
# define YY_IGNORE_MAYBE_UNINITIALIZED_END
#endif
#ifndef YY_INITIAL_VALUE
# define YY_INITIAL_VALUE(Value) /* Nothing. */
#endif

#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__
# define YY_IGNORE_USELESS_CAST_BEGIN                          \
    _Pragma ("GCC diagnostic push")                            \
    _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"")
# define YY_IGNORE_USELESS_CAST_END            \
    _Pragma ("GCC diagnostic pop")
#endif
#ifndef YY_IGNORE_USELESS_CAST_BEGIN
# define YY_IGNORE_USELESS_CAST_BEGIN
# define YY_IGNORE_USELESS_CAST_END
#endif


#define YY_ASSERT(E) ((void) (0 && (E)))

#if !defined yyoverflow

/* The parser invokes alloca or malloc; define the necessary symbols.  */

# ifdef YYSTACK_USE_ALLOCA
#  if YYSTACK_USE_ALLOCA
#   ifdef __GNUC__
#    define YYSTACK_ALLOC __builtin_alloca
#   elif defined __BUILTIN_VA_ARG_INCR
#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
#   elif defined _AIX
#    define YYSTACK_ALLOC __alloca
#   elif defined _MSC_VER
#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
#    define alloca _alloca
#   else
#    define YYSTACK_ALLOC alloca
#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
      /* Use EXIT_SUCCESS as a witness for stdlib.h.  */
#     ifndef EXIT_SUCCESS
#      define EXIT_SUCCESS 0
#     endif
#    endif
#   endif
#  endif
# endif

# ifdef YYSTACK_ALLOC
   /* Pacify GCC's 'empty if-body' warning.  */
#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
#  ifndef YYSTACK_ALLOC_MAXIMUM
    /* The OS might guarantee only one guard page at the bottom of the stack,
       and a page size can be as small as 4096 bytes.  So we cannot safely
       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
       to allow for a few compiler-allocated temporary stack slots.  */
#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
#  endif
# else
#  define YYSTACK_ALLOC YYMALLOC
#  define YYSTACK_FREE YYFREE
#  ifndef YYSTACK_ALLOC_MAXIMUM
#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
#  endif
#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
       && ! ((defined YYMALLOC || defined malloc) \
             && (defined YYFREE || defined free)))
#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
#   ifndef EXIT_SUCCESS
#    define EXIT_SUCCESS 0
#   endif
#  endif
#  ifndef YYMALLOC
#   define YYMALLOC malloc
#   if ! defined malloc && ! defined EXIT_SUCCESS
void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
#   endif
#  endif
#  ifndef YYFREE
#   define YYFREE free
#   if ! defined free && ! defined EXIT_SUCCESS
void free (void *); /* INFRINGES ON USER NAME SPACE */
#   endif
#  endif
# endif
#endif /* !defined yyoverflow */

#if (! defined yyoverflow \
     && (! defined __cplusplus \
         || (defined DERIVE_STYPE_IS_TRIVIAL && DERIVE_STYPE_IS_TRIVIAL)))

/* A type that is properly aligned for any stack member.  */
union yyalloc
{
  yy_state_t yyss_alloc;
  YYSTYPE yyvs_alloc;
};

/* The size of the maximum gap between one aligned stack and the next.  */
# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1)

/* The size of an array large to enough to hold all stacks, each with
   N elements.  */
# define YYSTACK_BYTES(N) \
     ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \
      + YYSTACK_GAP_MAXIMUM)

# define YYCOPY_NEEDED 1

/* Relocate STACK from its old location to the new one.  The
   local variables YYSIZE and YYSTACKSIZE give the old and new number of
   elements in the stack, and YYPTR gives the new location of the
   stack.  Advance YYPTR to a properly aligned location for the next
   stack.  */
# define YYSTACK_RELOCATE(Stack_alloc, Stack)                           \
    do                                                                  \
      {                                                                 \
        YYPTRDIFF_T yynewbytes;                                         \
        YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
        Stack = &yyptr->Stack_alloc;                                    \
        yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \
        yyptr += yynewbytes / YYSIZEOF (*yyptr);                        \
      }                                                                 \
    while (0)

#endif

#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
/* Copy COUNT objects from SRC to DST.  The source and destination do
   not overlap.  */
# ifndef YYCOPY
#  if defined __GNUC__ && 1 < __GNUC__
#   define YYCOPY(Dst, Src, Count) \
      __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src)))
#  else
#   define YYCOPY(Dst, Src, Count)              \
      do                                        \
        {                                       \
          YYPTRDIFF_T yyi;                      \
          for (yyi = 0; yyi < (Count); yyi++)   \
            (Dst)[yyi] = (Src)[yyi];            \
        }                                       \
      while (0)
#  endif
# endif
#endif /* !YYCOPY_NEEDED */

/* YYFINAL -- State number of the termination state.  */
#define YYFINAL  76
/* YYLAST -- Last index in YYTABLE.  */
#define YYLAST   1887

/* YYNTOKENS -- Number of terminals.  */
#define YYNTOKENS  54
/* YYNNTS -- Number of nonterminals.  */
#define YYNNTS  15
/* YYNRULES -- Number of rules.  */
#define YYNRULES  125
/* YYNSTATES -- Number of states.  */
#define YYNSTATES  242

/* YYMAXUTOK -- Last valid token kind.  */
#define YYMAXUTOK   308


/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
   as returned by yylex, with out-of-bounds checking.  */
#define YYTRANSLATE(YYX)                                \
  (0 <= (YYX) && (YYX) <= YYMAXUTOK                     \
   ? YY_CAST (yysymbol_kind_t, yytranslate[YYX])        \
   : YYSYMBOL_YYUNDEF)

/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
   as returned by yylex.  */
static const yytype_int8 yytranslate[] =
{
       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
      45,    46,    47,    48,    49,    50,    51,    52,    53
};

#if DERIVE_DEBUG
/* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
static const yytype_int16 yyrline[] =
{
       0,  4721,  4721,  4725,  4728,  4730,  4732,  4734,  4736,  4738,
    4740,  4742,  4744,  4746,  4748,  4750,  4755,  4758,  4760,  4762,
    4764,  4766,  4768,  4770,  4772,  4774,  4776,  4778,  4780,  4782,
    4784,  4789,  4791,  4806,  4820,  4822,  4827,  4831,  4833,  4835,
    4837,  4839,  4843,  4845,  4847,  4849,  4851,  4853,  4855,  4863,
    4871,  4879,  4888,  4890,  4892,  4896,  4898,  4900,  4902,  4904,
    4906,  4908,  4910,  4912,  4914,  4916,  4918,  4920,  4922,  4924,
    4926,  4928,  4937,  4936,  4957,  4961,  4962,  4965,  4989,  4992,
    4994,  5004,  5015,  5014,  5035,  5034,  5061,  5066,  5067,  5070,
    5092,  5108,  5125,  5156,  5163,  5164,  5165,  5166,  5169,  5198,
    5204,  5210,  5216,  5222,  5228,  5234,  5240,  5245,  5251,  5268,
    5276,  5281,  5283,  5285,  5287,  5289,  5291,  5293,  5295,  5297,
    5299,  5301,  5303,  5305,  5307,  5309
};
#endif

/** Accessing symbol of state STATE.  */
#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State])

#if DERIVE_DEBUG || 0
/* The user-facing name of the symbol whose (internal) number is
   YYSYMBOL.  No bounds checking.  */
static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED;

/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
static const char *const yytname[] =
{
  "\"end of file\"", "error", "\"invalid token\"", "L_UNDEF", "L_ERROR",
  "L_EOS", "L_PLUS", "L_MINUS", "L_STAR", "L_SLASH", "L_QUEST", "L_COLON",
  "L_LPAREN", "L_RPAREN", "L_AVG", "L_COUNT", "L_DELTA", "L_MAX", "L_MIN",
  "L_SUM", "L_ANON", "L_RATE", "L_INSTANT", "L_LT", "L_LEQ", "L_EQ",
  "L_GEQ", "L_GT", "L_NEQ", "L_AND", "L_OR", "L_MKCONST", "L_META",
  "L_NOVALUE", "L_RESCALE", "L_DEFINED", "L_MATCHINST", "L_SCALAR",
  "L_TYPE", "L_SEMANTICS", "L_UNITS", "L_INDOM", "L_ASSIGN", "L_COMMA",
  "EVENT_UNIT", "TIME_UNIT", "SPACE_UNIT", "L_INTEGER", "L_DOUBLE",
  "L_NAME", "L_STRING", "L_INSTNAME", "L_PATTERN", "L_NOT", "$accept",
  "defn", "expr", "novalue", "$@1", "novalueparam", "regexpr", "opt_bang",
  "num", "$@2", "$@3", "speclist", "spec", "specval", "func", YY_NULLPTR
};

static const char *
yysymbol_name (yysymbol_kind_t yysymbol)
{
  return yytname[yysymbol];
}
#endif

#define YYPACT_NINF (-157)

#define yypact_value_is_default(Yyn) \
  ((Yyn) == YYPACT_NINF)

#define YYTABLE_NINF (-80)

#define yytable_value_is_error(Yyn) \
  0

/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
   STATE-NUM.  */
static const yytype_int16 yypact[] =
{
    1581,     6,   152,    11,    31,    55,    61,   196,    68,    60,
      96,    97,   104,   114,   115,   129,   130,   131,   143,   144,
     145,   146,   147,   148,   154,   156,   153,   163,   164,   165,
     166,  -157,  -157,  1535,   239,   158,  1725,  -157,  -157,  -157,
    -157,  1667,    21,  -157,  -157,  -157,  -157,  -157,  1750,  -157,
       5,    12,   282,    14,    24,    25,    26,   325,   368,  -157,
    -157,  -157,  -157,  -157,  -157,  -157,  -157,    29,   411,   132,
       7,   454,  -157,  -157,  -157,   105,  -157,  -157,   497,   540,
     583,   626,     2,   178,   179,   669,   712,   755,   798,   841,
     884,   927,   970,  1013,  1056,  1099,  1142,  1185,  1228,  1271,
    1314,  1357,  1400,  1443,   133,  -157,   169,  -157,   172,  -157,
    1758,  -157,   177,  -157,   180,  -157,   181,  -157,   185,  -157,
    1783,  -157,  1791,     4,   149,  -157,  1699,   189,  -157,  -157,
      27,   155,  -157,  1816,   204,    21,  -157,    21,   206,  -157,
     209,  -157,  -157,   207,  1824,   210,  -157,  -157,   220,    37,
     222,    37,   223,    37,   224,    37,   225,    37,   226,    37,
     228,   105,   234,   105,  -157,  -157,  -157,  -157,  -157,  -157,
    -157,  -157,  -157,  -157,  -157,  -157,  -157,  -157,  -157,  -157,
    -157,  -157,  -157,  -157,  -157,  -157,  -157,  -157,     3,  -157,
    -157,  1486,  -157,  -157,  1536,   103,  1624,   197,   197,  -157,
     229,  -157,  1849,  -157,   122,  -157,  1857,  -157,  1857,  -157,
     199,   205,   208,   221,   227,   -12,  -157,    -3,  -157,  -157,
    -157,  -157,    -2,   190,    17,    17,    17,    17,  -157,   197,
    -157,  -157,  -157,  -157,  -157,  -157,  -157,  -157,  -157,  -157,
    -157,  -157
};

/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
   Performed when YYTABLE does not specify something else to do.  Zero
   means the default is an error.  */
static const yytype_int8 yydefact[] =
{
       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
       0,    80,    81,     0,     0,     0,     0,    34,    36,    16,
      60,     0,    41,    17,    18,    29,    30,    19,     0,    20,
       0,     0,     0,     0,     0,     0,     0,     0,     0,    21,
      22,    23,    24,    25,    26,    27,    28,     0,     0,     0,
       0,     0,    55,    32,    69,    54,     1,     2,     0,     0,
       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
       0,     0,     0,     0,    31,   112,     0,   113,     0,   114,
       0,   115,     0,   116,     0,   117,     0,   111,     0,   118,
       0,   119,     0,     0,     0,   120,     0,     0,   122,    78,
       0,     0,   125,     0,    56,    37,    57,    38,    58,    39,
      59,    40,    70,     0,     0,     0,     6,     7,    61,    42,
      62,    43,    63,    44,    64,    45,    65,    46,    66,    47,
      67,    52,    68,    53,    56,    58,    59,    61,    62,    63,
      64,    65,    66,    67,    68,    33,    99,   100,   101,   102,
     103,   104,    98,   105,   106,    86,    82,    84,     0,   107,
     123,     0,    77,   110,     0,     0,     0,     0,     0,   121,
       0,   124,     0,    74,     0,    71,    48,    50,    49,    51,
       0,     0,     0,     0,     0,     0,    88,     0,   108,   109,
      75,    73,     0,     0,     0,     0,     0,     0,    83,     0,
      85,    76,    93,    96,    97,    95,    94,    89,    90,    91,
      92,    87
};

/* YYPGOTO[NTERM-NUM].  */
static const yytype_int16 yypgoto[] =
{
    -157,  -157,     0,  -112,  -157,  -157,  -157,  -157,  -157,  -157,
    -157,  -156,    19,  -120,  -157
};

/* YYDEFGOTO[NTERM-NUM].  */
static const yytype_uint8 yydefgoto[] =
{
       0,    35,    48,   145,   204,   221,   130,   131,    37,   197,
     198,   215,   216,   237,    38
};

/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
   positive, shift that token.  If negative, reduce the rule whose
   number is the opposite.  If YYTABLE_NINF, syntax error.  */
static const yytype_int16 yytable[] =
{
      36,   228,    42,   142,   199,   185,   105,    39,   128,     2,
     230,   231,    43,   107,    41,   111,     9,    10,    11,    12,
      13,    14,    15,    16,    17,   113,   115,   117,   190,    94,
      95,   229,    44,    26,    75,   143,    27,    28,    29,    30,
     229,   229,   217,    93,    79,    94,    95,   186,   222,    31,
      32,    33,   110,   200,   106,    34,    45,   120,   122,   -79,
     129,   108,    46,   112,   233,   234,   235,   236,   126,    49,
     191,   133,    50,   114,   116,   118,   123,   124,   135,   137,
     139,   141,   144,   207,   209,   149,   151,   153,   155,   157,
     159,   161,   163,   135,   139,   141,   149,   151,   153,   155,
     157,   159,   161,   163,   205,   238,   239,   240,    51,    52,
       2,    93,    79,    94,    95,    41,    53,     9,    10,    11,
      12,    13,    14,    15,    16,    17,    54,    55,    96,    97,
      98,    99,   100,   101,    26,   220,   143,    27,    28,    29,
      30,    56,    57,    58,    59,    60,    61,    62,    63,    64,
      31,    32,    33,    40,   210,    65,    34,    66,    76,     2,
     211,   212,   213,   214,    41,    67,     9,    10,    11,    12,
      13,    14,    15,    16,    17,    68,    69,    70,    71,   146,
     147,   127,   176,    26,   175,   177,    27,    28,    29,    30,
     179,   202,   187,   180,   181,   206,   208,    47,   182,    31,
      32,    33,   189,     2,    -3,    34,    -4,   192,    41,    -5,
       9,    10,    11,    12,    13,    14,    15,    16,    17,   194,
      -8,   196,    -9,   -10,   -11,   -12,   -13,    26,   -14,   210,
      27,    28,    29,    30,   -15,   211,   212,   213,   214,   232,
      74,   223,   218,    31,    32,    33,     2,   224,   241,    34,
     225,    41,     0,     9,    10,    11,    12,    13,    14,    15,
      16,    17,     0,   226,     0,     0,     0,     0,     0,   227,
      26,     0,     0,    27,    28,    29,    30,     0,     0,     0,
       0,     0,     0,   109,     0,     0,    31,    32,    33,     2,
       0,     0,    34,     0,    41,     0,     9,    10,    11,    12,
      13,    14,    15,    16,    17,     0,     0,     0,     0,     0,
       0,     0,     0,    26,     0,     0,    27,    28,    29,    30,
       0,     0,     0,     0,     0,     0,   119,     0,     0,    31,
      32,    33,     2,     0,     0,    34,     0,    41,     0,     9,
      10,    11,    12,    13,    14,    15,    16,    17,     0,     0,
       0,     0,     0,     0,     0,     0,    26,     0,     0,    27,
      28,    29,    30,     0,     0,     0,     0,     0,     0,   121,
       0,     0,    31,    32,    33,     2,     0,     0,    34,     0,
      41,     0,     9,    10,    11,    12,    13,    14,    15,    16,
      17,     0,     0,     0,     0,     0,     0,     0,     0,    26,
       0,     0,    27,    28,    29,    30,     0,     0,     0,     0,
       0,     0,   125,     0,     0,    31,    32,    33,     2,     0,
       0,    34,     0,    41,     0,     9,    10,    11,    12,    13,
      14,    15,    16,    17,     0,     0,     0,     0,     0,     0,
       0,     0,    26,     0,     0,    27,    28,    29,    30,     0,
       0,     0,     0,     0,     0,   132,     0,     0,    31,    32,
      33,     2,     0,     0,    34,     0,    41,     0,     9,    10,
      11,    12,    13,    14,    15,    16,    17,     0,     0,     0,
       0,     0,     0,     0,     0,    26,     0,     0,    27,    28,
      29,    30,     0,     0,     0,     0,     0,     0,   134,     0,
       0,    31,    32,    33,     2,     0,     0,    34,     0,    41,
       0,     9,    10,    11,    12,    13,    14,    15,    16,    17,
       0,     0,     0,     0,     0,     0,     0,     0,    26,     0,
       0,    27,    28,    29,    30,     0,     0,     0,     0,     0,
       0,   136,     0,     0,    31,    32,    33,     2,     0,     0,
      34,     0,    41,     0,     9,    10,    11,    12,    13,    14,
      15,    16,    17,     0,     0,     0,     0,     0,     0,     0,
       0,    26,     0,     0,    27,    28,    29,    30,     0,     0,
       0,     0,     0,     0,   138,     0,     0,    31,    32,    33,
       2,     0,     0,    34,     0,    41,     0,     9,    10,    11,
      12,    13,    14,    15,    16,    17,     0,     0,     0,     0,
       0,     0,     0,     0,    26,     0,     0,    27,    28,    29,
      30,     0,     0,     0,     0,     0,     0,   140,     0,     0,
      31,    32,    33,     2,     0,     0,    34,     0,    41,     0,
       9,    10,    11,    12,    13,    14,    15,    16,    17,     0,
       0,     0,     0,     0,     0,     0,     0,    26,     0,     0,
      27,    28,    29,    30,     0,     0,     0,     0,     0,     0,
     148,     0,     0,    31,    32,    33,     2,     0,     0,    34,
       0,    41,     0,     9,    10,    11,    12,    13,    14,    15,
      16,    17,     0,     0,     0,     0,     0,     0,     0,     0,
      26,     0,     0,    27,    28,    29,    30,     0,     0,     0,
       0,     0,     0,   150,     0,     0,    31,    32,    33,     2,
       0,     0,    34,     0,    41,     0,     9,    10,    11,    12,
      13,    14,    15,    16,    17,     0,     0,     0,     0,     0,
       0,     0,     0,    26,     0,     0,    27,    28,    29,    30,
       0,     0,     0,     0,     0,     0,   152,     0,     0,    31,
      32,    33,     2,     0,     0,    34,     0,    41,     0,     9,
      10,    11,    12,    13,    14,    15,    16,    17,     0,     0,
       0,     0,     0,     0,     0,     0,    26,     0,     0,    27,
      28,    29,    30,     0,     0,     0,     0,     0,     0,   154,
       0,     0,    31,    32,    33,     2,     0,     0,    34,     0,
      41,     0,     9,    10,    11,    12,    13,    14,    15,    16,
      17,     0,     0,     0,     0,     0,     0,     0,     0,    26,
       0,     0,    27,    28,    29,    30,     0,     0,     0,     0,
       0,     0,   156,     0,     0,    31,    32,    33,     2,     0,
       0,    34,     0,    41,     0,     9,    10,    11,    12,    13,
      14,    15,    16,    17,     0,     0,     0,     0,     0,     0,
       0,     0,    26,     0,     0,    27,    28,    29,    30,     0,
       0,     0,     0,     0,     0,   158,     0,     0,    31,    32,
      33,     2,     0,     0,    34,     0,    41,     0,     9,    10,
      11,    12,    13,    14,    15,    16,    17,     0,     0,     0,
       0,     0,     0,     0,     0,    26,     0,     0,    27,    28,
      29,    30,     0,     0,     0,     0,     0,     0,   160,     0,
       0,    31,    32,    33,     2,     0,     0,    34,     0,    41,
       0,     9,    10,    11,    12,    13,    14,    15,    16,    17,
       0,     0,     0,     0,     0,     0,     0,     0,    26,     0,
       0,    27,    28,    29,    30,     0,     0,     0,     0,     0,
       0,   162,     0,     0,    31,    32,    33,     2,     0,     0,
      34,     0,    41,     0,     9,    10,    11,    12,    13,    14,
      15,    16,    17,     0,     0,     0,     0,     0,     0,     0,
       0,    26,     0,     0,    27,    28,    29,    30,     0,     0,
       0,     0,     0,     0,   164,     0,     0,    31,    32,    33,
       2,     0,     0,    34,     0,    41,     0,     9,    10,    11,
      12,    13,    14,    15,    16,    17,     0,     0,     0,     0,
       0,     0,     0,     0,    26,     0,     0,    27,    28,    29,
      30,     0,     0,     0,     0,     0,     0,   165,     0,     0,
      31,    32,    33,     2,     0,     0,    34,     0,    41,     0,
       9,    10,    11,    12,    13,    14,    15,    16,    17,     0,
       0,     0,     0,     0,     0,     0,     0,    26,     0,     0,
      27,    28,    29,    30,     0,     0,     0,     0,     0,     0,
     166,     0,     0,    31,    32,    33,     2,     0,     0,    34,
       0,    41,     0,     9,    10,    11,    12,    13,    14,    15,
      16,    17,     0,     0,     0,     0,     0,     0,     0,     0,
      26,     0,     0,    27,    28,    29,    30,     0,     0,     0,
       0,     0,     0,   167,     0,     0,    31,    32,    33,     2,
       0,     0,    34,     0,    41,     0,     9,    10,    11,    12,
      13,    14,    15,    16,    17,     0,     0,     0,     0,     0,
       0,     0,     0,    26,     0,     0,    27,    28,    29,    30,
       0,     0,     0,     0,     0,     0,   168,     0,     0,    31,
      32,    33,     2,     0,     0,    34,     0,    41,     0,     9,
      10,    11,    12,    13,    14,    15,    16,    17,     0,     0,
       0,     0,     0,     0,     0,     0,    26,     0,     0,    27,
      28,    29,    30,     0,     0,     0,     0,     0,     0,   169,
       0,     0,    31,    32,    33,     2,     0,     0,    34,     0,
      41,     0,     9,    10,    11,    12,    13,    14,    15,    16,
      17,     0,     0,     0,     0,     0,     0,     0,     0,    26,
       0,     0,    27,    28,    29,    30,     0,     0,     0,     0,
       0,     0,   170,     0,     0,    31,    32,    33,     2,     0,
       0,    34,     0,    41,     0,     9,    10,    11,    12,    13,
      14,    15,    16,    17,     0,     0,     0,     0,     0,     0,
       0,     0,    26,     0,     0,    27,    28,    29,    30,     0,
       0,     0,     0,     0,     0,   171,     0,     0,    31,    32,
      33,     2,     0,     0,    34,     0,    41,     0,     9,    10,
      11,    12,    13,    14,    15,    16,    17,     0,     0,     0,
       0,     0,     0,     0,     0,    26,     0,     0,    27,    28,
      29,    30,     0,     0,     0,     0,     0,     0,   172,     0,
       0,    31,    32,    33,     2,     0,     0,    34,     0,    41,
       0,     9,    10,    11,    12,    13,    14,    15,    16,    17,
       0,     0,     0,     0,     0,     0,     0,     0,    26,     0,
       0,    27,    28,    29,    30,     0,     0,     0,     0,     0,
       0,   173,     0,     0,    31,    32,    33,     2,     0,     0,
      34,     0,    41,     0,     9,    10,    11,    12,    13,    14,
      15,    16,    17,     0,     0,     0,     0,     0,     0,     0,
       0,    26,     0,     0,    27,    28,    29,    30,     0,     0,
       0,     0,     0,     0,   174,     0,     0,    31,    32,    33,
       2,     0,     0,    34,     0,    41,     0,     9,    10,    11,
      12,    13,    14,    15,    16,    17,     0,     0,     0,     0,
       0,     0,     0,     0,    26,     0,     0,    27,    28,    29,
      30,     0,     0,     0,     0,     0,     0,   201,     0,     0,
      31,    32,    33,     2,     0,     0,    34,     0,    41,     0,
       9,    10,    11,    12,    13,    14,    15,    16,    17,     0,
       0,     0,     0,     0,     0,     0,     0,    26,     0,     0,
      27,    28,    29,    30,     0,     0,     0,     0,     0,     0,
       0,     0,     0,    31,    32,    33,    72,   203,     0,    34,
     -35,   -35,   -35,   -35,   -35,   -35,   -35,   -35,   -35,   -72,
       0,     0,     0,     0,     0,     0,     0,     0,   -35,   -35,
     -35,   -35,   -35,   -35,   -35,   -35,     0,     0,   -72,     0,
       0,     0,     0,     0,   -72,   -72,   -72,   -72,   -35,     0,
       0,     0,     0,     0,     0,     0,    73,     1,     2,     3,
       4,     5,     6,     7,     8,     9,    10,    11,    12,    13,
      14,    15,    16,    17,    18,    19,    20,    21,    22,    23,
      24,    25,    26,     0,     0,    27,    28,    29,    30,     0,
       0,     0,     0,     0,     0,     0,     0,     0,    31,    32,
      33,     2,     0,     0,    34,     0,    41,     0,     9,    10,
      11,    12,    13,    14,    15,    16,    17,     0,     0,     0,
       0,     0,     0,     0,     0,    26,     0,   143,    27,    28,
      29,    30,     0,     0,     0,     0,     0,     0,     0,     0,
       0,    31,    32,    33,     2,     0,     0,    34,     0,    41,
       0,     9,    10,    11,    12,    13,    14,    15,    16,    17,
       0,     0,     0,     0,     0,     0,     0,     0,    26,     0,
       0,    27,    28,    29,    30,    93,    79,    94,    95,    82,
       0,     0,     0,     0,    31,    32,    33,     0,     0,     0,
      34,     0,    96,    97,    98,    99,   100,   101,   102,   103,
      77,    78,    79,    80,    81,    82,     0,    83,    84,     0,
       0,     0,   188,     0,     0,     0,     0,     0,    85,    86,
      87,    88,    89,    90,    91,    92,    93,    79,    94,    95,
      82,     0,     0,   104,    93,    79,    94,    95,    82,     0,
       0,   178,     0,    96,    97,    98,    99,   100,   101,   102,
     103,    96,    97,    98,    99,   100,   101,   102,   103,    93,
      79,    94,    95,    82,     0,     0,   183,    93,    79,    94,
      95,    82,     0,     0,   184,     0,    96,    97,    98,    99,
     100,   101,   102,   103,    96,    97,    98,    99,   100,   101,
     102,   103,    93,    79,    94,    95,    82,     0,     0,   193,
      93,    79,    94,    95,    82,   195,     0,     0,     0,    96,
      97,    98,    99,   100,   101,   102,   103,    96,    97,    98,
      99,   100,   101,   102,   103,    93,    79,    94,    95,    82,
       0,     0,   219,    93,    79,    94,    95,     0,     0,     0,
       0,     0,    96,    97,    98,    99,   100,   101,   102,   103,
      96,    97,    98,    99,   100,   101,   102,   103
};

static const yytype_int16 yycheck[] =
{
       0,    13,     2,     1,     1,     1,     1,     1,     1,     7,
      13,    13,     1,     1,    12,     1,    14,    15,    16,    17,
      18,    19,    20,    21,    22,     1,     1,     1,     1,     8,
       9,    43,     1,    31,    34,    33,    34,    35,    36,    37,
      43,    43,   198,     6,     7,     8,     9,    43,   204,    47,
      48,    49,    52,    50,    49,    53,     1,    57,    58,    52,
      53,    49,     1,    49,    47,    48,    49,    50,    68,     1,
      43,    71,    12,    49,    49,    49,    47,    48,    78,    79,
      80,    81,    82,   195,   196,    85,    86,    87,    88,    89,
      90,    91,    92,    93,    94,    95,    96,    97,    98,    99,
     100,   101,   102,   103,     1,   225,   226,   227,    12,    12,
       7,     6,     7,     8,     9,    12,    12,    14,    15,    16,
      17,    18,    19,    20,    21,    22,    12,    12,    23,    24,
      25,    26,    27,    28,    31,    13,    33,    34,    35,    36,
      37,    12,    12,    12,     1,     1,     1,     1,     1,     1,
      47,    48,    49,     1,    32,     1,    53,     1,     0,     7,
      38,    39,    40,    41,    12,    12,    14,    15,    16,    17,
      18,    19,    20,    21,    22,    12,    12,    12,    12,     1,
       1,    49,    13,    31,    51,    13,    34,    35,    36,    37,
      13,   191,    43,    13,    13,   195,   196,     1,    13,    47,
      48,    49,    13,     7,     0,    53,     0,    52,    12,     0,
      14,    15,    16,    17,    18,    19,    20,    21,    22,    12,
       0,    11,     0,     0,     0,     0,     0,    31,     0,    32,
      34,    35,    36,    37,     0,    38,    39,    40,    41,    49,
       1,    42,    13,    47,    48,    49,     7,    42,   229,    53,
      42,    12,    -1,    14,    15,    16,    17,    18,    19,    20,
      21,    22,    -1,    42,    -1,    -1,    -1,    -1,    -1,    42,
      31,    -1,    -1,    34,    35,    36,    37,    -1,    -1,    -1,
      -1,    -1,    -1,     1,    -1,    -1,    47,    48,    49,     7,
      -1,    -1,    53,    -1,    12,    -1,    14,    15,    16,    17,
      18,    19,    20,    21,    22,    -1,    -1,    -1,    -1,    -1,
      -1,    -1,    -1,    31,    -1,    -1,    34,    35,    36,    37,
      -1,    -1,    -1,    -1,    -1,    -1,     1,    -1,    -1,    47,
      48,    49,     7,    -1,    -1,    53,    -1,    12,    -1,    14,
      15,    16,    17,    18,    19,    20,    21,    22,    -1,    -1,
      -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,    -1,    34,
      35,    36,    37,    -1,    -1,    -1,    -1,    -1,    -1,     1,
      -1,    -1,    47,    48,    49,     7,    -1,    -1,    53,    -1,
      12,    -1,    14,    15,    16,    17,    18,    19,    20,    21,
      22,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    31,
      -1,    -1,    34,    35,    36,    37,    -1,    -1,    -1,    -1,
      -1,    -1,     1,    -1,    -1,    47,    48,    49,     7,    -1,
      -1,    53,    -1,    12,    -1,    14,    15,    16,    17,    18,
      19,    20,    21,    22,    -1,    -1,    -1,    -1,    -1,    -1,
      -1,    -1,    31,    -1,    -1,    34,    35,    36,    37,    -1,
      -1,    -1,    -1,    -1,    -1,     1,    -1,    -1,    47,    48,
      49,     7,    -1,    -1,    53,    -1,    12,    -1,    14,    15,
      16,    17,    18,    19,    20,    21,    22,    -1,    -1,    -1,
      -1,    -1,    -1,    -1,    -1,    31,    -1,    -1,    34,    35,
      36,    37,    -1,    -1,    -1,    -1,    -1,    -1,     1,    -1,
      -1,    47,    48,    49,     7,    -1,    -1,    53,    -1,    12,
      -1,    14,    15,    16,    17,    18,    19,    20,    21,    22,
      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,
      -1,    34,    35,    36,    37,    -1,    -1,    -1,    -1,    -1,
      -1,     1,    -1,    -1,    47,    48,    49,     7,    -1,    -1,
      53,    -1,    12,    -1,    14,    15,    16,    17,    18,    19,
      20,    21,    22,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
      -1,    31,    -1,    -1,    34,    35,    36,    37,    -1,    -1,
      -1,    -1,    -1,    -1,     1,    -1,    -1,    47,    48,    49,
       7,    -1,    -1,    53,    -1,    12,    -1,    14,    15,    16,
      17,    18,    19,    20,    21,    22,    -1,    -1,    -1,    -1,
      -1,    -1,    -1,    -1,    31,    -1,    -1,    34,    35,    36,
      37,    -1,    -1,    -1,    -1,    -1,    -1,     1,    -1,    -1,
      47,    48,    49,     7,    -1,    -1,    53,    -1,    12,    -1,
      14,    15,    16,    17,    18,    19,    20,    21,    22,    -1,
      -1,    -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,    -1,
      34,    35,    36,    37,    -1,    -1,    -1,    -1,    -1,    -1,
       1,    -1,    -1,    47,    48,    49,     7,    -1,    -1,    53,
      -1,    12,    -1,    14,    15,    16,    17,    18,    19,    20,
      21,    22,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
      31,    -1,    -1,    34,    35,    36,    37,    -1,    -1,    -1,
      -1,    -1,    -1,     1,    -1,    -1,    47,    48,    49,     7,
      -1,    -1,    53,    -1,    12,    -1,    14,    15,    16,    17,
      18,    19,    20,    21,    22,    -1,    -1,    -1,    -1,    -1,
      -1,    -1,    -1,    31,    -1,    -1,    34,    35,    36,    37,
      -1,    -1,    -1,    -1,    -1,    -1,     1,    -1,    -1,    47,
      48,    49,     7,    -1,    -1,    53,    -1,    12,    -1,    14,
      15,    16,    17,    18,    19,    20,    21,    22,    -1,    -1,
      -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,    -1,    34,
      35,    36,    37,    -1,    -1,    -1,    -1,    -1,    -1,     1,
      -1,    -1,    47,    48,    49,     7,    -1,    -1,    53,    -1,
      12,    -1,    14,    15,    16,    17,    18,    19,    20,    21,
      22,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    31,
      -1,    -1,    34,    35,    36,    37,    -1,    -1,    -1,    -1,
      -1,    -1,     1,    -1,    -1,    47,    48,    49,     7,    -1,
      -1,    53,    -1,    12,    -1,    14,    15,    16,    17,    18,
      19,    20,    21,    22,    -1,    -1,    -1,    -1,    -1,    -1,
      -1,    -1,    31,    -1,    -1,    34,    35,    36,    37,    -1,
      -1,    -1,    -1,    -1,    -1,     1,    -1,    -1,    47,    48,
      49,     7,    -1,    -1,    53,    -1,    12,    -1,    14,    15,
      16,    17,    18,    19,    20,    21,    22,    -1,    -1,    -1,
      -1,    -1,    -1,    -1,    -1,    31,    -1,    -1,    34,    35,
      36,    37,    -1,    -1,    -1,    -1,    -1,    -1,     1,    -1,
      -1,    47,    48,    49,     7,    -1,    -1,    53,    -1,    12,
      -1,    14,    15,    16,    17,    18,    19,    20,    21,    22,
      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,
      -1,    34,    35,    36,    37,    -1,    -1,    -1,    -1,    -1,
      -1,     1,    -1,    -1,    47,    48,    49,     7,    -1,    -1,
      53,    -1,    12,    -1,    14,    15,    16,    17,    18,    19,
      20,    21,    22,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
      -1,    31,    -1,    -1,    34,    35,    36,    37,    -1,    -1,
      -1,    -1,    -1,    -1,     1,    -1,    -1,    47,    48,    49,
       7,    -1,    -1,    53,    -1,    12,    -1,    14,    15,    16,
      17,    18,    19,    20,    21,    22,    -1,    -1,    -1,    -1,
      -1,    -1,    -1,    -1,    31,    -1,    -1,    34,    35,    36,
      37,    -1,    -1,    -1,    -1,    -1,    -1,     1,    -1,    -1,
      47,    48,    49,     7,    -1,    -1,    53,    -1,    12,    -1,
      14,    15,    16,    17,    18,    19,    20,    21,    22,    -1,
      -1,    -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,    -1,
      34,    35,    36,    37,    -1,    -1,    -1,    -1,    -1,    -1,
       1,    -1,    -1,    47,    48,    49,     7,    -1,    -1,    53,
      -1,    12,    -1,    14,    15,    16,    17,    18,    19,    20,
      21,    22,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
      31,    -1,    -1,    34,    35,    36,    37,    -1,    -1,    -1,
      -1,    -1,    -1,     1,    -1,    -1,    47,    48,    49,     7,
      -1,    -1,    53,    -1,    12,    -1,    14,    15,    16,    17,
      18,    19,    20,    21,    22,    -1,    -1,    -1,    -1,    -1,
      -1,    -1,    -1,    31,    -1,    -1,    34,    35,    36,    37,
      -1,    -1,    -1,    -1,    -1,    -1,     1,    -1,    -1,    47,
      48,    49,     7,    -1,    -1,    53,    -1,    12,    -1,    14,
      15,    16,    17,    18,    19,    20,    21,    22,    -1,    -1,
      -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,    -1,    34,
      35,    36,    37,    -1,    -1,    -1,    -1,    -1,    -1,     1,
      -1,    -1,    47,    48,    49,     7,    -1,    -1,    53,    -1,
      12,    -1,    14,    15,    16,    17,    18,    19,    20,    21,
      22,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    31,
      -1,    -1,    34,    35,    36,    37,    -1,    -1,    -1,    -1,
      -1,    -1,     1,    -1,    -1,    47,    48,    49,     7,    -1,
      -1,    53,    -1,    12,    -1,    14,    15,    16,    17,    18,
      19,    20,    21,    22,    -1,    -1,    -1,    -1,    -1,    -1,
      -1,    -1,    31,    -1,    -1,    34,    35,    36,    37,    -1,
      -1,    -1,    -1,    -1,    -1,     1,    -1,    -1,    47,    48,
      49,     7,    -1,    -1,    53,    -1,    12,    -1,    14,    15,
      16,    17,    18,    19,    20,    21,    22,    -1,    -1,    -1,
      -1,    -1,    -1,    -1,    -1,    31,    -1,    -1,    34,    35,
      36,    37,    -1,    -1,    -1,    -1,    -1,    -1,     1,    -1,
      -1,    47,    48,    49,     7,    -1,    -1,    53,    -1,    12,
      -1,    14,    15,    16,    17,    18,    19,    20,    21,    22,
      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,
      -1,    34,    35,    36,    37,    -1,    -1,    -1,    -1,    -1,
      -1,     1,    -1,    -1,    47,    48,    49,     7,    -1,    -1,
      53,    -1,    12,    -1,    14,    15,    16,    17,    18,    19,
      20,    21,    22,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
      -1,    31,    -1,    -1,    34,    35,    36,    37,    -1,    -1,
      -1,    -1,    -1,    -1,     1,    -1,    -1,    47,    48,    49,
       7,    -1,    -1,    53,    -1,    12,    -1,    14,    15,    16,
      17,    18,    19,    20,    21,    22,    -1,    -1,    -1,    -1,
      -1,    -1,    -1,    -1,    31,    -1,    -1,    34,    35,    36,
      37,    -1,    -1,    -1,    -1,    -1,    -1,     1,    -1,    -1,
      47,    48,    49,     7,    -1,    -1,    53,    -1,    12,    -1,
      14,    15,    16,    17,    18,    19,    20,    21,    22,    -1,
      -1,    -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,    -1,
      34,    35,    36,    37,    -1,    -1,    -1,    -1,    -1,    -1,
      -1,    -1,    -1,    47,    48,    49,     1,     1,    -1,    53,
       5,     6,     7,     8,     9,    10,    11,    12,    13,    13,
      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    23,    24,
      25,    26,    27,    28,    29,    30,    -1,    -1,    32,    -1,
      -1,    -1,    -1,    -1,    38,    39,    40,    41,    43,    -1,
      -1,    -1,    -1,    -1,    -1,    -1,    51,     6,     7,     8,
       9,    10,    11,    12,    13,    14,    15,    16,    17,    18,
      19,    20,    21,    22,    23,    24,    25,    26,    27,    28,
      29,    30,    31,    -1,    -1,    34,    35,    36,    37,    -1,
      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    47,    48,
      49,     7,    -1,    -1,    53,    -1,    12,    -1,    14,    15,
      16,    17,    18,    19,    20,    21,    22,    -1,    -1,    -1,
      -1,    -1,    -1,    -1,    -1,    31,    -1,    33,    34,    35,
      36,    37,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
      -1,    47,    48,    49,     7,    -1,    -1,    53,    -1,    12,
      -1,    14,    15,    16,    17,    18,    19,    20,    21,    22,
      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    31,    -1,
      -1,    34,    35,    36,    37,     6,     7,     8,     9,    10,
      -1,    -1,    -1,    -1,    47,    48,    49,    -1,    -1,    -1,
      53,    -1,    23,    24,    25,    26,    27,    28,    29,    30,
       5,     6,     7,     8,     9,    10,    -1,    12,    13,    -1,
      -1,    -1,    43,    -1,    -1,    -1,    -1,    -1,    23,    24,
      25,    26,    27,    28,    29,    30,     6,     7,     8,     9,
      10,    -1,    -1,    13,     6,     7,     8,     9,    10,    -1,
      -1,    13,    -1,    23,    24,    25,    26,    27,    28,    29,
      30,    23,    24,    25,    26,    27,    28,    29,    30,     6,
       7,     8,     9,    10,    -1,    -1,    13,     6,     7,     8,
       9,    10,    -1,    -1,    13,    -1,    23,    24,    25,    26,
      27,    28,    29,    30,    23,    24,    25,    26,    27,    28,
      29,    30,     6,     7,     8,     9,    10,    -1,    -1,    13,
       6,     7,     8,     9,    10,    11,    -1,    -1,    -1,    23,
      24,    25,    26,    27,    28,    29,    30,    23,    24,    25,
      26,    27,    28,    29,    30,     6,     7,     8,     9,    10,
      -1,    -1,    13,     6,     7,     8,     9,    -1,    -1,    -1,
      -1,    -1,    23,    24,    25,    26,    27,    28,    29,    30,
      23,    24,    25,    26,    27,    28,    29,    30
};

/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
   state STATE-NUM.  */
static const yytype_int8 yystos[] =
{
       0,     6,     7,     8,     9,    10,    11,    12,    13,    14,
      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
      25,    26,    27,    28,    29,    30,    31,    34,    35,    36,
      37,    47,    48,    49,    53,    55,    56,    62,    68,     1,
       1,    12,    56,     1,     1,     1,     1,     1,    56,     1,
      12,    12,    12,    12,    12,    12,    12,    12,    12,     1,
       1,     1,     1,     1,     1,     1,     1,    12,    12,    12,
      12,    12,     1,    51,     1,    56,     0,     5,     6,     7,
       8,     9,    10,    12,    13,    23,    24,    25,    26,    27,
      28,    29,    30,     6,     8,     9,    23,    24,    25,    26,
      27,    28,    29,    30,    13,     1,    49,     1,    49,     1,
      56,     1,    49,     1,    49,     1,    49,     1,    49,     1,
      56,     1,    56,    47,    48,     1,    56,    49,     1,    53,
      60,    61,     1,    56,     1,    56,     1,    56,     1,    56,
       1,    56,     1,    33,    56,    57,     1,     1,     1,    56,
       1,    56,     1,    56,     1,    56,     1,    56,     1,    56,
       1,    56,     1,    56,     1,     1,     1,     1,     1,     1,
       1,     1,     1,     1,     1,    51,    13,    13,    13,    13,
      13,    13,    13,    13,    13,     1,    43,    43,    43,    13,
       1,    43,    52,    13,    12,    11,    11,    63,    64,     1,
      50,     1,    56,     1,    58,     1,    56,    57,    56,    57,
      32,    38,    39,    40,    41,    65,    66,    65,    13,    13,
      13,    59,    65,    42,    42,    42,    42,    42,    13,    43,
      13,    13,    49,    47,    48,    49,    50,    67,    67,    67,
      67,    66
};

/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM.  */
static const yytype_int8 yyr1[] =
{
       0,    54,    55,    55,    55,    55,    55,    55,    55,    55,
      55,    55,    55,    55,    55,    55,    55,    55,    55,    55,
      55,    55,    55,    55,    55,    55,    55,    55,    55,    55,
      55,    56,    56,    56,    56,    56,    56,    56,    56,    56,
      56,    56,    56,    56,    56,    56,    56,    56,    56,    56,
      56,    56,    56,    56,    56,    56,    56,    56,    56,    56,
      56,    56,    56,    56,    56,    56,    56,    56,    56,    56,
      56,    56,    58,    57,    57,    59,    59,    60,    61,    61,
      62,    62,    63,    62,    64,    62,    62,    65,    65,    66,
      66,    66,    66,    66,    67,    67,    67,    67,    68,    68,
      68,    68,    68,    68,    68,    68,    68,    68,    68,    68,
      68,    68,    68,    68,    68,    68,    68,    68,    68,    68,
      68,    68,    68,    68,    68,    68
};

/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM.  */
static const yytype_int8 yyr2[] =
{
       0,     2,     2,     3,     3,     3,     3,     3,     3,     3,
       3,     3,     3,     3,     3,     3,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     3,     2,     4,     1,     1,     1,     3,     3,     3,
       3,     2,     3,     3,     3,     3,     3,     3,     5,     5,
       5,     5,     3,     3,     2,     2,     3,     3,     3,     3,
       2,     3,     3,     3,     3,     3,     3,     3,     3,     2,
       3,     5,     0,     4,     3,     1,     2,     2,     1,     0,
       1,     1,     0,     7,     0,     7,     4,     3,     1,     3,
       3,     3,     3,     3,     1,     1,     1,     1,     4,     4,
       4,     4,     4,     4,     4,     4,     4,     4,     6,     6,
       4,     3,     3,     3,     3,     3,     3,     3,     3,     3,
       3,     5,     3,     4,     5,     3
};


enum { YYENOMEM = -2 };

#define yyerrok         (yyerrstatus = 0)
#define yyclearin       (yychar = DERIVE_EMPTY)

#define YYACCEPT        goto yyacceptlab
#define YYABORT         goto yyabortlab
#define YYERROR         goto yyerrorlab
#define YYNOMEM         goto yyexhaustedlab


#define YYRECOVERING()  (!!yyerrstatus)

#define YYBACKUP(Token, Value)                                    \
  do                                                              \
    if (yychar == DERIVE_EMPTY)                                        \
      {                                                           \
        yychar = (Token);                                         \
        yylval = (Value);                                         \
        YYPOPSTACK (yylen);                                       \
        yystate = *yyssp;                                         \
        goto yybackup;                                            \
      }                                                           \
    else                                                          \
      {                                                           \
        yyerror (YY_("syntax error: cannot back up")); \
        YYERROR;                                                  \
      }                                                           \
  while (0)

/* Backward compatibility with an undocumented macro.
   Use DERIVE_error or DERIVE_UNDEF. */
#define YYERRCODE DERIVE_UNDEF


/* Enable debugging if requested.  */
#if DERIVE_DEBUG

# ifndef YYFPRINTF
#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
#  define YYFPRINTF fprintf
# endif

# define YYDPRINTF(Args)                        \
do {                                            \
  if (yydebug)                                  \
    YYFPRINTF Args;                             \
} while (0)




# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)                    \
do {                                                                      \
  if (yydebug)                                                            \
    {                                                                     \
      YYFPRINTF (stderr, "%s ", Title);                                   \
      yy_symbol_print (stderr,                                            \
                  Kind, Value); \
      YYFPRINTF (stderr, "\n");                                           \
    }                                                                     \
} while (0)


/*-----------------------------------.
| Print this symbol's value on YYO.  |
`-----------------------------------*/

static void
yy_symbol_value_print (FILE *yyo,
                       yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep)
{
  FILE *yyoutput = yyo;
  YY_USE (yyoutput);
  if (!yyvaluep)
    return;
  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
  YY_USE (yykind);
  YY_IGNORE_MAYBE_UNINITIALIZED_END
}


/*---------------------------.
| Print this symbol on YYO.  |
`---------------------------*/

static void
yy_symbol_print (FILE *yyo,
                 yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep)
{
  YYFPRINTF (yyo, "%s %s (",
             yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind));

  yy_symbol_value_print (yyo, yykind, yyvaluep);
  YYFPRINTF (yyo, ")");
}

/*------------------------------------------------------------------.
| yy_stack_print -- Print the state stack from its BOTTOM up to its |
| TOP (included).                                                   |
`------------------------------------------------------------------*/

static void
yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop)
{
  YYFPRINTF (stderr, "Stack now");
  for (; yybottom <= yytop; yybottom++)
    {
      int yybot = *yybottom;
      YYFPRINTF (stderr, " %d", yybot);
    }
  YYFPRINTF (stderr, "\n");
}

# define YY_STACK_PRINT(Bottom, Top)                            \
do {                                                            \
  if (yydebug)                                                  \
    yy_stack_print ((Bottom), (Top));                           \
} while (0)


/*------------------------------------------------.
| Report that the YYRULE is going to be reduced.  |
`------------------------------------------------*/

static void
yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp,
                 int yyrule)
{
  int yylno = yyrline[yyrule];
  int yynrhs = yyr2[yyrule];
  int yyi;
  YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n",
             yyrule - 1, yylno);
  /* The symbols being reduced.  */
  for (yyi = 0; yyi < yynrhs; yyi++)
    {
      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
      yy_symbol_print (stderr,
                       YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]),
                       &yyvsp[(yyi + 1) - (yynrhs)]);
      YYFPRINTF (stderr, "\n");
    }
}

# define YY_REDUCE_PRINT(Rule)          \
do {                                    \
  if (yydebug)                          \
    yy_reduce_print (yyssp, yyvsp, Rule); \
} while (0)

/* Nonzero means print parse trace.  It is left uninitialized so that
   multiple parsers can coexist.  */
int yydebug;
#else /* !DERIVE_DEBUG */
# define YYDPRINTF(Args) ((void) 0)
# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)
# define YY_STACK_PRINT(Bottom, Top)
# define YY_REDUCE_PRINT(Rule)
#endif /* !DERIVE_DEBUG */


/* YYINITDEPTH -- initial size of the parser's stacks.  */
#ifndef YYINITDEPTH
# define YYINITDEPTH 200
#endif

/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
   if the built-in stack extension method is used).

   Do not make this value too large; the results are undefined if
   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
   evaluated with infinite-precision integer arithmetic.  */

#ifndef YYMAXDEPTH
# define YYMAXDEPTH 10000
#endif






/*-----------------------------------------------.
| Release the memory associated to this symbol.  |
`-----------------------------------------------*/

static void
yydestruct (const char *yymsg,
            yysymbol_kind_t yykind, YYSTYPE *yyvaluep)
{
  YY_USE (yyvaluep);
  if (!yymsg)
    yymsg = "Deleting";
  YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp);

  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
  YY_USE (yykind);
  YY_IGNORE_MAYBE_UNINITIALIZED_END
}


/* Lookahead token kind.  */
int yychar;

/* The semantic value of the lookahead symbol.  */
YYSTYPE yylval;
/* Number of syntax errors so far.  */
int yynerrs;




/*----------.
| yyparse.  |
`----------*/

int
yyparse (void)
{
    yy_state_fast_t yystate = 0;
    /* Number of tokens to shift before error messages enabled.  */
    int yyerrstatus = 0;

    /* Refer to the stacks through separate pointers, to allow yyoverflow
       to reallocate them elsewhere.  */

    /* Their size.  */
    YYPTRDIFF_T yystacksize = YYINITDEPTH;

    /* The state stack: array, bottom, top.  */
    yy_state_t yyssa[YYINITDEPTH];
    yy_state_t *yyss = yyssa;
    yy_state_t *yyssp = yyss;

    /* The semantic value stack: array, bottom, top.  */
    YYSTYPE yyvsa[YYINITDEPTH];
    YYSTYPE *yyvs = yyvsa;
    YYSTYPE *yyvsp = yyvs;

  int yyn;
  /* The return value of yyparse.  */
  int yyresult;
  /* Lookahead symbol kind.  */
  yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY;
  /* The variables used to return semantic value and location from the
     action routines.  */
  YYSTYPE yyval;



#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))

  /* The number of symbols on the RHS of the reduced rule.
     Keep to zero when no symbol should be popped.  */
  int yylen = 0;

  YYDPRINTF ((stderr, "Starting parse\n"));

  yychar = DERIVE_EMPTY; /* Cause a token to be read.  */

  goto yysetstate;


/*------------------------------------------------------------.
| yynewstate -- push a new state, which is found in yystate.  |
`------------------------------------------------------------*/
yynewstate:
  /* In all cases, when you get here, the value and location stacks
     have just been pushed.  So pushing a state here evens the stacks.  */
  yyssp++;


/*--------------------------------------------------------------------.
| yysetstate -- set current state (the top of the stack) to yystate.  |
`--------------------------------------------------------------------*/
yysetstate:
  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
  YY_ASSERT (0 <= yystate && yystate < YYNSTATES);
  YY_IGNORE_USELESS_CAST_BEGIN
  *yyssp = YY_CAST (yy_state_t, yystate);
  YY_IGNORE_USELESS_CAST_END
  YY_STACK_PRINT (yyss, yyssp);

  if (yyss + yystacksize - 1 <= yyssp)
#if !defined yyoverflow && !defined YYSTACK_RELOCATE
    YYNOMEM;
#else
    {
      /* Get the current used size of the three stacks, in elements.  */
      YYPTRDIFF_T yysize = yyssp - yyss + 1;

# if defined yyoverflow
      {
        /* Give user a chance to reallocate the stack.  Use copies of
           these so that the &'s don't force the real ones into
           memory.  */
        yy_state_t *yyss1 = yyss;
        YYSTYPE *yyvs1 = yyvs;

        /* Each stack pointer address is followed by the size of the
           data in use in that stack, in bytes.  This used to be a
           conditional around just the two extra args, but that might
           be undefined if yyoverflow is a macro.  */
        yyoverflow (YY_("memory exhausted"),
                    &yyss1, yysize * YYSIZEOF (*yyssp),
                    &yyvs1, yysize * YYSIZEOF (*yyvsp),
                    &yystacksize);
        yyss = yyss1;
        yyvs = yyvs1;
      }
# else /* defined YYSTACK_RELOCATE */
      /* Extend the stack our own way.  */
      if (YYMAXDEPTH <= yystacksize)
        YYNOMEM;
      yystacksize *= 2;
      if (YYMAXDEPTH < yystacksize)
        yystacksize = YYMAXDEPTH;

      {
        yy_state_t *yyss1 = yyss;
        union yyalloc *yyptr =
          YY_CAST (union yyalloc *,
                   YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize))));
        if (! yyptr)
          YYNOMEM;
        YYSTACK_RELOCATE (yyss_alloc, yyss);
        YYSTACK_RELOCATE (yyvs_alloc, yyvs);
#  undef YYSTACK_RELOCATE
        if (yyss1 != yyssa)
          YYSTACK_FREE (yyss1);
      }
# endif

      yyssp = yyss + yysize - 1;
      yyvsp = yyvs + yysize - 1;

      YY_IGNORE_USELESS_CAST_BEGIN
      YYDPRINTF ((stderr, "Stack size increased to %ld\n",
                  YY_CAST (long, yystacksize)));
      YY_IGNORE_USELESS_CAST_END

      if (yyss + yystacksize - 1 <= yyssp)
        YYABORT;
    }
#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */


  if (yystate == YYFINAL)
    YYACCEPT;

  goto yybackup;


/*-----------.
| yybackup.  |
`-----------*/
yybackup:
  /* Do appropriate processing given the current state.  Read a
     lookahead token if we need one and don't already have one.  */

  /* First try to decide what to do without reference to lookahead token.  */
  yyn = yypact[yystate];
  if (yypact_value_is_default (yyn))
    goto yydefault;

  /* Not known => get a lookahead token if don't already have one.  */

  /* YYCHAR is either empty, or end-of-input, or a valid lookahead.  */
  if (yychar == DERIVE_EMPTY)
    {
      YYDPRINTF ((stderr, "Reading a token\n"));
      yychar = yylex ();
    }

  if (yychar <= DERIVE_EOF)
    {
      yychar = DERIVE_EOF;
      yytoken = YYSYMBOL_YYEOF;
      YYDPRINTF ((stderr, "Now at end of input.\n"));
    }
  else if (yychar == DERIVE_error)
    {
      /* The scanner already issued an error message, process directly
         to error recovery.  But do not keep the error token as
         lookahead, it is too special and may lead us to an endless
         loop in error recovery. */
      yychar = DERIVE_UNDEF;
      yytoken = YYSYMBOL_YYerror;
      goto yyerrlab1;
    }
  else
    {
      yytoken = YYTRANSLATE (yychar);
      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
    }

  /* If the proper action on seeing token YYTOKEN is to reduce or to
     detect an error, take that action.  */
  yyn += yytoken;
  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
    goto yydefault;
  yyn = yytable[yyn];
  if (yyn <= 0)
    {
      if (yytable_value_is_error (yyn))
        goto yyerrlab;
      yyn = -yyn;
      goto yyreduce;
    }

  /* Count tokens shifted since error; after three, turn off error
     status.  */
  if (yyerrstatus)
    yyerrstatus--;

  /* Shift the lookahead token.  */
  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
  yystate = yyn;
  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
  *++yyvsp = yylval;
  YY_IGNORE_MAYBE_UNINITIALIZED_END

  /* Discard the shifted token.  */
  yychar = DERIVE_EMPTY;
  goto yynewstate;


/*-----------------------------------------------------------.
| yydefault -- do the default action for the current state.  |
`-----------------------------------------------------------*/
yydefault:
  yyn = yydefact[yystate];
  if (yyn == 0)
    goto yyerrlab;
  goto yyreduce;


/*-----------------------------.
| yyreduce -- do a reduction.  |
`-----------------------------*/
yyreduce:
  /* yyn is the number of a rule to reduce with.  */
  yylen = yyr2[yyn];

  /* If YYLEN is nonzero, implement the default value of the action:
     '$$ = $1'.

     Otherwise, the following line sets YYVAL to garbage.
     This behavior is undocumented and Bison
     users should not rely upon it.  Assigning to YYVAL
     unconditionally makes the parser a bit smaller, and it avoids a
     GCC warning that YYVAL may be used uninitialized.  */
  yyval = yyvsp[1-yylen];


  YY_REDUCE_PRINT (yyn);
  switch (yyn)
    {
  case 2: /* defn: expr L_EOS  */
#line 4722 "derive_parser.y"
                { parse_tree = (yyval.n); YYACCEPT;  }
#line 6212 "derive_parser.tab.c"
    break;

  case 3: /* defn: expr L_PLUS error  */
#line 4726 "derive_parser.y"
                { gramerr(unexpected_str, NULL, "+"); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6218 "derive_parser.tab.c"
    break;

  case 4: /* defn: expr L_STAR error  */
#line 4729 "derive_parser.y"
                { gramerr(unexpected_str, NULL, "*"); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6224 "derive_parser.tab.c"
    break;

  case 5: /* defn: expr L_SLASH error  */
#line 4731 "derive_parser.y"
                { gramerr(unexpected_str, NULL, "/"); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6230 "derive_parser.tab.c"
    break;

  case 6: /* defn: expr L_LPAREN error  */
#line 4733 "derive_parser.y"
                { gramerr(unexpected_str, NULL, "("); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6236 "derive_parser.tab.c"
    break;

  case 7: /* defn: expr L_RPAREN error  */
#line 4735 "derive_parser.y"
                { gramerr(unexpected_str, NULL, ")"); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6242 "derive_parser.tab.c"
    break;

  case 8: /* defn: expr L_LT error  */
#line 4737 "derive_parser.y"
                { gramerr(unexpected_str, NULL, "<"); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6248 "derive_parser.tab.c"
    break;

  case 9: /* defn: expr L_LEQ error  */
#line 4739 "derive_parser.y"
                { gramerr(unexpected_str, NULL, "<="); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6254 "derive_parser.tab.c"
    break;

  case 10: /* defn: expr L_EQ error  */
#line 4741 "derive_parser.y"
                { gramerr(unexpected_str, NULL, "=="); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6260 "derive_parser.tab.c"
    break;

  case 11: /* defn: expr L_GEQ error  */
#line 4743 "derive_parser.y"
                { gramerr(unexpected_str, NULL, ">="); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6266 "derive_parser.tab.c"
    break;

  case 12: /* defn: expr L_GT error  */
#line 4745 "derive_parser.y"
                { gramerr(unexpected_str, NULL, ">"); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6272 "derive_parser.tab.c"
    break;

  case 13: /* defn: expr L_NEQ error  */
#line 4747 "derive_parser.y"
                { gramerr(unexpected_str, NULL, "!="); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6278 "derive_parser.tab.c"
    break;

  case 14: /* defn: expr L_AND error  */
#line 4749 "derive_parser.y"
                { gramerr(unexpected_str, NULL, "&&"); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6284 "derive_parser.tab.c"
    break;

  case 15: /* defn: expr L_OR error  */
#line 4751 "derive_parser.y"
                { gramerr(unexpected_str, NULL, "||"); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6290 "derive_parser.tab.c"
    break;

  case 16: /* defn: L_PLUS error  */
#line 4756 "derive_parser.y"
                { gramerr(initial_str, NULL, "+"); YYERROR; }
#line 6296 "derive_parser.tab.c"
    break;

  case 17: /* defn: L_STAR error  */
#line 4759 "derive_parser.y"
                { gramerr(initial_str, NULL, "*"); YYERROR; }
#line 6302 "derive_parser.tab.c"
    break;

  case 18: /* defn: L_SLASH error  */
#line 4761 "derive_parser.y"
                { gramerr(initial_str, NULL, "/"); YYERROR; }
#line 6308 "derive_parser.tab.c"
    break;

  case 19: /* defn: L_LPAREN error  */
#line 4763 "derive_parser.y"
                { gramerr(initial_str, NULL, "("); YYERROR; }
#line 6314 "derive_parser.tab.c"
    break;

  case 20: /* defn: L_RPAREN error  */
#line 4765 "derive_parser.y"
                { gramerr(initial_str, NULL, ")"); YYERROR; }
#line 6320 "derive_parser.tab.c"
    break;

  case 21: /* defn: L_LT error  */
#line 4767 "derive_parser.y"
                { gramerr(initial_str, NULL, "<"); YYERROR; }
#line 6326 "derive_parser.tab.c"
    break;

  case 22: /* defn: L_LEQ error  */
#line 4769 "derive_parser.y"
                { gramerr(initial_str, NULL, "<="); YYERROR; }
#line 6332 "derive_parser.tab.c"
    break;

  case 23: /* defn: L_EQ error  */
#line 4771 "derive_parser.y"
                { gramerr(initial_str, NULL, "=="); YYERROR; }
#line 6338 "derive_parser.tab.c"
    break;

  case 24: /* defn: L_GEQ error  */
#line 4773 "derive_parser.y"
                { gramerr(initial_str, NULL, ">="); YYERROR; }
#line 6344 "derive_parser.tab.c"
    break;

  case 25: /* defn: L_GT error  */
#line 4775 "derive_parser.y"
                { gramerr(initial_str, NULL, ">"); YYERROR; }
#line 6350 "derive_parser.tab.c"
    break;

  case 26: /* defn: L_NEQ error  */
#line 4777 "derive_parser.y"
                { gramerr(initial_str, NULL, "!="); YYERROR; }
#line 6356 "derive_parser.tab.c"
    break;

  case 27: /* defn: L_AND error  */
#line 4779 "derive_parser.y"
                { gramerr(initial_str, NULL, "&&"); YYERROR; }
#line 6362 "derive_parser.tab.c"
    break;

  case 28: /* defn: L_OR error  */
#line 4781 "derive_parser.y"
                { gramerr(initial_str, NULL, "||"); YYERROR; }
#line 6368 "derive_parser.tab.c"
    break;

  case 29: /* defn: L_QUEST error  */
#line 4783 "derive_parser.y"
                { gramerr(initial_str, NULL, "?"); YYERROR; }
#line 6374 "derive_parser.tab.c"
    break;

  case 30: /* defn: L_COLON error  */
#line 4785 "derive_parser.y"
                { gramerr(initial_str, NULL, ":"); YYERROR; }
#line 6380 "derive_parser.tab.c"
    break;

  case 31: /* expr: L_LPAREN expr L_RPAREN  */
#line 4790 "derive_parser.y"
                { (yyval.n) = (yyvsp[-1].n); }
#line 6386 "derive_parser.tab.c"
    break;

  case 32: /* expr: L_NAME L_INSTNAME  */
#line 4792 "derive_parser.y"
                { np = newnode(N_FILTERINST);
		  np->right = newnode(N_NAME);
		  np->right->value = (yyvsp[-1].s);
		  np->left = newnode(N_PATTERN);
		  np->left->value = (yyvsp[0].s);
		  if ((np->left->data.pattern = (pattern_t *)malloc(sizeof(pattern_t))) == NULL) {
		      PM_UNLOCK(registered.mutex);
		      pmNoMem("pmRegisterDerived: alloc pattern", (int)sizeof(pattern_t), PM_FATAL_ERR);
		      /*NOTREACHED*/
		  }
		  np->left->data.pattern->ftype = F_EXACT;
		  np->left->data.pattern->inst = PM_IN_NULL;
		  (yyval.n) = np;
		}
#line 6405 "derive_parser.tab.c"
    break;

  case 33: /* expr: L_LPAREN expr L_RPAREN L_INSTNAME  */
#line 4807 "derive_parser.y"
                { np = newnode(N_FILTERINST);
		  np->right = (yyvsp[-2].n);
		  np->left = newnode(N_PATTERN);
		  np->left->value = (yyvsp[0].s);
		  if ((np->left->data.pattern = (pattern_t *)malloc(sizeof(pattern_t))) == NULL) {
		      PM_UNLOCK(registered.mutex);
		      pmNoMem("pmRegisterDerived: alloc pattern", (int)sizeof(pattern_t), PM_FATAL_ERR);
		      /*NOTREACHED*/
		  }
		  np->left->data.pattern->ftype = F_EXACT;
		  np->left->data.pattern->inst = PM_IN_NULL;
		  (yyval.n) = np;
		}
#line 6423 "derive_parser.tab.c"
    break;

  case 34: /* expr: num  */
#line 4821 "derive_parser.y"
                { (yyval.n) = (yyvsp[0].n); }
#line 6429 "derive_parser.tab.c"
    break;

  case 35: /* expr: L_NAME  */
#line 4823 "derive_parser.y"
                { np = newnode(N_NAME);
		  np->value = (yyvsp[0].s);
		  (yyval.n) = np;
		}
#line 6438 "derive_parser.tab.c"
    break;

  case 36: /* expr: func  */
#line 4828 "derive_parser.y"
                { (yyval.n) = (yyvsp[0].n); }
#line 6444 "derive_parser.tab.c"
    break;

  case 37: /* expr: expr L_PLUS expr  */
#line 4832 "derive_parser.y"
                { np = newnode(N_PLUS); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6450 "derive_parser.tab.c"
    break;

  case 38: /* expr: expr L_MINUS expr  */
#line 4834 "derive_parser.y"
                { np = newnode(N_MINUS); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6456 "derive_parser.tab.c"
    break;

  case 39: /* expr: expr L_STAR expr  */
#line 4836 "derive_parser.y"
                { np = newnode(N_STAR); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6462 "derive_parser.tab.c"
    break;

  case 40: /* expr: expr L_SLASH expr  */
#line 4838 "derive_parser.y"
                { np = newnode(N_SLASH); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6468 "derive_parser.tab.c"
    break;

  case 41: /* expr: L_MINUS expr  */
#line 4840 "derive_parser.y"
                { np = newnode(N_NEG); np->left = (yyvsp[0].n); (yyval.n) = np; }
#line 6474 "derive_parser.tab.c"
    break;

  case 42: /* expr: expr L_LT expr  */
#line 4844 "derive_parser.y"
                { np = newnode(N_LT); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6480 "derive_parser.tab.c"
    break;

  case 43: /* expr: expr L_LEQ expr  */
#line 4846 "derive_parser.y"
                { np = newnode(N_LEQ); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6486 "derive_parser.tab.c"
    break;

  case 44: /* expr: expr L_EQ expr  */
#line 4848 "derive_parser.y"
                { np = newnode(N_EQ); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6492 "derive_parser.tab.c"
    break;

  case 45: /* expr: expr L_GEQ expr  */
#line 4850 "derive_parser.y"
                { np = newnode(N_GEQ); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6498 "derive_parser.tab.c"
    break;

  case 46: /* expr: expr L_GT expr  */
#line 4852 "derive_parser.y"
                { np = newnode(N_GT); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6504 "derive_parser.tab.c"
    break;

  case 47: /* expr: expr L_NEQ expr  */
#line 4854 "derive_parser.y"
                { np = newnode(N_NEQ); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6510 "derive_parser.tab.c"
    break;

  case 48: /* expr: expr L_QUEST expr L_COLON expr  */
#line 4856 "derive_parser.y"
                { np = newnode(N_QUEST);
		  np->left = (yyvsp[-4].n);
		  np->right = newnode(N_COLON);
		  np->right->left = (yyvsp[-2].n);
		  np->right->right = (yyvsp[0].n);
		  (yyval.n) = np;
		}
#line 6522 "derive_parser.tab.c"
    break;

  case 49: /* expr: expr L_QUEST novalue L_COLON expr  */
#line 4864 "derive_parser.y"
                { np = newnode(N_QUEST);
		  np->left = (yyvsp[-4].n);
		  np->right = newnode(N_COLON);
		  np->right->left = (yyvsp[-2].n);
		  np->right->right = (yyvsp[0].n);
		  (yyval.n) = np;
		}
#line 6534 "derive_parser.tab.c"
    break;

  case 50: /* expr: expr L_QUEST expr L_COLON novalue  */
#line 4872 "derive_parser.y"
                { np = newnode(N_QUEST);
		  np->left = (yyvsp[-4].n);
		  np->right = newnode(N_COLON);
		  np->right->left = (yyvsp[-2].n);
		  np->right->right = (yyvsp[0].n);
		  (yyval.n) = np;
		}
#line 6546 "derive_parser.tab.c"
    break;

  case 51: /* expr: expr L_QUEST novalue L_COLON novalue  */
#line 4880 "derive_parser.y"
                { PM_TPD(derive_errmsg) = "novalue() may appear at most once in a ternary expression";
		  free_expr((yyvsp[-4].n));
		  free_expr((yyvsp[-2].n));
		  free_expr((yyvsp[0].n));
		  YYERROR;
		}
#line 6557 "derive_parser.tab.c"
    break;

  case 52: /* expr: expr L_AND expr  */
#line 4889 "derive_parser.y"
                { np = newnode(N_AND); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6563 "derive_parser.tab.c"
    break;

  case 53: /* expr: expr L_OR expr  */
#line 4891 "derive_parser.y"
                { np = newnode(N_OR); np->left = (yyvsp[-2].n); np->right = (yyvsp[0].n); (yyval.n) = np; }
#line 6569 "derive_parser.tab.c"
    break;

  case 54: /* expr: L_NOT expr  */
#line 4893 "derive_parser.y"
                { np = newnode(N_NOT); np->left = (yyvsp[0].n); (yyval.n) = np; }
#line 6575 "derive_parser.tab.c"
    break;

  case 55: /* expr: L_NAME error  */
#line 4897 "derive_parser.y"
                { gramerr(op_str, follow, __dmnode_type_str(N_NAME)); YYERROR; }
#line 6581 "derive_parser.tab.c"
    break;

  case 56: /* expr: expr L_PLUS error  */
#line 4899 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_PLUS)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6587 "derive_parser.tab.c"
    break;

  case 57: /* expr: expr L_MINUS error  */
#line 4901 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_MINUS)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6593 "derive_parser.tab.c"
    break;

  case 58: /* expr: expr L_STAR error  */
#line 4903 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_STAR)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6599 "derive_parser.tab.c"
    break;

  case 59: /* expr: expr L_SLASH error  */
#line 4905 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_SLASH)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6605 "derive_parser.tab.c"
    break;

  case 60: /* expr: L_MINUS error  */
#line 4907 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_NEG)); YYERROR; }
#line 6611 "derive_parser.tab.c"
    break;

  case 61: /* expr: expr L_LT error  */
#line 4909 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_LT)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6617 "derive_parser.tab.c"
    break;

  case 62: /* expr: expr L_LEQ error  */
#line 4911 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_LEQ)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6623 "derive_parser.tab.c"
    break;

  case 63: /* expr: expr L_EQ error  */
#line 4913 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_EQ)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6629 "derive_parser.tab.c"
    break;

  case 64: /* expr: expr L_GEQ error  */
#line 4915 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_GEQ)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6635 "derive_parser.tab.c"
    break;

  case 65: /* expr: expr L_GT error  */
#line 4917 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_GT)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6641 "derive_parser.tab.c"
    break;

  case 66: /* expr: expr L_NEQ error  */
#line 4919 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_NEQ)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6647 "derive_parser.tab.c"
    break;

  case 67: /* expr: expr L_AND error  */
#line 4921 "derive_parser.y"
                { gramerr(bexpr_str, follow, __dmnode_type_str(N_AND)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6653 "derive_parser.tab.c"
    break;

  case 68: /* expr: expr L_OR error  */
#line 4923 "derive_parser.y"
                { gramerr(bexpr_str, follow, __dmnode_type_str(N_OR)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6659 "derive_parser.tab.c"
    break;

  case 69: /* expr: L_NOT error  */
#line 4925 "derive_parser.y"
                { gramerr(bexpr_str, follow, __dmnode_type_str(N_NOT)); YYERROR; }
#line 6665 "derive_parser.tab.c"
    break;

  case 70: /* expr: expr L_QUEST error  */
#line 4927 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_QUEST)); free_expr((yyvsp[-2].n)); YYERROR; }
#line 6671 "derive_parser.tab.c"
    break;

  case 71: /* expr: expr L_QUEST expr L_COLON error  */
#line 4929 "derive_parser.y"
                { gramerr(aexpr_str, follow, __dmnode_type_str(N_COLON));
		  free_expr((yyvsp[-4].n));
		  free_expr((yyvsp[-2].n));
		  YYERROR;
		}
#line 6681 "derive_parser.tab.c"
    break;

  case 72: /* $@1: %empty  */
#line 4937 "derive_parser.y"
                { np = newnode(N_NOVALUE);
		  /*
		   * defaults ... not needed, but makes __dmdumpexpr()
		   * happy in case is is called
		   */
		  specdesc.pmid = PM_ID_NULL;
		  specdesc.type = PM_TYPE_32;
		  specdesc.indom = PM_INDOM_NULL;
		  specdesc.sem = PM_SEM_DISCRETE;
		  specdesc.units = noUnits;
		}
#line 6697 "derive_parser.tab.c"
    break;

  case 73: /* novalue: L_NOVALUE L_LPAREN $@1 novalueparam  */
#line 4948 "derive_parser.y"
                { np->desc = specdesc;	/* struct copy */
		  if (specmeta != NULL) {
		      np->left = specmeta;
		      specmeta = NULL;
		  }
		  np->flags = specflags;
		  specflags = 0;
		  (yyval.n) = np;
		}
#line 6711 "derive_parser.tab.c"
    break;

  case 74: /* novalue: L_NOVALUE L_LPAREN error  */
#line 4958 "derive_parser.y"
                { gramerr("metadata keyword", follow, "novalue("); YYERROR; }
#line 6717 "derive_parser.tab.c"
    break;

  case 77: /* regexpr: opt_bang L_PATTERN  */
#line 4966 "derive_parser.y"
                { int	lsts;
		  np = newnode(N_PATTERN);
		  np->value = (yyvsp[0].s);
		  if ((np->data.pattern = (pattern_t *)malloc(sizeof(pattern_t))) == NULL) {
		      PM_UNLOCK(registered.mutex);
		      pmNoMem("pmRegisterDerived: alloc pattern", (int)sizeof(pattern_t), PM_FATAL_ERR);
		      /*NOTREACHED*/
		  }
		  np->data.pattern->ftype = F_REGEX;
		  if ((lsts = regcomp(&np->data.pattern->regex, np->value, REG_EXTENDED|REG_NOSUB)) != 0) {
		      /* regcomp() failed ... */
		      char	errmsg[128];
		      regerror(lsts, &np->data.pattern->regex, errmsg, sizeof(errmsg));
		      gramerr("bad regular expression:", NULL, errmsg);
		      derive_error(NULL);
		      return -1;
		  }
		  np->data.pattern->invert = (yyvsp[-1].i);
		  np->data.pattern->used = 0;
		  __pmHashInit(&np->data.pattern->hash);
		  (yyval.n) = np;
		}
#line 6744 "derive_parser.tab.c"
    break;

  case 78: /* opt_bang: L_NOT  */
#line 4990 "derive_parser.y"
                { (yyval.i) = 1; }
#line 6750 "derive_parser.tab.c"
    break;

  case 79: /* opt_bang: %empty  */
#line 4992 "derive_parser.y"
                { (yyval.i) = 0; }
#line 6756 "derive_parser.tab.c"
    break;

  case 80: /* num: L_INTEGER  */
#line 4995 "derive_parser.y"
                { np = newnode(N_INTEGER);
		  np->value = (yyvsp[0].s);
		  np->desc.pmid = PM_ID_NULL;
		  np->desc.type = PM_TYPE_U32;
		  np->desc.indom = PM_INDOM_NULL;
		  np->desc.sem = PM_SEM_DISCRETE;
		  np->desc.units = noUnits;
		  (yyval.n) = np;
		}
#line 6770 "derive_parser.tab.c"
    break;

  case 81: /* num: L_DOUBLE  */
#line 5005 "derive_parser.y"
                { np = newnode(N_DOUBLE);
		  np->value = (yyvsp[0].s);
		  np->desc.pmid = PM_ID_NULL;
		  np->desc.type = PM_TYPE_DOUBLE;
		  np->desc.indom = PM_INDOM_NULL;
		  np->desc.sem = PM_SEM_DISCRETE;
		  np->desc.units = noUnits;
		  (yyval.n) = np;
		}
#line 6784 "derive_parser.tab.c"
    break;

  case 82: /* $@2: %empty  */
#line 5015 "derive_parser.y"
                { np = newnode(N_INTEGER);
		  np->value = (yyvsp[-1].s);
		  /* defaults ... */
		  specdesc.pmid = PM_ID_NULL;
		  specdesc.type = PM_TYPE_U32;
		  specdesc.indom = PM_INDOM_NULL;
		  specdesc.sem = PM_SEM_DISCRETE;
		  specdesc.units = noUnits;
		}
#line 6798 "derive_parser.tab.c"
    break;

  case 83: /* num: L_MKCONST L_LPAREN L_INTEGER L_COMMA $@2 speclist L_RPAREN  */
#line 5025 "derive_parser.y"
                { np->desc = specdesc;	/* struct copy */
		  if (specmeta != NULL) {
		      np->left = specmeta;
		      specmeta = NULL;
		  }
		  np->flags = specflags;
		  specflags = 0;
		  (yyval.n) = np;
		}
#line 6812 "derive_parser.tab.c"
    break;

  case 84: /* $@3: %empty  */
#line 5035 "derive_parser.y"
                { np = newnode(N_DOUBLE);
		  np->value = (yyvsp[-1].s);
		  /* defaults ... */
		  specdesc.pmid = PM_ID_NULL;
		  specdesc.type = PM_TYPE_DOUBLE;
		  specdesc.indom = PM_INDOM_NULL;
		  specdesc.sem = PM_SEM_DISCRETE;
		  specdesc.units = noUnits;
		}
#line 6826 "derive_parser.tab.c"
    break;

  case 85: /* num: L_MKCONST L_LPAREN L_DOUBLE L_COMMA $@3 speclist L_RPAREN  */
#line 5045 "derive_parser.y"
                { if (specdesc.type != PM_TYPE_DOUBLE &&
		      specdesc.type != PM_TYPE_FLOAT) {
		      char	strbuf[20];
		      gramerr("Incompatible floating point constant and type", NULL, pmTypeStr_r(specdesc.type, strbuf, sizeof(strbuf)));
		      derive_error(NULL);
		      return -1;
		  }
		  np->desc = specdesc;	/* struct copy */
		  if (specmeta != NULL) {
		      np->left = specmeta;
		      specmeta = NULL;
		  }
		  np->flags = specflags;
		  specflags = 0;
		  (yyval.n) = np;
		}
#line 6847 "derive_parser.tab.c"
    break;

  case 86: /* num: L_MKCONST L_LPAREN L_INTEGER error  */
#line 5062 "derive_parser.y"
                { gramerr("metadata keyword", follow, "mkconst(type"); YYERROR; }
#line 6853 "derive_parser.tab.c"
    break;

  case 89: /* spec: L_TYPE L_ASSIGN specval  */
#line 5071 "derive_parser.y"
                { if (strcasecmp((yyvsp[0].s), "32") == 0)
		      specdesc.type = PM_TYPE_32;
		  else if (strcasecmp((yyvsp[0].s), "U32") == 0)
		      specdesc.type = PM_TYPE_U32;
		  else if (strcasecmp((yyvsp[0].s), "64") == 0)
		      specdesc.type = PM_TYPE_64;
		  else if (strcasecmp((yyvsp[0].s), "U64") == 0)
		      specdesc.type = PM_TYPE_U64;
		  else if (strcasecmp((yyvsp[0].s), "FLOAT") == 0)
		      specdesc.type = PM_TYPE_FLOAT;
		  else if (strcasecmp((yyvsp[0].s), "DOUBLE") == 0)
		      specdesc.type = PM_TYPE_DOUBLE;
		  else {
		      gramerr("Unrecognized value for type", NULL, (yyvsp[0].s));
		      derive_error(NULL);
		      return -1;
		  }
		  free((yyvsp[0].s));
		  specflags |= META_TYPE;
		  (yyval.d) = specdesc;
		}
#line 6879 "derive_parser.tab.c"
    break;

  case 90: /* spec: L_SEMANTICS L_ASSIGN specval  */
#line 5093 "derive_parser.y"
                { if (strcasecmp((yyvsp[0].s), "counter") == 0)
		      specdesc.sem = PM_SEM_COUNTER;
		  else if (strcasecmp((yyvsp[0].s), "instant") == 0)
		      specdesc.sem = PM_SEM_INSTANT;
		  else if (strcasecmp((yyvsp[0].s), "discrete") == 0)
		      specdesc.sem = PM_SEM_DISCRETE;
		  else {
		      gramerr("Unrecognized value for semantics", NULL, (yyvsp[0].s));
		      derive_error(NULL);
		      return -1;
		  }
		  free((yyvsp[0].s));
		  specflags |= META_SEM;
		  (yyval.d) = specdesc;
		}
#line 6899 "derive_parser.tab.c"
    break;

  case 91: /* spec: L_UNITS L_ASSIGN specval  */
#line 5109 "derive_parser.y"
                { double		mult;
		  struct pmUnits	units;
		  char			*errmsg;
		  if (pmParseUnitsStr((yyvsp[0].s), &units, &mult, &errmsg) == 0) {
		      specdesc.units = units;
		  }
		  else {
		      gramerr("Illegal units:", NULL, errmsg);
		      free(errmsg);
		      derive_error(NULL);
		      return -1;
		  }
		  free((yyvsp[0].s));
		  specflags |= META_UNITS;
		  (yyval.d) = specdesc;
		}
#line 6920 "derive_parser.tab.c"
    break;

  case 92: /* spec: L_INDOM L_ASSIGN specval  */
#line 5126 "derive_parser.y"
                { int		ok = 1;
		  if (strcasecmp((yyvsp[0].s), "null") == 0) {
		      specdesc.indom = PM_INDOM_NULL;
		  }
		  else {
		      unsigned int	domain;
		      unsigned int	serial;
		      int		nscanned;
		      /*
		       * if strlen($3) != nscanned we have something bad
		       * like "123.456foo"
		       * - unsigned => no need for >= 0 test (thanks Coverity)
		       */
		      if (sscanf((yyvsp[0].s), "%u.%u%n", &domain, &serial, &nscanned) == 2 &&
			  (int)strlen((yyvsp[0].s)) == nscanned && domain < 511 && serial < (1<<22)) {
			  /* valid indom number */
			  specdesc.indom = pmInDom_build(domain, serial);
		      }
		      else
			ok = 0;
		  }
		  if (!ok) {
		      gramerr("Illegal indom:", NULL, (yyvsp[0].s));
		      derive_error(NULL);
		      return -1;
		  }
		  specflags |= META_INDOM;
		  free((yyvsp[0].s));
		  (yyval.d) = specdesc;
		}
#line 6955 "derive_parser.tab.c"
    break;

  case 93: /* spec: L_META L_ASSIGN L_NAME  */
#line 5157 "derive_parser.y"
{ specmeta = newnode(N_NAME);
    specmeta->value = (yyvsp[0].s);
		  (yyval.d) = specdesc;	/* not used from this rule */
		}
#line 6964 "derive_parser.tab.c"
    break;

  case 98: /* func: L_ANON L_LPAREN L_NAME L_RPAREN  */
#line 5170 "derive_parser.y"
                { np = newnode(N_ANON);
		  np->left = newnode(N_NAME);
		  np->left->value = (yyvsp[-1].s);
		  if (strcmp((yyvsp[-1].s), "PM_TYPE_32") == 0)
		      np->left->desc.type = PM_TYPE_32;
		  else if (strcmp((yyvsp[-1].s), "PM_TYPE_U32") == 0)
		      np->left->desc.type = PM_TYPE_U32;
		  else if (strcmp((yyvsp[-1].s), "PM_TYPE_64") == 0)
		      np->left->desc.type = PM_TYPE_64;
		  else if (strcmp((yyvsp[-1].s), "PM_TYPE_U64") == 0)
		      np->left->desc.type = PM_TYPE_U64;
		  else if (strcmp((yyvsp[-1].s), "PM_TYPE_FLOAT") == 0)
		      np->left->desc.type = PM_TYPE_FLOAT;
		  else if (strcmp((yyvsp[-1].s), "PM_TYPE_DOUBLE") == 0)
		      np->left->desc.type = PM_TYPE_DOUBLE;
		  else {
		      fprintf(stderr, "Error: type=%s not allowed for anon()\n", (yyvsp[-1].s));
		      free_expr(np->left);
		      free_expr(np);
		      (yyval.n) = NULL;
		  }
		  np->left->desc.pmid = PM_ID_NULL;
		  np->left->desc.indom = PM_INDOM_NULL;
		  np->left->desc.sem = PM_SEM_DISCRETE;
		  memset((void *)&np->left->desc.units, 0, sizeof(np->left->desc.units));
		  np->left->type = N_INTEGER;
		  (yyval.n) = np;
		}
#line 6997 "derive_parser.tab.c"
    break;

  case 99: /* func: L_AVG L_LPAREN L_NAME L_RPAREN  */
#line 5199 "derive_parser.y"
                { np = newnode(N_AVG);
		  np->left = newnode(N_NAME);
		  np->left->value = (yyvsp[-1].s);
		  (yyval.n) = np;
		}
#line 7007 "derive_parser.tab.c"
    break;

  case 100: /* func: L_COUNT L_LPAREN L_NAME L_RPAREN  */
#line 5205 "derive_parser.y"
                { np = newnode(N_COUNT);
		  np->left = newnode(N_NAME);
		  np->left->value = (yyvsp[-1].s);
		  (yyval.n) = np;
		}
#line 7017 "derive_parser.tab.c"
    break;

  case 101: /* func: L_DELTA L_LPAREN expr L_RPAREN  */
#line 5211 "derive_parser.y"
                { np = newnode(N_DELTA);
		  np->left = (yyvsp[-1].n);
		  np->left->save_last = 1;
		  (yyval.n) = np;
		}
#line 7027 "derive_parser.tab.c"
    break;

  case 102: /* func: L_MAX L_LPAREN L_NAME L_RPAREN  */
#line 5217 "derive_parser.y"
                { np = newnode(N_MAX);
		  np->left = newnode(N_NAME);
		  np->left->value = (yyvsp[-1].s);
		  (yyval.n) = np;
		}
#line 7037 "derive_parser.tab.c"
    break;

  case 103: /* func: L_MIN L_LPAREN L_NAME L_RPAREN  */
#line 5223 "derive_parser.y"
                { np = newnode(N_MIN);
		  np->left = newnode(N_NAME);
		  np->left->value = (yyvsp[-1].s);
		  (yyval.n) = np;
		}
#line 7047 "derive_parser.tab.c"
    break;

  case 104: /* func: L_SUM L_LPAREN L_NAME L_RPAREN  */
#line 5229 "derive_parser.y"
                { np = newnode(N_SUM);
		  np->left = newnode(N_NAME);
		  np->left->value = (yyvsp[-1].s);
		  (yyval.n) = np;
		}
#line 7057 "derive_parser.tab.c"
    break;

  case 105: /* func: L_RATE L_LPAREN expr L_RPAREN  */
#line 5235 "derive_parser.y"
                { np = newnode(N_RATE);
		  np->left = (yyvsp[-1].n);
		  np->left->save_last = 1;
		  (yyval.n) = np;
		}
#line 7067 "derive_parser.tab.c"
    break;

  case 106: /* func: L_INSTANT L_LPAREN expr L_RPAREN  */
#line 5241 "derive_parser.y"
                { np = newnode(N_INSTANT);
		  np->left = (yyvsp[-1].n);
		  (yyval.n) = np;
		}
#line 7076 "derive_parser.tab.c"
    break;

  case 107: /* func: L_DEFINED L_LPAREN L_NAME L_RPAREN  */
#line 5246 "derive_parser.y"
                { np = newnode(N_DEFINED);
		  np->left = newnode(N_NAME);
		  np->left->value = (yyvsp[-1].s);
		  (yyval.n) = np;
		}
#line 7086 "derive_parser.tab.c"
    break;

  case 108: /* func: L_RESCALE L_LPAREN expr L_COMMA L_STRING L_RPAREN  */
#line 5252 "derive_parser.y"
                { double		mult;
		  struct pmUnits	units;
		  char			*errmsg;
		  np = newnode(N_RESCALE);
		  np->left = (yyvsp[-3].n);
		  if (pmParseUnitsStr((yyvsp[-1].s), &units, &mult, &errmsg) < 0) {
		      gramerr("Illegal units:", NULL, errmsg);
		      free(errmsg);
		      derive_error(NULL);
		      return -1;
		  }
		  np->right = newnode(N_SCALE);
		  np->right->desc.units = units;	/* struct copy */
		  free((yyvsp[-1].s));
		  (yyval.n) = np;
		}
#line 7107 "derive_parser.tab.c"
    break;

  case 109: /* func: L_MATCHINST L_LPAREN regexpr L_COMMA expr L_RPAREN  */
#line 5269 "derive_parser.y"
                {
		  np = newnode(N_FILTERINST);
		  np->left = (yyvsp[-3].n);
		  np->right = (yyvsp[-1].n);
		  (yyval.n) = np;
		  in_matchinst = 0;
		}
#line 7119 "derive_parser.tab.c"
    break;

  case 110: /* func: L_SCALAR L_LPAREN expr L_RPAREN  */
#line 5277 "derive_parser.y"
                { np = newnode(N_SCALAR);
		  np->left = (yyvsp[-1].n);
		  (yyval.n) = np;
		}
#line 7128 "derive_parser.tab.c"
    break;

  case 111: /* func: L_ANON L_LPAREN error  */
#line 5282 "derive_parser.y"
                { gramerr(name_str, follow, "anon("); YYERROR; }
#line 7134 "derive_parser.tab.c"
    break;

  case 112: /* func: L_AVG L_LPAREN error  */
#line 5284 "derive_parser.y"
                { gramerr(name_str, follow, "avg("); YYERROR; }
#line 7140 "derive_parser.tab.c"
    break;

  case 113: /* func: L_COUNT L_LPAREN error  */
#line 5286 "derive_parser.y"
                { gramerr(name_str, follow, "count("); YYERROR; }
#line 7146 "derive_parser.tab.c"
    break;

  case 114: /* func: L_DELTA L_LPAREN error  */
#line 5288 "derive_parser.y"
                { gramerr("<expr>", follow, "delta("); YYERROR; }
#line 7152 "derive_parser.tab.c"
    break;

  case 115: /* func: L_MAX L_LPAREN error  */
#line 5290 "derive_parser.y"
                { gramerr(name_str, follow, "max("); YYERROR; }
#line 7158 "derive_parser.tab.c"
    break;

  case 116: /* func: L_MIN L_LPAREN error  */
#line 5292 "derive_parser.y"
                { gramerr(name_str, follow, "min("); YYERROR; }
#line 7164 "derive_parser.tab.c"
    break;

  case 117: /* func: L_SUM L_LPAREN error  */
#line 5294 "derive_parser.y"
                { gramerr(name_str, follow, "sum("); YYERROR; }
#line 7170 "derive_parser.tab.c"
    break;

  case 118: /* func: L_RATE L_LPAREN error  */
#line 5296 "derive_parser.y"
                { gramerr("<expr>", follow, "rate("); YYERROR; }
#line 7176 "derive_parser.tab.c"
    break;

  case 119: /* func: L_INSTANT L_LPAREN error  */
#line 5298 "derive_parser.y"
                { gramerr(name_str, follow, "instant("); YYERROR; }
#line 7182 "derive_parser.tab.c"
    break;

  case 120: /* func: L_RESCALE L_LPAREN error  */
#line 5300 "derive_parser.y"
                { gramerr(op_str, follow, "rescale("); YYERROR; }
#line 7188 "derive_parser.tab.c"
    break;

  case 121: /* func: L_RESCALE L_LPAREN expr L_COMMA error  */
#line 5302 "derive_parser.y"
                { gramerr("Units string", follow, "rescale(<expr>,"); YYERROR; }
#line 7194 "derive_parser.tab.c"
    break;

  case 122: /* func: L_MATCHINST L_LPAREN error  */
#line 5304 "derive_parser.y"
                { gramerr("<filter>, <expr>)", follow, "matchinst("); YYERROR; }
#line 7200 "derive_parser.tab.c"
    break;

  case 123: /* func: L_MATCHINST L_LPAREN regexpr error  */
#line 5306 "derive_parser.y"
                { gramerr(", <expr>)", follow, "matchinst(<filter>"); YYERROR; }
#line 7206 "derive_parser.tab.c"
    break;

  case 124: /* func: L_MATCHINST L_LPAREN regexpr L_COMMA error  */
#line 5308 "derive_parser.y"
                { gramerr("<expr>)", follow, "matchinst(<filter>,"); YYERROR; }
#line 7212 "derive_parser.tab.c"
    break;

  case 125: /* func: L_SCALAR L_LPAREN error  */
#line 5310 "derive_parser.y"
                { gramerr("<expr>", follow, "scalar("); YYERROR; }
#line 7218 "derive_parser.tab.c"
    break;


#line 7222 "derive_parser.tab.c"

      default: break;
    }
  /* User semantic actions sometimes alter yychar, and that requires
     that yytoken be updated with the new translation.  We take the
     approach of translating immediately before every use of yytoken.
     One alternative is translating here after every semantic action,
     but that translation would be missed if the semantic action invokes
     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
     incorrect destructor might then be invoked immediately.  In the
     case of YYERROR or YYBACKUP, subsequent parser actions might lead
     to an incorrect destructor call or verbose syntax error message
     before the lookahead is translated.  */
  YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc);

  YYPOPSTACK (yylen);
  yylen = 0;

  *++yyvsp = yyval;

  /* Now 'shift' the result of the reduction.  Determine what state
     that goes to, based on the state we popped back to and the rule
     number reduced by.  */
  {
    const int yylhs = yyr1[yyn] - YYNTOKENS;
    const int yyi = yypgoto[yylhs] + *yyssp;
    yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp
               ? yytable[yyi]
               : yydefgoto[yylhs]);
  }

  goto yynewstate;


/*--------------------------------------.
| yyerrlab -- here on detecting error.  |
`--------------------------------------*/
yyerrlab:
  /* Make sure we have latest lookahead translation.  See comments at
     user semantic actions for why this is necessary.  */
  yytoken = yychar == DERIVE_EMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar);
  /* If not already recovering from an error, report this error.  */
  if (!yyerrstatus)
    {
      ++yynerrs;
      yyerror (YY_("syntax error"));
    }

  if (yyerrstatus == 3)
    {
      /* If just tried and failed to reuse lookahead token after an
         error, discard it.  */

      if (yychar <= DERIVE_EOF)
        {
          /* Return failure if at end of input.  */
          if (yychar == DERIVE_EOF)
            YYABORT;
        }
      else
        {
          yydestruct ("Error: discarding",
                      yytoken, &yylval);
          yychar = DERIVE_EMPTY;
        }
    }

  /* Else will try to reuse lookahead token after shifting the error
     token.  */
  goto yyerrlab1;


/*---------------------------------------------------.
| yyerrorlab -- error raised explicitly by YYERROR.  |
`---------------------------------------------------*/
yyerrorlab:
  /* Pacify compilers when the user code never invokes YYERROR and the
     label yyerrorlab therefore never appears in user code.  */
  if (0)
    YYERROR;
  ++yynerrs;

  /* Do not reclaim the symbols of the rule whose action triggered
     this YYERROR.  */
  YYPOPSTACK (yylen);
  yylen = 0;
  YY_STACK_PRINT (yyss, yyssp);
  yystate = *yyssp;
  goto yyerrlab1;


/*-------------------------------------------------------------.
| yyerrlab1 -- common code for both syntax error and YYERROR.  |
`-------------------------------------------------------------*/
yyerrlab1:
  yyerrstatus = 3;      /* Each real token shifted decrements this.  */

  /* Pop stack until we find a state that shifts the error token.  */
  for (;;)
    {
      yyn = yypact[yystate];
      if (!yypact_value_is_default (yyn))
        {
          yyn += YYSYMBOL_YYerror;
          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror)
            {
              yyn = yytable[yyn];
              if (0 < yyn)
                break;
            }
        }

      /* Pop the current state because it cannot handle the error token.  */
      if (yyssp == yyss)
        YYABORT;


      yydestruct ("Error: popping",
                  YY_ACCESSING_SYMBOL (yystate), yyvsp);
      YYPOPSTACK (1);
      yystate = *yyssp;
      YY_STACK_PRINT (yyss, yyssp);
    }

  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
  *++yyvsp = yylval;
  YY_IGNORE_MAYBE_UNINITIALIZED_END


  /* Shift the error token.  */
  YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp);

  yystate = yyn;
  goto yynewstate;


/*-------------------------------------.
| yyacceptlab -- YYACCEPT comes here.  |
`-------------------------------------*/
yyacceptlab:
  yyresult = 0;
  goto yyreturnlab;


/*-----------------------------------.
| yyabortlab -- YYABORT comes here.  |
`-----------------------------------*/
yyabortlab:
  yyresult = 1;
  goto yyreturnlab;


/*-----------------------------------------------------------.
| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here.  |
`-----------------------------------------------------------*/
yyexhaustedlab:
  yyerror (YY_("memory exhausted"));
  yyresult = 2;
  goto yyreturnlab;


/*----------------------------------------------------------.
| yyreturnlab -- parsing is finished, clean up and return.  |
`----------------------------------------------------------*/
yyreturnlab:
  if (yychar != DERIVE_EMPTY)
    {
      /* Make sure we have latest lookahead translation.  See comments at
         user semantic actions for why this is necessary.  */
      yytoken = YYTRANSLATE (yychar);
      yydestruct ("Cleanup: discarding lookahead",
                  yytoken, &yylval);
    }
  /* Do not reclaim the symbols of the rule whose action triggered
     this YYABORT or YYACCEPT.  */
  YYPOPSTACK (yylen);
  YY_STACK_PRINT (yyss, yyssp);
  while (yyssp != yyss)
    {
      yydestruct ("Cleanup: popping",
                  YY_ACCESSING_SYMBOL (+*yyssp), yyvsp);
      YYPOPSTACK (1);
    }
#ifndef yyoverflow
  if (yyss != yyssa)
    YYSTACK_FREE (yyss);
#endif

  return yyresult;
}

#line 5313 "derive_parser.y"


/* function table for lexer */
static const struct {
    int		f_type;
    char	*f_name;
} func[] = {
    { L_AVG,	"avg" },
    { L_COUNT,	"count" },
    { L_DELTA,	"delta" },
    { L_MAX,	"max" },
    { L_MIN,	"min" },
    { L_SUM,	"sum" },
    { L_ANON,	"anon" },
    { L_RATE,	"rate" },
    { L_INSTANT,"instant" },
    { L_MKCONST,"mkconst" },
    { L_META,	"meta" },
    { L_NOVALUE,"novalue" },
    { L_RESCALE,"rescale" },
    { L_DEFINED,"defined" },
    { L_MATCHINST, "matchinst" },
    { L_SCALAR,	"scalar" },
    { L_UNDEF,	NULL }
};
static struct {
    int		ltype;
    int		ntype;
    char	*long_name;
    char	*short_name;
} typetab[] = {
    { L_UNDEF,		0,		"UNDEF",	NULL },
    { L_ERROR,		0,		"ERROR",	NULL },
    { L_EOS,		0,		"EOS",		NULL },
    { L_INTEGER,	N_INTEGER,	"INTEGER",	NULL },
    { L_DOUBLE,		N_DOUBLE,	"DOUBLE",	NULL },
    { L_NAME,		N_NAME,		"NAME",		NULL },
    { L_PLUS,		N_PLUS,		"PLUS",		"+" },
    { L_MINUS,		N_MINUS,	"MINUS",	"-" },
    { L_STAR,		N_STAR,		"STAR",		"*" },
    { L_SLASH,		N_SLASH,	"SLASH",	"/" },
    { L_QUEST,		N_QUEST,	"QUEST",	"?" },
    { L_COLON,		N_COLON,	"COLON",	":" },
    { L_LPAREN,		0,		"LPAREN",	"(" },
    { L_RPAREN,		0,		"RPAREN",	")" },
    { L_TYPE,		0,		"TYPE",		NULL },
    { L_SEMANTICS,	0,		"SEMANTICS",	NULL },
    { L_UNITS,		0,		"UNITS",	NULL },
    { L_INDOM,		0,		"INDOM",	NULL },
    { L_ASSIGN,		0,		"ASSIGN",	"=" },
    { L_COMMA,		0,		"COMMA",	"," },
    { L_STRING,		0,		"STRING",	NULL },
    { L_INSTNAME,	0,		"INSTNAME",	NULL },
    { L_PATTERN,	0,		"PATTERN",	NULL },
    { L_AVG,		N_AVG,		"AVG",		NULL },
    { L_COUNT,		N_COUNT,	"COUNT",	NULL },
    { L_DELTA,		N_DELTA,	"DELTA",	NULL },
    { L_MAX,		N_MAX,		"MAX",		NULL },
    { L_MIN,		N_MIN,		"MIN",		NULL },
    { L_SUM,		N_SUM,		"SUM",		NULL },
    { L_ANON,		N_ANON,		"ANON",		NULL },
    { L_RATE,		N_RATE,		"RATE",		NULL },
    { L_INSTANT,	N_INSTANT,	"INSTANT",	NULL },
    { L_MKCONST,	0,		"MKCONST",	NULL },
    { L_META,		N_META,		"META",		NULL },
    { L_NOVALUE,	N_NOVALUE,	"NOVALUE",	NULL },
    { L_RESCALE,	N_RESCALE,	"RESCALE",	NULL },
    { 0,		N_SCALE,	"SCALE",	NULL },
    { L_DEFINED,	N_DEFINED,	"DEFINED",	NULL },
    { L_MATCHINST,	N_FILTERINST,	"FILTERINST",	NULL },
    { L_SCALAR,		N_SCALAR,	"SCALAR",	NULL },
    { 0,		N_PATTERN,	"PATTERN",	NULL },
    { L_LT,		N_LT,		"LT",		"<" },
    { L_LEQ,		N_LEQ,		"LEQ",		"<=" },
    { L_EQ,		N_EQ,		"EQ",		"==" },
    { L_GEQ,		N_GEQ,		"GEQ",		">=" },
    { L_GT,		N_GT,		"GT",		">" },
    { L_NEQ,		N_NEQ,		"NEQ",		"!=" },
    { L_AND,		N_AND,		"AND",		"&&" },
    { L_OR,		N_OR,		"OR",		"||" },
    { L_NOT,		N_NOT,		"NOT",		"!" },
    { 0,		N_NEG,		"NEG",		"-" },
    { -1,		-1,		NULL,		NULL }
};

/* full name for all node types */
char *
__dmnode_type_str(int type)
{
    int		i;
    /* long enough for ... "unknown type XXXXXXXXXXX!" */
    static char n_eh_str[30];

    for (i = 0; typetab[i].ntype != -1; i++) {
	if (type == typetab[i].ntype) {
	    return typetab[i].long_name;
	}
    }
    pmsprintf(n_eh_str, sizeof(n_eh_str), "unknown type %d!", type);
    return n_eh_str;
}

/* short string for the operator node types */
static char *
n_type_c(int type)
{
    int		i;
    /* long enough for ... "op XXXXXXXXXXX!" */
    static char n_eh_c[20];

    for (i = 0; typetab[i].ntype != -1; i++) {
	if (type == typetab[i].ntype) {
	    return typetab[i].short_name;
	}
    }
    pmsprintf(n_eh_c, sizeof(n_eh_c), "op %d!", type);
    return n_eh_c;
}

/* full name for all lex types */
static char *
l_type_str(int type)
{
    int		i;
    /* long enough for ... "unknown type XXXXXXXXXXX!" */
    static char l_eh_str[30];

    for (i = 0; typetab[i].ltype != -1; i++) {
	if (type == typetab[i].ltype) {
	    return typetab[i].long_name;
	}
    }
    pmsprintf(l_eh_str, sizeof(l_eh_str), "unknown type %d!", type);
    return l_eh_str;
}

static void
unget(int c)
{
    lexpeek = c;
}

static int
get()
{
    int		c;
    if (lexpeek != 0) {
	c = lexpeek;
	lexpeek = 0;
	return c;
    }
    c = *string;
    if (c == '\0') {
	return EOF;
    }
    string++;
    return c;
}

static int
derive_lex(void)
{
    int		c;
    char	*p = tokbuf;
    int		ltype = L_UNDEF;
    int		i;
    int		firstch = 1;
    int		ret = L_UNDEF;
    int		escape = 0;

    for ( ; ret == L_UNDEF; ) {
	c = get();
	if (firstch) {
	    if (isspace((int)c)) continue;
	    lexicon = &string[-1];
	    firstch = 0;
	}
	if (c == EOF) {
	    if (ltype != L_UNDEF) {
		/* force end of last token */
		c = 0;
	    }
	    else {
		/* really the end of the input buffer */
		ret = L_EOS;
		break;
	    }
	}
	if (p == NULL) {
	    tokbuflen = 128;
	    if ((p = tokbuf = (char *)malloc(tokbuflen)) == NULL) {
		PM_UNLOCK(registered.mutex);
		pmNoMem("pmRegisterDerived: alloc tokbuf", tokbuflen, PM_FATAL_ERR);
		/*NOTREACHED*/
	    }
	}
	else if (p >= &tokbuf[tokbuflen]) {
	    int		x = p - tokbuf;
	    tokbuflen *= 2;
	    if ((tokbuf = (char *)realloc(tokbuf, tokbuflen)) == NULL) {
		PM_UNLOCK(registered.mutex);
		pmNoMem("pmRegisterDerived: realloc tokbuf", tokbuflen, PM_FATAL_ERR);
		/*NOTREACHED*/
	    }
	    p = &tokbuf[x];
	}

	*p++ = (char)c;

	if (ltype == L_UNDEF) {
	    if (isdigit((int)c))
		ltype = L_INTEGER;
	    else if (c == '.')
		ltype = L_DOUBLE;
	    else if (isalpha((int)c))
		ltype = L_NAME;
	    else {
		switch (c) {
		    case '+':
			*p = '\0';
			ret = L_PLUS;
			break;

		    case '-':
			*p = '\0';
			ret = L_MINUS;
			break;

		    case '*':
			*p = '\0';
			ret = L_STAR;
			break;

		    case '/':
			if (!in_matchinst) {
			    *p = '\0';
			    ret = L_SLASH;
			}
			else
			    ltype = L_PATTERN;
			break;

		    case '(':
			*p = '\0';
			ret = L_LPAREN;
			break;

		    case ')':
			*p = '\0';
			ret = L_RPAREN;
			break;

		    case '<':
			ltype = L_LT;
			break;

		    case '=':
			ltype = L_EQ;
			break;

		    case '>':
			ltype = L_GT;
			break;

		    case '!':
			ltype = L_NEQ;
			break;

		    case '&':
			ltype = L_AND;
			break;

		    case '|':
			ltype = L_OR;
			break;

		    case '?':
			*p = '\0';
			ret = L_QUEST;
			break;

		    case ':':
			*p = '\0';
			ret = L_COLON;
			break;

		    case ',':
			*p = '\0';
			ret = L_COMMA;
			break;

		    case '"':
			ltype = L_STRING;
			break;

		    case '[':
			ltype = L_INSTNAME;
			break;

		    default:
			*p = '\0';
			PM_TPD(derive_errmsg) = "Illegal character";
			ret = L_ERROR;
			break;
		}
	    }
	}
	else {
	    if (ltype == L_INTEGER) {
		if (c == '.') {
		    ltype = L_DOUBLE;
		}
		else if (!isdigit((int)c)) {
		    char	*endptr;
		    __uint64_t	check;
		    unget(c);
		    p[-1] = '\0';
		    check = strtoull(tokbuf, &endptr, 10);
		    if (*endptr != '\0' || check > 0xffffffffUL) {
			PM_TPD(derive_errmsg) = "Constant value too large";
			ret = L_ERROR;
			break;
		    }
		    if ((derive_lval.s = strdup(tokbuf)) == NULL) {
			PM_TPD(derive_errmsg) = "strdup() for INTEGER failed";
			ret = L_ERROR;
			break;
		    }
		    ret = L_INTEGER;
		    break;
		}
	    }
	    else if (ltype == L_DOUBLE) {
		if (!isdigit((int)c)) {
		    unget(c);
		    p[-1] = '\0';
		    if ((derive_lval.s = strdup(tokbuf)) == NULL) {
			PM_TPD(derive_errmsg) = "strdup() for DOUBLE failed";
			ret = L_ERROR;
			break;
		    }
		    ret = L_DOUBLE;
		    break;
		}
	    }
	    else if (ltype == L_NAME) {
		if (isalpha((int)c) || isdigit((int)c) || c == '_' || c == '.')
		    continue;
		if (c == '(') {
		    /* check for functions ... */
		    int		namelen = p - tokbuf - 1;
		    for (i = 0; func[i].f_name != NULL; i++) {
			if (namelen == strlen(func[i].f_name) &&
			    strncmp(tokbuf, func[i].f_name, namelen) == 0) {
			    /* current character is ( after name */
			    unget(c);
			    p[-1] = '\0';
			    ret = func[i].f_type;
			    if (ret == L_MATCHINST)
				in_matchinst = 1;
			    break;
			}
		    }
		    if (func[i].f_name != NULL)
			/* match func name */
			break;
		}
		/* current character is end of name */
		unget(c);
		p[-1] = '\0';
		if (strcmp(tokbuf, "type") == 0) {
		    ret = L_TYPE;
		    break;
		}
		else if (strcmp(tokbuf, "semantics") == 0) {
		    ret = L_SEMANTICS;
		    break;
		}
		else if (strcmp(tokbuf, "units") == 0) {
		    ret = L_UNITS;
		    break;
		}
		else if (strcmp(tokbuf, "indom") == 0) {
		    ret = L_INDOM;
		    break;
		}
		else if (strcmp(tokbuf, "meta") == 0) {
		    ret = L_META;
		    break;
		}
		if ((derive_lval.s = strdup(tokbuf)) == NULL) {
		    PM_TPD(derive_errmsg) = "strdup() for NAME failed";
		    ret = L_ERROR;
		    break;
		}
		ret = L_NAME;
		break;
	    }
	    else if (ltype == L_STRING) {
		if (c != '"') {
		    if (c == '\0') {
			PM_TPD(derive_errmsg) = "unterminated STRING";
			ret = L_ERROR;
			break;
		    }
		    continue;
		}
		/* [-1] and [1] to strip quotes */
		p[-1] = '\0';
		if ((derive_lval.s = strdup(&tokbuf[1])) == NULL) {
		    PM_TPD(derive_errmsg) = "strdup() for STRING failed";
		    ret = L_ERROR;
		    break;
		}
		ret = L_STRING;
		break;
	    }
	    else if (ltype == L_INSTNAME) {
		if (escape && c != '\0') {
		    escape = 0;
		    continue;
		}
		if (escape == 0 && c == '\\') {
		    p--;
		    escape = 1;
		    continue;
		}
		if (c != ']') {
		    if (c == '\0') {
			PM_TPD(derive_errmsg) = "unterminated instance name";
			ret = L_ERROR;
			break;
		    }
		    continue;
		}
		/* [-1] and [1] to strip [ .. ] */
		p[-1] = '\0';
		if ((derive_lval.s = strdup(&tokbuf[1])) == NULL) {
		    PM_TPD(derive_errmsg) = "strdup() for INSTNAME failed";
		    ret = L_ERROR;
		    break;
		}
		ret = L_INSTNAME;
		break;
	    }
	    else if (ltype == L_PATTERN) {
		if (escape && c != '\0') {
		    escape = 0;
		    continue;
		}
		if (escape == 0 && c == '\\') {
		    p--;
		    escape = 1;
		    continue;
		}
		if (c != '/') {
		    if (c == '\0') {
			PM_TPD(derive_errmsg) = "unterminated pattern";
			ret = L_ERROR;
			break;
		    }
		    continue;
		}
		/* [-1] and [1] to strip / .. / */
		p[-1] = '\0';
		if ((derive_lval.s = strdup(&tokbuf[1])) == NULL) {
		    PM_TPD(derive_errmsg) = "strdup() for PATTERN failed";
		    ret = L_ERROR;
		    break;
		}
		ret = L_PATTERN;
		break;
	    }
	    else if (ltype == L_LT) {
		if (c == '=') {
		    *p = '\0';
		    ret = L_LEQ;
		    break;
		}
		else {
		    unget(c);
		    p[-1] = '\0';
		    ret = L_LT;
		    break;
		}
	    }
	    else if (ltype == L_GT) {
		if (c == '=') {
		    *p = '\0';
		    ret = L_GEQ;
		    break;
		}
		else {
		    unget(c);
		    p[-1] = '\0';
		    ret = L_GT;
		    break;
		}
	    }
	    else if (ltype == L_EQ) {
		if (c == '=') {
		    *p = '\0';
		    ret = L_EQ;
		    break;
		}
		else {
		    unget(c);
		    p[-1] = '\0';
		    ret = L_ASSIGN;
		    break;
		}
	    }
	    else if (ltype == L_NEQ) {
		if (c == '=') {
		    *p = '\0';
		    ret = L_NEQ;
		    break;
		}
		else {
		    unget(c);
		    p[-1] = '\0';
		    ret = L_NOT;
		    break;
		}
	    }
	    else if (ltype == L_AND) {
		if (c == '&') {
		    *p = '\0';
		    ret = L_AND;
		    break;
		}
		else {
		    unget(c);
		    p[-1] = '\0';
		    PM_TPD(derive_errmsg) = "Illegal character";
		    ret = L_ERROR;
		    break;
		}
	    }
	    else if (ltype == L_OR) {
		if (c == '|') {
		    *p = '\0';
		    ret = L_OR;
		    break;
		}
		else {
		    unget(c);
		    p[-1] = '\0';
		    PM_TPD(derive_errmsg) = "Illegal character";
		    ret = L_ERROR;
		    break;
		}
	    }
	}

    }
    if (pmDebugOptions.derive && pmDebugOptions.appl0) {
	if (ltype == L_STRING || ltype == L_INSTNAME || ltype == L_PATTERN)
	    fprintf(stderr, "derive_lex() -> type=L_%s \"%s\"\n", l_type_str(ret), &tokbuf[1]);
	else
	    fprintf(stderr, "derive_lex() -> type=L_%s \"%s\"\n", l_type_str(ret), ret == L_EOS ? "" : tokbuf);
    }

    return ret;
}

static void
derive_error(char *s)
{
    parse_tree = NULL;
}
