   /*******************************************************/
   /*      "C" Language Integrated Production System      */
   /*                                                     */
   /*             CLIPS Version 6.20  01/31/02            */
   /*                                                     */
   /*                  ANALYSIS MODULE                    */
   /*******************************************************/

/*************************************************************/
/* Purpose: Analyzes LHS patterns to check for semantic      */
/*   errors and to determine variable comparisons and other  */
/*   tests which must be performed either in the pattern or  */
/*   join networks.                                          */
/*                                                           */
/* Principal Programmer(s):                                  */
/*      Gary D. Riley                                        */
/*                                                           */
/* Contributing Programmer(s):                               */
/*                                                           */
/* Revision History:                                         */
/*                                                           */
/*************************************************************/

#define _ANALYSIS_SOURCE_

#include "setup.h"

#if (! RUN_TIME) && (! BLOAD_ONLY) && DEFRULE_CONSTRUCT

#include <stdio.h>
#define _STDIO_INCLUDED_

#include "constant.h"
#include "symbol.h"
#include "memalloc.h"
#include "exprnpsr.h"
#include "reorder.h"
#include "generate.h"
#include "pattern.h"
#include "router.h"
#include "ruledef.h"
#include "cstrnchk.h"
#include "cstrnutl.h"
#include "cstrnops.h"
#include "rulecstr.h"
#include "modulutl.h"
#include "analysis.h"

#if DEFGLOBAL_CONSTRUCT
#include "globldef.h"
#endif

/***************************************/
/* LOCAL INTERNAL FUNCTION DEFINITIONS */
/***************************************/

   static int                     GetVariables(void *,struct lhsParseNode *);
   static BOOLEAN                 UnboundVariablesInPattern(void *,struct lhsParseNode *,int);
   static int                     PropagateVariableToNodes(void *,
                                                           struct lhsParseNode *,
                                                           int,
                                                           struct symbolHashNode *,
                                                           struct lhsParseNode *,
                                                           int,int,int);
   static struct lhsParseNode    *CheckExpression(void *,
                                                  struct lhsParseNode *,
                                                  struct lhsParseNode *,
                                                  int,
                                                  struct symbolHashNode *,
                                                  int);
   static void                    VariableReferenceErrorMessage(void *,
                                                                struct symbolHashNode *,
                                                                struct lhsParseNode *,
                                                                int,
                                                                struct symbolHashNode *,
                                                                int);
   static int                     ProcessField(void *theEnv,
                                               struct lhsParseNode *,
                                               struct lhsParseNode *,
                                               struct lhsParseNode *);
   static int                     ProcessVariable(void *,
                                               struct lhsParseNode *,
                                               struct lhsParseNode *,
                                               struct lhsParseNode *);
   static void                    VariableMixingErrorMessage(void *,struct symbolHashNode *);
   static int                     PropagateVariableDriver(void *,
                                                          struct lhsParseNode *,
                                                          struct lhsParseNode *,
                                                          struct lhsParseNode *,
                                                          int,struct symbolHashNode *,
                                                          struct lhsParseNode *,
                                                          int);

