Esempio n. 1
0
static void ExtractAnds(
  void *theEnv,
  EXEC_STATUS,
  struct lhsParseNode *andField,
  int testInPatternNetwork,
  struct expr **patternNetTest,
  struct expr **joinNetTest,
  struct expr **nandTest,
  struct expr **constantSelector,
  struct expr **constantValue,
  int nandField)
  {
   struct expr *newPNTest, *newJNTest, *newNandTest, *newConstantSelector, *newConstantValue;

   /*=================================================*/
   /* Before starting, the subfield has no pattern or */
   /* join network expressions associated with it.    */
   /*=================================================*/

   *patternNetTest = NULL;
   *joinNetTest = NULL;
   *nandTest = NULL;
   *constantSelector = NULL;
   *constantValue = NULL;

   /*=========================================*/
   /* Loop through each of the subfields tied */
   /* together by the & constraint.           */
   /*=========================================*/

   for (;
        andField != NULL;
        andField = andField->right)
     {
      /*======================================*/
      /* Extract the pattern and join network */
      /* expressions from the subfield.       */
      /*======================================*/

      ExtractFieldTest(theEnv,execStatus,andField,testInPatternNetwork,&newPNTest,&newJNTest,&newNandTest,&newConstantSelector,&newConstantValue,nandField);

      /*=================================================*/
      /* Add the new expressions to the list of pattern  */
      /* and join network expressions being constructed. */
      /*=================================================*/

      *patternNetTest = CombineExpressions(theEnv,execStatus,*patternNetTest,newPNTest);
      *joinNetTest = CombineExpressions(theEnv,execStatus,*joinNetTest,newJNTest);
      *nandTest = CombineExpressions(theEnv,execStatus,*nandTest,newNandTest);
      *constantSelector = CombineExpressions(theEnv,execStatus,*constantSelector,newConstantSelector);
      *constantValue = CombineExpressions(theEnv,execStatus,*constantValue,newConstantValue);
     }
  }
Esempio n. 2
0
/****************************************************
  NAME         : GenObjectLengthTest
  DESCRIPTION  : Generates a test on the cardinality
                 of a slot matching an object pattern
  INPUTS       : The first lhsParseNode for a slot
                 in an object pattern
  RETURNS      : Nothing useful
  SIDE EFFECTS : The lhsParseNode network test is
                 modified to include the length test
  NOTES        : None
 ****************************************************/
globle void GenObjectLengthTest(
  void *theEnv,
  struct lhsParseNode *theNode)
  {
   struct ObjectMatchLength hack;
   EXPRESSION *theTest;

   if ((theNode->singleFieldsAfter == 0) &&
       (theNode->type != SF_VARIABLE) &&
       (theNode->type != SF_WILDCARD))
     return;

   ClearBitString((void *) &hack,(int) sizeof(struct ObjectMatchLength));

   if ((theNode->type != MF_VARIABLE) &&
       (theNode->type != MF_WILDCARD) &&
       (theNode->multiFieldsAfter == 0))
     hack.exactly = 1;
   else
     hack.exactly = 0;

   if ((theNode->type == SF_VARIABLE) || (theNode->type == SF_WILDCARD))
     hack.minLength = 1 + theNode->singleFieldsAfter;
   else
     hack.minLength = theNode->singleFieldsAfter;

   theTest = GenConstant(theEnv,OBJ_SLOT_LENGTH,EnvAddBitMap(theEnv,(void *) &hack,
                                         (int) sizeof(struct ObjectMatchLength)));
                                         
   if (theNode->constantSelector != NULL)
     { theNode->constantSelector->nextArg = CopyExpression(theEnv,theTest); }

   theNode->networkTest = CombineExpressions(theEnv,theTest,theNode->networkTest);
  }
