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