/******************************************************************/
/* VariableAnalysis: Propagates variables references to other     */
/*   variables in the LHS and determines if there are any illegal */
/*   variable references (e.g. referring to an unbound variable). */
/*   The propagation of variable references simply means all      */
/*   subsequent references of a variable are made to "point" back */
/*   to the variable being propagated.                            */
/******************************************************************/
globle int VariableAnalysis(
  void *theEnv,
  struct lhsParseNode *patternPtr)
  {
   struct lhsParseNode *rv, *theList, *tempList;
   int errorFlag = FALSE;

   /*======================================================*/
   /* Loop through all of the CEs in the rule to determine */
   /* which variables refer to other variables and whether */
   /* any semantic errors exist when refering to variables */
   /* (such as referring to a variable that was not        */
   /* previously bound).                                   */
   /*======================================================*/

   while (patternPtr != NULL)
     {
      /*=========================================================*/
      /* If a pattern CE is encountered, propagate any variables */
      /* found in the pattern and note any illegal references to */
      /* other variables.                                        */
      /*=========================================================*/

      if (patternPtr->type == PATTERN_CE)
        {
         /*====================================================*/
         /* Determine if the fact address associated with this */
         /* pattern illegally refers to other variables.       */
         /*====================================================*/

         if ((patternPtr->value != NULL) &&
             (patternPtr->referringNode != NULL))
           {
            errorFlag = TRUE;
            if (patternPtr->referringNode->index == -1)
              {
               PrintErrorID(theEnv,"ANALYSIS",1,TRUE);
               EnvPrintRouter(theEnv,WERROR,"Duplicate pattern-address ?");
               EnvPrintRouter(theEnv,WERROR,ValueToString(patternPtr->value));
               EnvPrintRouter(theEnv,WERROR," found in CE #");
               PrintLongInteger(theEnv,WERROR,(long) patternPtr->whichCE);
               EnvPrintRouter(theEnv,WERROR,".\n");
              }
            else
              {
               PrintErrorID(theEnv,"ANALYSIS",2,TRUE);
               EnvPrintRouter(theEnv,WERROR,"Pattern-address ?");
               EnvPrintRouter(theEnv,WERROR,ValueToString(patternPtr->value));
               EnvPrintRouter(theEnv,WERROR," used in CE #");
               PrintLongInteger(theEnv,WERROR,(long) patternPtr->whichCE);
               EnvPrintRouter(theEnv,WERROR," was previously bound within a pattern CE.\n");
              }
           }

         /*====================================================*/
         /* Propagate the pattern and field location of bound  */
         /* variables found in this pattern to other variables */
         /* in the same semantic scope as the bound variable.  */
         /*====================================================*/

         if (GetVariables(theEnv,patternPtr)) return(TRUE);
        }

      /*==============================================================*/
      /* If a test CE is encountered, make sure that all references   */
      /* to variables have been previously bound. If they are bound   */
      /* then replace the references to variables with function calls */
      /* to retrieve the variables.                                   */
      /*==============================================================*/

      else if (patternPtr->type == TEST_CE)
        {
         /*=====================================================*/
         /* Verify that all variables were referenced properly. */
         /*=====================================================*/

         rv = CheckExpression(theEnv,patternPtr->expression,NULL,(int) patternPtr->whichCE,NULL,0);

         /*=========================================================*/
         /* Determine the type and value constraints implied by the */
         /* expression and propagate these constraints to other     */
         /* variables in the LHS. For example, the expression       */
         /* (+ ?x 1) implies that ?x is a number.                   */
         /*=========================================================*/

         theList = GetExpressionVarConstraints(theEnv,patternPtr->expression);
         for (tempList = theList; tempList != NULL; tempList = tempList->right)
            {
             if (PropagateVariableDriver(theEnv,patternPtr,patternPtr,NULL,SF_VARIABLE,
                                         (SYMBOL_HN *) tempList->value,tempList,FALSE))
               {
                ReturnLHSParseNodes(theEnv,theList);
                return(TRUE);
               }
            }
         ReturnLHSParseNodes(theEnv,theList);

         /*========================================================*/
         /* If the variables in the expression were all referenced */
         /* properly, then create the expression to use in the     */
         /* join network.                                          */
         /*========================================================*/

         if (rv != NULL)
           { errorFlag = TRUE; }
         else
           { patternPtr->networkTest = GetvarReplace(theEnv,patternPtr->expression); }
        }

      /*=====================================================*/
      /* Move on to the next pattern in the LHS of the rule. */
      /*=====================================================*/

      patternPtr = patternPtr->bottom;
     }

   /*==========================================*/
   /* Return the error status of the analysis. */
   /*==========================================*/

   return(errorFlag);
  }

