1    /*******************************************************/
2    /*      "C" Language Integrated Production System      */
3    /*                                                     */
4    /*             CLIPS Version 6.30  08/16/14            */
5    /*                                                     */
6    /*             EXPRESSION OPERATIONS MODULE            */
7    /*******************************************************/
8 
9 /*************************************************************/
10 /* Purpose: Provides utility routines for manipulating and   */
11 /*   examining expressions.                                  */
12 /*                                                           */
13 /* Principal Programmer(s):                                  */
14 /*      Gary D. Riley                                        */
15 /*                                                           */
16 /* Contributing Programmer(s):                               */
17 /*      Brian L. Dantes                                      */
18 /*                                                           */
19 /* Revision History:                                         */
20 /*                                                           */
21 /*      6.24: Renamed BOOLEAN macro type to intBool.         */
22 /*                                                           */
23 /*      6.30: Add NegateExpression function.                 */
24 /*                                                           */
25 /*            Added const qualifiers to remove C++           */
26 /*            deprecation warnings.                          */
27 /*                                                           */
28 /*************************************************************/
29 
30 #define _EXPRNOPS_SOURCE_
31 
32 #include "setup.h"
33 
34 #include <stdio.h>
35 #define _STDIO_INCLUDED_
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39 
40 #include "memalloc.h"
41 #include "envrnmnt.h"
42 #include "router.h"
43 #include "extnfunc.h"
44 #include "cstrnchk.h"
45 #include "prntutil.h"
46 #include "cstrnutl.h"
47 #include "cstrnops.h"
48 
49 #include "exprnops.h"
50 
51 #if (! RUN_TIME)
52 
53 /**************************************************************/
54 /* CheckArgumentAgainstRestriction: Compares an argument to a */
55 /*   function to the set of restrictions for that function to */
56 /*   determine if any incompatibilities exist. If so, the     */
57 /*   value TRUE is returned, otherwise FALSE is returned.     */
58 /*   Restrictions checked are:                                */
59 /*     a - external address                                   */
60 /*     d - float                                              */
61 /*     e - instance address, instance name, or symbol         */
62 /*     f - float                                              */
63 /*     g - integer, float, or symbol                          */
64 /*     h - instance address, instance name, fact address,     */
65 /*         integer, or symbol                                 */
66 /*     i - integer                                            */
67 /*     j - symbol, string, or instance name                   */
68 /*     k - symbol or string                                   */
69 /*     l - integer                                            */
70 /*     m - multifield                                         */
71 /*     n - float or integer                                   */
72 /*     o - instance name                                      */
73 /*     p - instance name or symbol                            */
74 /*     q - string, symbol, or multifield                      */
75 /*     s - string                                             */
76 /*     u - unknown (any type allowed)                         */
77 /*     w - symbol                                             */
78 /*     x - instance address                                   */
79 /*     y - fact address                                       */
80 /*     z - fact address, integer, or symbol (*)               */
81 /**************************************************************/
CheckArgumentAgainstRestriction(void * theEnv,struct expr * theExpression,int theRestriction)82 globle int CheckArgumentAgainstRestriction(
83   void *theEnv,
84   struct expr *theExpression,
85   int theRestriction)
86   {
87    CONSTRAINT_RECORD *cr1, *cr2, *cr3;
88 
89    /*=============================================*/
90    /* Generate a constraint record for the actual */
91    /* argument passed to the function.            */
92    /*=============================================*/
93 
94    cr1 = ExpressionToConstraintRecord(theEnv,theExpression);
95 
96    /*================================================*/
97    /* Generate a constraint record based on the type */
98    /* of argument expected by the function.          */
99    /*================================================*/
100 
101    cr2 = ArgumentTypeToConstraintRecord(theEnv,theRestriction);
102 
103    /*===============================================*/
104    /* Intersect the two constraint records and then */
105    /* discard them.                                 */
106    /*===============================================*/
107 
108    cr3 = IntersectConstraints(theEnv,cr1,cr2);
109 
110    RemoveConstraint(theEnv,cr1);
111    RemoveConstraint(theEnv,cr2);
112 
113    /*====================================================*/
114    /* If the intersection of the two constraint records  */
115    /* is empty, then the argument passed to the function */
116    /* doesn't satisfy the restrictions for the argument. */
117    /*====================================================*/
118 
119    if (UnmatchableConstraint(cr3))
120      {
121       RemoveConstraint(theEnv,cr3);
122       return(TRUE);
123      }
124 
125    /*===================================================*/
126    /* The argument satisfies the function restrictions. */
127    /*===================================================*/
128 
129    RemoveConstraint(theEnv,cr3);
130    return(FALSE);
131   }
132 
133 #endif /* (! RUN_TIME) */
134 
135 /************************************************************/
136 /* ConstantExpression: Returns TRUE if the expression */
137 /*   is a constant, otherwise FALSE.                  */
138 /************************************************************/
ConstantExpression(struct expr * testPtr)139 globle intBool ConstantExpression(
140   struct expr *testPtr)
141   {
142    while (testPtr != NULL)
143      {
144       if ((testPtr->type != SYMBOL) && (testPtr->type != STRING) &&
145 #if OBJECT_SYSTEM
146           (testPtr->type != INSTANCE_NAME) && (testPtr->type != INSTANCE_ADDRESS) &&
147 #endif
148           (testPtr->type != INTEGER) && (testPtr->type != FLOAT))
149         { return(FALSE); }
150       testPtr = testPtr->nextArg;
151      }
152 
153    return(TRUE);
154   }
155 
156 /************************************************/
157 /* ConstantType: Returns TRUE if the type */
158 /*   is a constant, otherwise FALSE.      */
159 /************************************************/
ConstantType(int theType)160 globle intBool ConstantType(
161   int theType)
162   {
163    switch (theType)
164      {
165       case SYMBOL:
166       case STRING:
167       case INTEGER:
168       case FLOAT:
169 #if OBJECT_SYSTEM
170       case INSTANCE_NAME:
171       case INSTANCE_ADDRESS:
172 #endif
173         return(TRUE);
174      }
175 
176    return(FALSE);
177   }
178 
179 /*****************************************************************************/
180 /* IdenticalExpression: Determines if two expressions are identical. Returns */
181 /*   TRUE if the expressions are identical, otherwise FALSE is returned.     */
182 /*****************************************************************************/
IdenticalExpression(struct expr * firstList,struct expr * secondList)183 globle intBool IdenticalExpression(
184   struct expr *firstList,
185   struct expr *secondList)
186   {
187    /*==============================================*/
188    /* Compare each argument in both expressions by */
189    /* following the nextArg list.                  */
190    /*==============================================*/
191 
192    for (;
193         (firstList != NULL) && (secondList != NULL);
194         firstList = firstList->nextArg, secondList = secondList->nextArg)
195      {
196       /*=========================*/
197       /* Compare type and value. */
198       /*=========================*/
199 
200       if (firstList->type != secondList->type)
201         { return(FALSE); }
202 
203       if (firstList->value != secondList->value)
204         { return (FALSE); }
205 
206       /*==============================*/
207       /* Compare the arguments lists. */
208       /*==============================*/
209 
210       if (IdenticalExpression(firstList->argList,secondList->argList) == FALSE)
211         { return(FALSE); }
212      }
213 
214    /*=====================================================*/
215    /* If firstList and secondList aren't both NULL, then  */
216    /* one of the lists contains more expressions than the */
217    /* other.                                              */
218    /*=====================================================*/
219 
220    if (firstList != secondList) return(FALSE);
221 
222    /*============================*/
223    /* Expressions are identical. */
224    /*============================*/
225 
226    return(TRUE);
227   }
228 
229 /****************************************************/
230 /* CountArguments: Returns the number of structures */
231 /*   stored in an expression as traversed through   */
232 /*   the nextArg pointer but not the argList        */
233 /*   pointer.                                       */
234 /****************************************************/
CountArguments(struct expr * testPtr)235 globle int CountArguments(
236   struct expr *testPtr)
237   {
238    int size = 0;
239 
240    while (testPtr != NULL)
241      {
242       size++;
243       testPtr = testPtr->nextArg;
244      }
245 
246    return(size);
247   }
248 
249 /******************************************/
250 /* CopyExpresssion: Copies an expression. */
251 /******************************************/
CopyExpression(void * theEnv,struct expr * original)252 globle struct expr *CopyExpression(
253   void *theEnv,
254   struct expr *original)
255   {
256    struct expr *topLevel, *next, *last;
257 
258    if (original == NULL) return(NULL);
259 
260    topLevel = GenConstant(theEnv,original->type,original->value);
261    topLevel->argList = CopyExpression(theEnv,original->argList);
262 
263    last = topLevel;
264    original = original->nextArg;
265    while (original != NULL)
266      {
267       next = GenConstant(theEnv,original->type,original->value);
268       next->argList = CopyExpression(theEnv,original->argList);
269 
270       last->nextArg = next;
271       last = next;
272       original = original->nextArg;
273      }
274 
275    return(topLevel);
276   }
277 
278 /************************************************************/
279 /* ExpressionContainsVariables: Determines if an expression */
280 /*   contains any variables. Returns TRUE if the expression */
281 /*   contains any variables, otherwise FALSE is returned.   */
282 /************************************************************/
ExpressionContainsVariables(struct expr * theExpression,intBool globalsAreVariables)283 globle intBool ExpressionContainsVariables(
284   struct expr *theExpression,
285   intBool globalsAreVariables)
286   {
287    while (theExpression != NULL)
288      {
289       if (theExpression->argList != NULL)
290         {
291          if (ExpressionContainsVariables(theExpression->argList,globalsAreVariables))
292            { return(TRUE); }
293         }
294 
295       if ((theExpression->type == MF_VARIABLE) ||
296           (theExpression->type == SF_VARIABLE) ||
297           (theExpression->type == FACT_ADDRESS) ||
298           (((theExpression->type == GBL_VARIABLE) ||
299             (theExpression->type == MF_GBL_VARIABLE)) &&
300            (globalsAreVariables == TRUE)))
301         { return(TRUE); }
302 
303       theExpression = theExpression->nextArg;
304      }
305 
306    return(FALSE);
307   }
308 
309 /*****************************************/
310 /* ExpressionSize: Returns the number of */
311 /*   structures stored in an expression. */
312 /*****************************************/
ExpressionSize(struct expr * testPtr)313 globle long ExpressionSize(
314   struct expr *testPtr)
315   {
316    long size = 0;
317 
318    while (testPtr != NULL)
319      {
320       size++;
321       if (testPtr->argList != NULL)
322         { size += ExpressionSize(testPtr->argList); }
323       testPtr = testPtr->nextArg;
324      }
325    return(size);
326   }
327 
328 /************************************************/
329 /* GenConstant: Generates a constant expression */
330 /*   value of type string, symbol, or number.   */
331 /************************************************/
GenConstant(void * theEnv,unsigned short type,void * value)332 globle struct expr *GenConstant(
333   void *theEnv,
334   unsigned short type,
335   void *value)
336   {
337    struct expr *top;
338 
339    top = get_struct(theEnv,expr);
340    top->nextArg = NULL;
341    top->argList = NULL;
342    top->type = type;
343    top->value = value;
344 
345    return(top);
346   }
347 
348 /*************************************************/
349 /* PrintExpression: Pretty prints an expression. */
350 /*************************************************/
PrintExpression(void * theEnv,const char * fileid,struct expr * theExpression)351 globle void PrintExpression(
352   void *theEnv,
353   const char *fileid,
354   struct expr *theExpression)
355   {
356    struct expr *oldExpression;
357 
358    if (theExpression == NULL)
359      { return; }
360 
361    while (theExpression != NULL)
362      {
363       switch (theExpression->type)
364         {
365          case SF_VARIABLE:
366          case GBL_VARIABLE:
367             EnvPrintRouter(theEnv,fileid,"?");
368             EnvPrintRouter(theEnv,fileid,ValueToString(theExpression->value));
369             break;
370 
371          case MF_VARIABLE:
372          case MF_GBL_VARIABLE:
373             EnvPrintRouter(theEnv,fileid,"$?");
374             EnvPrintRouter(theEnv,fileid,ValueToString(theExpression->value));
375             break;
376 
377          case FCALL:
378            EnvPrintRouter(theEnv,fileid,"(");
379            EnvPrintRouter(theEnv,fileid,ValueToString(ExpressionFunctionCallName(theExpression)));
380            if (theExpression->argList != NULL) { EnvPrintRouter(theEnv,fileid," "); }
381            PrintExpression(theEnv,fileid,theExpression->argList);
382            EnvPrintRouter(theEnv,fileid,")");
383            break;
384 
385          default:
386            oldExpression = EvaluationData(theEnv)->CurrentExpression;
387            EvaluationData(theEnv)->CurrentExpression = theExpression;
388            PrintAtom(theEnv,fileid,theExpression->type,theExpression->value);
389            EvaluationData(theEnv)->CurrentExpression = oldExpression;
390            break;
391         }
392 
393       theExpression = theExpression->nextArg;
394       if (theExpression != NULL) EnvPrintRouter(theEnv,fileid," ");
395      }
396 
397    return;
398   }
399 
400 /*************************************************************************/
401 /* CombineExpressions: Combines two expressions into a single equivalent */
402 /*   expression. Mainly serves to merge expressions containing "and"     */
403 /*   and "or" expressions without unnecessary duplication of the "and"   */
404 /*   and "or" expressions (i.e., two "and" expressions can be merged by  */
405 /*   placing them as arguments within another "and" expression, but it   */
406 /*   is more efficient to add the arguments of one of the "and"          */
407 /*   expressions to the list of arguments for the other and expression). */
408 /*************************************************************************/
CombineExpressions(void * theEnv,struct expr * expr1,struct expr * expr2)409 globle struct expr *CombineExpressions(
410   void *theEnv,
411   struct expr *expr1,
412   struct expr *expr2)
413   {
414    struct expr *tempPtr;
415 
416    /*===========================================================*/
417    /* If the 1st expression is NULL, return the 2nd expression. */
418    /*===========================================================*/
419 
420    if (expr1 == NULL) return(expr2);
421 
422    /*===========================================================*/
423    /* If the 2nd expression is NULL, return the 1st expression. */
424    /*===========================================================*/
425 
426    if (expr2 == NULL) return(expr1);
427 
428    /*============================================================*/
429    /* If the 1st expression is an "and" expression, and the 2nd  */
430    /* expression is not an "and" expression, then include the    */
431    /* 2nd expression in the argument list of the 1st expression. */
432    /*============================================================*/
433 
434    if ((expr1->value == ExpressionData(theEnv)->PTR_AND) &&
435        (expr2->value != ExpressionData(theEnv)->PTR_AND))
436      {
437       tempPtr = expr1->argList;
438       if (tempPtr == NULL)
439         {
440          rtn_struct(theEnv,expr,expr1);
441          return(expr2);
442         }
443 
444       while (tempPtr->nextArg != NULL)
445         { tempPtr = tempPtr->nextArg; }
446 
447       tempPtr->nextArg = expr2;
448       return(expr1);
449      }
450 
451    /*============================================================*/
452    /* If the 2nd expression is an "and" expression, and the 1st  */
453    /* expression is not an "and" expression, then include the    */
454    /* 1st expression in the argument list of the 2nd expression. */
455    /*============================================================*/
456 
457    if ((expr1->value != ExpressionData(theEnv)->PTR_AND) &&
458        (expr2->value == ExpressionData(theEnv)->PTR_AND))
459      {
460       tempPtr = expr2->argList;
461       if (tempPtr == NULL)
462         {
463          rtn_struct(theEnv,expr,expr2);
464          return(expr1);
465         }
466 
467       expr2->argList = expr1;
468       expr1->nextArg = tempPtr;
469 
470       return(expr2);
471      }
472 
473    /*===========================================================*/
474    /* If both expressions are "and" expressions, then add the   */
475    /* 2nd expression to the argument list of the 1st expression */
476    /* and throw away the extraneous "and" expression.           */
477    /*===========================================================*/
478 
479    if ((expr1->value == ExpressionData(theEnv)->PTR_AND) &&
480        (expr2->value == ExpressionData(theEnv)->PTR_AND))
481      {
482       tempPtr = expr1->argList;
483       if (tempPtr == NULL)
484         {
485          rtn_struct(theEnv,expr,expr1);
486          return(expr2);
487         }
488 
489       while (tempPtr->nextArg != NULL)
490         { tempPtr = tempPtr->nextArg; }
491 
492       tempPtr->nextArg = expr2->argList;
493       rtn_struct(theEnv,expr,expr2);
494 
495       return(expr1);
496      }
497 
498    /*=====================================================*/
499    /* If neither expression is an "and" expression, then  */
500    /* create an "and" expression and add both expressions */
501    /* to the argument list of that "and" expression.      */
502    /*=====================================================*/
503 
504    tempPtr = GenConstant(theEnv,FCALL,ExpressionData(theEnv)->PTR_AND);
505    tempPtr->argList = expr1;
506    expr1->nextArg = expr2;
507    return(tempPtr);
508   }
509 
510 /*********************/
511 /* NegateExpression: */
512 /*********************/
NegateExpression(void * theEnv,struct expr * theExpression)513 globle struct expr *NegateExpression(
514   void *theEnv,
515   struct expr *theExpression)
516   {
517    struct expr *tempPtr;
518 
519    /*=========================================*/
520    /* If the expression is NULL, return NULL. */
521    /*=========================================*/
522 
523    if (theExpression == NULL) return(NULL);
524 
525    /*==================================================*/
526    /* The expression is already wrapped within a "not" */
527    /* function call, just remove the function call.    */
528    /*==================================================*/
529 
530    if (theExpression->value == ExpressionData(theEnv)->PTR_NOT)
531      {
532       tempPtr = theExpression->argList;
533       rtn_struct(theEnv,expr,theExpression);
534       return(tempPtr);
535      }
536 
537    /*===================================================*/
538    /* Wrap the expression within a "not" function call. */
539    /*===================================================*/
540 
541    tempPtr = GenConstant(theEnv,FCALL,ExpressionData(theEnv)->PTR_NOT);
542    tempPtr->argList = theExpression;
543 
544    return(tempPtr);
545   }
546 
547 /********************************************************/
548 /* AppendExpressions: Attaches an expression to the end */
549 /*   of another expression's nextArg list.              */
550 /********************************************************/
AppendExpressions(struct expr * expr1,struct expr * expr2)551 globle struct expr *AppendExpressions(
552   struct expr *expr1,
553   struct expr *expr2)
554   {
555    struct expr *tempPtr;
556 
557    /*===========================================================*/
558    /* If the 1st expression is NULL, return the 2nd expression. */
559    /*===========================================================*/
560 
561    if (expr1 == NULL) return(expr2);
562 
563    /*===========================================================*/
564    /* If the 2nd expression is NULL, return the 1st expression. */
565    /*===========================================================*/
566 
567    if (expr2 == NULL) return(expr1);
568 
569    /*====================================*/
570    /* Find the end of the 1st expression */
571    /* and attach the 2nd expression.     */
572    /*====================================*/
573 
574    tempPtr = expr1;
575    while (tempPtr->nextArg != NULL) tempPtr = tempPtr->nextArg;
576    tempPtr->nextArg = expr2;
577 
578    /*===============================*/
579    /* Return the merged expression. */
580    /*===============================*/
581 
582    return(expr1);
583   }
584 
585