/*******************************************************/ /* "C" Language Integrated Production System */ /* */ /* CLIPS Version 6.40 09/22/17 */ /* */ /* INSTANCE-SET QUERIES PARSER MODULE */ /*******************************************************/ /*************************************************************/ /* Purpose: Instance_set Queries Parsing Routines */ /* */ /* Principal Programmer(s): */ /* Brian L. Dantes */ /* */ /* Contributing Programmer(s): */ /* */ /* */ /* Revision History: */ /* */ /* 6.23: Changed name of variable exp to theExp */ /* because of Unix compiler warnings of shadowed */ /* definitions. */ /* */ /* 6.24: Renamed BOOLEAN macro type to intBool. */ /* */ /* 6.30: Fixed memory leaks when error occurred. */ /* */ /* Changed integer type/precision. */ /* */ /* Support for long long integers. */ /* */ /* Added const qualifiers to remove C++ */ /* deprecation warnings. */ /* */ /* Added code to keep track of pointers to */ /* constructs that are contained externally to */ /* to constructs, DanglingConstructs. */ /* */ /* 6.31: Error check for non-symbolic slot names. */ /* */ /* 6.40: Pragma once and other inclusion changes. */ /* */ /* Added support for booleans with . */ /* */ /* Removed use of void pointers for specific */ /* data structures. */ /* */ /* Eval support for run time and bload only. */ /* */ /*************************************************************/ /* ========================================= ***************************************** EXTERNAL DEFINITIONS ========================================= ***************************************** */ #include "setup.h" #if INSTANCE_SET_QUERIES #include #include "classcom.h" #include "envrnmnt.h" #include "exprnpsr.h" #include "insquery.h" #include "prcdrpsr.h" #include "pprint.h" #include "prntutil.h" #include "router.h" #include "scanner.h" #include "strngrtr.h" #include "insqypsr.h" /* ========================================= ***************************************** CONSTANTS ========================================= ***************************************** */ #define INSTANCE_SLOT_REF ':' /***************************************/ /* LOCAL INTERNAL FUNCTION DEFINITIONS */ /***************************************/ static Expression *ParseQueryRestrictions(Environment *,Expression *,const char *,struct token *); static bool ReplaceClassNameWithReference(Environment *,Expression *); static bool ParseQueryTestExpression(Environment *,Expression *,const char *); static bool ParseQueryActionExpression(Environment *,Expression *,const char *,Expression *,struct token *); static bool ReplaceInstanceVariables(Environment *,Expression *,Expression *,bool,int); static bool ReplaceSlotReference(Environment *,Expression *,Expression *, struct functionDefinition *,int); static bool IsQueryFunction(Expression *); /* ========================================= ***************************************** EXTERNALLY VISIBLE FUNCTIONS ========================================= ***************************************** */ /*********************************************************************** NAME : ParseQueryNoAction DESCRIPTION : Parses the following functions : (any-instancep) (find-first-instance) (find-all-instances) INPUTS : 1) The address of the top node of the query function 2) The logical name of the input RETURNS : The completed expression chain, or NULL on errors SIDE EFFECTS : The expression chain is extended, or the "top" node is deleted on errors NOTES : H/L Syntax : ( ) :== (+) :== ( +) Parses into following form : | V -> -> -> (QDS) -> -> -> (QDS) -> ... ***********************************************************************/ Expression *ParseQueryNoAction( Environment *theEnv, Expression *top, const char *readSource) { Expression *insQuerySetVars; struct token queryInputToken; insQuerySetVars = ParseQueryRestrictions(theEnv,top,readSource,&queryInputToken); if (insQuerySetVars == NULL) return NULL; IncrementIndentDepth(theEnv,3); PPCRAndIndent(theEnv); if (ParseQueryTestExpression(theEnv,top,readSource) == false) { DecrementIndentDepth(theEnv,3); ReturnExpression(theEnv,insQuerySetVars); return NULL; } DecrementIndentDepth(theEnv,3); GetToken(theEnv,readSource,&queryInputToken); if (queryInputToken.tknType != RIGHT_PARENTHESIS_TOKEN) { SyntaxErrorMessage(theEnv,"instance-set query function"); ReturnExpression(theEnv,top); ReturnExpression(theEnv,insQuerySetVars); return NULL; } if (ReplaceInstanceVariables(theEnv,insQuerySetVars,top->argList,true,0)) { ReturnExpression(theEnv,top); ReturnExpression(theEnv,insQuerySetVars); return NULL; } ReturnExpression(theEnv,insQuerySetVars); return top; } /*********************************************************************** NAME : ParseQueryAction DESCRIPTION : Parses the following functions : (do-for-instance) (do-for-all-instances) (delayed-do-for-all-instances) INPUTS : 1) The address of the top node of the query function 2) The logical name of the input RETURNS : The completed expression chain, or NULL on errors SIDE EFFECTS : The expression chain is extended, or the "top" node is deleted on errors NOTES : H/L Syntax : ( ) :== (+) :== ( +) Parses into following form : | V -> -> -> -> (QDS) -> -> -> (QDS) -> ... ***********************************************************************/ Expression *ParseQueryAction( Environment *theEnv, Expression *top, const char *readSource) { Expression *insQuerySetVars; struct token queryInputToken; insQuerySetVars = ParseQueryRestrictions(theEnv,top,readSource,&queryInputToken); if (insQuerySetVars == NULL) return NULL; IncrementIndentDepth(theEnv,3); PPCRAndIndent(theEnv); if (ParseQueryTestExpression(theEnv,top,readSource) == false) { DecrementIndentDepth(theEnv,3); ReturnExpression(theEnv,insQuerySetVars); return NULL; } PPCRAndIndent(theEnv); if (ParseQueryActionExpression(theEnv,top,readSource,insQuerySetVars,&queryInputToken) == false) { DecrementIndentDepth(theEnv,3); ReturnExpression(theEnv,insQuerySetVars); return NULL; } DecrementIndentDepth(theEnv,3); if (queryInputToken.tknType != RIGHT_PARENTHESIS_TOKEN) { SyntaxErrorMessage(theEnv,"instance-set query function"); ReturnExpression(theEnv,top); ReturnExpression(theEnv,insQuerySetVars); return NULL; } if (ReplaceInstanceVariables(theEnv,insQuerySetVars,top->argList,true,0)) { ReturnExpression(theEnv,top); ReturnExpression(theEnv,insQuerySetVars); return NULL; } if (ReplaceInstanceVariables(theEnv,insQuerySetVars,top->argList->nextArg,false,0)) { ReturnExpression(theEnv,top); ReturnExpression(theEnv,insQuerySetVars); return NULL; } ReturnExpression(theEnv,insQuerySetVars); return top; } /* ========================================= ***************************************** INTERNALLY VISIBLE FUNCTIONS ========================================= ***************************************** */ /*************************************************************** NAME : ParseQueryRestrictions DESCRIPTION : Parses the class restrictions for a query INPUTS : 1) The top node of the query expression 2) The logical name of the input 3) Caller's token buffer RETURNS : The instance-variable expressions SIDE EFFECTS : Entire query expression deleted on errors Nodes allocated for restrictions and instance variable expressions Class restrictions attached to query-expression as arguments NOTES : Expects top != NULL ***************************************************************/ static Expression *ParseQueryRestrictions( Environment *theEnv, Expression *top, const char *readSource, struct token *queryInputToken) { Expression *insQuerySetVars = NULL,*lastInsQuerySetVars = NULL, *classExp = NULL,*lastClassExp, *tmp,*lastOne = NULL; bool error = false; SavePPBuffer(theEnv," "); GetToken(theEnv,readSource,queryInputToken); if (queryInputToken->tknType != LEFT_PARENTHESIS_TOKEN) goto ParseQueryRestrictionsError1; GetToken(theEnv,readSource,queryInputToken); if (queryInputToken->tknType != LEFT_PARENTHESIS_TOKEN) goto ParseQueryRestrictionsError1; while (queryInputToken->tknType == LEFT_PARENTHESIS_TOKEN) { GetToken(theEnv,readSource,queryInputToken); if (queryInputToken->tknType != SF_VARIABLE_TOKEN) goto ParseQueryRestrictionsError1; tmp = insQuerySetVars; while (tmp != NULL) { if (tmp->value == queryInputToken->value) { PrintErrorID(theEnv,"INSQYPSR",1,false); WriteString(theEnv,STDERR,"Duplicate instance member variable name in function '"); WriteString(theEnv,STDERR,ExpressionFunctionCallName(top)->contents); WriteString(theEnv,STDERR,"'.\n"); goto ParseQueryRestrictionsError2; } tmp = tmp->nextArg; } tmp = GenConstant(theEnv,SF_VARIABLE,queryInputToken->value); if (insQuerySetVars == NULL) insQuerySetVars = tmp; else lastInsQuerySetVars->nextArg = tmp; lastInsQuerySetVars = tmp; SavePPBuffer(theEnv," "); classExp = ArgumentParse(theEnv,readSource,&error); if (error) goto ParseQueryRestrictionsError2; if (classExp == NULL) goto ParseQueryRestrictionsError1; if (ReplaceClassNameWithReference(theEnv,classExp) == false) goto ParseQueryRestrictionsError2; lastClassExp = classExp; SavePPBuffer(theEnv," "); while ((tmp = ArgumentParse(theEnv,readSource,&error)) != NULL) { if (ReplaceClassNameWithReference(theEnv,tmp) == false) goto ParseQueryRestrictionsError2; lastClassExp->nextArg = tmp; lastClassExp = tmp; SavePPBuffer(theEnv," "); } if (error) goto ParseQueryRestrictionsError2; PPBackup(theEnv); PPBackup(theEnv); SavePPBuffer(theEnv,")"); tmp = GenConstant(theEnv,SYMBOL_TYPE,InstanceQueryData(theEnv)->QUERY_DELIMITER_SYMBOL); lastClassExp->nextArg = tmp; lastClassExp = tmp; if (top->argList == NULL) top->argList = classExp; else lastOne->nextArg = classExp; lastOne = lastClassExp; classExp = NULL; SavePPBuffer(theEnv," "); GetToken(theEnv,readSource,queryInputToken); } if (queryInputToken->tknType != RIGHT_PARENTHESIS_TOKEN) goto ParseQueryRestrictionsError1; PPBackup(theEnv); PPBackup(theEnv); SavePPBuffer(theEnv,")"); return(insQuerySetVars); ParseQueryRestrictionsError1: SyntaxErrorMessage(theEnv,"instance-set query function"); ParseQueryRestrictionsError2: ReturnExpression(theEnv,classExp); ReturnExpression(theEnv,top); ReturnExpression(theEnv,insQuerySetVars); return NULL; } /*************************************************** NAME : ReplaceClassNameWithReference DESCRIPTION : In parsing an instance-set query, this function replaces a constant class name with an actual pointer to the class INPUTS : The expression RETURNS : True if all OK, false if class cannot be found SIDE EFFECTS : The expression type and value are modified if class is found NOTES : Searches current and imported modules for reference ***************************************************/ static bool ReplaceClassNameWithReference( Environment *theEnv, Expression *theExp) { const char *theClassName; Defclass *theDefclass; if (theExp->type == SYMBOL_TYPE) { theClassName = theExp->lexemeValue->contents; theDefclass = LookupDefclassByMdlOrScope(theEnv,theClassName); if (theDefclass == NULL) { CantFindItemErrorMessage(theEnv,"class",theClassName,true); return false; } theExp->type = DEFCLASS_PTR; theExp->value = theDefclass; #if (! RUN_TIME) && (! BLOAD_ONLY) if (! ConstructData(theEnv)->ParsingConstruct) { ConstructData(theEnv)->DanglingConstructs++; } #endif } return true; } /************************************************************* NAME : ParseQueryTestExpression DESCRIPTION : Parses the test-expression for a query INPUTS : 1) The top node of the query expression 2) The logical name of the input RETURNS : True if all OK, false otherwise SIDE EFFECTS : Entire query-expression deleted on errors Nodes allocated for new expression Test shoved in front of class-restrictions on query argument list NOTES : Expects top != NULL *************************************************************/ static bool ParseQueryTestExpression( Environment *theEnv, Expression *top, const char *readSource) { Expression *qtest; bool error; struct BindInfo *oldBindList; error = false; oldBindList = GetParsedBindNames(theEnv); SetParsedBindNames(theEnv,NULL); qtest = ArgumentParse(theEnv,readSource,&error); if (error == true) { ClearParsedBindNames(theEnv); SetParsedBindNames(theEnv,oldBindList); ReturnExpression(theEnv,top); return false; } if (qtest == NULL) { ClearParsedBindNames(theEnv); SetParsedBindNames(theEnv,oldBindList); SyntaxErrorMessage(theEnv,"instance-set query function"); ReturnExpression(theEnv,top); return false; } qtest->nextArg = top->argList; top->argList = qtest; if (ParsedBindNamesEmpty(theEnv) == false) { ClearParsedBindNames(theEnv); SetParsedBindNames(theEnv,oldBindList); PrintErrorID(theEnv,"INSQYPSR",2,false); WriteString(theEnv,STDERR,"Binds are not allowed in instance-set query in function '"); WriteString(theEnv,STDERR,ExpressionFunctionCallName(top)->contents); WriteString(theEnv,STDERR,"'.\n"); ReturnExpression(theEnv,top); return false; } SetParsedBindNames(theEnv,oldBindList); return true; } /************************************************************* NAME : ParseQueryActionExpression DESCRIPTION : Parses the action-expression for a query INPUTS : 1) The top node of the query expression 2) The logical name of the input 3) List of query parameters RETURNS : True if all OK, false otherwise SIDE EFFECTS : Entire query-expression deleted on errors Nodes allocated for new expression Action shoved in front of class-restrictions and in back of test-expression on query argument list NOTES : Expects top != NULL && top->argList != NULL *************************************************************/ static bool ParseQueryActionExpression( Environment *theEnv, Expression *top, const char *readSource, Expression *insQuerySetVars, struct token *queryInputToken) { Expression *qaction, *tmpInsSetVars; struct BindInfo *oldBindList,*newBindList,*prev; oldBindList = GetParsedBindNames(theEnv); SetParsedBindNames(theEnv,NULL); ExpressionData(theEnv)->BreakContext = true; ExpressionData(theEnv)->ReturnContext = ExpressionData(theEnv)->svContexts->rtn; qaction = GroupActions(theEnv,readSource,queryInputToken,true,NULL,false); PPBackup(theEnv); PPBackup(theEnv); SavePPBuffer(theEnv,queryInputToken->printForm); ExpressionData(theEnv)->BreakContext = false; if (qaction == NULL) { ClearParsedBindNames(theEnv); SetParsedBindNames(theEnv,oldBindList); SyntaxErrorMessage(theEnv,"instance-set query function"); ReturnExpression(theEnv,top); return false; } qaction->nextArg = top->argList->nextArg; top->argList->nextArg = qaction; newBindList = GetParsedBindNames(theEnv); prev = NULL; while (newBindList != NULL) { tmpInsSetVars = insQuerySetVars; while (tmpInsSetVars != NULL) { if (tmpInsSetVars->value == (void *) newBindList->name) { ClearParsedBindNames(theEnv); SetParsedBindNames(theEnv,oldBindList); PrintErrorID(theEnv,"INSQYPSR",3,false); WriteString(theEnv,STDERR,"Cannot rebind instance-set member variable ?"); WriteString(theEnv,STDERR,tmpInsSetVars->lexemeValue->contents); WriteString(theEnv,STDERR," in function '"); WriteString(theEnv,STDERR,ExpressionFunctionCallName(top)->contents); WriteString(theEnv,STDERR,"'.\n"); ReturnExpression(theEnv,top); return false; } tmpInsSetVars = tmpInsSetVars->nextArg; } prev = newBindList; newBindList = newBindList->next; } if (prev == NULL) SetParsedBindNames(theEnv,oldBindList); else prev->next = oldBindList; return true; } /*********************************************************************************** NAME : ReplaceInstanceVariables DESCRIPTION : Replaces all references to instance-variables within an instance query-function with function calls to query-instance (which references the instance array at run-time) INPUTS : 1) The instance-variable list 2) A boolean expression containing variable references 3) A flag indicating whether to allow slot references of the type : for direct slot access or not 4) Nesting depth of query functions RETURNS : Nothing useful SIDE EFFECTS : If a SF_VARIABLE node is found and is on the list of instance variables, it is replaced with a query-instance function call. NOTES : Other SF_VARIABLE(S) are left alone for replacement by other parsers. This implies that a user may use defgeneric, defrule, and defmessage-handler variables within a query-function where they do not conflict with instance-variable names. ***********************************************************************************/ static bool ReplaceInstanceVariables( Environment *theEnv, Expression *vlist, Expression *bexp, bool sdirect, int ndepth) { Expression *eptr; struct functionDefinition *rindx_func,*rslot_func; int posn; rindx_func = FindFunction(theEnv,"(query-instance)"); rslot_func = FindFunction(theEnv,"(query-instance-slot)"); while (bexp != NULL) { if (bexp->type == SF_VARIABLE) { eptr = vlist; posn = 0; while ((eptr != NULL) ? (eptr->value != bexp->value) : false) { eptr = eptr->nextArg; posn++; } if (eptr != NULL) { bexp->type = FCALL; bexp->value = rindx_func; eptr = GenConstant(theEnv,INTEGER_TYPE,CreateInteger(theEnv,ndepth)); eptr->nextArg = GenConstant(theEnv,INTEGER_TYPE,CreateInteger(theEnv,posn)); bexp->argList = eptr; } else if (sdirect == true) { if (ReplaceSlotReference(theEnv,vlist,bexp,rslot_func,ndepth)) { return true; } } } if (bexp->argList != NULL) { if (IsQueryFunction(bexp)) { if (ReplaceInstanceVariables(theEnv,vlist,bexp->argList,sdirect,ndepth+1)) { return true; } } else { if (ReplaceInstanceVariables(theEnv,vlist,bexp->argList,sdirect,ndepth)) { return true; } } } bexp = bexp->nextArg; } return false; } /************************************************************************* NAME : ReplaceSlotReference DESCRIPTION : Replaces instance-set query function variable references of the form: : with function calls to get these instance-slots at run time INPUTS : 1) The instance-set variable list 2) The expression containing the variable 3) The address of the instance slot access function 4) Nesting depth of query functions RETURNS : Nothing useful SIDE EFFECTS : If the variable is a slot reference, then it is replaced with the appropriate function-call. NOTES : None *************************************************************************/ static bool ReplaceSlotReference( Environment *theEnv, Expression *vlist, Expression *theExp, struct functionDefinition *func, int ndepth) { size_t len; int posn; bool oldpp; size_t i; const char *str; Expression *eptr; struct token itkn; str = theExp->lexemeValue->contents; len = strlen(str); if (len < 3) { return false; } for (i = len-2 ; i >= 1 ; i--) { if ((str[i] == INSTANCE_SLOT_REF) ? (i >= 1) : false) { eptr = vlist; posn = 0; while (eptr && ((i != strlen(eptr->lexemeValue->contents)) || strncmp(eptr->lexemeValue->contents,str, (STD_SIZE) i))) { eptr = eptr->nextArg; posn++; } if (eptr != NULL) { OpenStringSource(theEnv,"query-var",str+i+1,0); oldpp = GetPPBufferStatus(theEnv); SetPPBufferStatus(theEnv,false); GetToken(theEnv,"query-var",&itkn); SetPPBufferStatus(theEnv,oldpp); CloseStringSource(theEnv,"query-var"); if (itkn.tknType != SYMBOL_TOKEN) { InvalidVarSlotErrorMessage(theEnv,str); SetEvaluationError(theEnv,true); return true; } theExp->type = FCALL; theExp->value = func; theExp->argList = GenConstant(theEnv,INTEGER_TYPE,CreateInteger(theEnv,ndepth)); theExp->argList->nextArg = GenConstant(theEnv,INTEGER_TYPE,CreateInteger(theEnv,posn)); theExp->argList->nextArg->nextArg = GenConstant(theEnv,TokenTypeToType(itkn.tknType),itkn.value); theExp->argList->nextArg->nextArg->nextArg = GenConstant(theEnv,SYMBOL_TYPE,CreateSymbol(theEnv,str)); return false; } } } return false; } /******************************************************************** NAME : IsQueryFunction DESCRIPTION : Determines if an expression is a query function call INPUTS : The expression RETURNS : True if query function call, false otherwise SIDE EFFECTS : None NOTES : None ********************************************************************/ static bool IsQueryFunction( Expression *theExp) { int (*fptr)(void); if (theExp->type != FCALL) return false; fptr = (int (*)(void)) ExpressionFunctionPointer(theExp); if (fptr == (int (*)(void)) AnyInstances) return true; if (fptr == (int (*)(void)) QueryFindInstance) return true; if (fptr == (int (*)(void)) QueryFindAllInstances) return true; if (fptr == (int (*)(void)) QueryDoForInstance) return true; if (fptr == (int (*)(void)) QueryDoForAllInstances) return true; if (fptr == (int (*)(void)) DelayedQueryDoForAllInstances) return true; return false; } #endif