/****************************************************************/
/* GetVariables: Loops through each field/slot within a pattern */
/*   and propagates the pattern and field location of bound     */
/*   variables found in the pattern to other variables within   */
/*   the same semantic scope as the bound variables.            */
/****************************************************************/
static int GetVariables(
  void *theEnv,
  struct lhsParseNode *thePattern)
  {
   struct lhsParseNode *patternHead = thePattern;
   struct lhsParseNode *multifieldHeader = NULL;

   /*======================================================*/
   /* Loop through all the fields/slots found in a pattern */
   /* looking for binding instances of variables.          */
   /*======================================================*/

   while (thePattern != NULL)
     {
      /*================================================*/
      /* A multifield slot contains a sublist of fields */
      /* that must be traversed and checked.            */
      /*================================================*/

      if (thePattern->multifieldSlot)
        {
         multifieldHeader = thePattern;
         thePattern = thePattern->bottom;
        }

      /*==================================================*/
      /* Propagate the binding occurences of single field */
      /* variables, multifield variables, and fact        */
      /* addresses to other occurences of the variable.   */
      /* If an error is encountered, return TRUE.         */
      /*==================================================*/

      if (thePattern != NULL)
        {
         if ((thePattern->type == SF_VARIABLE) ||
             (thePattern->type == MF_VARIABLE) ||
             ((thePattern->type == PATTERN_CE) && (thePattern->value != NULL)))
           {
            if (ProcessVariable(theEnv,thePattern,multifieldHeader,patternHead))
              { return(TRUE); }
           }
         else
           {
            if (ProcessField(theEnv,thePattern,multifieldHeader,patternHead))
              { return(TRUE); }
           }
        }

      /*===============================================*/
      /* Move on to the next field/slot in the pattern */
      /* or to the next field in a multifield slot.    */
      /*===============================================*/

      if (thePattern == NULL)
        { thePattern = multifieldHeader; }
      else if ((thePattern->right == NULL) && (multifieldHeader != NULL))
        {
         thePattern = multifieldHeader;
         multifieldHeader = NULL;
        }

      thePattern = thePattern->right;
     }

   /*===============================*/
   /* Return FALSE to indicate that */
   /* no errors were detected.      */
   /*===============================*/

   return(FALSE);
  }

/******************************************************/
/* ProcessVariable: Processes a single occurence of a */
/*   variable by propagating references to it.        */
/******************************************************/
static int ProcessVariable(
  void *theEnv,
  struct lhsParseNode *thePattern,
  struct lhsParseNode *multifieldHeader,
  struct lhsParseNode *patternHead)
  {
   int theType;
   struct symbolHashNode *theVariable;
   struct constraintRecord *theConstraints;

   /*=============================================================*/
   /* If a pattern address is being propagated, then treat it as  */
   /* a single field pattern variable and create a constraint     */
   /* which indicates that is must be a fact or instance address. */
   /* This code will have to be modified for new data types which */
   /* can match patterns.                                         */
   /*=============================================================*/

   if (thePattern->type == PATTERN_CE)
     {
      theType = SF_VARIABLE;
      theVariable = (struct symbolHashNode *) thePattern->value;
      if (thePattern->derivedConstraints) RemoveConstraint(theEnv,thePattern->constraints);
      theConstraints = GetConstraintRecord(theEnv);
      thePattern->constraints = theConstraints;
      thePattern->constraints->anyAllowed = FALSE;
      thePattern->constraints->instanceAddressesAllowed = TRUE;
      thePattern->constraints->factAddressesAllowed = TRUE;
      thePattern->derivedConstraints = TRUE;
     }

   /*===================================================*/
   /* Otherwise a pattern variable is being propagated. */
   /*===================================================*/

   else
     {
      theType = thePattern->type;
      theVariable = (struct symbolHashNode *) thePattern->value;
     }

   /*===================================================*/
   /* Propagate the variable location to any additional */
   /* fields associated with the binding variable.      */
   /*===================================================*/

   if (thePattern->type != PATTERN_CE)
     {
      PropagateVariableToNodes(theEnv,thePattern->bottom,theType,theVariable,
                               thePattern,patternHead->beginNandDepth,
                               TRUE,FALSE);

      if (ProcessField(theEnv,thePattern,multifieldHeader,patternHead))
        { return(TRUE); }
     }

   /*=================================================================*/
   /* Propagate the constraints to other fields, slots, and patterns. */
   /*=================================================================*/

   return(PropagateVariableDriver(theEnv,patternHead,thePattern,multifieldHeader,theType,
                                  theVariable,thePattern,TRUE));
  }