Esempio n. 3
0
static void AttachTestCEsToPatternCEs(
  void *theEnv,
  struct lhsParseNode *theLHS)
  {
   struct lhsParseNode *lastNode, *tempNode;

   if (theLHS == NULL) return;

   /*=============================================================*/
   /* Attach test CEs that can be attached directly to a pattern. */
   /*=============================================================*/
   
   lastNode = theLHS;
   theLHS = lastNode->bottom;
   
   while (theLHS != NULL)
     {
      if ((theLHS->type != TEST_CE) ||
          (lastNode->beginNandDepth != lastNode->endNandDepth) ||
          (lastNode->beginNandDepth != theLHS->beginNandDepth))
        {
         lastNode = theLHS;
         theLHS = theLHS->bottom;
         continue;
        }
        
      if (lastNode->negated)
        {
         lastNode->secondaryNetworkTest =
            CombineExpressions(theEnv,lastNode->secondaryNetworkTest,theLHS->networkTest);
        }
      else
        {
         lastNode->networkTest =
            CombineExpressions(theEnv,lastNode->networkTest,theLHS->networkTest);
        }
        
      theLHS->networkTest = NULL;
      tempNode = theLHS->bottom;
      theLHS->bottom = NULL;
      lastNode->bottom = tempNode;
      lastNode->endNandDepth = theLHS->endNandDepth;
      ReturnLHSParseNodes(theEnv,theLHS);
      theLHS = tempNode;
     }
  }
Esempio n. 4
0
/****************************************************
  NAME         : GenObjectZeroLengthTest
  DESCRIPTION  : Generates a test on the cardinality
                 of a slot matching an object pattern
  INPUTS       : The first lhsParseNode for a slot
                 in an object pattern
  RETURNS      : Nothing useful
  SIDE EFFECTS : The lhsParseNode network test is
                 modified to include the length test
  NOTES        : None
 ****************************************************/
globle void GenObjectZeroLengthTest(
  struct lhsParseNode *theNode)
  {
   struct ObjectMatchLength hack;
   EXPRESSION *theTest;

   ClearBitString((void *) &hack,(int) sizeof(struct ObjectMatchLength));
   hack.exactly = 1;
   hack.minLength = 0;
   theTest = GenConstant(OBJ_SLOT_LENGTH,AddBitMap((void *) &hack,
                                         (int) sizeof(struct ObjectMatchLength)));
   theNode->networkTest = CombineExpressions(theTest,theNode->networkTest);
  }
Esempio n. 5
0
globle void AddNandUnification(
  void *theEnv,
  struct lhsParseNode *nodeList,
  struct nandFrame *theNandFrames)
  {
   struct nandFrame *theFrame;
   struct expr *tempExpression;

   /*====================================================*/
   /* If the reference is to a prior variable within the */
   /* same nand group, then there's no need to create an */
   /* external network test.                             */
   /*====================================================*/
   
   if (nodeList->beginNandDepth == nodeList->referringNode->beginNandDepth)
     { return; }
     
   /*=========================================*/
   /* Don't generate an external network test */
   /* if one has already been generated.      */
   /*=========================================*/
   
   if (nodeList->referringNode->marked)
     { return; }
     
   /*======================================================*/
   /* Find the frame to which the test should be attached. */
   /*======================================================*/

   for (theFrame = theNandFrames;
        theFrame != NULL;
        theFrame = theFrame->next)
     {
      if (theFrame->depth >= nodeList->referringNode->beginNandDepth)
        {
         nodeList->referringNode->marked = TRUE;
      
         tempExpression = GenJNVariableComparison(theEnv,nodeList->referringNode,nodeList->referringNode,TRUE);

         theFrame->nandCE->externalNetworkTest = CombineExpressions(theEnv,theFrame->nandCE->externalNetworkTest,tempExpression);
   
         tempExpression = (*nodeList->referringNode->patternType->genGetJNValueFunction)(theEnv,nodeList->referringNode,LHS);
         theFrame->nandCE->externalRightHash = AppendExpressions(theFrame->nandCE->externalRightHash,tempExpression);

         tempExpression = (*nodeList->referringNode->patternType->genGetJNValueFunction)(theEnv,nodeList->referringNode,LHS);
         theFrame->nandCE->externalLeftHash = AppendExpressions(theFrame->nandCE->externalLeftHash,tempExpression);
        }
     }
  }
