#include "setup.h"
#include <ctype.h>
#include <stdlib.h>
#include "argacces.h"
#include "constant.h"
#include "envrnmnt.h"
#include "exprnpsr.h"
#include "factmngr.h"
#include "memalloc.h"
#include "router.h"
#if OBJECT_SYSTEM
#include "inscom.h"
#endif
#include "extnfunc.h"
static void AddHashFunction(Environment *,struct functionDefinition *);
static void InitializeFunctionHashTable(Environment *);
static void DeallocateExternalFunctionData(Environment *);
#if (! RUN_TIME)
static bool RemoveHashFunction(Environment *,struct functionDefinition *);
static AddUDFError DefineFunction(Environment *,const char *,unsigned,void (*)(Environment *,UDFContext *,UDFValue *),
const char *,unsigned short,unsigned short,const char *,void *);
#endif
static void PrintType(Environment *,const char *,int,int *,const char *);
static void AssignErrorValue(UDFContext *);
void InitializeExternalFunctionData(
Environment *theEnv)
{
AllocateEnvironmentData(theEnv,EXTERNAL_FUNCTION_DATA,sizeof(struct externalFunctionData),DeallocateExternalFunctionData);
}
static void DeallocateExternalFunctionData(
Environment *theEnv)
{
struct FunctionHash *fhPtr, *nextFHPtr;
int i;
#if ! RUN_TIME
struct functionDefinition *tmpPtr, *nextPtr;
tmpPtr = ExternalFunctionData(theEnv)->ListOfFunctions;
while (tmpPtr != NULL)
{
nextPtr = tmpPtr->next;
rtn_struct(theEnv,functionDefinition,tmpPtr);
tmpPtr = nextPtr;
}
#endif
if (ExternalFunctionData(theEnv)->FunctionHashtable == NULL)
{ return; }
for (i = 0; i < SIZE_FUNCTION_HASH; i++)
{
fhPtr = ExternalFunctionData(theEnv)->FunctionHashtable[i];
while (fhPtr != NULL)
{
nextFHPtr = fhPtr->next;
rtn_struct(theEnv,FunctionHash,fhPtr);
fhPtr = nextFHPtr;
}
}
genfree(theEnv,ExternalFunctionData(theEnv)->FunctionHashtable,
sizeof (struct FunctionHash *) * SIZE_FUNCTION_HASH);
}
#if (! RUN_TIME)
AddUDFError AddUDF(
Environment *theEnv,
const char *clipsFunctionName,
const char *returnTypes,
unsigned short minArgs,
unsigned short maxArgs,
const char *argumentTypes,
UserDefinedFunction *cFunctionPointer,
const char *cFunctionName,
void *context)
{
unsigned returnTypeBits;
size_t i;
const char *validTypeChars = "bdefilmnsyv*;";
if ((minArgs != UNBOUNDED) && (minArgs > maxArgs))
{ return AUE_MIN_EXCEEDS_MAX_ERROR; }
if (argumentTypes != NULL)
{
for (i = 0; argumentTypes[i] != EOS; i++)
{
if (strchr(validTypeChars,argumentTypes[i]) == NULL)
{ return AUE_INVALID_ARGUMENT_TYPE_ERROR; }
}
}
if (returnTypes != NULL)
{
for (i = 0; returnTypes[i] != EOS; i++)
{
if (strchr(validTypeChars,returnTypes[i]) == NULL)
{ return AUE_INVALID_RETURN_TYPE_ERROR; }
}
PopulateRestriction(theEnv,&returnTypeBits,ANY_TYPE_BITS,returnTypes,0);
}
else
{ returnTypeBits = ANY_TYPE_BITS; }
return DefineFunction(theEnv,clipsFunctionName,returnTypeBits,cFunctionPointer,
cFunctionName,minArgs,maxArgs,argumentTypes,context);
}
static AddUDFError DefineFunction(
Environment *theEnv,
const char *name,
unsigned returnTypeBits,
void (*pointer)(Environment *,UDFContext *,UDFValue *),
const char *actualName,
unsigned short minArgs,
unsigned short maxArgs,
const char *restrictions,
void *context)
{
struct functionDefinition *newFunction;
newFunction = FindFunction(theEnv,name);
if (newFunction != NULL)
{ return AUE_FUNCTION_NAME_IN_USE_ERROR; }
newFunction = get_struct(theEnv,functionDefinition);
newFunction->callFunctionName = CreateSymbol(theEnv,name);
IncrementLexemeCount(newFunction->callFunctionName);
newFunction->next = GetFunctionList(theEnv);
ExternalFunctionData(theEnv)->ListOfFunctions = newFunction;
AddHashFunction(theEnv,newFunction);
newFunction->unknownReturnValueType = returnTypeBits;
newFunction->functionPointer = pointer;
newFunction->actualFunctionName = actualName;
newFunction->minArgs = minArgs;
newFunction->maxArgs = maxArgs;
if (restrictions == NULL)
{ newFunction->restrictions = NULL; }
else
{
newFunction->restrictions = CreateString(theEnv,restrictions);
IncrementLexemeCount(newFunction->restrictions);
}
newFunction->parser = NULL;
newFunction->overloadable = true;
newFunction->sequenceuseok = true;
newFunction->usrData = NULL;
newFunction->context = context;
return AUE_NO_ERROR;
}
bool RemoveUDF(
Environment *theEnv,
const char *functionName)
{
CLIPSLexeme *findValue;
struct functionDefinition *fPtr, *lastPtr = NULL;
findValue = FindSymbolHN(theEnv,functionName,SYMBOL_BIT);
for (fPtr = ExternalFunctionData(theEnv)->ListOfFunctions;
fPtr != NULL;
fPtr = fPtr->next)
{
if (fPtr->callFunctionName == findValue)
{
ReleaseLexeme(theEnv,fPtr->callFunctionName);
RemoveHashFunction(theEnv,fPtr);
if (lastPtr == NULL)
{ ExternalFunctionData(theEnv)->ListOfFunctions = fPtr->next; }
else
{ lastPtr->next = fPtr->next; }
if (fPtr->restrictions != NULL)
{ ReleaseLexeme(theEnv,fPtr->restrictions); }
ClearUserDataList(theEnv,fPtr->usrData);
rtn_struct(theEnv,functionDefinition,fPtr);
return true;
}
lastPtr = fPtr;
}
return false;
}
static bool RemoveHashFunction(
Environment *theEnv,
struct functionDefinition *fdPtr)
{
struct FunctionHash *fhPtr, *lastPtr = NULL;
size_t hashValue;
hashValue = HashSymbol(fdPtr->callFunctionName->contents,SIZE_FUNCTION_HASH);
for (fhPtr = ExternalFunctionData(theEnv)->FunctionHashtable[hashValue];
fhPtr != NULL;
fhPtr = fhPtr->next)
{
if (fhPtr->fdPtr == fdPtr)
{
if (lastPtr == NULL)
{ ExternalFunctionData(theEnv)->FunctionHashtable[hashValue] = fhPtr->next; }
else
{ lastPtr->next = fhPtr->next; }
rtn_struct(theEnv,FunctionHash,fhPtr);
return true;
}
lastPtr = fhPtr;
}
return false;
}
#endif
bool AddFunctionParser(
Environment *theEnv,
const char *functionName,
struct expr *(*fpPtr)(Environment *,struct expr *,const char *))
{
struct functionDefinition *fdPtr;
fdPtr = FindFunction(theEnv,functionName);
if (fdPtr == NULL)
{
WriteString(theEnv,STDERR,"Function parsers can only be added for existing functions.\n");
return false;
}
fdPtr->parser = fpPtr;
fdPtr->overloadable = false;
return true;
}
#if (! RUN_TIME)
bool RemoveFunctionParser(
Environment *theEnv,
const char *functionName)
{
struct functionDefinition *fdPtr;
fdPtr = FindFunction(theEnv,functionName);
if (fdPtr == NULL)
{
WriteString(theEnv,STDERR,"Function parsers can only be removed from existing functions.\n");
return false;
}
fdPtr->parser = NULL;
return true;
}
bool FuncSeqOvlFlags(
Environment *theEnv,
const char *functionName,
bool seqp,
bool ovlp)
{
struct functionDefinition *fdPtr;
fdPtr = FindFunction(theEnv,functionName);
if (fdPtr == NULL)
{
WriteString(theEnv,STDERR,"Only existing functions can be marked as using sequence expansion arguments/overloadable or not.\n");
return false;
}
fdPtr->sequenceuseok = (seqp ? true : false);
fdPtr->overloadable = (ovlp ? true : false);
return true;
}
#endif
unsigned GetNthRestriction(
Environment *theEnv,
struct functionDefinition *theFunction,
unsigned int position)
{
unsigned rv, df;
const char *restrictions;
if (theFunction == NULL) return(ANY_TYPE_BITS);
if (theFunction->restrictions == NULL) return(ANY_TYPE_BITS);
restrictions = theFunction->restrictions->contents;
PopulateRestriction(theEnv,&df,ANY_TYPE_BITS,restrictions,0);
PopulateRestriction(theEnv,&rv,df,restrictions,position);
return rv;
}
struct functionDefinition *GetFunctionList(
Environment *theEnv)
{
return(ExternalFunctionData(theEnv)->ListOfFunctions);
}
void InstallFunctionList(
Environment *theEnv,
struct functionDefinition *value)
{
int i;
struct FunctionHash *fhPtr, *nextPtr;
if (ExternalFunctionData(theEnv)->FunctionHashtable != NULL)
{
for (i = 0; i < SIZE_FUNCTION_HASH; i++)
{
fhPtr = ExternalFunctionData(theEnv)->FunctionHashtable[i];
while (fhPtr != NULL)
{
nextPtr = fhPtr->next;
rtn_struct(theEnv,FunctionHash,fhPtr);
fhPtr = nextPtr;
}
ExternalFunctionData(theEnv)->FunctionHashtable[i] = NULL;
}
}
ExternalFunctionData(theEnv)->ListOfFunctions = value;
while (value != NULL)
{
AddHashFunction(theEnv,value);
value = value->next;
}
}
struct functionDefinition *FindFunction(
Environment *theEnv,
const char *functionName)
{
struct FunctionHash *fhPtr;
size_t hashValue;
CLIPSLexeme *findValue;
if (ExternalFunctionData(theEnv)->FunctionHashtable == NULL) return NULL;
hashValue = HashSymbol(functionName,SIZE_FUNCTION_HASH);
findValue = FindSymbolHN(theEnv,functionName,SYMBOL_BIT);
for (fhPtr = ExternalFunctionData(theEnv)->FunctionHashtable[hashValue];
fhPtr != NULL;
fhPtr = fhPtr->next)
{
if (fhPtr->fdPtr->callFunctionName == findValue)
{ return(fhPtr->fdPtr); }
}
return NULL;
}
void *GetUDFContext(
Environment *theEnv,
const char *functionName)
{
struct FunctionHash *fhPtr;
size_t hashValue;
CLIPSLexeme *findValue;
if (ExternalFunctionData(theEnv)->FunctionHashtable == NULL) return NULL;
hashValue = HashSymbol(functionName,SIZE_FUNCTION_HASH);
findValue = FindSymbolHN(theEnv,functionName,SYMBOL_BIT);
for (fhPtr = ExternalFunctionData(theEnv)->FunctionHashtable[hashValue];
fhPtr != NULL;
fhPtr = fhPtr->next)
{
if (fhPtr->fdPtr->callFunctionName == findValue)
{ return fhPtr->fdPtr->context; }
}
return NULL;
}
static void InitializeFunctionHashTable(
Environment *theEnv)
{
int i;
ExternalFunctionData(theEnv)->FunctionHashtable = (struct FunctionHash **)
gm2(theEnv,sizeof (struct FunctionHash *) *
SIZE_FUNCTION_HASH);
for (i = 0; i < SIZE_FUNCTION_HASH; i++) ExternalFunctionData(theEnv)->FunctionHashtable[i] = NULL;
}
static void AddHashFunction(
Environment *theEnv,
struct functionDefinition *fdPtr)
{
struct FunctionHash *newhash, *temp;
size_t hashValue;
if (ExternalFunctionData(theEnv)->FunctionHashtable == NULL) InitializeFunctionHashTable(theEnv);
newhash = get_struct(theEnv,FunctionHash);
newhash->fdPtr = fdPtr;
hashValue = HashSymbol(fdPtr->callFunctionName->contents,SIZE_FUNCTION_HASH);
temp = ExternalFunctionData(theEnv)->FunctionHashtable[hashValue];
ExternalFunctionData(theEnv)->FunctionHashtable[hashValue] = newhash;
newhash->next = temp;
}
int GetMinimumArgs(
struct functionDefinition *theFunction)
{
return theFunction->minArgs;
}
int GetMaximumArgs(
struct functionDefinition *theFunction)
{
return theFunction->maxArgs;
}
void AssignErrorValue(
UDFContext *context)
{
if (context->theFunction->unknownReturnValueType & BOOLEAN_BIT)
{ context->returnValue->lexemeValue = context->environment->FalseSymbol; }
else if (context->theFunction->unknownReturnValueType & STRING_BIT)
{ context->returnValue->lexemeValue = CreateString(context->environment,""); }
else if (context->theFunction->unknownReturnValueType & SYMBOL_BIT)
{ context->returnValue->lexemeValue = CreateSymbol(context->environment,"nil"); }
else if (context->theFunction->unknownReturnValueType & INTEGER_BIT)
{ context->returnValue->integerValue = CreateInteger(context->environment,0); }
else if (context->theFunction->unknownReturnValueType & FLOAT_BIT)
{ context->returnValue->floatValue = CreateFloat(context->environment,0.0); }
else if (context->theFunction->unknownReturnValueType & MULTIFIELD_BIT)
{ SetMultifieldErrorValue(context->environment,context->returnValue); }
else if (context->theFunction->unknownReturnValueType & INSTANCE_NAME_BIT)
{ context->returnValue->lexemeValue = CreateInstanceName(context->environment,"nil"); }
else if (context->theFunction->unknownReturnValueType & FACT_ADDRESS_BIT)
{ context->returnValue->factValue = &FactData(context->environment)->DummyFact; }
#if OBJECT_SYSTEM
else if (context->theFunction->unknownReturnValueType & INSTANCE_ADDRESS_BIT)
{ context->returnValue->value = &InstanceData(context->environment)->DummyInstance; }
#endif
else if (context->theFunction->unknownReturnValueType & EXTERNAL_ADDRESS_BIT)
{ context->returnValue->value = CreateExternalAddress(context->environment,NULL,0); }
else
{ context->returnValue->value = context->environment->VoidConstant; }
}
unsigned int UDFArgumentCount(
UDFContext *context)
{
unsigned int count = 0;
struct expr *argPtr;
for (argPtr = EvaluationData(context->environment)->CurrentExpression->argList;
argPtr != NULL;
argPtr = argPtr->nextArg)
{ count++; }
return count;
}
bool UDFFirstArgument(
UDFContext *context,
unsigned expectedType,
UDFValue *returnValue)
{
context->lastArg = EvaluationData(context->environment)->CurrentExpression->argList;
context->lastPosition = 1;
return UDFNextArgument(context,expectedType,returnValue);
}
bool UDFNextArgument(
UDFContext *context,
unsigned expectedType,
UDFValue *returnValue)
{
struct expr *argPtr = context->lastArg;
unsigned int argumentPosition = context->lastPosition;
Environment *theEnv = context->environment;
if (argPtr == NULL)
{
SetHaltExecution(theEnv,true);
SetEvaluationError(theEnv,true);
return false;
}
context->lastPosition++;
context->lastArg = context->lastArg->nextArg;
switch (argPtr->type)
{
case INTEGER_TYPE:
returnValue->value = argPtr->value;
if (expectedType & INTEGER_BIT) return true;
ExpectedTypeError0(theEnv,UDFContextFunctionName(context),argumentPosition);
PrintTypesString(theEnv,STDERR,expectedType,true);
SetHaltExecution(theEnv,true);
SetEvaluationError(theEnv,true);
AssignErrorValue(context);
return false;
break;
case FLOAT_TYPE:
returnValue->value = argPtr->value;
if (expectedType & FLOAT_BIT) return true;
ExpectedTypeError0(theEnv,UDFContextFunctionName(context),argumentPosition);
PrintTypesString(theEnv,STDERR,expectedType,true);
SetHaltExecution(theEnv,true);
SetEvaluationError(theEnv,true);
AssignErrorValue(context);
return false;
break;
case SYMBOL_TYPE:
returnValue->value = argPtr->value;
if (expectedType & SYMBOL_BIT) return true;
if (expectedType & BOOLEAN_BIT)
{
if ((returnValue->lexemeValue == FalseSymbol(theEnv)) ||
(returnValue->lexemeValue == TrueSymbol(theEnv)))
{ return true; }
}
ExpectedTypeError0(theEnv,UDFContextFunctionName(context),argumentPosition);
PrintTypesString(theEnv,STDERR,expectedType,true);
SetHaltExecution(theEnv,true);
SetEvaluationError(theEnv,true);
AssignErrorValue(context);
return false;
break;
case STRING_TYPE:
returnValue->value = argPtr->value;
if (expectedType & STRING_BIT) return true;
ExpectedTypeError0(theEnv,UDFContextFunctionName(context),argumentPosition);
PrintTypesString(theEnv,STDERR,expectedType,true);
SetHaltExecution(theEnv,true);
SetEvaluationError(theEnv,true);
AssignErrorValue(context);
return false;
break;
case INSTANCE_NAME_TYPE:
returnValue->value = argPtr->value;
if (expectedType & INSTANCE_NAME_BIT) return true;
ExpectedTypeError0(theEnv,UDFContextFunctionName(context),argumentPosition);
PrintTypesString(theEnv,STDERR,expectedType,true);
SetHaltExecution(theEnv,true);
SetEvaluationError(theEnv,true);
AssignErrorValue(context);
return false;
break;
}
EvaluateExpression(theEnv,argPtr,returnValue);
switch (returnValue->header->type)
{
case VOID_TYPE:
if (expectedType & VOID_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else return true;
}
break;
case INTEGER_TYPE:
if (expectedType & INTEGER_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else return true;
}
break;
case FLOAT_TYPE:
if (expectedType & FLOAT_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else return true;
}
break;
case SYMBOL_TYPE:
if (expectedType & SYMBOL_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else return true;
}
if (expectedType & BOOLEAN_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else if ((returnValue->lexemeValue == FalseSymbol(theEnv)) ||
(returnValue->lexemeValue == TrueSymbol(theEnv)))
{ return true; }
}
break;
case STRING_TYPE:
if (expectedType & STRING_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else return true;
}
break;
case INSTANCE_NAME_TYPE:
if (expectedType & INSTANCE_NAME_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else return true;
}
break;
case EXTERNAL_ADDRESS_TYPE:
if (expectedType & EXTERNAL_ADDRESS_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else return true;
}
break;
case FACT_ADDRESS_TYPE:
if (expectedType & FACT_ADDRESS_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else return true;
}
break;
case INSTANCE_ADDRESS_TYPE:
if (expectedType & INSTANCE_ADDRESS_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else return true;
}
break;
case MULTIFIELD_TYPE:
if (expectedType & MULTIFIELD_BIT)
{
if (EvaluationData(theEnv)->EvaluationError)
{
AssignErrorValue(context);
return false;
}
else return true;
}
break;
}
ExpectedTypeError0(theEnv,UDFContextFunctionName(context),argumentPosition);
PrintTypesString(theEnv,STDERR,expectedType,true);
SetHaltExecution(theEnv,true);
SetEvaluationError(theEnv,true);
AssignErrorValue(context);
return false;
}
bool UDFNthArgument(
UDFContext *context,
unsigned int argumentPosition,
unsigned expectedType,
UDFValue *returnValue)
{
if (argumentPosition < context->lastPosition)
{
context->lastArg = EvaluationData(context->environment)->CurrentExpression->argList;
context->lastPosition = 1;
}
for ( ; (context->lastArg != NULL) && (context->lastPosition < argumentPosition) ;
context->lastArg = context->lastArg->nextArg)
{ context->lastPosition++; }
return UDFNextArgument(context,expectedType,returnValue);
}
void UDFInvalidArgumentMessage(
UDFContext *context,
const char *typeString)
{
ExpectedTypeError1(context->environment,
UDFContextFunctionName(context),
context->lastPosition-1,typeString);
}
void UDFThrowError(
UDFContext *context)
{
Environment *theEnv = context->environment;
SetHaltExecution(theEnv,true);
SetEvaluationError(theEnv,true);
}
const char *UDFContextFunctionName(
UDFContext *context)
{
return context->theFunction->callFunctionName->contents;
}
static void PrintType(
Environment *theEnv,
const char *logicalName,
int typeCount,
int *typesPrinted,
const char *typeName)
{
if (*typesPrinted == 0)
{
WriteString(theEnv,logicalName,typeName);
(*typesPrinted)++;
return;
}
if (typeCount == 2)
{ WriteString(theEnv,logicalName," or "); }
else if (((*typesPrinted) + 1) == typeCount)
{ WriteString(theEnv,logicalName,", or "); }
else
{ WriteString(theEnv,logicalName,", "); }
WriteString(theEnv,logicalName,typeName);
(*typesPrinted)++;
}
void PrintTypesString(
Environment *theEnv,
const char *logicalName,
unsigned expectedType,
bool printCRLF)
{
int typeCount, typesPrinted;
typeCount = 0;
if (expectedType & INTEGER_BIT) typeCount++;
if (expectedType & FLOAT_BIT) typeCount++;
if (expectedType & (SYMBOL_BIT | BOOLEAN_BIT)) typeCount++;
if (expectedType & STRING_BIT) typeCount++;
if (expectedType & INSTANCE_NAME_BIT) typeCount++;
if (expectedType & INSTANCE_ADDRESS_BIT) typeCount++;
if (expectedType & FACT_ADDRESS_BIT) typeCount++;
if (expectedType & EXTERNAL_ADDRESS_BIT) typeCount++;
if (expectedType & MULTIFIELD_BIT) typeCount++;
typesPrinted = 0;
if (expectedType & INTEGER_BIT)
{ PrintType(theEnv,logicalName,typeCount,&typesPrinted,"integer"); }
if (expectedType & FLOAT_BIT)
{ PrintType(theEnv,logicalName,typeCount,&typesPrinted,"float"); }
if (expectedType & SYMBOL_BIT)
{ PrintType(theEnv,logicalName,typeCount,&typesPrinted,"symbol"); }
else if (expectedType & BOOLEAN_BIT)
{ PrintType(theEnv,logicalName,typeCount,&typesPrinted,"boolean"); }
if (expectedType & STRING_BIT)
{ PrintType(theEnv,logicalName,typeCount,&typesPrinted,"string"); }
if (expectedType & INSTANCE_NAME_BIT)
{ PrintType(theEnv,logicalName,typeCount,&typesPrinted,"instance name"); }
if (expectedType & INSTANCE_ADDRESS_BIT)
{ PrintType(theEnv,logicalName,typeCount,&typesPrinted,"instance address"); }
if (expectedType & FACT_ADDRESS_BIT)
{ PrintType(theEnv,logicalName,typeCount,&typesPrinted,"fact address"); }
if (expectedType & EXTERNAL_ADDRESS_BIT)
{ PrintType(theEnv,logicalName,typeCount,&typesPrinted,"external address"); }
if (expectedType & MULTIFIELD_BIT)
{ PrintType(theEnv,logicalName,typeCount,&typesPrinted,"multifield"); }
if (printCRLF)
{ WriteString(theEnv,logicalName,".\n"); }
}