/*******************************************/
/* PropagateVariableDriver: Driver routine */
/*   for propagating variable references.  */
/*******************************************/
static int PropagateVariableDriver(
  void *theEnv,
  struct lhsParseNode *patternHead,
  struct lhsParseNode *theNode,
  struct lhsParseNode *multifieldHeader,
  int theType,
  struct symbolHashNode *variableName,
  struct lhsParseNode *theReference,
  int assignReference)
  {
   /*===================================================*/
   /* Propagate the variable location to any additional */
   /* constraints associated with the binding variable. */
   /*===================================================*/

   if (multifieldHeader != NULL)
     {
      if (PropagateVariableToNodes(theEnv,multifieldHeader->right,theType,variableName,
                                   theReference,patternHead->beginNandDepth,assignReference,FALSE))
        {
         VariableMixingErrorMessage(theEnv,variableName);
         return(TRUE);
        }
     }

   /*========================================================*/
   /* Propagate the variable location to fields/slots in the */
   /* same pattern which appear after the binding variable.  */
   /*========================================================*/

   if (PropagateVariableToNodes(theEnv,theNode->right,theType,variableName,theReference,
                                patternHead->beginNandDepth,assignReference,FALSE))
     {
      VariableMixingErrorMessage(theEnv,variableName);
      return(TRUE);
     }

   /*======================================================*/
   /* Propagate values to other patterns if the pattern in */
   /* which the variable is found is not a "not" CE or the */
   /* last pattern within a nand CE.                       */
   /*======================================================*/

   if (((patternHead->type == PATTERN_CE) || (patternHead->type == TEST_CE)) &&
       (patternHead->negated == FALSE) &&
       (patternHead->beginNandDepth <= patternHead->endNandDepth))
     {
      int ignoreVariableMixing;

      /*============================================================*/
      /* If the variables are propagated from a test CE, then don't */
      /* check for mixing of single and multifield variables (since */
      /* previously bound multifield variables typically have the $ */
      /* removed when passed as an argument to a function unless    */
      /* sequence expansion is desired).                            */
      /*============================================================*/

      if (patternHead->type == TEST_CE) ignoreVariableMixing = TRUE;
      else ignoreVariableMixing = FALSE;

      /*==========================*/
      /* Propagate the reference. */
      /*==========================*/

      if (PropagateVariableToNodes(theEnv,patternHead->bottom,theType,variableName,theReference,
                                   patternHead->beginNandDepth,assignReference,
                                   ignoreVariableMixing))
       {
         VariableMixingErrorMessage(theEnv,variableName);
         return(TRUE);
        }
     }

   /*==============================================*/
   /* Return FALSE to indicate that no errors were */
   /* generated by the variable propagation.       */
   /*==============================================*/

   return(FALSE);
  }