Esempio n. 6
0
static void AttachTestCEsToPatternCEs(
  void *theEnv,
  struct lhsParseNode *theLHS)
  {
   struct lhsParseNode *lastNode, *tempNode;

   if (theLHS == NULL) return;

   /*=============================================================*/
   /* Attach test CEs that can be attached directly to a pattern. */
   /*=============================================================*/
   
   lastNode = theLHS;
   theLHS = lastNode->bottom;
   
   while (theLHS != NULL)
     {
      if ((theLHS->type != TEST_CE) ||
          (lastNode->beginNandDepth != lastNode->endNandDepth) ||
          (lastNode->beginNandDepth != theLHS->beginNandDepth))
        {
         lastNode = theLHS;
         theLHS = theLHS->bottom;
         continue;
        }
        
      if (lastNode->negated)
        {
         lastNode->secondaryNetworkTest =
            CombineExpressions(theEnv,lastNode->secondaryNetworkTest,theLHS->networkTest);
        }
      else
        {
         lastNode->networkTest =
            CombineExpressions(theEnv,lastNode->networkTest,theLHS->networkTest);
        }

      /*==============================================================*/
      /* If an exists with multiple conditions has just a pattern and */
      /* a test, it should be treated as a single pattern and use the */
      /* simpler exists logic rather than the logic used for multiple */
      /* patterns with an exists CE.                                  */
      /*==============================================================*/
      
      if (lastNode->existsNand)
        {
         lastNode->existsNand = FALSE;
         lastNode->exists = TRUE;
         lastNode->negated = TRUE;
         lastNode->beginNandDepth = theLHS->endNandDepth;
        }

      theLHS->networkTest = NULL;
      tempNode = theLHS->bottom;
      theLHS->bottom = NULL;
      lastNode->bottom = tempNode;
      lastNode->endNandDepth = theLHS->endNandDepth;
      ReturnLHSParseNodes(theEnv,theLHS);
      theLHS = tempNode;
     }
  }
