#include "setup.h"
#if BLOAD || BLOAD_AND_BSAVE
#include "bload.h"
#endif
#include "classcom.h"
#include "classfun.h"
#include "clsltpsr.h"
#include "cstrcpsr.h"
#include "envrnmnt.h"
#include "inherpsr.h"
#include "memalloc.h"
#include "modulpsr.h"
#include "modulutl.h"
#include "msgpsr.h"
#include "pprint.h"
#include "prntutil.h"
#include "router.h"
#include "scanner.h"
#include "classpsr.h"
#define ROLE_RLN "role"
#define ABSTRACT_RLN "abstract"
#define CONCRETE_RLN "concrete"
#define HANDLER_DECL "message-handler"
#define SLOT_RLN "slot"
#define MLT_SLOT_RLN "multislot"
#define DIRECT 0
#define INHERIT 1
#if OBJECT_SYSTEM && (! BLOAD_ONLY) && (! RUN_TIME)
static bool ValidClassName(Environment *,const char *,Defclass **);
static bool ParseSimpleQualifier(Environment *,const char *,const char *,const char *,const char *,bool *,bool *);
static bool ReadUntilClosingParen(Environment *,const char *,struct token *);
static void AddClass(Environment *,Defclass *);
static void BuildSubclassLinks(Environment *,Defclass *);
static void FormInstanceTemplate(Environment *,Defclass *);
static void FormSlotNameMap(Environment *,Defclass *);
static TEMP_SLOT_LINK *MergeSlots(Environment *,TEMP_SLOT_LINK *,Defclass *,unsigned short *,unsigned short);
static void PackSlots(Environment *,Defclass *,TEMP_SLOT_LINK *);
static void CreatePublicSlotMessageHandlers(Environment *,Defclass *);
bool ParseDefclass(
Environment *theEnv,
const char *readSource)
{
CLIPSLexeme *cname;
Defclass *cls;
PACKED_CLASS_LINKS *sclasses,*preclist;
TEMP_SLOT_LINK *slots = NULL;
bool parseError;
bool roleSpecified = false, abstract = false;
#if DEFRULE_CONSTRUCT
bool patternMatchSpecified = false;
bool reactive = true;
#endif
SetPPBufferStatus(theEnv,true);
FlushPPBuffer(theEnv);
SetIndentDepth(theEnv,3);
SavePPBuffer(theEnv,"(defclass ");
#if BLOAD || BLOAD_ONLY || BLOAD_AND_BSAVE
if ((Bloaded(theEnv)) && (! ConstructData(theEnv)->CheckSyntaxMode))
{
CannotLoadWithBloadMessage(theEnv,"defclass");
return true;
}
#endif
cname = GetConstructNameAndComment(theEnv,readSource,&DefclassData(theEnv)->ObjectParseToken,"defclass",
(FindConstructFunction *) FindDefclassInModule,NULL,"#",true,
true,true,false);
if (cname == NULL)
return true;
if (ValidClassName(theEnv,cname->contents,&cls) == false)
return true;
sclasses = ParseSuperclasses(theEnv,readSource,cname);
if (sclasses == NULL)
return true;
preclist = FindPrecedenceList(theEnv,cls,sclasses);
if (preclist == NULL)
{
DeletePackedClassLinks(theEnv,sclasses,true);
return true;
}
parseError = false;
GetToken(theEnv,readSource,&DefclassData(theEnv)->ObjectParseToken);
while (DefclassData(theEnv)->ObjectParseToken.tknType != RIGHT_PARENTHESIS_TOKEN)
{
if (DefclassData(theEnv)->ObjectParseToken.tknType != LEFT_PARENTHESIS_TOKEN)
{
SyntaxErrorMessage(theEnv,"defclass");
parseError = true;
break;
}
PPBackup(theEnv);
PPCRAndIndent(theEnv);
SavePPBuffer(theEnv,"(");
GetToken(theEnv,readSource,&DefclassData(theEnv)->ObjectParseToken);
if (DefclassData(theEnv)->ObjectParseToken.tknType != SYMBOL_TOKEN)
{
SyntaxErrorMessage(theEnv,"defclass");
parseError = true;
break;
}
if (strcmp(DefclassData(theEnv)->ObjectParseToken.lexemeValue->contents,ROLE_RLN) == 0)
{
if (ParseSimpleQualifier(theEnv,readSource,ROLE_RLN,CONCRETE_RLN,ABSTRACT_RLN,
&roleSpecified,&abstract) == false)
{
parseError = true;
break;
}
}
#if DEFRULE_CONSTRUCT
else if (strcmp(DefclassData(theEnv)->ObjectParseToken.lexemeValue->contents,MATCH_RLN) == 0)
{
if (ParseSimpleQualifier(theEnv,readSource,MATCH_RLN,NONREACTIVE_RLN,REACTIVE_RLN,
&patternMatchSpecified,&reactive) == false)
{
parseError = true;
break;
}
}
#endif
else if (strcmp(DefclassData(theEnv)->ObjectParseToken.lexemeValue->contents,SLOT_RLN) == 0)
{
slots = ParseSlot(theEnv,readSource,cname->contents,slots,preclist,false);
if (slots == NULL)
{
parseError = true;
break;
}
}
else if (strcmp(DefclassData(theEnv)->ObjectParseToken.lexemeValue->contents,MLT_SLOT_RLN) == 0)
{
slots = ParseSlot(theEnv,readSource,cname->contents,slots,preclist,true);
if (slots == NULL)
{
parseError = true;
break;
}
}
else if (strcmp(DefclassData(theEnv)->ObjectParseToken.lexemeValue->contents,HANDLER_DECL) == 0)
{
if (ReadUntilClosingParen(theEnv,readSource,&DefclassData(theEnv)->ObjectParseToken) == false)
{
parseError = true;
break;
}
}
else
{
SyntaxErrorMessage(theEnv,"defclass");
parseError = true;
break;
}
GetToken(theEnv,readSource,&DefclassData(theEnv)->ObjectParseToken);
}
if ((DefclassData(theEnv)->ObjectParseToken.tknType != RIGHT_PARENTHESIS_TOKEN) ||
(parseError == true))
{
DeletePackedClassLinks(theEnv,sclasses,true);
DeletePackedClassLinks(theEnv,preclist,true);
DeleteSlots(theEnv,slots);
return true;
}
SavePPBuffer(theEnv,"\n");
if (roleSpecified == false)
{
if (preclist->classArray[1]->system &&
(DefclassData(theEnv)->ClassDefaultsModeValue == CONVENIENCE_MODE))
{ abstract = false; }
else
{ abstract = preclist->classArray[1]->abstract; }
}
#if DEFRULE_CONSTRUCT
if (patternMatchSpecified == false)
{
if ((preclist->classArray[1]->system) &&
(! abstract) &&
(DefclassData(theEnv)->ClassDefaultsModeValue == CONVENIENCE_MODE))
{ reactive = true; }
else
{ reactive = preclist->classArray[1]->reactive; }
}
if (abstract && reactive)
{
PrintErrorID(theEnv,"CLASSPSR",1,false);
WriteString(theEnv,STDERR,"An abstract class cannot be reactive.\n");
DeletePackedClassLinks(theEnv,sclasses,true);
DeletePackedClassLinks(theEnv,preclist,true);
DeleteSlots(theEnv,slots);
return true;
}
#endif
if (ConstructData(theEnv)->CheckSyntaxMode)
{
DeletePackedClassLinks(theEnv,sclasses,true);
DeletePackedClassLinks(theEnv,preclist,true);
DeleteSlots(theEnv,slots);
return false;
}
cls = NewClass(theEnv,cname);
cls->abstract = abstract;
#if DEFRULE_CONSTRUCT
cls->reactive = reactive;
#endif
cls->directSuperclasses.classCount = sclasses->classCount;
cls->directSuperclasses.classArray = sclasses->classArray;
preclist->classArray[0] = cls;
cls->allSuperclasses.classCount = preclist->classCount;
cls->allSuperclasses.classArray = preclist->classArray;
rtn_struct(theEnv,packedClassLinks,sclasses);
rtn_struct(theEnv,packedClassLinks,preclist);
if (slots != NULL)
PackSlots(theEnv,cls,slots);
AddClass(theEnv,cls);
return false;
}
static bool ValidClassName(
Environment *theEnv,
const char *theClassName,
Defclass **theDefclass)
{
*theDefclass = FindDefclassInModule(theEnv,theClassName);
if (*theDefclass != NULL)
{
if ((*theDefclass)->system)
{
PrintErrorID(theEnv,"CLASSPSR",2,false);
WriteString(theEnv,STDERR,"Cannot redefine a predefined system class.\n");
return false;
}
if ((DefclassIsDeletable(*theDefclass) == false) &&
(! ConstructData(theEnv)->CheckSyntaxMode))
{
PrintErrorID(theEnv,"CLASSPSR",3,false);
WriteString(theEnv,STDERR,"Class '");
WriteString(theEnv,STDERR,DefclassName(*theDefclass));
WriteString(theEnv,STDERR,"' cannot be redefined while ");
WriteString(theEnv,STDERR,"outstanding references to it still exist.\n");
return false;
}
}
return true;
}
static bool ParseSimpleQualifier(
Environment *theEnv,
const char *readSource,
const char *classQualifier,
const char *clearRelation,
const char *setRelation,
bool *alreadyTestedFlag,
bool *binaryFlag)
{
if (*alreadyTestedFlag)
{
PrintErrorID(theEnv,"CLASSPSR",4,false);
WriteString(theEnv,STDERR,"The '");
WriteString(theEnv,STDERR,classQualifier);
WriteString(theEnv,STDERR,"' class attribute is already specified.\n");
return false;
}
SavePPBuffer(theEnv," ");
GetToken(theEnv,readSource,&DefclassData(theEnv)->ObjectParseToken);
if (DefclassData(theEnv)->ObjectParseToken.tknType != SYMBOL_TOKEN)
goto ParseSimpleQualifierError;
if (strcmp(DefclassData(theEnv)->ObjectParseToken.lexemeValue->contents,setRelation) == 0)
*binaryFlag = true;
else if (strcmp(DefclassData(theEnv)->ObjectParseToken.lexemeValue->contents,clearRelation) == 0)
*binaryFlag = false;
else
goto ParseSimpleQualifierError;
GetToken(theEnv,readSource,&DefclassData(theEnv)->ObjectParseToken);
if (DefclassData(theEnv)->ObjectParseToken.tknType != RIGHT_PARENTHESIS_TOKEN)
goto ParseSimpleQualifierError;
*alreadyTestedFlag = true;
return true;
ParseSimpleQualifierError:
SyntaxErrorMessage(theEnv,"defclass");
return false;
}
static bool ReadUntilClosingParen(
Environment *theEnv,
const char *readSource,
struct token *inputToken)
{
int cnt = 1;
bool lparen_read = false;
do
{
if (lparen_read == false)
SavePPBuffer(theEnv," ");
GetToken(theEnv,readSource,inputToken);
if (inputToken->tknType == STOP_TOKEN)
{
SyntaxErrorMessage(theEnv,"message-handler declaration");
return false;
}
else if (inputToken->tknType == LEFT_PARENTHESIS_TOKEN)
{
lparen_read = true;
cnt++;
}
else if (inputToken->tknType == RIGHT_PARENTHESIS_TOKEN)
{
cnt--;
if (lparen_read == false)
{
PPBackup(theEnv);
PPBackup(theEnv);
SavePPBuffer(theEnv,")");
}
lparen_read = false;
}
else
lparen_read = false;
}
while (cnt > 0);
return true;
}
static void AddClass(
Environment *theEnv,
Defclass *cls)
{
Defclass *ctmp;
#if DEBUGGING_FUNCTIONS
bool oldTraceInstances = false,
oldTraceSlots = false;
#endif
cls->hashTableIndex = HashClass(GetDefclassNamePointer(cls));
ctmp = FindDefclassInModule(theEnv,DefclassName(cls));
if (ctmp != NULL)
{
#if DEBUGGING_FUNCTIONS
oldTraceInstances = ctmp->traceInstances;
oldTraceSlots = ctmp->traceSlots;
#endif
DeleteClassUAG(theEnv,ctmp);
}
PutClassInTable(theEnv,cls);
BuildSubclassLinks(theEnv,cls);
InstallClass(theEnv,cls,true);
AddConstructToModule(&cls->header);
FormInstanceTemplate(theEnv,cls);
FormSlotNameMap(theEnv,cls);
AssignClassID(theEnv,cls);
#if DEBUGGING_FUNCTIONS
if (cls->abstract)
{
cls->traceInstances = false;
cls->traceSlots = false;
}
else
{
if (oldTraceInstances)
cls->traceInstances = true;
if (oldTraceSlots)
cls->traceSlots = true;
}
#endif
#if DEBUGGING_FUNCTIONS
if (GetConserveMemory(theEnv) == false)
SetDefclassPPForm(theEnv,cls,CopyPPBuffer(theEnv));
#endif
#if DEFMODULE_CONSTRUCT
cls->scopeMap = (CLIPSBitMap *) CreateClassScopeMap(theEnv,cls);
#endif
CreatePublicSlotMessageHandlers(theEnv,cls);
}
static void BuildSubclassLinks(
Environment *theEnv,
Defclass *cls)
{
unsigned long i;
for (i = 0 ; i < cls->directSuperclasses.classCount ; i++)
AddClassLink(theEnv,&cls->directSuperclasses.classArray[i]->directSubclasses,cls,true,0);
}
static void FormInstanceTemplate(
Environment *theEnv,
Defclass *cls)
{
TEMP_SLOT_LINK *islots = NULL,*stmp;
unsigned short scnt = 0;
unsigned long i;
islots = MergeSlots(theEnv,islots,cls,&scnt,DIRECT);
for (i = 1 ; i < cls->allSuperclasses.classCount ; i++)
islots = MergeSlots(theEnv,islots,cls->allSuperclasses.classArray[i],&scnt,INHERIT);
cls->instanceSlotCount = scnt;
cls->localInstanceSlotCount = 0;
if (scnt > 0)
cls->instanceTemplate = (SlotDescriptor **) gm2(theEnv,(scnt * sizeof(SlotDescriptor *)));
for (i = 0 ; i < scnt ; i++)
{
stmp = islots;
islots = islots->nxt;
cls->instanceTemplate[i] = stmp->desc;
if (stmp->desc->shared == 0)
cls->localInstanceSlotCount++;
rtn_struct(theEnv,tempSlotLink,stmp);
}
}
static void FormSlotNameMap(
Environment *theEnv,
Defclass *cls)
{
unsigned i;
cls->maxSlotNameID = 0;
cls->slotNameMap = NULL;
if (cls->instanceSlotCount == 0)
return;
for (i = 0 ; i < cls->instanceSlotCount ; i++)
if (cls->instanceTemplate[i]->slotName->id > cls->maxSlotNameID)
cls->maxSlotNameID = cls->instanceTemplate[i]->slotName->id;
cls->slotNameMap = (unsigned *) gm2(theEnv,(sizeof(unsigned) * (cls->maxSlotNameID + 1)));
for (i = 0 ; i <= cls->maxSlotNameID ; i++)
cls->slotNameMap[i] = 0;
for (i = 0 ; i < cls->instanceSlotCount ; i++)
cls->slotNameMap[cls->instanceTemplate[i]->slotName->id] = i + 1;
}
static TEMP_SLOT_LINK *MergeSlots(
Environment *theEnv,
TEMP_SLOT_LINK *old,
Defclass *cls,
unsigned short *scnt,
unsigned short src)
{
TEMP_SLOT_LINK *cur,*tmp;
unsigned int i;
SlotDescriptor *newSlot;
for (i = cls->slotCount; i > 0 ; i--)
{
newSlot = &cls->slots[i-1];
if ((newSlot->noInherit == 0) ? true : (src == DIRECT))
{
cur = old;
while ((cur != NULL) ? (newSlot->slotName != cur->desc->slotName) : false)
cur = cur->nxt;
if (cur == NULL)
{
tmp = get_struct(theEnv,tempSlotLink);
tmp->desc = newSlot;
tmp->nxt = old;
old = tmp;
(*scnt)++;
}
}
}
return old;
}
static void PackSlots(
Environment *theEnv,
Defclass *cls,
TEMP_SLOT_LINK *slots)
{
TEMP_SLOT_LINK *stmp,*sprv;
long i;
stmp = slots;
while (stmp != NULL)
{
stmp->desc->cls = cls;
cls->slotCount++;
stmp = stmp->nxt;
}
cls->slots = (SlotDescriptor *) gm2(theEnv,(sizeof(SlotDescriptor) * cls->slotCount));
stmp = slots;
for (i = 0 ; i < cls->slotCount ; i++)
{
sprv = stmp;
stmp = stmp->nxt;
GenCopyMemory(SlotDescriptor,1,&(cls->slots[i]),sprv->desc);
cls->slots[i].sharedValue.desc = &(cls->slots[i]);
cls->slots[i].sharedValue.value = NULL;
rtn_struct(theEnv,slotDescriptor,sprv->desc);
rtn_struct(theEnv,tempSlotLink,sprv);
}
}
static void CreatePublicSlotMessageHandlers(
Environment *theEnv,
Defclass *theDefclass)
{
long i;
SlotDescriptor *sd;
for (i = 0 ; i < theDefclass->slotCount ; i++)
{
sd = &theDefclass->slots[i];
CreateGetAndPutHandlers(theEnv,sd);
}
for (i = 0 ; i < theDefclass->handlerCount ; i++)
theDefclass->handlers[i].system = true;
}
#endif
#if DEFMODULE_CONSTRUCT && OBJECT_SYSTEM
void *CreateClassScopeMap(
Environment *theEnv,
Defclass *theDefclass)
{
unsigned short scopeMapSize;
char *scopeMap;
const char *className;
Defmodule *matchModule, *theModule;
unsigned long moduleID;
unsigned int count;
void *theBitMap;
className = theDefclass->header.name->contents;
matchModule = theDefclass->header.whichModule->theModule;
scopeMapSize = (sizeof(char) * ((GetNumberOfDefmodules(theEnv) / BITS_PER_BYTE) + 1));
scopeMap = (char *) gm2(theEnv,scopeMapSize);
ClearBitString(scopeMap,scopeMapSize);
SaveCurrentModule(theEnv);
for (theModule = GetNextDefmodule(theEnv,NULL) ;
theModule != NULL ;
theModule = GetNextDefmodule(theEnv,theModule))
{
SetCurrentModule(theEnv,theModule);
moduleID = theModule->header.bsaveID;
if (FindImportedConstruct(theEnv,"defclass",matchModule,
className,&count,true,NULL) != NULL)
SetBitMap(scopeMap,moduleID);
}
RestoreCurrentModule(theEnv);
theBitMap = (CLIPSBitMap *) AddBitMap(theEnv,scopeMap,scopeMapSize);
IncrementBitMapCount(theBitMap);
rm(theEnv,scopeMap,scopeMapSize);
return(theBitMap);
}
#endif