/********************************************************/
/* ProcessField: Processes a field or slot of a pattern */
/*   which does not contain a binding variable.         */
/********************************************************/
static int ProcessField(
  void *theEnv,
  struct lhsParseNode *thePattern,
  struct lhsParseNode *multifieldHeader,
  struct lhsParseNode *patternHead)
  {
   struct lhsParseNode *theList, *tempList;

   /*====================================================*/
   /* Nothing needs to be done for the node representing */
   /* the entire pattern. Return FALSE to indicate that  */
   /* no errors were generated.                          */
   /*====================================================*/

   if (thePattern->type == PATTERN_CE) return(FALSE);

   /*====================================================================*/
   /* Derive a set of constraints based on values found in the slot or   */
   /* field. For example, if a slot can only contain the values 1, 2, or */
   /* 3, the field constraint ~2 would generate a constraint record that */
   /* only allows the value 1 or 3. Once generated, the constraints are  */
   /* propagated to other slots and fields.                              */
   /*====================================================================*/

   theList = DeriveVariableConstraints(theEnv,thePattern);
   for (tempList = theList; tempList != NULL; tempList = tempList->right)
     {
      if (PropagateVariableDriver(theEnv,patternHead,thePattern,multifieldHeader,tempList->type,
                                  (SYMBOL_HN *) tempList->value,tempList,FALSE))
        {
         ReturnLHSParseNodes(theEnv,theList);
         return(TRUE);
        }
     }
   ReturnLHSParseNodes(theEnv,theList);

   /*===========================================================*/
   /* Check for "variable referenced, but not previously bound" */
   /* errors. Return TRUE if this type of error is detected.    */
   /*===========================================================*/

   if (UnboundVariablesInPattern(theEnv,thePattern,(int) patternHead->whichCE))
     { return(TRUE); }

   /*==================================================*/
   /* Check for constraint errors for this slot/field. */
   /* If the slot/field has unmatchable constraints    */
   /* then return TRUE to indicate a semantic error.   */
   /*==================================================*/

   if (ProcessConnectedConstraints(theEnv,thePattern,multifieldHeader,patternHead))
     { return(TRUE); }

   /*==============================================================*/
   /* Convert the slot/field constraint to a series of expressions */
   /* that will be used in the pattern and join networks.          */
   /*==============================================================*/

   FieldConversion(theEnv,thePattern,patternHead);

   /*=========================================================*/
   /* Return FALSE to indicate that no errors were generated. */
   /*=========================================================*/

   return(FALSE);
  }

/*************************************************************/
/* PropagateVariableToNodes: Propagates variable references  */
/*  to all other variables within the semantic scope of the  */
/*  bound variable. That is, a variable reference cannot be  */
/*  beyond an enclosing not/and CE combination. The          */
/*  restriction of propagating variables beyond an enclosing */
/*  not CE is handled within the GetVariables function.      */
/*************************************************************/
static int PropagateVariableToNodes(
  void *theEnv,
  struct lhsParseNode *theNode,
  int theType,
  struct symbolHashNode *variableName,
  struct lhsParseNode *theReference,
  int startDepth,
  int assignReference,
  int ignoreVariableTypes)
  {
   struct constraintRecord *tempConstraints;

   /*===========================================*/
   /* Traverse the nodes using the bottom link. */
   /*===========================================*/

   while (theNode != NULL)
     {
      /*==================================================*/
      /* If the field/slot contains a predicate or return */
      /* value constraint, then propagate the variable to */
      /* the expression associated with that constraint.  */
      /*==================================================*/

      if (theNode->expression != NULL)
        {
         PropagateVariableToNodes(theEnv,theNode->expression,theType,variableName,
                                  theReference,startDepth,assignReference,TRUE);
        }

      /*======================================================*/
      /* If the field/slot is a single or multifield variable */
      /* with the same name as the propagated variable,       */
      /* then propagate the variable location to this node.   */
      /*======================================================*/

      else if (((theNode->type == SF_VARIABLE) || (theNode->type == MF_VARIABLE)) &&
               (theNode->value == (void *) variableName))
        {
         /*======================================================*/
         /* Check for mixing of single and multifield variables. */
         /*======================================================*/

         if (ignoreVariableTypes == FALSE)
           {
            if (((theType == SF_VARIABLE) && (theNode->type == MF_VARIABLE)) ||
                ((theType == MF_VARIABLE) && (theNode->type == SF_VARIABLE)))
              { return(TRUE); }
           }

         /*======================================================*/
         /* Intersect the propagated variable's constraints with */
         /* the current constraints for this field/slot.         */
         /*======================================================*/

         if ((theReference->constraints != NULL) && (! theNode->negated))
           {
            tempConstraints = theNode->constraints;
            theNode->constraints = IntersectConstraints(theEnv,theReference->constraints,
                                                        tempConstraints);
            if (theNode->derivedConstraints)
              { RemoveConstraint(theEnv,tempConstraints); }

            theNode->derivedConstraints = TRUE;
           }


         /*=====================================================*/
         /* Don't propagate the variable if it originates from  */
         /* a different type of pattern object and the variable */
         /* reference has already been resolved.                */
         /*=====================================================*/

         if (assignReference)
           {
            if (theNode->referringNode == NULL)
              { theNode->referringNode = theReference; }
            else if (theReference->pattern == theNode->pattern)
              { theNode->referringNode = theReference; }
            else if (theReference->patternType == theNode->patternType)
              { theNode->referringNode = theReference; }
           }
        }

      /*========================================================*/
      /* If the field/slot is the node representing the entire  */
      /* pattern, then propagate the variable location to the   */
      /* fact address associated with the pattern (if it is the */
      /* same variable name).                                   */
      /*========================================================*/

      else if ((theNode->type == PATTERN_CE) &&
               (theNode->value == (void *) variableName) &&
               (assignReference == TRUE))
        {
         if (theType == MF_VARIABLE) return(TRUE);

         theNode->referringNode = theReference;
        }

      /*=====================================================*/
      /* Propagate the variable to other fields contained    */
      /* within the same & field constraint or same pattern. */
      /*=====================================================*/

      if (theNode->right != NULL)
        {
         if (PropagateVariableToNodes(theEnv,theNode->right,theType,variableName,
                                      theReference,startDepth,assignReference,ignoreVariableTypes))
           { return(TRUE); }
        }

      /*============================================================*/
      /* Propagate the variable to other patterns within the same   */
      /* semantic scope (if dealing with the node for an entire     */
      /* pattern) or to the next | field constraint within a field. */
      /*============================================================*/

      if ((theNode->type == PATTERN_CE) || (theNode->type == TEST_CE))
        {
         if (theNode->endNandDepth < startDepth) theNode = NULL;
         else theNode = theNode->bottom;
        }
      else
        { theNode = theNode->bottom; }
     }

   /*========================================================*/
   /* Return FALSE to indicate that no errors were detected. */
   /*========================================================*/

   return(FALSE);
  }