Esempio n. 7
0
static struct lhsParseNode *RemoveUnneededSlots(
  void *theEnv,
  struct lhsParseNode *thePattern)
  {
   struct lhsParseNode *tempPattern = thePattern;
   struct lhsParseNode *lastPattern = NULL, *head = thePattern;
   struct expr *theTest;

   while (tempPattern != NULL)
     {
      /*=============================================================*/
      /* A single field slot that has no pattern network expression  */
      /* associated with it can be removed (i.e. any value contained */
      /* in this slot will satisfy the pattern being matched).       */
      /*=============================================================*/

      if (((tempPattern->type == SF_WILDCARD) || (tempPattern->type == SF_VARIABLE)) &&
          (tempPattern->networkTest == NULL))
        {
         if (lastPattern != NULL) lastPattern->right = tempPattern->right;
         else head = tempPattern->right;

         tempPattern->right = NULL;
         ReturnLHSParseNodes(theEnv,tempPattern);

         if (lastPattern != NULL) tempPattern = lastPattern->right;
         else tempPattern = head;
        }

      /*=======================================================*/
      /* A multifield variable or wildcard within a multifield */
      /* slot can be removed if there are no other multifield  */
      /* variables or wildcards contained in the same slot     */
      /* (and the multifield has no expressions which must be  */
      /* evaluated in the fact pattern network).               */
      /*=======================================================*/

      else if (((tempPattern->type == MF_WILDCARD) || (tempPattern->type == MF_VARIABLE)) &&
               (tempPattern->multifieldSlot == FALSE) &&
               (tempPattern->networkTest == NULL) &&
               (tempPattern->multiFieldsBefore == 0) &&
               (tempPattern->multiFieldsAfter == 0))
        {
         if (lastPattern != NULL) lastPattern->right = tempPattern->right;
         else head = tempPattern->right;

         tempPattern->right = NULL;
         ReturnLHSParseNodes(theEnv,tempPattern);

         if (lastPattern != NULL) tempPattern = lastPattern->right;
         else tempPattern = head;
        }

      /*==================================================================*/
      /* A multifield wildcard or variable contained in a multifield slot */
      /* that contains no other multifield wildcards or variables, but    */
      /* does have an expression that must be evaluated, can be changed   */
      /* to a single field pattern node with the same expression.         */
      /*==================================================================*/

      else if (((tempPattern->type == MF_WILDCARD) || (tempPattern->type == MF_VARIABLE)) &&
               (tempPattern->multifieldSlot == FALSE) &&
               (tempPattern->networkTest != NULL) &&
               (tempPattern->multiFieldsBefore == 0) &&
               (tempPattern->multiFieldsAfter == 0))
        {
         tempPattern->type = SF_WILDCARD;
         lastPattern = tempPattern;
         tempPattern = tempPattern->right;
        }

      /*=========================================================*/
      /* If we're dealing with a multifield slot with no slot    */
      /* restrictions, then treat the multfield slot as a single */
      /* field slot, but attach a test which verifies that the   */
      /* slot contains a zero length multifield value.           */
      /*=========================================================*/

      else if ((tempPattern->type == MF_WILDCARD) &&
               (tempPattern->multifieldSlot == TRUE) &&
               (tempPattern->bottom == NULL))
        {
         tempPattern->type = SF_WILDCARD;
         tempPattern->networkTest = FactGenCheckZeroLength(theEnv,tempPattern->slotNumber);
         tempPattern->multifieldSlot = FALSE;
         lastPattern = tempPattern;
         tempPattern = tempPattern->right;
        }

      /*===================================================*/
      /* Recursively call RemoveUnneededSlots for the slot */
      /* restrictions contained within a multifield slot.  */
      /*===================================================*/

      else if ((tempPattern->type == MF_WILDCARD) &&
               (tempPattern->multifieldSlot == TRUE))
        {
         /*=======================================================*/
         /* Add an expression to the first pattern restriction in */
         /* the multifield slot that determines whether or not    */
         /* the fact's slot value contains the minimum number of  */
         /* required fields to satisfy the pattern restrictions   */
         /* for this slot. The length check is place before any   */
         /* other tests, so that preceeding checks do not have to */
         /* determine if there are enough fields in the slot to   */
         /* safely retrieve a value.                              */
         /*=======================================================*/

         theTest = FactGenCheckLength(theEnv,tempPattern->bottom);
         theTest = CombineExpressions(theEnv,theTest,tempPattern->bottom->networkTest);
         tempPattern->bottom->networkTest = theTest;

         /*=========================================================*/
         /* Remove any unneeded pattern restrictions from the slot. */
         /*=========================================================*/

         tempPattern->bottom = RemoveUnneededSlots(theEnv,tempPattern->bottom);

         /*===========================================================*/
         /* If the slot no longer contains any restrictions, then the */
         /* multifield slot can be completely removed. In any case,   */
         /* move on to the next slot to be examined for removal.      */
         /*===========================================================*/

         if (tempPattern->bottom == NULL)
           {
            if (lastPattern != NULL) lastPattern->right = tempPattern->right;
            else head = tempPattern->right;

            tempPattern->right = NULL;
            ReturnLHSParseNodes(theEnv,tempPattern);

            if (lastPattern != NULL) tempPattern = lastPattern->right;
            else tempPattern = head;
           }
         else
           {
            lastPattern = tempPattern;
            tempPattern = tempPattern->right;
           }
        }

      /*=======================================================*/
      /* If none of the other tests for removing slots or slot */
      /* restrictions apply, then move on to the next slot or  */
      /* slot restriction to be tested.                        */
      /*=======================================================*/

      else
        {
         lastPattern = tempPattern;
         tempPattern = tempPattern->right;
        }
     }

   /*======================================*/
   /* Return the pattern with unused slots */
   /* and slot restrictions removed.       */
   /*======================================*/

   return(head);
  }
