text:    /*******************************************************/
     /*      "C" Language Integrated Production System      */
     /*                                                     */
     /*             CLIPS Version 6.31  05/29/19            */
     /*                                                     */
     /*             EXPRESSION OPERATIONS MODULE            */
     /*******************************************************/
  
  /*************************************************************/
  /* Purpose: Provides utility routines for manipulating and   */
  /*   examining expressions.                                  */
  /*                                                           */
  /* Principal Programmer(s):                                  */
  /*      Gary D. Riley                                        */
  /*                                                           */
  /* Contributing Programmer(s):                               */
  /*      Brian L. Dantes                                      */
  /*                                                           */
  /* Revision History:                                         */
  /*                                                           */
  /*      6.24: Renamed BOOLEAN macro type to intBool.         */
  /*                                                           */
  /*      6.30: Add NegateExpression function.                 */
  /*                                                           */
  /*            Added const qualifiers to remove C++           */
  /*            deprecation warnings.                          */
  /*                                                           */
  /*      6.31: Removed FACT_ADDRESS type from                 */
  /*            ExpressionContainsVariables function.          */
  /*                                                           */
  /*************************************************************/
  
  #define _EXPRNOPS_SOURCE_
  
  #include "setup.h"
  
  #include <stdio.h>
  #define _STDIO_INCLUDED_
  #include <stdlib.h>
  #include <string.h>
  #include <ctype.h>
  
  #include "memalloc.h"
  #include "envrnmnt.h"
  #include "router.h"
  #include "extnfunc.h"
  #include "cstrnchk.h"
  #include "prntutil.h"
  #include "cstrnutl.h"
  #include "cstrnops.h"
  
  #include "exprnops.h"
  
  #if (! RUN_TIME)
  
  /**************************************************************/
  /* CheckArgumentAgainstRestriction: Compares an argument to a */
  /*   function to the set of restrictions for that function to */
  /*   determine if any incompatibilities exist. If so, the     */
  /*   value TRUE is returned, otherwise FALSE is returned.     */
  /*   Restrictions checked are:                                */
  /*     a - external address                                   */
  /*     d - float                                              */
  /*     e - instance address, instance name, or symbol         */
  /*     f - float                                              */
  /*     g - integer, float, or symbol                          */
  /*     h - instance address, instance name, fact address,     */
  /*         integer, or symbol                                 */
  /*     i - integer                                            */
  /*     j - symbol, string, or instance name                   */
  /*     k - symbol or string                                   */
  /*     l - integer                                            */
  /*     m - multifield                                         */
  /*     n - float or integer                                   */
  /*     o - instance name                                      */
  /*     p - instance name or symbol                            */
  /*     q - string, symbol, or multifield                      */
  /*     s - string                                             */
  /*     u - unknown (any type allowed)                         */
  /*     w - symbol                                             */
  /*     x - instance address                                   */
  /*     y - fact address                                       */
  /*     z - fact address, integer, or symbol (*)               */
  /**************************************************************/
  globle int CheckArgumentAgainstRestriction(
    void *theEnv,
    struct expr *theExpression,
    int theRestriction)
    {
     CONSTRAINT_RECORD *cr1, *cr2, *cr3;
  
     /*=============================================*/
     /* Generate a constraint record for the actual */
     /* argument passed to the function.            */
     /*=============================================*/
  
     cr1 = ExpressionToConstraintRecord(theEnv,theExpression);
  
     /*================================================*/
     /* Generate a constraint record based on the type */
     /* of argument expected by the function.          */
     /*================================================*/
  
     cr2 = ArgumentTypeToConstraintRecord(theEnv,theRestriction);
  
     /*===============================================*/
     /* Intersect the two constraint records and then */
     /* discard them.                                 */
     /*===============================================*/
  
     cr3 = IntersectConstraints(theEnv,cr1,cr2);
  
     RemoveConstraint(theEnv,cr1);
     RemoveConstraint(theEnv,cr2);
  
     /*====================================================*/
     /* If the intersection of the two constraint records  */
     /* is empty, then the argument passed to the function */
     /* doesn't satisfy the restrictions for the argument. */
     /*====================================================*/
  
     if (UnmatchableConstraint(cr3))
       {
        RemoveConstraint(theEnv,cr3);
        return(TRUE);
       }
  
     /*===================================================*/
     /* The argument satisfies the function restrictions. */
     /*===================================================*/
  
     RemoveConstraint(theEnv,cr3);
     return(FALSE);
    }
  
  #endif /* (! RUN_TIME) */
  
  /************************************************************/
  /* ConstantExpression: Returns TRUE if the expression */
  /*   is a constant, otherwise FALSE.                  */
  /************************************************************/
  globle intBool ConstantExpression(
    struct expr *testPtr)
    {
     while (testPtr != NULL)
       {
        if ((testPtr->type != SYMBOL) && (testPtr->type != STRING) &&
  #if OBJECT_SYSTEM
            (testPtr->type != INSTANCE_NAME) && (testPtr->type != INSTANCE_ADDRESS) &&
  #endif
            (testPtr->type != INTEGER) && (testPtr->type != FLOAT))
          { return(FALSE); }
        testPtr = testPtr->nextArg;
       }
  
     return(TRUE);
    }
  
  /************************************************/
  /* ConstantType: Returns TRUE if the type */
  /*   is a constant, otherwise FALSE.      */
  /************************************************/
  globle intBool ConstantType(
    int theType)
    {
     switch (theType)
       {
        case SYMBOL:
        case STRING:
        case INTEGER:
        case FLOAT:
  #if OBJECT_SYSTEM
        case INSTANCE_NAME:
        case INSTANCE_ADDRESS:
  #endif
          return(TRUE);
       }
  
     return(FALSE);
    }
  
  /*****************************************************************************/
  /* IdenticalExpression: Determines if two expressions are identical. Returns */
  /*   TRUE if the expressions are identical, otherwise FALSE is returned.     */
  /*****************************************************************************/
  globle intBool IdenticalExpression(
    struct expr *firstList,
    struct expr *secondList)
    {
     /*==============================================*/
     /* Compare each argument in both expressions by */
     /* following the nextArg list.                  */
     /*==============================================*/
  
     for (;
          (firstList != NULL) && (secondList != NULL);
          firstList = firstList->nextArg, secondList = secondList->nextArg)
       {
        /*=========================*/
        /* Compare type and value. */
        /*=========================*/
  
        if (firstList->type != secondList->type)
          { return(FALSE); }
  
        if (firstList->value != secondList->value)
          { return (FALSE); }
  
        /*==============================*/
        /* Compare the arguments lists. */
        /*==============================*/
  
        if (IdenticalExpression(firstList->argList,secondList->argList) == FALSE)
          { return(FALSE); }
       }
  
     /*=====================================================*/
     /* If firstList and secondList aren't both NULL, then  */
     /* one of the lists contains more expressions than the */
     /* other.                                              */
     /*=====================================================*/
  
     if (firstList != secondList) return(FALSE);
  
     /*============================*/
     /* Expressions are identical. */
     /*============================*/
  
     return(TRUE);
    }
  
  /****************************************************/
  /* CountArguments: Returns the number of structures */
  /*   stored in an expression as traversed through   */
  /*   the nextArg pointer but not the argList        */
  /*   pointer.                                       */
  /****************************************************/
  globle int CountArguments(
    struct expr *testPtr)
    {
     int size = 0;
  
     while (testPtr != NULL)
       {
        size++;
        testPtr = testPtr->nextArg;
       }
  
     return(size);
    }
  
  /******************************************/
  /* CopyExpresssion: Copies an expression. */
  /******************************************/
  globle struct expr *CopyExpression(
    void *theEnv,
    struct expr *original)
    {
     struct expr *topLevel, *next, *last;
  
     if (original == NULL) return(NULL);
  
     topLevel = GenConstant(theEnv,original->type,original->value);
     topLevel->argList = CopyExpression(theEnv,original->argList);
  
     last = topLevel;
     original = original->nextArg;
     while (original != NULL)
       {
        next = GenConstant(theEnv,original->type,original->value);
        next->argList = CopyExpression(theEnv,original->argList);
  
        last->nextArg = next;
        last = next;
        original = original->nextArg;
       }
  
     return(topLevel);
    }
  
  /************************************************************/
  /* ExpressionContainsVariables: Determines if an expression */
  /*   contains any variables. Returns TRUE if the expression */
  /*   contains any variables, otherwise FALSE is returned.   */
  /************************************************************/
  globle intBool ExpressionContainsVariables(
    struct expr *theExpression,
    intBool globalsAreVariables)
    {
     while (theExpression != NULL)
       {
        if (theExpression->argList != NULL)
          {
           if (ExpressionContainsVariables(theExpression->argList,globalsAreVariables))
             { return(TRUE); }
          }
  
        if ((theExpression->type == MF_VARIABLE) ||
            (theExpression->type == SF_VARIABLE) ||
            (((theExpression->type == GBL_VARIABLE) ||
              (theExpression->type == MF_GBL_VARIABLE)) &&
             (globalsAreVariables == TRUE)))
          { return(TRUE); }
  
        theExpression = theExpression->nextArg;
       }
  
     return(FALSE);
    }
  
  /*****************************************/
  /* ExpressionSize: Returns the number of */
  /*   structures stored in an expression. */
  /*****************************************/
  globle long ExpressionSize(
    struct expr *testPtr)
    {
     long size = 0;
  
     while (testPtr != NULL)
       {
        size++;
        if (testPtr->argList != NULL)
          { size += ExpressionSize(testPtr->argList); }
        testPtr = testPtr->nextArg;
       }
     return(size);
    }
  
  /************************************************/
  /* GenConstant: Generates a constant expression */
  /*   value of type string, symbol, or number.   */
  /************************************************/
  globle struct expr *GenConstant(
    void *theEnv,
    unsigned short type,
    void *value)
    {
     struct expr *top;
  
     top = get_struct(theEnv,expr);
     top->nextArg = NULL;
     top->argList = NULL;
     top->type = type;
     top->value = value;
  
     return(top);
    }
  
  /*************************************************/
  /* PrintExpression: Pretty prints an expression. */
  /*************************************************/
  globle void PrintExpression(
    void *theEnv,
    const char *fileid,
    struct expr *theExpression)
    {
     struct expr *oldExpression;
  
     if (theExpression == NULL)
       { return; }
  
     while (theExpression != NULL)
       {
        switch (theExpression->type)
          {
           case SF_VARIABLE:
           case GBL_VARIABLE:
              EnvPrintRouter(theEnv,fileid,"?");
              EnvPrintRouter(theEnv,fileid,ValueToString(theExpression->value));
              break;
  
           case MF_VARIABLE:
           case MF_GBL_VARIABLE:
              EnvPrintRouter(theEnv,fileid,"$?");
              EnvPrintRouter(theEnv,fileid,ValueToString(theExpression->value));
              break;
  
           case FCALL:
             EnvPrintRouter(theEnv,fileid,"(");
             EnvPrintRouter(theEnv,fileid,ValueToString(ExpressionFunctionCallName(theExpression)));
             if (theExpression->argList != NULL) { EnvPrintRouter(theEnv,fileid," "); }
             PrintExpression(theEnv,fileid,theExpression->argList);
             EnvPrintRouter(theEnv,fileid,")");
             break;
  
           default:
             oldExpression = EvaluationData(theEnv)->CurrentExpression;
             EvaluationData(theEnv)->CurrentExpression = theExpression;
             PrintAtom(theEnv,fileid,theExpression->type,theExpression->value);
             EvaluationData(theEnv)->CurrentExpression = oldExpression;
             break;
          }
  
        theExpression = theExpression->nextArg;
        if (theExpression != NULL) EnvPrintRouter(theEnv,fileid," ");
       }
  
     return;
    }
  
  /*************************************************************************/
  /* CombineExpressions: Combines two expressions into a single equivalent */
  /*   expression. Mainly serves to merge expressions containing "and"     */
  /*   and "or" expressions without unnecessary duplication of the "and"   */
  /*   and "or" expressions (i.e., two "and" expressions can be merged by  */
  /*   placing them as arguments within another "and" expression, but it   */
  /*   is more efficient to add the arguments of one of the "and"          */
  /*   expressions to the list of arguments for the other and expression). */
  /*************************************************************************/
  globle struct expr *CombineExpressions(
    void *theEnv,
    struct expr *expr1,
    struct expr *expr2)
    {
     struct expr *tempPtr;
  
     /*===========================================================*/
     /* If the 1st expression is NULL, return the 2nd expression. */
     /*===========================================================*/
  
     if (expr1 == NULL) return(expr2);
  
     /*===========================================================*/
     /* If the 2nd expression is NULL, return the 1st expression. */
     /*===========================================================*/
  
     if (expr2 == NULL) return(expr1);
  
     /*============================================================*/
     /* If the 1st expression is an "and" expression, and the 2nd  */
     /* expression is not an "and" expression, then include the    */
     /* 2nd expression in the argument list of the 1st expression. */
     /*============================================================*/
  
     if ((expr1->value == ExpressionData(theEnv)->PTR_AND) &&
         (expr2->value != ExpressionData(theEnv)->PTR_AND))
       {
        tempPtr = expr1->argList;
        if (tempPtr == NULL)
          {
           rtn_struct(theEnv,expr,expr1);
           return(expr2);
          }
  
        while (tempPtr->nextArg != NULL)
          { tempPtr = tempPtr->nextArg; }
  
        tempPtr->nextArg = expr2;
        return(expr1);
       }
  
     /*============================================================*/
     /* If the 2nd expression is an "and" expression, and the 1st  */
     /* expression is not an "and" expression, then include the    */
     /* 1st expression in the argument list of the 2nd expression. */
     /*============================================================*/
  
     if ((expr1->value != ExpressionData(theEnv)->PTR_AND) &&
         (expr2->value == ExpressionData(theEnv)->PTR_AND))
       {
        tempPtr = expr2->argList;
        if (tempPtr == NULL)
          {
           rtn_struct(theEnv,expr,expr2);
           return(expr1);
          }
  
        expr2->argList = expr1;
        expr1->nextArg = tempPtr;
  
        return(expr2);
       }
  
     /*===========================================================*/
     /* If both expressions are "and" expressions, then add the   */
     /* 2nd expression to the argument list of the 1st expression */
     /* and throw away the extraneous "and" expression.           */
     /*===========================================================*/
  
     if ((expr1->value == ExpressionData(theEnv)->PTR_AND) &&
         (expr2->value == ExpressionData(theEnv)->PTR_AND))
       {
        tempPtr = expr1->argList;
        if (tempPtr == NULL)
          {
           rtn_struct(theEnv,expr,expr1);
           return(expr2);
          }
  
        while (tempPtr->nextArg != NULL)
          { tempPtr = tempPtr->nextArg; }
  
        tempPtr->nextArg = expr2->argList;
        rtn_struct(theEnv,expr,expr2);
  
        return(expr1);
       }
  
     /*=====================================================*/
     /* If neither expression is an "and" expression, then  */
     /* create an "and" expression and add both expressions */
     /* to the argument list of that "and" expression.      */
     /*=====================================================*/
  
     tempPtr = GenConstant(theEnv,FCALL,ExpressionData(theEnv)->PTR_AND);
     tempPtr->argList = expr1;
     expr1->nextArg = expr2;
     return(tempPtr);
    }
  
  /*********************/
  /* NegateExpression: */
  /*********************/
  globle struct expr *NegateExpression(
    void *theEnv,
    struct expr *theExpression)
    {
     struct expr *tempPtr;
  
     /*=========================================*/
     /* If the expression is NULL, return NULL. */
     /*=========================================*/
  
     if (theExpression == NULL) return(NULL);
  
     /*==================================================*/
     /* The expression is already wrapped within a "not" */
     /* function call, just remove the function call.    */
     /*==================================================*/
  
     if (theExpression->value == ExpressionData(theEnv)->PTR_NOT)
       {
        tempPtr = theExpression->argList;
        rtn_struct(theEnv,expr,theExpression);
        return(tempPtr);
       }
  
     /*===================================================*/
     /* Wrap the expression within a "not" function call. */
     /*===================================================*/
  
     tempPtr = GenConstant(theEnv,FCALL,ExpressionData(theEnv)->PTR_NOT);
     tempPtr->argList = theExpression;
  
     return(tempPtr);
    }
  
  /********************************************************/
  /* AppendExpressions: Attaches an expression to the end */
  /*   of another expression's nextArg list.              */
  /********************************************************/
  globle struct expr *AppendExpressions(
    struct expr *expr1,
    struct expr *expr2)
    {
     struct expr *tempPtr;
  
     /*===========================================================*/
     /* If the 1st expression is NULL, return the 2nd expression. */
     /*===========================================================*/
  
     if (expr1 == NULL) return(expr2);
  
     /*===========================================================*/
     /* If the 2nd expression is NULL, return the 1st expression. */
     /*===========================================================*/
  
     if (expr2 == NULL) return(expr1);
  
     /*====================================*/
     /* Find the end of the 1st expression */
     /* and attach the 2nd expression.     */
     /*====================================*/
  
     tempPtr = expr1;
     while (tempPtr->nextArg != NULL) tempPtr = tempPtr->nextArg;
     tempPtr->nextArg = expr2;
  
     /*===============================*/
     /* Return the merged expression. */
     /*===============================*/
  
     return(expr1);
    }
  
  