/*************************************************************/
/* UnboundVariablesInPattern: Verifies that variables within */
/*   a slot/field have been referenced properly (i.e. that   */
/*   variables have been previously bound if they are not a  */
/*   binding occurrence).                                    */
/*************************************************************/
static BOOLEAN UnboundVariablesInPattern(
  void *theEnv,
  struct lhsParseNode *theSlot,
  int pattern)
  {
   struct lhsParseNode *andField;
   struct lhsParseNode *rv;
   int result;
   struct lhsParseNode *orField;
   struct symbolHashNode *slotName;
   CONSTRAINT_RECORD *theConstraints;
   int theField;

   /*===================================================*/
   /* If a multifield slot is being checked, then check */
   /* each of the fields grouped with the multifield.   */
   /*===================================================*/

   if (theSlot->multifieldSlot)
     {
      theSlot = theSlot->bottom;
      while (theSlot != NULL)
        {
         if (UnboundVariablesInPattern(theEnv,theSlot,pattern))
           { return(TRUE); }
         theSlot = theSlot->right;
        }

      return(FALSE);
     }

   /*=======================*/
   /* Check a single field. */
   /*=======================*/

   slotName = theSlot->slot;
   theField = theSlot->index;
   theConstraints = theSlot->constraints;

   /*===========================================*/
   /* Loop through each of the '|' constraints. */
   /*===========================================*/

   for (orField = theSlot->bottom;
        orField != NULL;
        orField = orField->bottom)
     {
      /*===========================================*/
      /* Loop through each of the fields connected */
      /* by the '&' within the '|' constraint.     */
      /*===========================================*/

      for (andField = orField;
           andField != NULL;
           andField = andField->right)
        {
         /*=======================================================*/
         /* If this is not a binding occurence of a variable and  */
         /* there is no previous binding occurence of a variable, */
         /* then generate an error message for a variable that is */
         /* referred to but not bound.                            */
         /*=======================================================*/

         if (((andField->type == SF_VARIABLE) || (andField->type == MF_VARIABLE)) &&
             (andField->referringNode == NULL))
           {
            VariableReferenceErrorMessage(theEnv,(SYMBOL_HN *) andField->value,NULL,pattern,
                                          slotName,theField);
            return(TRUE);
           }

         /*==============================================*/
         /* Check predicate and return value constraints */
         /* to insure that all variables used within the */
         /* constraint have been previously bound.       */
         /*==============================================*/

         else if ((andField->type == PREDICATE_CONSTRAINT) ||
                  (andField->type == RETURN_VALUE_CONSTRAINT))
           {
            rv = CheckExpression(theEnv,andField->expression,NULL,pattern,slotName,theField);
            if (rv != NULL) return(TRUE);
           }

         /*========================================================*/
         /* If static constraint checking is being performed, then */
         /* determine if constant values have violated the set of  */
         /* derived constraints for the slot/field (based on the   */
         /* deftemplate definition and propagated constraints).    */
         /*========================================================*/

         else if (((andField->type == INTEGER) || (andField->type == FLOAT) ||
                   (andField->type == SYMBOL) || (andField->type == STRING) ||
                   (andField->type == INSTANCE_NAME)) &&
                  EnvGetStaticConstraintChecking(theEnv))
           {
            result = ConstraintCheckValue(theEnv,andField->type,andField->value,theConstraints);
            if (result != NO_VIOLATION)
              {
               ConstraintViolationErrorMessage(theEnv,"A literal restriction value",
                                               NULL,FALSE,pattern,
                                               slotName,theField,result,
                                               theConstraints,TRUE);
               return(TRUE);
              }
           }
        }
     }

   /*===============================*/
   /* Return FALSE to indicate that */
   /* no errors were detected.      */
   /*===============================*/

   return(FALSE);
  }