Esempio n. 8
0
globle void FieldConversion(
  void *theEnv,
  struct lhsParseNode *theField,
  struct lhsParseNode *thePattern,
  struct nandFrame *theNandFrames)
  {
   int testInPatternNetwork = TRUE;
   struct lhsParseNode *patternPtr;
   struct expr *headOfPNExpression, *headOfJNExpression;
   struct expr *lastPNExpression, *lastJNExpression;
   struct expr *tempExpression;
   struct expr *patternNetTest = NULL;
   struct expr *joinNetTest = NULL;
   struct expr *constantSelector = NULL;
   struct expr *constantValue = NULL;

   /*==================================================*/
   /* Consider a NULL pointer to be an internal error. */
   /*==================================================*/

   if (theField == NULL)
     {
      SystemError(theEnv,"ANALYSIS",3);
      EnvExitRouter(theEnv,EXIT_FAILURE);
     }

   /*========================================================*/
   /* Determine if constant testing must be performed in the */
   /* join network. Only possible when a field contains an   */
   /* or ('|') and references are made to variables outside  */
   /* the pattern.                                           */
   /*========================================================*/

   if (theField->bottom != NULL)
     {
      if (theField->bottom->bottom != NULL)
        { testInPatternNetwork = AllVariablesInPattern(theField->bottom,theField->pattern); }
     }

   /*=============================================================*/
   /* Extract pattern and join network expressions. Loop through  */
   /* the or'ed constraints of the field, extracting pattern and  */
   /* join network expressions and adding them to a running list. */
   /*=============================================================*/

   headOfPNExpression = lastPNExpression = NULL;
   headOfJNExpression = lastJNExpression = NULL;

   for (patternPtr = theField->bottom;
        patternPtr != NULL;
        patternPtr = patternPtr->bottom)
     {
      /*=============================================*/
      /* Extract pattern and join network tests from */
      /* the or'ed constraint being examined.        */
      /*=============================================*/

      ExtractAnds(theEnv,patternPtr,testInPatternNetwork,&patternNetTest,&joinNetTest,
                  &constantSelector,&constantValue,theNandFrames);

      /*=============================================================*/
      /* Constant hashing is only used in the pattern network if the */
      /* field doesn't contain an or'ed constraint. For example, the */
      /* constaint "red | blue" can not use hashing.                 */
      /*=============================================================*/
      
      if (constantSelector != NULL)
        {
         if ((patternPtr == theField->bottom) &&
             (patternPtr->bottom == NULL))
           {
            theField->constantSelector = constantSelector;
            theField->constantValue = constantValue;
           }
         else
           {
            ReturnExpression(theEnv,constantSelector);
            ReturnExpression(theEnv,constantValue);
            ReturnExpression(theEnv,theField->constantSelector);
            ReturnExpression(theEnv,theField->constantValue);
            theField->constantSelector = NULL;
            theField->constantValue = NULL;
           }
        }
        
      /*=====================================================*/
      /* Add the new pattern network expressions to the list */
      /* of pattern network expressions being constructed.   */
      /*=====================================================*/

      if (patternNetTest != NULL)
        {
         if (lastPNExpression == NULL)
           { headOfPNExpression = patternNetTest; }
         else
           { lastPNExpression->nextArg = patternNetTest; }
         lastPNExpression = patternNetTest;
        }

      /*==================================================*/
      /* Add the new join network expressions to the list */
      /* of join network expressions being constructed.   */
      /*==================================================*/

      if (joinNetTest != NULL)
        {
         if (lastJNExpression == NULL)
           { headOfJNExpression = joinNetTest; }
         else
           { lastJNExpression->nextArg = joinNetTest; }
         lastJNExpression = joinNetTest;
        }
     }

   /*==========================================================*/
   /* If there was more than one expression generated from the */
   /* or'ed field constraints for the pattern network, then    */
   /* enclose the expressions within an "or" function call.    */
   /*==========================================================*/

   if ((headOfPNExpression != NULL) ? (headOfPNExpression->nextArg != NULL) : FALSE)
     {
      tempExpression = GenConstant(theEnv,FCALL,ExpressionData(theEnv)->PTR_OR);
      tempExpression->argList = headOfPNExpression;
      headOfPNExpression = tempExpression;
     }

   /*==========================================================*/
   /* If there was more than one expression generated from the */
   /* or'ed field constraints for the join network, then       */
   /* enclose the expressions within an "or" function call.    */
   /*==========================================================*/

   if ((headOfJNExpression != NULL) ? (headOfJNExpression->nextArg != NULL) : FALSE)
     {
      tempExpression = GenConstant(theEnv,FCALL,ExpressionData(theEnv)->PTR_OR);
      tempExpression->argList = headOfJNExpression;
      headOfJNExpression = tempExpression;
     }
     
   /*===============================================================*/
   /* If the field constraint binds a variable that was previously  */
   /* bound somewhere in the LHS of the rule, then generate an      */
   /* expression to compare this binding occurrence of the variable */
   /* to the previous binding occurrence.                           */
   /*===============================================================*/

   if (((theField->type == MF_VARIABLE) || (theField->type == SF_VARIABLE)) &&
       (theField->referringNode != NULL))
     {
      /*================================================================*/
      /* If the previous variable reference is within the same pattern, */
      /* then the variable comparison can occur in the pattern network. */
      /*================================================================*/

      if (theField->referringNode->pattern == theField->pattern)
        {
         tempExpression = GenPNVariableComparison(theEnv,theField,theField->referringNode);
         headOfPNExpression = CombineExpressions(theEnv,tempExpression,headOfPNExpression);
        }

      /*====================================*/
      /* Otherwise, the variable comparison */
      /* must occur in the join network.    */
      /*====================================*/

      else if (theField->referringNode->pattern > 0)
        {
         AddNandUnification(theEnv,theField,theNandFrames);
         
         /*====================================*/
         /* Generate an expression to test the */
         /* variable in a non-nand join.       */
         /*====================================*/

         tempExpression = GenJNVariableComparison(theEnv,theField,theField->referringNode,FALSE);
         headOfJNExpression = CombineExpressions(theEnv,tempExpression,headOfJNExpression);
            
         /*==========================*/
         /* Generate the hash index. */
         /*==========================*/

         if (theField->patternType->genGetPNValueFunction != NULL)
           {
            tempExpression = (*theField->patternType->genGetPNValueFunction)(theEnv,theField);
            thePattern->rightHash = AppendExpressions(tempExpression,thePattern->rightHash);
           }
           
         if (theField->referringNode->patternType->genGetJNValueFunction)
           {
            tempExpression = (*theField->referringNode->patternType->genGetJNValueFunction)(theEnv,theField->referringNode,LHS);
            thePattern->leftHash = AppendExpressions(tempExpression,thePattern->leftHash);
           }
        }
     }
     
   /*======================================================*/
   /* Attach the pattern network expressions to the field. */
   /*======================================================*/

   theField->networkTest = headOfPNExpression;

   /*=====================================================*/
   /* Attach the join network expressions to the pattern. */
   /*=====================================================*/

   thePattern->networkTest = CombineExpressions(theEnv,thePattern->networkTest,headOfJNExpression);
  }
