eval.c revision 80ab886d233f514d54c2a6bdeb9fdfd951bd6881
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* eval.c -- constraint evaluation module
*
* this module evaluates constraints.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "alloc.h"
#include "out.h"
#include "stable.h"
#include "literals.h"
#include "lut.h"
#include "tree.h"
#include "ptree.h"
#include "itree.h"
#include "ipath.h"
#include "eval.h"
#include "config.h"
#include "platform.h"
#include "fme.h"
#include "stats.h"
/*
* begins_with -- return true if rhs path begins with everything in lhs path
*/
static int
{
int lnum;
int rnum;
return (1); /* yep -- it all matched */
return (0); /* nope, ran out of rhs first */
return (0); /* nope, different component names */
else
else
return (0); /* nope, instance numbers were different */
}
/*
* evaluate a variety of functions and place result in valuep. return 1 if
* function evaluation was successful; 0 if otherwise (e.g., the case of an
* invalid argument to the function)
*/
/*ARGSUSED*/
static int
{
/* within()'s are not really constraints -- always true */
valuep->v = 1;
return (1);
} else if (funcname == L_is_under) {
else
else
/* eval_dup will expand wildcards, iterators, etc... */
return (1);
}
if (try)
return (0);
return (1);
return (1);
return (1);
} else if (funcname == L_is_connected) {
} else if (funcname == L_is_present) {
} else if (funcname == L_confprop) {
"eval_func: %s not yet supported", funcname);
} else if (funcname == L_payloadprop) {
/* platform_payloadprop() returned false */
return (0);
} else {
switch (valuep->t) {
case UINT64:
case NODEPTR:
break;
case STRING:
break;
default:
break;
}
return (1);
}
} else if (funcname == L_setpayloadprop) {
struct evalue *payloadvalp;
"setpayloadprop: %s: %s=",
/*
* allocate a struct evalue to hold the payload property's
* value, unless we've been here already, in which case we
* might calculate a different value, but we'll store it
* in the already-allocated struct evalue.
*/
}
payloadvalp->t = UINT64;
payloadvalp->v = 0;
} else {
if (payloadvalp->t == UINT64)
" (%llu)", payloadvalp->v);
else
(char *)(uintptr_t)payloadvalp->v);
}
/* add to table of payload properties for current problem */
(void *)payloadvalp, NULL);
/* function is always true */
valuep->v = 1;
return (1);
} else if (funcname == L_payloadprop_defined) {
/* platform_payloadprop() returned false */
valuep->v = 0;
} else {
valuep->v = 1;
}
return (1);
} else if (funcname == L_payloadprop_contains) {
int nvals;
"payloadprop_contains(\"%s\", ",
/* evaluate the expression we're comparing against */
"(cannot eval, using zero) ");
cmpval.v = 0;
} else {
"(%llu) ", cmpval.v);
else
}
/* get the payload values and check for a match */
&nvals);
valuep->v = 0;
if (nvals == 0) {
} else {
int i;
for (i = 0; i < nvals; i++) {
continue;
/*
* If we auto-converted the value to a
* string, we need to free the
* original tree value.
*/
T_NAME) {
preval.v);
}
valuep->v = 1;
break;
}
}
if (valuep->v)
else
for (i = 0; i < nvals; i++) {
vals[i].v);
break;
}
}
}
return (1);
} else if (funcname == L_confcall) {
struct istat_entry ent;
valuep->v = 0;
else
return (1);
} else
"eval_func: unexpected func: %s", funcname);
/*NOTREACHED*/
return (0);
}
static struct node *
{
int i;
return (NULL);
return (NULL);
}
/*
* get to this point if np does not match any of the entries in
* epnames. check if np is a path that must preceded by a wildcard
* portion. for this case we must first determine which epnames[]
* entry should be used for wildcarding.
*/
break;
}
break;
}
}
}
break;
}
/* no match; np is not a path to be wildcarded */
return (NULL);
}
/*
* dup (npstart -- npend) which is the wildcarded portion. all
* children should be T_NUMs.
*/
} else {
}
}
/* now append the nonwildcarded portion */
return (retp);
}
static struct node *
{
return (NULL);
switch (np->t) {
case T_GLOBID:
case T_ASSIGN:
case T_CONDIF:
case T_CONDELSE:
case T_NE:
case T_EQ:
case T_LT:
case T_LE:
case T_GT:
case T_GE:
case T_BITAND:
case T_BITOR:
case T_BITXOR:
case T_BITNOT:
case T_LSHIFT:
case T_RSHIFT:
case T_LIST:
case T_AND:
case T_OR:
case T_NOT:
case T_ADD:
case T_SUB:
case T_MUL:
case T_DIV:
case T_MOD:
case T_NAME: {
/* explicit iterator; not part of pathname */
return (newnp);
}
/* see if np is a path with wildcard portion */
return (newnp);
/* turn off wildcarding for child */
/*
* not a number, eh? we must resolve this
* to a number.
*/
"eval_dup: could not resolve "
}
}
/* turn off wildcarding for next */
return (tree_name_append(newnp,
} else {
return (newnp);
}
} else {
"eval_dup: internal error: \"%s\" is neither "
}
/*NOTREACHED*/
break;
}
case T_EVENT:
return (tree_event(newnp,
case T_FUNC:
case T_QUOTE:
return (newnp);
case T_NUM:
return (newnp);
default:
"eval_dup: unexpected node type: %s",
ptree_nodetype2str(np->t));
}
/*NOTREACHED*/
return (0);
}
/*
* eval_potential -- see if constraint is potentially true
*
* this function is used at instance tree creation time to see if
* any constraints are already known to be false. if this function
* returns false, then the constraint will always be false and there's
* no need to include the propagation arrow in the instance tree.
*
* if this routine returns true, either the constraint is known to
* be always true (so there's no point in attaching the constraint
* to the propagation arrow in the instance tree), or the constraint
* contains "deferred" expressions like global variables or poller calls
* and so it must be evaluated during calls to fme_eval(). in this last
* case, where a constraint needs to be attached to the propagation arrow
* in the instance tree, this routine returns a newly created constraint
* in *newc where all the non-deferred things have been filled in.
*
* so in summary:
*
* return of false: constraint can never be true, *newc will be NULL.
*
* return of true with *newc unchanged: constraint will always be true.
*
* return of true with *newc changed: use new constraint in *newc.
*
* the lookup table for all explicit iterators, ex, is passed in.
*
* *newc can either be NULL on entry, or if can contain constraints from
* previous calls to eval_potential() (i.e. for building up an instance
* tree constraint from several potential constraints). if *newc already
* contains constraints, anything added to it will be joined by adding
* a T_AND node at the top of *newc.
*/
int
{
/*
* couldn't eval expression because
* it contains deferred items. make
* a duplicate expression with all the
* non-deferred items expanded.
*/
/*
* constraint is potentially true if deferred
* expression in newnp is true. *newc was NULL
* so new constraint is just the one in newnp.
*/
return (1);
} else {
/*
* constraint is potentially true if deferred
* expression in newnp is true. *newc already
* contained a constraint so add an AND with the
* constraint in newnp.
*/
return (1);
}
/* constraint can never be true */
return (0);
/* constraint can never be true */
return (0);
} else {
/* constraint is always true (nothing deferred to eval) */
return (1);
}
}
static int
{
/* auto-convert T_NAMES to strings */
T_NAME) {
FREE(s);
}
T_NAME) {
FREE(s);
}
/* auto-convert strings to numbers */
}
}
}
"invalid datatype of argument for operation %s",
ptree_nodetype2str(np->t));
return (1);
}
"mismatch in datatype of arguments for operation %s",
ptree_nodetype2str(np->t));
return (1);
}
return (0);
}
/*
* eval_expr -- evaluate expression into *valuep
*
* the meaning of the return value depends on the input value of try.
*
* for try == 1: if any deferred items are encounted, bail out and return
* false. returns true if we made it through entire expression without
* hitting any deferred items.
*
* for try == 0: return true if all operations were performed successfully.
* return false if otherwise. for example, any of the following conditions
* will result in a false return value:
* - attempted use of an uninitialized global variable
* - failure in function evaluation
* - illegal arithmetic operation (argument out of range)
*/
int
{
return (1);
}
switch (np->t) {
case T_GLOBID:
if (try)
return (0);
/*
* only handle case of getting (and not setting) the value
* of a global variable
*/
return (0);
} else {
return (1);
}
case T_ASSIGN:
if (try)
return (0);
/*
* first evaluate rhs, then try to store value in lhs which
* should be a global variable
*/
return (0);
}
"assign $%s=%llu",
} else {
"assign $%s=\"%s\"",
}
/*
* but always return true -- an assignment should not
* cause a constraint to be false.
*/
valuep->v = 1;
return (1);
case T_EQ:
#define IMPLICIT_ASSIGN_IN_EQ
#ifdef IMPLICIT_ASSIGN_IN_EQ
/*
* if lhs is an uninitialized global variable, perform
* an assignment.
*
* one insidious side effect of implicit assignment is
* that the "==" operator does not return a Boolean if
* implicit assignment was performed.
*/
if (try == 0 &&
return (0);
return (1);
}
#endif /* IMPLICIT_ASSIGN_IN_EQ */
return (0);
return (0);
return (0);
return (1);
case T_LT:
return (0);
return (0);
return (0);
return (1);
case T_LE:
return (0);
return (0);
return (0);
return (1);
case T_GT:
return (0);
return (0);
return (0);
return (1);
case T_GE:
return (0);
return (0);
return (0);
return (1);
case T_BITAND:
return (0);
return (0);
return (0);
return (1);
case T_BITOR:
return (0);
return (0);
return (0);
return (1);
case T_BITXOR:
return (0);
return (0);
return (0);
return (1);
case T_BITNOT:
return (0);
return (0);
return (1);
case T_LSHIFT:
return (0);
return (0);
return (0);
return (1);
case T_RSHIFT:
return (0);
return (0);
return (0);
return (1);
case T_CONDIF: {
int dotrue = 0;
/*
* evaluate
* expression ? stmtA [ : stmtB ]
*
* first see if expression is true or false, then determine
* if stmtA (or stmtB, if it exists) should be evaluated.
*
* "dotrue = 1" means stmtA should be evaluated.
*/
dotrue = 1;
if (dotrue)
else
} else {
/* no ELSE clause */
if (dotrue)
else {
valuep->v = 0;
return (0);
}
}
return (0);
return (1);
}
case T_CONDELSE:
/*
* shouldn't get here, since T_CONDELSE is supposed to be
* evaluated as part of T_CONDIF
*/
ptree_nodetype2str(np->t));
return (0);
case T_NE:
return (0);
return (0);
return (0);
return (1);
case T_LIST:
case T_AND:
return (0);
if (valuep->v == 0) {
return (1);
}
return (0);
return (1);
case T_OR:
return (0);
if (valuep->v != 0) {
valuep->v = 1;
return (1);
}
return (0);
return (1);
case T_NOT:
return (0);
return (1);
case T_ADD:
return (0);
return (0);
return (0);
return (1);
case T_SUB:
return (0);
return (0);
return (0);
/* since valuep is unsigned, return false if lval.v < rval.v */
return (0);
}
return (1);
case T_MUL:
return (0);
return (0);
return (0);
return (1);
case T_DIV:
return (0);
return (0);
return (0);
/* return false if dividing by zero */
if (rval.v == 0) {
return (0);
}
return (1);
case T_MOD:
return (0);
return (0);
return (0);
/* return false if dividing by zero */
if (rval.v == 0) {
return (0);
}
return (1);
case T_NAME:
if (try) {
/*
* at itree_create() time, we can expand simple
* iterators. anything else we'll punt on.
*/
/* explicit iterator; not part of pathname */
return (1);
}
return (0);
}
/* return address of struct node */
return (1);
case T_QUOTE:
return (1);
case T_FUNC:
case T_NUM:
return (1);
default:
"eval_expr: unexpected node type: %s",
ptree_nodetype2str(np->t));
}
/*NOTREACHED*/
return (0);
}
/*
* eval_fru() and eval_asru() don't do much, but are called from a number
* of places.
*/
struct node *
{
return (np);
}
struct node *
{
return (np);
}