/******************************************************************/
/* CheckExpression: Verifies that variables within an expression  */
/*   have been referenced properly. All variables within an       */
/*   expression must have been previously bound.                  */
/******************************************************************/
static struct lhsParseNode *CheckExpression(
  void *theEnv,
  struct lhsParseNode *exprPtr,
  struct lhsParseNode *lastOne,
  int whichCE,
  struct symbolHashNode *slotName,
  int theField)
  {
   struct lhsParseNode *rv;
   int i = 1;

   while (exprPtr != NULL)
     {
      /*===============================================================*/
      /* Check that single field variables contained in the expression */
      /* were previously defined in the LHS. Also check to see if the  */
      /* variable has unmatchable constraints.                         */
      /*===============================================================*/

      if (exprPtr->type == SF_VARIABLE)
        {
         if (exprPtr->referringNode == NULL)
           {
            VariableReferenceErrorMessage(theEnv,(SYMBOL_HN *) exprPtr->value,lastOne,
                                          whichCE,slotName,theField);
            return(exprPtr);
           }
         else if ((UnmatchableConstraint(exprPtr->constraints)) &&
                  EnvGetStaticConstraintChecking(theEnv))
           {
            ConstraintReferenceErrorMessage(theEnv,(SYMBOL_HN *) exprPtr->value,lastOne,i,
                                            whichCE,slotName,theField);
            return(exprPtr);
           }
        }

      /*==================================================*/
      /* Check that multifield variables contained in the */
      /* expression were previously defined in the LHS.   */
      /*==================================================*/

      else if ((exprPtr->type == MF_VARIABLE) && (exprPtr->referringNode == NULL))
        {
         VariableReferenceErrorMessage(theEnv,(SYMBOL_HN *) exprPtr->value,lastOne,
                                       whichCE,slotName,theField);
         return(exprPtr);
        }

      /*=====================================================*/
      /* Check that global variables are referenced properly */
      /* (i.e. if you reference a global variable, it must   */
      /* already be defined by a defglobal construct).       */
      /*=====================================================*/

#if DEFGLOBAL_CONSTRUCT
      else if (exprPtr->type == GBL_VARIABLE)
        {
         int count;

         if (FindImportedConstruct(theEnv,"defglobal",NULL,ValueToString(exprPtr->value),
                                   &count,TRUE,NULL) == NULL)
           {
            VariableReferenceErrorMessage(theEnv,(SYMBOL_HN *) exprPtr->value,lastOne,
                                          whichCE,slotName,theField);
            return(exprPtr);
           }
        }
#endif

      /*============================================*/
      /* Recursively check other function calls to  */
      /* insure variables are referenced correctly. */
      /*============================================*/

      else if (((exprPtr->type == FCALL)
#if DEFGENERIC_CONSTRUCT
             || (exprPtr->type == GCALL)
#endif
#if DEFFUNCTION_CONSTRUCT
             || (exprPtr->type == PCALL)
#endif
         ) && (exprPtr->bottom != NULL))
        {
         if ((rv = CheckExpression(theEnv,exprPtr->bottom,exprPtr,whichCE,slotName,theField)) != NULL)
           { return(rv); }
        }

      /*=============================================*/
      /* Move on to the next part of the expression. */
      /*=============================================*/

      i++;
      exprPtr = exprPtr->right;
     }

   /*================================================*/
   /* Return NULL to indicate no error was detected. */
   /*================================================*/

   return(NULL);
  }