Esempio n. 9
0
static int TestCEAnalysis(
  void *theEnv,
  struct lhsParseNode *patternPtr,
  struct lhsParseNode *theExpression,
  int secondary,
  int *errorFlag,
  struct nandFrame *theNandFrames)
  {
   struct lhsParseNode *rv, *theList, *tempList, *tempRight;

   if (theExpression == NULL) return FALSE;
   
   /*=====================================================*/
   /* Verify that all variables were referenced properly. */
   /*=====================================================*/

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

   /*====================================================================*/
   /* Temporarily disconnect the right nodes. If this is a pattern node  */
   /* with an attached test CE, we only want to propagate to following   */
   /* patterns, not to nodes of this pattern which preceded the test CE. */
   /*====================================================================*/
   
   tempRight = patternPtr->right;
   patternPtr->right = NULL;
      
   /*=========================================================*/
   /* Determine the type and value constraints implied by the */
   /* expression and propagate these constraints to other     */
   /* variables in the LHS. For example, the expression       */
   /* (+ ?x 1) implies that ?x is a number.                   */
   /*=========================================================*/
   
   theList = GetExpressionVarConstraints(theEnv,theExpression);
   for (tempList = theList; tempList != NULL; tempList = tempList->right)
      {
       if (PropagateVariableDriver(theEnv,patternPtr,patternPtr,NULL,SF_VARIABLE,
                                   (SYMBOL_HN *) tempList->value,tempList,FALSE,TEST_CE))
         {
          ReturnLHSParseNodes(theEnv,theList);
          patternPtr->right = tempRight;
          return(TRUE);
         }
      }
      
   ReturnLHSParseNodes(theEnv,theList);
   
   /*============================*/
   /* Reconnect the right nodes. */
   /*============================*/
   
   patternPtr->right = tempRight;
   
   /*========================================================*/
   /* If the variables in the expression were all referenced */
   /* properly, then create the expression to use in the     */
   /* join network.                                          */
   /*========================================================*/

   if (rv != NULL)
     { *errorFlag = TRUE; }
   else if (secondary)
     { patternPtr->secondaryNetworkTest = CombineExpressions(theEnv,patternPtr->secondaryNetworkTest,GetvarReplace(theEnv,theExpression,FALSE,theNandFrames)); }
   else
     { patternPtr->networkTest = CombineExpressions(theEnv,patternPtr->networkTest,GetvarReplace(theEnv,theExpression,FALSE,theNandFrames)); }
     
   return FALSE;
  }