picoc/variable.c

365 lines
14 KiB
C
Raw Normal View History

/* picoc variable storage. This provides ways of defining and accessing
* variables */
#include "interpreter.h"
/* maximum size of a value to temporarily copy while we create a variable */
#define MAX_TMP_COPY_BUF 256
/* initialise the variable system */
void VariableInit(Picoc *pc)
{
TableInitTable(&(pc->GlobalTable), &(pc->GlobalHashTable)[0], GLOBAL_TABLE_SIZE, TRUE);
TableInitTable(&pc->StringLiteralTable, &pc->StringLiteralHashTable[0], STRING_LITERAL_TABLE_SIZE, TRUE);
pc->TopStackFrame = NULL;
}
/* deallocate the contents of a variable */
void VariableFree(Picoc *pc, struct Value *Val)
{
if (Val->ValOnHeap || Val->AnyValOnHeap)
{
/* free function bodies */
if (Val->Typ == &pc->FunctionType && Val->Val->FuncDef.Intrinsic == NULL && Val->Val->FuncDef.Body.Pos != NULL)
HeapFreeMem(pc, (void *)Val->Val->FuncDef.Body.Pos);
/* free macro bodies */
if (Val->Typ == &pc->MacroType)
HeapFreeMem(pc, (void *)Val->Val->MacroDef.Body.Pos);
/* free the AnyValue */
if (Val->AnyValOnHeap)
HeapFreeMem(pc, Val->Val);
}
/* free the value */
if (Val->ValOnHeap)
HeapFreeMem(pc, Val);
}
/* deallocate the global table and the string literal table */
void VariableTableCleanup(Picoc *pc, struct Table *HashTable)
{
struct TableEntry *Entry;
struct TableEntry *NextEntry;
int Count;
for (Count = 0; Count < HashTable->Size; Count++)
{
for (Entry = HashTable->HashTable[Count]; Entry != NULL; Entry = NextEntry)
{
NextEntry = Entry->Next;
VariableFree(pc, Entry->p.v.Val);
/* free the hash table entry */
HeapFreeMem(pc, Entry);
}
}
}
void VariableCleanup(Picoc *pc)
{
VariableTableCleanup(pc, &pc->GlobalTable);
VariableTableCleanup(pc, &pc->StringLiteralTable);
}
/* allocate some memory, either on the heap or the stack and check if we've run out */
void *VariableAlloc(Picoc *pc, struct ParseState *Parser, int Size, int OnHeap)
{
void *NewValue;
if (OnHeap)
NewValue = HeapAllocMem(pc, Size);
else
NewValue = HeapAllocStack(pc, Size);
if (NewValue == NULL)
ProgramFail(Parser, "out of memory");
#ifdef DEBUG_HEAP
if (!OnHeap)
printf("pushing %d at 0x%lx\n", Size, (unsigned long)NewValue);
#endif
return NewValue;
}
/* allocate a value either on the heap or the stack using space dependent on what type we want */
struct Value *VariableAllocValueAndData(Picoc *pc, struct ParseState *Parser, int DataSize, int IsLValue, struct Value *LValueFrom, int OnHeap)
{
struct Value *NewValue = VariableAlloc(pc, Parser, MEM_ALIGN(sizeof(struct Value)) + DataSize, OnHeap);
NewValue->Val = (union AnyValue *)((char *)NewValue + MEM_ALIGN(sizeof(struct Value)));
NewValue->ValOnHeap = OnHeap;
NewValue->AnyValOnHeap = FALSE;
NewValue->ValOnStack = !OnHeap;
NewValue->IsLValue = IsLValue;
NewValue->LValueFrom = LValueFrom;
return NewValue;
}
/* allocate a value given its type */
struct Value *VariableAllocValueFromType(Picoc *pc, struct ParseState *Parser, struct ValueType *Typ, int IsLValue, struct Value *LValueFrom, int OnHeap)
{
int Size = TypeSize(Typ, Typ->ArraySize, FALSE);
struct Value *NewValue = VariableAllocValueAndData(pc, Parser, Size, IsLValue, LValueFrom, OnHeap);
assert(Size >= 0 || Typ == &pc->VoidType);
NewValue->Typ = Typ;
return NewValue;
}
/* allocate a value either on the heap or the stack and copy its value. handles overlapping data */
struct Value *VariableAllocValueAndCopy(Picoc *pc, struct ParseState *Parser, struct Value *FromValue, int OnHeap)
{
struct ValueType *DType = FromValue->Typ;
struct Value *NewValue;
char TmpBuf[MAX_TMP_COPY_BUF];
int CopySize = TypeSizeValue(FromValue, TRUE);
assert(CopySize <= MAX_TMP_COPY_BUF);
memcpy((void *)&TmpBuf[0], (void *)FromValue->Val, CopySize);
NewValue = VariableAllocValueAndData(pc, Parser, CopySize, FromValue->IsLValue, FromValue->LValueFrom, OnHeap);
NewValue->Typ = DType;
memcpy((void *)NewValue->Val, (void *)&TmpBuf[0], CopySize);
return NewValue;
}
/* allocate a value either on the heap or the stack from an existing AnyValue and type */
struct Value *VariableAllocValueFromExistingData(struct ParseState *Parser, struct ValueType *Typ, union AnyValue *FromValue, int IsLValue, struct Value *LValueFrom)
{
struct Value *NewValue = VariableAlloc(Parser->pc, Parser, sizeof(struct Value), FALSE);
NewValue->Typ = Typ;
NewValue->Val = FromValue;
NewValue->ValOnHeap = FALSE;
NewValue->AnyValOnHeap = FALSE;
NewValue->ValOnStack = FALSE;
NewValue->IsLValue = IsLValue;
NewValue->LValueFrom = LValueFrom;
return NewValue;
}
/* allocate a value either on the heap or the stack from an existing Value, sharing the value */
struct Value *VariableAllocValueShared(struct ParseState *Parser, struct Value *FromValue)
{
return VariableAllocValueFromExistingData(Parser, FromValue->Typ, FromValue->Val, FromValue->IsLValue, FromValue->IsLValue ? FromValue : NULL);
}
/* reallocate a variable so its data has a new size */
void VariableRealloc(struct ParseState *Parser, struct Value *FromValue, int NewSize)
{
if (FromValue->AnyValOnHeap)
HeapFreeMem(Parser->pc, FromValue->Val);
FromValue->Val = VariableAlloc(Parser->pc, Parser, NewSize, TRUE);
FromValue->AnyValOnHeap = TRUE;
}
/* define a variable. Ident must be registered */
struct Value *VariableDefine(Picoc *pc, struct ParseState *Parser, char *Ident, struct Value *InitValue, struct ValueType *Typ, int MakeWritable)
{
struct Value *AssignValue;
if (InitValue != NULL)
AssignValue = VariableAllocValueAndCopy(pc, Parser, InitValue, pc->TopStackFrame == NULL);
else
AssignValue = VariableAllocValueFromType(pc, Parser, Typ, MakeWritable, NULL, pc->TopStackFrame == NULL);
AssignValue->IsLValue = MakeWritable;
if (!TableSet(pc, (pc->TopStackFrame == NULL) ? &(pc->GlobalTable) : &(pc->TopStackFrame)->LocalTable, Ident, AssignValue, Parser ? ((char *)Parser->FileName) : NULL, Parser ? Parser->Line : 0, Parser ? Parser->CharacterPos : 0))
ProgramFail(Parser, "'%s' is already defined", Ident);
return AssignValue;
}
/* define a variable. Ident must be registered. If it's a redefinition from the same declaration don't throw an error */
struct Value *VariableDefineButIgnoreIdentical(struct ParseState *Parser, char *Ident, struct ValueType *Typ, int IsStatic, int *FirstVisit)
{
Picoc *pc = Parser->pc;
struct Value *ExistingValue;
const char *DeclFileName;
int DeclLine;
int DeclColumn;
/* is the type a forward declaration? */
if (TypeIsForwardDeclared(Parser, Typ))
ProgramFail(Parser, "type '%t' isn't defined", Typ);
if (IsStatic)
{
char MangledName[LINEBUFFER_MAX];
char *MNPos = &MangledName[0];
char *MNEnd = &MangledName[LINEBUFFER_MAX-1];
const char *RegisteredMangledName;
/* make the mangled static name (avoiding using sprintf() to minimise library impact) */
memset((void *)&MangledName, '\0', sizeof(MangledName));
*MNPos++ = '/';
strncpy(MNPos, (char *)Parser->FileName, MNEnd - MNPos);
MNPos += strlen(MNPos);
if (pc->TopStackFrame != NULL)
{
/* we're inside a function */
if (MNEnd - MNPos > 0) *MNPos++ = '/';
strncpy(MNPos, (char *)pc->TopStackFrame->FuncName, MNEnd - MNPos);
MNPos += strlen(MNPos);
}
if (MNEnd - MNPos > 0) *MNPos++ = '/';
strncpy(MNPos, Ident, MNEnd - MNPos);
RegisteredMangledName = TableStrRegister(pc, MangledName);
/* is this static already defined? */
if (!TableGet(&pc->GlobalTable, RegisteredMangledName, &ExistingValue, &DeclFileName, &DeclLine, &DeclColumn))
{
/* define the mangled-named static variable store in the global scope */
ExistingValue = VariableAllocValueFromType(Parser->pc, Parser, Typ, TRUE, NULL, TRUE);
TableSet(pc, &pc->GlobalTable, (char *)RegisteredMangledName, ExistingValue, (char *)Parser->FileName, Parser->Line, Parser->CharacterPos);
*FirstVisit = TRUE;
}
/* static variable exists in the global scope - now make a mirroring variable in our own scope with the short name */
VariableDefinePlatformVar(Parser->pc, Parser, Ident, ExistingValue->Typ, ExistingValue->Val, TRUE);
return ExistingValue;
}
else
{
if (Parser->Line != 0 && TableGet((pc->TopStackFrame == NULL) ? &pc->GlobalTable : &pc->TopStackFrame->LocalTable, Ident, &ExistingValue, &DeclFileName, &DeclLine, &DeclColumn)
&& DeclFileName == Parser->FileName && DeclLine == Parser->Line && DeclColumn == Parser->CharacterPos)
return ExistingValue;
else
return VariableDefine(Parser->pc, Parser, Ident, NULL, Typ, TRUE);
}
}
/* check if a variable with a given name is defined. Ident must be registered */
int VariableDefined(Picoc *pc, const char *Ident)
{
struct Value *FoundValue;
if (pc->TopStackFrame == NULL || !TableGet(&pc->TopStackFrame->LocalTable, Ident, &FoundValue, NULL, NULL, NULL))
{
if (!TableGet(&pc->GlobalTable, Ident, &FoundValue, NULL, NULL, NULL))
return FALSE;
}
return TRUE;
}
/* get the value of a variable. must be defined. Ident must be registered */
void VariableGet(Picoc *pc, struct ParseState *Parser, const char *Ident, struct Value **LVal)
{
if (pc->TopStackFrame == NULL || !TableGet(&pc->TopStackFrame->LocalTable, Ident, LVal, NULL, NULL, NULL))
{
if (!TableGet(&pc->GlobalTable, Ident, LVal, NULL, NULL, NULL))
ProgramFail(Parser, "'%s' is undefined", Ident);
}
}
/* define a global variable shared with a platform global. Ident will be registered */
void VariableDefinePlatformVar(Picoc *pc, struct ParseState *Parser, char *Ident, struct ValueType *Typ, union AnyValue *FromValue, int IsWritable)
{
struct Value *SomeValue = VariableAllocValueAndData(pc, NULL, 0, IsWritable, NULL, TRUE);
SomeValue->Typ = Typ;
SomeValue->Val = FromValue;
if (!TableSet(pc, (pc->TopStackFrame == NULL) ? &pc->GlobalTable : &pc->TopStackFrame->LocalTable, TableStrRegister(pc, Ident), SomeValue, Parser ? Parser->FileName : NULL, Parser ? Parser->Line : 0, Parser ? Parser->CharacterPos : 0))
ProgramFail(Parser, "'%s' is already defined", Ident);
}
/* free and/or pop the top value off the stack. Var must be the top value on the stack! */
void VariableStackPop(struct ParseState *Parser, struct Value *Var)
{
int Success;
#ifdef DEBUG_HEAP
if (Var->ValOnStack)
printf("popping %ld at 0x%lx\n", (unsigned long)(sizeof(struct Value) + TypeSizeValue(Var, FALSE)), (unsigned long)Var);
#endif
if (Var->ValOnHeap)
{
if (Var->Val != NULL)
HeapFreeMem(Parser->pc, Var->Val);
Success = HeapPopStack(Parser->pc, Var, sizeof(struct Value)); /* free from heap */
}
else if (Var->ValOnStack)
Success = HeapPopStack(Parser->pc, Var, sizeof(struct Value) + TypeSizeValue(Var, FALSE)); /* free from stack */
else
Success = HeapPopStack(Parser->pc, Var, sizeof(struct Value)); /* value isn't our problem */
if (!Success)
ProgramFail(Parser, "stack underrun");
}
/* add a stack frame when doing a function call */
void VariableStackFrameAdd(struct ParseState *Parser, const char *FuncName, int NumParams)
{
struct StackFrame *NewFrame;
HeapPushStackFrame(Parser->pc);
NewFrame = HeapAllocStack(Parser->pc, sizeof(struct StackFrame) + sizeof(struct Value *) * NumParams);
if (NewFrame == NULL)
ProgramFail(Parser, "out of memory");
ParserCopy(&NewFrame->ReturnParser, Parser);
NewFrame->FuncName = FuncName;
NewFrame->Parameter = (NumParams > 0) ? ((void *)((char *)NewFrame + sizeof(struct StackFrame))) : NULL;
TableInitTable(&NewFrame->LocalTable, &NewFrame->LocalHashTable[0], LOCAL_TABLE_SIZE, FALSE);
NewFrame->PreviousStackFrame = Parser->pc->TopStackFrame;
Parser->pc->TopStackFrame = NewFrame;
}
/* remove a stack frame */
void VariableStackFramePop(struct ParseState *Parser)
{
if (Parser->pc->TopStackFrame == NULL)
ProgramFail(Parser, "stack is empty - can't go back");
ParserCopy(Parser, &Parser->pc->TopStackFrame->ReturnParser);
Parser->pc->TopStackFrame = Parser->pc->TopStackFrame->PreviousStackFrame;
HeapPopStackFrame(Parser->pc);
}
/* get a string literal. assumes that Ident is already registered. NULL if not found */
struct Value *VariableStringLiteralGet(Picoc *pc, char *Ident)
{
struct Value *LVal = NULL;
if (TableGet(&pc->StringLiteralTable, Ident, &LVal, NULL, NULL, NULL))
return LVal;
else
return NULL;
}
/* define a string literal. assumes that Ident is already registered */
void VariableStringLiteralDefine(Picoc *pc, char *Ident, struct Value *Val)
{
TableSet(pc, &pc->StringLiteralTable, Ident, Val, NULL, 0, 0);
}
/* check a pointer for validity and dereference it for use */
void *VariableDereferencePointer(struct ParseState *Parser, struct Value *PointerValue, struct Value **DerefVal, int *DerefOffset, struct ValueType **DerefType, int *DerefIsLValue)
{
if (DerefVal != NULL)
*DerefVal = NULL;
if (DerefType != NULL)
*DerefType = PointerValue->Typ->FromType;
if (DerefOffset != NULL)
*DerefOffset = 0;
if (DerefIsLValue != NULL)
*DerefIsLValue = TRUE;
return PointerValue->Val->Pointer;
}