1    /*******************************************************/
2    /*      "C" Language Integrated Production System      */
3    /*                                                     */
4    /*             CLIPS Version 6.30  01/25/15            */
5    /*                                                     */
6    /*              DEFTEMPLATE PARSER MODULE              */
7    /*******************************************************/
8 
9 /*************************************************************/
10 /* Purpose: Parses the deftemplate construct.                */
11 /*                                                           */
12 /* Principal Programmer(s):                                  */
13 /*      Gary D. Riley                                        */
14 /*                                                           */
15 /* Contributing Programmer(s):                               */
16 /*                                                           */
17 /* Revision History:                                         */
18 /*                                                           */
19 /*      6.23: Added support for templates maintaining their  */
20 /*            own list of facts.                             */
21 /*                                                           */
22 /*      6.30: Removed conditional code for unsupported       */
23 /*            compilers/operating systems (IBM_MCW and       */
24 /*            MAC_MCW).                                      */
25 /*                                                           */
26 /*            GetConstructNameAndComment API change.         */
27 /*                                                           */
28 /*            Support for deftemplate slot facets.           */
29 /*                                                           */
30 /*            Added const qualifiers to remove C++           */
31 /*            deprecation warnings.                          */
32 /*                                                           */
33 /*            Changed find construct functionality so that   */
34 /*            imported modules are search when locating a    */
35 /*            named construct.                               */
36 /*                                                           */
37 /*************************************************************/
38 
39 #define _TMPLTPSR_SOURCE_
40 
41 #include "setup.h"
42 
43 #if DEFTEMPLATE_CONSTRUCT
44 
45 #include <stdio.h>
46 #define _STDIO_INCLUDED_
47 #include <string.h>
48 
49 #include "constant.h"
50 #include "memalloc.h"
51 #include "symbol.h"
52 #include "scanner.h"
53 #include "exprnpsr.h"
54 #include "router.h"
55 #include "constrct.h"
56 #include "envrnmnt.h"
57 #include "factmngr.h"
58 #include "cstrnchk.h"
59 #include "cstrnpsr.h"
60 #include "cstrcpsr.h"
61 #if BLOAD || BLOAD_AND_BSAVE
62 #include "bload.h"
63 #endif
64 #include "default.h"
65 #include "pattern.h"
66 #include "watch.h"
67 #include "cstrnutl.h"
68 
69 #include "tmpltdef.h"
70 #include "tmpltbsc.h"
71 
72 #include "tmpltpsr.h"
73 
74 /***************************************/
75 /* LOCAL INTERNAL FUNCTION DEFINITIONS */
76 /***************************************/
77 
78 #if (! RUN_TIME) && (! BLOAD_ONLY)
79    static struct templateSlot    *SlotDeclarations(void *,const char *,struct token *);
80    static struct templateSlot    *ParseSlot(void *,const char *,struct token *,struct templateSlot *);
81    static struct templateSlot    *DefinedSlots(void *,const char *,SYMBOL_HN *,int,struct token *);
82    static intBool                 ParseFacetAttribute(void *,const char *,struct templateSlot *,intBool);
83 #endif
84 
85 /*******************************************************/
86 /* ParseDeftemplate: Parses the deftemplate construct. */
87 /*******************************************************/
ParseDeftemplate(void * theEnv,const char * readSource)88 globle int ParseDeftemplate(
89   void *theEnv,
90   const char *readSource)
91   {
92 #if (! RUN_TIME) && (! BLOAD_ONLY)
93    SYMBOL_HN *deftemplateName;
94    struct deftemplate *newDeftemplate;
95    struct templateSlot *slots;
96    struct token inputToken;
97 
98    /*================================================*/
99    /* Initialize pretty print and error information. */
100    /*================================================*/
101 
102    DeftemplateData(theEnv)->DeftemplateError = FALSE;
103    SetPPBufferStatus(theEnv,ON);
104    FlushPPBuffer(theEnv);
105    SavePPBuffer(theEnv,"(deftemplate ");
106 
107    /*==============================================================*/
108    /* Deftemplates can not be added when a binary image is loaded. */
109    /*==============================================================*/
110 
111 #if BLOAD || BLOAD_AND_BSAVE
112    if ((Bloaded(theEnv) == TRUE) && (! ConstructData(theEnv)->CheckSyntaxMode))
113      {
114       CannotLoadWithBloadMessage(theEnv,"deftemplate");
115       return(TRUE);
116      }
117 #endif
118 
119    /*=======================================================*/
120    /* Parse the name and comment fields of the deftemplate. */
121    /*=======================================================*/
122 
123 #if DEBUGGING_FUNCTIONS
124    DeftemplateData(theEnv)->DeletedTemplateDebugFlags = 0;
125 #endif
126 
127    deftemplateName = GetConstructNameAndComment(theEnv,readSource,&inputToken,"deftemplate",
128                                                 EnvFindDeftemplateInModule,EnvUndeftemplate,"%",
129                                                 TRUE,TRUE,TRUE,FALSE);
130    if (deftemplateName == NULL) return(TRUE);
131 
132    if (ReservedPatternSymbol(theEnv,ValueToString(deftemplateName),"deftemplate"))
133      {
134       ReservedPatternSymbolErrorMsg(theEnv,ValueToString(deftemplateName),"a deftemplate name");
135       return(TRUE);
136      }
137 
138    /*===========================================*/
139    /* Parse the slot fields of the deftemplate. */
140    /*===========================================*/
141 
142    slots = SlotDeclarations(theEnv,readSource,&inputToken);
143    if (DeftemplateData(theEnv)->DeftemplateError == TRUE) return(TRUE);
144 
145    /*==============================================*/
146    /* If we're only checking syntax, don't add the */
147    /* successfully parsed deftemplate to the KB.   */
148    /*==============================================*/
149 
150    if (ConstructData(theEnv)->CheckSyntaxMode)
151      {
152       ReturnSlots(theEnv,slots);
153       return(FALSE);
154      }
155 
156    /*=====================================*/
157    /* Create a new deftemplate structure. */
158    /*=====================================*/
159 
160    newDeftemplate = get_struct(theEnv,deftemplate);
161    newDeftemplate->header.name =  deftemplateName;
162    newDeftemplate->header.next = NULL;
163    newDeftemplate->header.usrData = NULL;
164    newDeftemplate->slotList = slots;
165    newDeftemplate->implied = FALSE;
166    newDeftemplate->numberOfSlots = 0;
167    newDeftemplate->busyCount = 0;
168    newDeftemplate->watch = 0;
169    newDeftemplate->inScope = TRUE;
170    newDeftemplate->patternNetwork = NULL;
171    newDeftemplate->factList = NULL;
172    newDeftemplate->lastFact = NULL;
173    newDeftemplate->header.whichModule = (struct defmoduleItemHeader *)
174                                         GetModuleItem(theEnv,NULL,DeftemplateData(theEnv)->DeftemplateModuleIndex);
175 
176    /*================================*/
177    /* Determine the number of slots. */
178    /*================================*/
179 
180    while (slots != NULL)
181      {
182       newDeftemplate->numberOfSlots++;
183       slots = slots->next;
184      }
185 
186    /*====================================*/
187    /* Store pretty print representation. */
188    /*====================================*/
189 
190    if (EnvGetConserveMemory(theEnv) == TRUE)
191      { newDeftemplate->header.ppForm = NULL; }
192    else
193      { newDeftemplate->header.ppForm = CopyPPBuffer(theEnv); }
194 
195    /*=======================================================================*/
196    /* If a template is redefined, then we want to restore its watch status. */
197    /*=======================================================================*/
198 
199 #if DEBUGGING_FUNCTIONS
200    if ((BitwiseTest(DeftemplateData(theEnv)->DeletedTemplateDebugFlags,0)) || EnvGetWatchItem(theEnv,"facts"))
201      { EnvSetDeftemplateWatch(theEnv,ON,(void *) newDeftemplate); }
202 #endif
203 
204    /*==============================================*/
205    /* Add deftemplate to the list of deftemplates. */
206    /*==============================================*/
207 
208    AddConstructToModule(&newDeftemplate->header);
209 
210    InstallDeftemplate(theEnv,newDeftemplate);
211 
212 #else
213 #if MAC_XCD
214 #pragma unused(theEnv)
215 #endif
216 #endif
217 
218    return(FALSE);
219   }
220 
221 #if (! RUN_TIME) && (! BLOAD_ONLY)
222 
223 /**************************************************************/
224 /* InstallDeftemplate: Increments all occurrences in the hash */
225 /*   table of symbols found in an deftemplate and adds it to  */
226 /*   the hash table.                                          */
227 /**************************************************************/
InstallDeftemplate(void * theEnv,struct deftemplate * theDeftemplate)228 globle void InstallDeftemplate(
229   void *theEnv,
230   struct deftemplate *theDeftemplate)
231   {
232    struct templateSlot *slotPtr;
233    struct expr *tempExpr;
234 
235    IncrementSymbolCount(theDeftemplate->header.name);
236 
237    for (slotPtr = theDeftemplate->slotList;
238         slotPtr != NULL;
239         slotPtr = slotPtr->next)
240      {
241       IncrementSymbolCount(slotPtr->slotName);
242       tempExpr = AddHashedExpression(theEnv,slotPtr->defaultList);
243       ReturnExpression(theEnv,slotPtr->defaultList);
244       slotPtr->defaultList = tempExpr;
245       tempExpr = AddHashedExpression(theEnv,slotPtr->facetList);
246       ReturnExpression(theEnv,slotPtr->facetList);
247       slotPtr->facetList = tempExpr;
248       slotPtr->constraints = AddConstraint(theEnv,slotPtr->constraints);
249      }
250   }
251 
252 /********************************************************************/
253 /* SlotDeclarations: Parses the slot declarations of a deftemplate. */
254 /********************************************************************/
SlotDeclarations(void * theEnv,const char * readSource,struct token * inputToken)255 static struct templateSlot *SlotDeclarations(
256   void *theEnv,
257   const char *readSource,
258   struct token *inputToken)
259   {
260    struct templateSlot *newSlot, *slotList = NULL, *lastSlot = NULL;
261    struct templateSlot *multiSlot = NULL;
262 
263    while (inputToken->type != RPAREN)
264      {
265       /*====================================================*/
266       /* Slots begin with a '(' followed by a slot keyword. */
267       /*====================================================*/
268 
269       if (inputToken->type != LPAREN)
270         {
271          SyntaxErrorMessage(theEnv,"deftemplate");
272          ReturnSlots(theEnv,slotList);
273          ReturnSlots(theEnv,multiSlot);
274          DeftemplateData(theEnv)->DeftemplateError = TRUE;
275          return(NULL);
276         }
277 
278       GetToken(theEnv,readSource,inputToken);
279       if (inputToken->type != SYMBOL)
280         {
281          SyntaxErrorMessage(theEnv,"deftemplate");
282          ReturnSlots(theEnv,slotList);
283          ReturnSlots(theEnv,multiSlot);
284          DeftemplateData(theEnv)->DeftemplateError = TRUE;
285          return(NULL);
286         }
287 
288       /*=================*/
289       /* Parse the slot. */
290       /*=================*/
291 
292       newSlot = ParseSlot(theEnv,readSource,inputToken,slotList);
293       if (DeftemplateData(theEnv)->DeftemplateError == TRUE)
294         {
295          ReturnSlots(theEnv,newSlot);
296          ReturnSlots(theEnv,slotList);
297          ReturnSlots(theEnv,multiSlot);
298          return(NULL);
299         }
300 
301       /*===========================================*/
302       /* Attach the new slot to the list of slots. */
303       /*===========================================*/
304 
305       if (newSlot != NULL)
306         {
307          if (lastSlot == NULL)
308            { slotList = newSlot; }
309          else
310            { lastSlot->next = newSlot; }
311          lastSlot = newSlot;
312         }
313 
314       /*================================*/
315       /* Check for closing parenthesis. */
316       /*================================*/
317 
318       GetToken(theEnv,readSource,inputToken);
319       if (inputToken->type != RPAREN)
320         {
321          PPBackup(theEnv);
322          SavePPBuffer(theEnv,"\n   ");
323          SavePPBuffer(theEnv,inputToken->printForm);
324         }
325      }
326 
327   SavePPBuffer(theEnv,"\n");
328 
329   /*=======================*/
330   /* Return the slot list. */
331   /*=======================*/
332 
333   return(slotList);
334  }
335 
336 /*****************************************************/
337 /* ParseSlot: Parses a single slot of a deftemplate. */
338 /*****************************************************/
ParseSlot(void * theEnv,const char * readSource,struct token * inputToken,struct templateSlot * slotList)339 static struct templateSlot *ParseSlot(
340   void *theEnv,
341   const char *readSource,
342   struct token *inputToken,
343   struct templateSlot *slotList)
344   {
345    int parsingMultislot;
346    SYMBOL_HN *slotName;
347    struct templateSlot *newSlot;
348    int rv;
349 
350    /*=====================================================*/
351    /* Slots must  begin with keyword field or multifield. */
352    /*=====================================================*/
353 
354    if ((strcmp(ValueToString(inputToken->value),"field") != 0) &&
355        (strcmp(ValueToString(inputToken->value),"multifield") != 0) &&
356        (strcmp(ValueToString(inputToken->value),"slot") != 0) &&
357        (strcmp(ValueToString(inputToken->value),"multislot") != 0))
358      {
359       SyntaxErrorMessage(theEnv,"deftemplate");
360       DeftemplateData(theEnv)->DeftemplateError = TRUE;
361       return(NULL);
362      }
363 
364    /*===============================================*/
365    /* Determine if multifield slot is being parsed. */
366    /*===============================================*/
367 
368    if ((strcmp(ValueToString(inputToken->value),"multifield") == 0) ||
369        (strcmp(ValueToString(inputToken->value),"multislot") == 0))
370      { parsingMultislot = TRUE; }
371    else
372      { parsingMultislot = FALSE; }
373 
374    /*========================================*/
375    /* The name of the slot must be a symbol. */
376    /*========================================*/
377 
378    SavePPBuffer(theEnv," ");
379    GetToken(theEnv,readSource,inputToken);
380    if (inputToken->type != SYMBOL)
381      {
382       SyntaxErrorMessage(theEnv,"deftemplate");
383       DeftemplateData(theEnv)->DeftemplateError = TRUE;
384       return(NULL);
385      }
386 
387    slotName = (SYMBOL_HN *) inputToken->value;
388 
389    /*================================================*/
390    /* Determine if the slot has already been parsed. */
391    /*================================================*/
392 
393    while (slotList != NULL)
394      {
395       if (slotList->slotName == slotName)
396         {
397          AlreadyParsedErrorMessage(theEnv,"slot ",ValueToString(slotList->slotName));
398          DeftemplateData(theEnv)->DeftemplateError = TRUE;
399          return(NULL);
400         }
401 
402       slotList = slotList->next;
403      }
404 
405    /*===================================*/
406    /* Parse the attributes of the slot. */
407    /*===================================*/
408 
409    newSlot = DefinedSlots(theEnv,readSource,slotName,parsingMultislot,inputToken);
410    if (newSlot == NULL)
411      {
412       DeftemplateData(theEnv)->DeftemplateError = TRUE;
413       return(NULL);
414      }
415 
416    /*=================================*/
417    /* Check for slot conflict errors. */
418    /*=================================*/
419 
420    if (CheckConstraintParseConflicts(theEnv,newSlot->constraints) == FALSE)
421      {
422       ReturnSlots(theEnv,newSlot);
423       DeftemplateData(theEnv)->DeftemplateError = TRUE;
424       return(NULL);
425      }
426 
427    if ((newSlot->defaultPresent) || (newSlot->defaultDynamic))
428      { rv = ConstraintCheckExpressionChain(theEnv,newSlot->defaultList,newSlot->constraints); }
429    else
430      { rv = NO_VIOLATION; }
431 
432    if ((rv != NO_VIOLATION) && EnvGetStaticConstraintChecking(theEnv))
433      {
434       const char *temp;
435       if (newSlot->defaultDynamic) temp = "the default-dynamic attribute";
436       else temp = "the default attribute";
437       ConstraintViolationErrorMessage(theEnv,"An expression",temp,FALSE,0,
438                                       newSlot->slotName,0,rv,newSlot->constraints,TRUE);
439       ReturnSlots(theEnv,newSlot);
440       DeftemplateData(theEnv)->DeftemplateError = TRUE;
441       return(NULL);
442      }
443 
444    /*==================*/
445    /* Return the slot. */
446    /*==================*/
447 
448    return(newSlot);
449   }
450 
451 /**************************************************************/
452 /* DefinedSlots: Parses a field or multifield slot attribute. */
453 /**************************************************************/
DefinedSlots(void * theEnv,const char * readSource,SYMBOL_HN * slotName,int multifieldSlot,struct token * inputToken)454 static struct templateSlot *DefinedSlots(
455   void *theEnv,
456   const char *readSource,
457   SYMBOL_HN *slotName,
458   int multifieldSlot,
459   struct token *inputToken)
460   {
461    struct templateSlot *newSlot;
462    struct expr *defaultList;
463    int defaultFound = FALSE;
464    int noneSpecified, deriveSpecified;
465    CONSTRAINT_PARSE_RECORD parsedConstraints;
466 
467    /*===========================*/
468    /* Build the slot container. */
469    /*===========================*/
470 
471    newSlot = get_struct(theEnv,templateSlot);
472    newSlot->slotName = slotName;
473    newSlot->defaultList = NULL;
474    newSlot->facetList = NULL;
475    newSlot->constraints = GetConstraintRecord(theEnv);
476    if (multifieldSlot)
477      { newSlot->constraints->multifieldsAllowed = TRUE; }
478    newSlot->multislot = multifieldSlot;
479    newSlot->noDefault = FALSE;
480    newSlot->defaultPresent = FALSE;
481    newSlot->defaultDynamic = FALSE;
482    newSlot->next = NULL;
483 
484    /*========================================*/
485    /* Parse the primitive slot if it exists. */
486    /*========================================*/
487 
488    InitializeConstraintParseRecord(&parsedConstraints);
489    GetToken(theEnv,readSource,inputToken);
490 
491    while (inputToken->type != RPAREN)
492      {
493       PPBackup(theEnv);
494       SavePPBuffer(theEnv," ");
495       SavePPBuffer(theEnv,inputToken->printForm);
496 
497       /*================================================*/
498       /* Slot attributes begin with a left parenthesis. */
499       /*================================================*/
500 
501       if (inputToken->type != LPAREN)
502         {
503          SyntaxErrorMessage(theEnv,"deftemplate");
504          ReturnSlots(theEnv,newSlot);
505          DeftemplateData(theEnv)->DeftemplateError = TRUE;
506          return(NULL);
507         }
508 
509       /*=============================================*/
510       /* The name of the attribute must be a symbol. */
511       /*=============================================*/
512 
513       GetToken(theEnv,readSource,inputToken);
514       if (inputToken->type != SYMBOL)
515         {
516          SyntaxErrorMessage(theEnv,"deftemplate");
517          ReturnSlots(theEnv,newSlot);
518          DeftemplateData(theEnv)->DeftemplateError = TRUE;
519          return(NULL);
520         }
521 
522       /*================================================================*/
523       /* Determine if the attribute is one of the standard constraints. */
524       /*================================================================*/
525 
526       if (StandardConstraint(ValueToString(inputToken->value)))
527         {
528          if (ParseStandardConstraint(theEnv,readSource,(ValueToString(inputToken->value)),
529                                      newSlot->constraints,&parsedConstraints,
530                                      multifieldSlot) == FALSE)
531            {
532             DeftemplateData(theEnv)->DeftemplateError = TRUE;
533             ReturnSlots(theEnv,newSlot);
534             return(NULL);
535            }
536         }
537 
538       /*=================================================*/
539       /* else if the attribute is the default attribute, */
540       /* then get the default list for this slot.        */
541       /*=================================================*/
542 
543       else if ((strcmp(ValueToString(inputToken->value),"default") == 0) ||
544                (strcmp(ValueToString(inputToken->value),"default-dynamic") == 0))
545         {
546          /*======================================================*/
547          /* Check to see if the default has already been parsed. */
548          /*======================================================*/
549 
550          if (defaultFound)
551            {
552             AlreadyParsedErrorMessage(theEnv,"default attribute",NULL);
553             DeftemplateData(theEnv)->DeftemplateError = TRUE;
554             ReturnSlots(theEnv,newSlot);
555             return(NULL);
556            }
557 
558          newSlot->noDefault = FALSE;
559 
560          /*=====================================================*/
561          /* Determine whether the default is dynamic or static. */
562          /*=====================================================*/
563 
564          if (strcmp(ValueToString(inputToken->value),"default") == 0)
565            {
566             newSlot->defaultPresent = TRUE;
567             newSlot->defaultDynamic = FALSE;
568            }
569          else
570            {
571             newSlot->defaultPresent = FALSE;
572             newSlot->defaultDynamic = TRUE;
573            }
574 
575          /*===================================*/
576          /* Parse the list of default values. */
577          /*===================================*/
578 
579          defaultList = ParseDefault(theEnv,readSource,multifieldSlot,(int) newSlot->defaultDynamic,
580                                   TRUE,&noneSpecified,&deriveSpecified,&DeftemplateData(theEnv)->DeftemplateError);
581          if (DeftemplateData(theEnv)->DeftemplateError == TRUE)
582            {
583             ReturnSlots(theEnv,newSlot);
584             return(NULL);
585            }
586 
587          /*==================================*/
588          /* Store the default with the slot. */
589          /*==================================*/
590 
591          defaultFound = TRUE;
592          if (deriveSpecified) newSlot->defaultPresent = FALSE;
593          else if (noneSpecified)
594            {
595             newSlot->noDefault = TRUE;
596             newSlot->defaultPresent = FALSE;
597            }
598          newSlot->defaultList = defaultList;
599         }
600 
601       /*===============================================*/
602       /* else if the attribute is the facet attribute. */
603       /*===============================================*/
604 
605       else if (strcmp(ValueToString(inputToken->value),"facet") == 0)
606         {
607          if (! ParseFacetAttribute(theEnv,readSource,newSlot,FALSE))
608            {
609             ReturnSlots(theEnv,newSlot);
610             DeftemplateData(theEnv)->DeftemplateError = TRUE;
611             return(NULL);
612            }
613         }
614 
615       else if (strcmp(ValueToString(inputToken->value),"multifacet") == 0)
616         {
617          if (! ParseFacetAttribute(theEnv,readSource,newSlot,TRUE))
618            {
619             ReturnSlots(theEnv,newSlot);
620             DeftemplateData(theEnv)->DeftemplateError = TRUE;
621             return(NULL);
622            }
623         }
624 
625       /*============================================*/
626       /* Otherwise the attribute is an invalid one. */
627       /*============================================*/
628 
629       else
630         {
631          SyntaxErrorMessage(theEnv,"slot attributes");
632          ReturnSlots(theEnv,newSlot);
633          DeftemplateData(theEnv)->DeftemplateError = TRUE;
634          return(NULL);
635         }
636 
637       /*===================================*/
638       /* Begin parsing the next attribute. */
639       /*===================================*/
640 
641       GetToken(theEnv,readSource,inputToken);
642      }
643 
644    /*============================*/
645    /* Return the attribute list. */
646    /*============================*/
647 
648    return(newSlot);
649   }
650 
651 /***************************************************/
652 /* ParseFacetAttribute: Parses the type attribute. */
653 /***************************************************/
ParseFacetAttribute(void * theEnv,const char * readSource,struct templateSlot * theSlot,intBool multifacet)654 static intBool ParseFacetAttribute(
655   void *theEnv,
656   const char *readSource,
657   struct templateSlot *theSlot,
658   intBool multifacet)
659   {
660    struct token inputToken;
661    SYMBOL_HN *facetName;
662    struct expr *facetPair, *tempFacet, *facetValue = NULL, *lastValue = NULL;
663 
664    /*==============================*/
665    /* Parse the name of the facet. */
666    /*==============================*/
667 
668    SavePPBuffer(theEnv," ");
669    GetToken(theEnv,readSource,&inputToken);
670 
671    /*==================================*/
672    /* The facet name must be a symbol. */
673    /*==================================*/
674 
675    if (inputToken.type != SYMBOL)
676      {
677       if (multifacet) SyntaxErrorMessage(theEnv,"multifacet attribute");
678       else SyntaxErrorMessage(theEnv,"facet attribute");
679       return(FALSE);
680      }
681 
682    facetName = (SYMBOL_HN *) inputToken.value;
683 
684    /*===================================*/
685    /* Don't allow facets with the same  */
686    /* name as a predefined CLIPS facet. */
687    /*===================================*/
688 
689    /*====================================*/
690    /* Has the facet already been parsed? */
691    /*====================================*/
692 
693    for (tempFacet = theSlot->facetList;
694         tempFacet != NULL;
695         tempFacet = tempFacet->nextArg)
696      {
697       if (tempFacet->value == facetName)
698         {
699          if (multifacet) AlreadyParsedErrorMessage(theEnv,"multifacet ",ValueToString(facetName));
700          else AlreadyParsedErrorMessage(theEnv,"facet ",ValueToString(facetName));
701          return(FALSE);
702         }
703      }
704 
705    /*===============================*/
706    /* Parse the value of the facet. */
707    /*===============================*/
708 
709    SavePPBuffer(theEnv," ");
710    GetToken(theEnv,readSource,&inputToken);
711 
712    while (inputToken.type != RPAREN)
713      {
714       /*=====================================*/
715       /* The facet value must be a constant. */
716       /*=====================================*/
717 
718       if (! ConstantType(inputToken.type))
719         {
720          if (multifacet) SyntaxErrorMessage(theEnv,"multifacet attribute");
721          else SyntaxErrorMessage(theEnv,"facet attribute");
722          ReturnExpression(theEnv,facetValue);
723          return(FALSE);
724         }
725 
726       /*======================================*/
727       /* Add the value to the list of values. */
728       /*======================================*/
729 
730       if (lastValue == NULL)
731         {
732          facetValue = GenConstant(theEnv,inputToken.type,inputToken.value);
733          lastValue = facetValue;
734         }
735       else
736         {
737          lastValue->nextArg = GenConstant(theEnv,inputToken.type,inputToken.value);
738          lastValue = lastValue->nextArg;
739         }
740 
741       /*=====================*/
742       /* Get the next token. */
743       /*=====================*/
744 
745       SavePPBuffer(theEnv," ");
746       GetToken(theEnv,readSource,&inputToken);
747 
748       /*===============================================*/
749       /* A facet can't contain more than one constant. */
750       /*===============================================*/
751 
752       if ((! multifacet) && (inputToken.type != RPAREN))
753         {
754          SyntaxErrorMessage(theEnv,"facet attribute");
755          ReturnExpression(theEnv,facetValue);
756          return(FALSE);
757         }
758      }
759 
760    /*========================================================*/
761    /* Remove the space before the closing right parenthesis. */
762    /*========================================================*/
763 
764    PPBackup(theEnv);
765    PPBackup(theEnv);
766    SavePPBuffer(theEnv,")");
767 
768    /*====================================*/
769    /* A facet must contain one constant. */
770    /*====================================*/
771 
772    if ((! multifacet) && (facetValue == NULL))
773      {
774       SyntaxErrorMessage(theEnv,"facet attribute");
775       return(FALSE);
776      }
777 
778    /*=================================================*/
779    /* Add the facet to the list of the slot's facets. */
780    /*=================================================*/
781 
782    facetPair = GenConstant(theEnv,SYMBOL,facetName);
783 
784    if (multifacet)
785      {
786       facetPair->argList = GenConstant(theEnv,FCALL,(void *) FindFunction(theEnv,"create$"));
787       facetPair->argList->argList = facetValue;
788      }
789    else
790      { facetPair->argList = facetValue; }
791 
792    facetPair->nextArg = theSlot->facetList;
793    theSlot->facetList = facetPair;
794 
795    /*===============================================*/
796    /* The facet/multifacet was successfully parsed. */
797    /*===============================================*/
798 
799    return(TRUE);
800   }
801 
802 #endif /* (! RUN_TIME) && (! BLOAD_ONLY) */
803 
804 #endif /* DEFTEMPLATE_CONSTRUCT */
805 
806 
807