/********************************************************/
/* VariableReferenceErrorMessage: Generic error message */
/*   for referencing a variable before it is defined.   */
/********************************************************/
static void VariableReferenceErrorMessage(
  void *theEnv,
  struct symbolHashNode *theVariable,
  struct lhsParseNode *theExpression,
  int whichCE,
  struct symbolHashNode *slotName,
  int theField)
  {
   struct expr *temprv;

   /*=============================*/
   /* Print the error message ID. */
   /*=============================*/

   PrintErrorID(theEnv,"ANALYSIS",4,TRUE);

   /*=================================*/
   /* Print the name of the variable. */
   /*=================================*/

   EnvPrintRouter(theEnv,WERROR,"Variable ?");
   EnvPrintRouter(theEnv,WERROR,ValueToString(theVariable));
   EnvPrintRouter(theEnv,WERROR," ");

   /*=================================================*/
   /* If the variable was found inside an expression, */
   /* then print the expression.                      */
   /*=================================================*/

   if (theExpression != NULL)
     {
      temprv = LHSParseNodesToExpression(theEnv,theExpression);
      ReturnExpression(theEnv,temprv->nextArg);
      temprv->nextArg = NULL;
      EnvPrintRouter(theEnv,WERROR,"found in the expression ");
      PrintExpression(theEnv,WERROR,temprv);
      EnvPrintRouter(theEnv,WERROR,"\n");
      ReturnExpression(theEnv,temprv);
     }

   /*====================================================*/
   /* Print the CE in which the variable was referenced. */
   /*====================================================*/

   EnvPrintRouter(theEnv,WERROR,"was referenced in CE #");
   PrintLongInteger(theEnv,WERROR,(long int) whichCE);

   /*=====================================*/
   /* Identify the slot or field in which */
   /* the variable was found.             */
   /*=====================================*/

   if (slotName == NULL)
     {
      if (theField > 0)
        {
         EnvPrintRouter(theEnv,WERROR," field #");
         PrintLongInteger(theEnv,WERROR,(long int) theField);
        }
     }
   else
     {
      EnvPrintRouter(theEnv,WERROR," slot ");
      EnvPrintRouter(theEnv,WERROR,ValueToString(slotName));
     }

   EnvPrintRouter(theEnv,WERROR," before being defined.\n");
  }

/************************************************************/
/* VariableMixingErrorMessage: Prints the error message for */
/*   the illegal mixing of single and multifield variables  */
/*   on the LHS of a rule.                                  */
/************************************************************/
static void VariableMixingErrorMessage(
  void *theEnv,
  struct symbolHashNode *theVariable)
  {
   PrintErrorID(theEnv,"ANALYSIS",3,TRUE);
   EnvPrintRouter(theEnv,WERROR,"Variable ?");
   EnvPrintRouter(theEnv,WERROR,ValueToString(theVariable));
   EnvPrintRouter(theEnv,WERROR," is used as both a single and multifield variable in the LHS\n");
  }

#endif /* (! RUN_TIME) && (! BLOAD_ONLY) && DEFRULE_CONSTRUCT */


