From f2fbeb3a68ef6712029f93c8fe9f660299a93cb9 Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Mon, 13 Jul 2026 11:49:59 +1000 Subject: [PATCH] bug fixes and getfield implementation beta --- README.md | 19 ++++ include/ground.h | 18 +++- src/Bytecode/Instruction/execute.c | 27 +++-- src/Copy/BytecodeValue.c | 158 ++++++++++++++++++++++++++++- src/Copy/String.c | 2 +- src/Free/BytecodeValue.c | 41 ++++++-- src/Free/Object.c | 1 + src/Free/Struct.c | 1 + src/New/Bytecode.c | 112 ++++++++++++++++++++ src/New/BytecodeValue.c | 12 ++- src/Program/preprocess.c | 3 +- 11 files changed, 371 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 4cde3f3..b9dfa28 100644 --- a/README.md +++ b/README.md @@ -53,3 +53,22 @@ Under the hood, Ground uses a bytecode format to store things. `extern` no longer loads a dynamic library from /usr/lib/ground, it is now used to detail a system shared library to open. Format: `extern "libName" "symbolName" !functionName -returnType -argType...` + +### `getfield`, `setfield`, `callmethod` + +Due to the new bytecode format, these instructions go from this format + +``` +getfield $parent &fields... &output +setfield &parent &fields... $input +callmethod &parent &fields... !functionName $args... &output +``` + +to this format: +``` +getfield $parent -type (&field -type)... &output +setfield &parent -type (&field -type)... $input +callmethod &parent -type (&field -type)... !functionName $args... &output +``` + +so offsets for instructions can be computed before runtime. diff --git a/include/ground.h b/include/ground.h index aaeafd7..8c41538 100644 --- a/include/ground.h +++ b/include/ground.h @@ -1,3 +1,13 @@ +/** + * @file ground.h + * @brief Defines the interface for the GroundVM. + * + * This header has two main sections: the types, and the interface. The types are the layout of the data that the GroundVM uses, whereas the interface declares all the functions which GroundVM provides for interfacing with Ground programs. The interface resides entirely in the struct _Ground, which has pointers to implementation functions. + * + * @author Maxwell Jeffress + * @date 2026-07-12 + */ + #ifndef GROUND_H #define GROUND_H @@ -394,10 +404,14 @@ struct _Ground { bool error; } Flags; + /** + * @brief Functions to create new instances of Ground-specific types. Use Ground.Free to free when no longer needed. + */ struct { - // Creates a new value of the specified type. - // Returns a GroundValue + /** + * @brief Creates a new GroundValue, which wraps the underlying value for the VM. Copies the input value. + */ struct { GroundValue (*Int) (GroundInt in); GroundValue (*Double) (GroundDouble in); diff --git a/src/Bytecode/Instruction/execute.c b/src/Bytecode/Instruction/execute.c index 52d0731..dc73901 100644 --- a/src/Bytecode/Instruction/execute.c +++ b/src/Bytecode/Instruction/execute.c @@ -713,6 +713,9 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns return CONTINUE; } } else { + for (GroundSize i = 0; i < function->closure->len; i++) { + Ground.Free.BytecodeValue(&function->closure->heap[i]); + } free(function->closure->heap); } @@ -724,11 +727,6 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; - } else { - // forget all values in previous closure - for (GroundSize i = 0; i < function->closure->len; i++) { - Ground.Free.BytecodeValue(&function->closure->heap[i]); - } } for (GroundSize i = 0; i < heap->len; i++) { @@ -961,6 +959,9 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns return CONTINUE; } } else { + for (GroundSize i = 0; i < function->closure->len; i++) { + Ground.Free.BytecodeValue(&function->closure->heap[i]); + } free(function->closure->heap); } @@ -972,11 +973,6 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; - } else { - // forget all values in previous closure - for (GroundSize i = 0; i < function->closure->len; i++) { - Ground.Free.BytecodeValue(&function->closure->heap[i]); - } } for (GroundSize i = 0; i < heap->len; i++) { @@ -1039,6 +1035,17 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns return CONTINUE; } GETFIELD: { + // args: [parent_offset, field0_offset, ..., fieldF_offset, output_offset] + GroundBytecodeValue* current = HEAP_GET(heap, instruction->args.at[0]); + + for (GroundSize i = 1; i < instruction->args.len - 1; i++) { + current = ¤t->as.Object.values[instruction->args.at[i]]; + } + + GroundBytecodeValue* output = HEAP_GET(heap, instruction->args.at[instruction->args.len - 1]); + Ground.Free.BytecodeValue(output); + *output = Ground.Copy.BytecodeValue(current); + return CONTINUE; } SETFIELD: { diff --git a/src/Copy/BytecodeValue.c b/src/Copy/BytecodeValue.c index 3cd2a09..d8f1dac 100644 --- a/src/Copy/BytecodeValue.c +++ b/src/Copy/BytecodeValue.c @@ -1,4 +1,5 @@ #include "../../include/ground.h" +#include GroundBytecodeValue _GroundCopyBytecodeValue(GroundBytecodeValue* value) { GroundBytecodeValue newValue = *value; @@ -7,6 +8,7 @@ GroundBytecodeValue _GroundCopyBytecodeValue(GroundBytecodeValue* value) { case GroundType_Double: case GroundType_Bool: case GroundType_Char: + case GroundType_CoreType: break; case GroundType_String: { @@ -14,7 +16,161 @@ GroundBytecodeValue _GroundCopyBytecodeValue(GroundBytecodeValue* value) { break; } - // TODO: Implement copying for everything else + case GroundType_Object: { + GroundBytecodeObject* src = &value->as.Object; + GroundBytecodeObject dst = { + .size = src->size, + .capacity = src->capacity, + }; + if (src->capacity > 0) { + dst.values = malloc(sizeof(GroundBytecodeValue) * src->capacity); + if (dst.values == NULL) { + Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue"); + Ground.Flags.error = true; + break; + } + for (GroundSize i = 0; i < src->size; i++) { + dst.values[i] = Ground.Copy.BytecodeValue(&src->values[i]); + } + } + newValue.as.Object = dst; + break; + } + + case GroundType_Struct: { + GroundBytecodeStruct* src = &value->as.Struct; + GroundBytecodeStruct dst = { + .size = src->size, + .capacity = src->capacity, + }; + if (src->capacity > 0) { + dst.values = malloc(sizeof(GroundBytecodeValue) * src->capacity); + if (dst.values == NULL) { + Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue"); + Ground.Flags.error = true; + break; + } + for (GroundSize i = 0; i < src->size; i++) { + dst.values[i] = Ground.Copy.BytecodeValue(&src->values[i]); + } + } + newValue.as.Struct = dst; + break; + } + + case GroundType_List: { + GroundBytecodeList* src = &value->as.List; + GroundBytecodeList dst = { + .count = src->count, + .capacity = src->capacity, + }; + if (src->capacity > 0) { + dst.at = malloc(sizeof(GroundBytecodeValue) * src->capacity); + if (dst.at == NULL) { + Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue"); + Ground.Flags.error = true; + break; + } + for (GroundSize i = 0; i < src->count; i++) { + dst.at[i] = Ground.Copy.BytecodeValue(&src->at[i]); + } + } + newValue.as.List = dst; + break; + } + + case GroundType_Function: { + GroundBytecodeFunction* src = &value->as.Function; + GroundBytecodeFunction dst = *src; + + if (src->args.at != NULL && src->args.count > 0) { + dst.args.at = malloc(sizeof(GroundFunctionArg) * src->args.count); + if (dst.args.at == NULL) { + Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue"); + Ground.Flags.error = true; + break; + } + memcpy(dst.args.at, src->args.at, sizeof(GroundFunctionArg) * src->args.count); + } else { + dst.args.at = NULL; + } + + if (!src->isNativeFunction && src->program.ground != NULL) { + dst.program.ground = malloc(sizeof(GroundBytecodeProgram)); + if (dst.program.ground == NULL) { + Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue"); + Ground.Flags.error = true; + free(dst.args.at); + break; + } + *dst.program.ground = *src->program.ground; + if (src->program.ground->at != NULL && src->program.ground->len > 0) { + dst.program.ground->at = malloc(sizeof(GroundBytecodeInstruction) * src->program.ground->len); + if (dst.program.ground->at == NULL) { + Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue"); + Ground.Flags.error = true; + free(dst.program.ground); + free(dst.args.at); + break; + } + for (GroundSize i = 0; i < src->program.ground->len; i++) { + dst.program.ground->at[i] = src->program.ground->at[i]; + if (src->program.ground->at[i].args.at != NULL && src->program.ground->at[i].args.len > 0) { + dst.program.ground->at[i].args.at = malloc(sizeof(GroundSize) * src->program.ground->at[i].args.len); + if (dst.program.ground->at[i].args.at == NULL) { + Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue"); + Ground.Flags.error = true; + break; + } + memcpy(dst.program.ground->at[i].args.at, src->program.ground->at[i].args.at, sizeof(GroundSize) * src->program.ground->at[i].args.len); + } + } + } + } + + if (src->closure != NULL) { + dst.closure = malloc(sizeof(GroundBytecodeHeap)); + if (dst.closure == NULL) { + Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue"); + Ground.Flags.error = true; + break; + } + dst.closure->capacity = src->closure->capacity; + dst.closure->len = src->closure->len; + if (src->closure->heap != NULL && src->closure->len > 0) { + dst.closure->heap = malloc(sizeof(GroundBytecodeValue) * src->closure->capacity); + if (dst.closure->heap == NULL) { + Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue"); + Ground.Flags.error = true; + free(dst.closure); + break; + } + for (GroundSize i = 0; i < src->closure->len; i++) { + if (src->closure->heap[i].type.type == GroundType_Function) { + dst.closure->heap[i] = src->closure->heap[i]; + } else { + dst.closure->heap[i] = Ground.Copy.BytecodeValue(&src->closure->heap[i]); + } + } + } else { + dst.closure->heap = NULL; + } + } + + if (src->isNativeFunction && src->returnType != NULL) { + dst.returnType = malloc(sizeof(GroundType)); + if (dst.returnType == NULL) { + Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue"); + Ground.Flags.error = true; + break; + } + *dst.returnType = *src->returnType; + } + + newValue.as.Function = dst; + break; + } + default: break; } diff --git a/src/Copy/String.c b/src/Copy/String.c index d42446a..8104f4a 100644 --- a/src/Copy/String.c +++ b/src/Copy/String.c @@ -5,7 +5,7 @@ GroundString _GroundCopyString(GroundString* in) { string.cstr = malloc(in->len + 1); if (string.cstr == NULL) { - Ground.Flags.error = false; + Ground.Flags.error = true; return string; } diff --git a/src/Free/BytecodeValue.c b/src/Free/BytecodeValue.c index 5cbb255..c8184cb 100644 --- a/src/Free/BytecodeValue.c +++ b/src/Free/BytecodeValue.c @@ -2,27 +2,56 @@ void _GroundFreeBytecodeValue(GroundBytecodeValue* in) { - // TODO: Implement all this - switch (in->type.type) { case GroundType_String: { Ground.Free.String(&in->as.String); break; } case GroundType_List: { - // Ground.Free.List(&in->as.List); + for (GroundSize i = 0; i < in->as.List.count; i++) { + Ground.Free.BytecodeValue(&in->as.List.at[i]); + } + free(in->as.List.at); break; } case GroundType_Function: { - // Ground.Free.Function(&in->as.Function); + GroundBytecodeFunction* fn = &in->as.Function; + if (fn->closure != NULL) { + for (GroundSize i = 0; i < fn->closure->len; i++) { + if (fn->closure->heap[i].type.type != GroundType_Function) { + Ground.Free.BytecodeValue(&fn->closure->heap[i]); + } + } + free(fn->closure->heap); + free(fn->closure); + } + free(fn->args.at); + if (!fn->isNativeFunction && fn->program.ground != NULL) { + for (GroundSize i = 0; i < fn->program.ground->len; i++) { + free(fn->program.ground->at[i].args.at); + } + free(fn->program.ground->at); + free(fn->program.ground); + } + if (fn->isNativeFunction && fn->returnType != NULL) { + free(fn->returnType); + } break; } case GroundType_Struct: { - // Ground.Free.Struct(&in->as.Struct); + GroundBytecodeStruct* gbs = &in->as.Struct; + for (GroundSize i = 0; i < gbs->size; i++) { + Ground.Free.BytecodeValue(&gbs->values[i]); + } + free(gbs->values); break; } case GroundType_Object: { - // Ground.Free.Object(&in->as.Object); + GroundBytecodeObject* gbo = &in->as.Object; + for (GroundSize i = 0; i < gbo->size; i++) { + Ground.Free.BytecodeValue(&gbo->values[i]); + } + free(gbo->values); break; } diff --git a/src/Free/Object.c b/src/Free/Object.c index 877a78e..0f42803 100644 --- a/src/Free/Object.c +++ b/src/Free/Object.c @@ -8,5 +8,6 @@ void _GroundFreeObject(GroundObject* in) { HASH_DEL(in->fields, s); Ground.Free.Value(&s->value); + free(s); } } diff --git a/src/Free/Struct.c b/src/Free/Struct.c index 5e6f197..34d6731 100644 --- a/src/Free/Struct.c +++ b/src/Free/Struct.c @@ -8,5 +8,6 @@ void _GroundFreeStruct(GroundStruct* in) { HASH_DEL(in->fields, s); Ground.Free.Value(&s->value); + free(s); } } diff --git a/src/New/Bytecode.c b/src/New/Bytecode.c index 2428128..0211ed3 100644 --- a/src/New/Bytecode.c +++ b/src/New/Bytecode.c @@ -1,4 +1,5 @@ #include "../../include/ground.h" +#include static inline void doLabels(GroundProgram* program, GroundState* state) { @@ -158,6 +159,117 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground continue; } case GroundInstruction_GETFIELD: { + // getfield $parent -type (&field -type)... &field &output + // The last field does not need a type annotation. + // Bytecode: parent_offset, field0_offset, ..., fieldF_offset, output_offset + GroundInstruction* inst = &gp->at[i]; + GroundSize gbiSize = 2 + ((inst->args.len - 2) / 2); + + GroundBytecodeInstruction newInst = { + .type = GroundInstruction_GETFIELD, + .args = { + .at = malloc(sizeof(GroundSize) * gbiSize), + .capacity = gbiSize, + .len = gbiSize + } + }; + + if (newInst.args.at == NULL) { + Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + + // Resolve the parent variable to a heap offset + GroundArg* arg = &inst->args.at[0]; + + GroundVariable* item = NULL; + HASH_FIND_STR(state->variables, arg->as.ref->string, item); + + if (item == NULL) { + item = malloc(sizeof(GroundVariable)); + if (item == NULL) { + Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + item->value = Ground.New.Value.Int(0); + strncpy(item->name, arg->as.ref->string, 2047); + item->_offset = size++; + HASH_ADD_STR(state->variables, name, item); + } + + arg->_offset = item->_offset; + newInst.args.at[0] = arg->_offset; + + // Get the type of the parent (type annotation is at args[1]) + GroundVariable* var = NULL; + HASH_FIND_STR(state->variables, inst->args.at[1].as.ref->string, var); + if (var == NULL) { + Ground.Log.Error("unknown struct name in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + if (var->value.type.type != GroundType_Struct) { + Ground.Log.Error("known name is not a struct in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + + GroundStruct* currentType = &var->value.as.Struct; + + // Resolve each field's offset within its containing struct + for (GroundSize j = 0; j < gbiSize - 2; j++) { + GroundArg* arg = &inst->args.at[2 + (j * 2)]; + + GroundObjectField* field; + HASH_FIND_STR(currentType->fields, arg->as.ref->string, field); + if (field == NULL) { + Ground.Log.Error("unknown field in struct in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + newInst.args.at[1 + j] = field->offset; + + // Resolve the type for the next field (not needed for the last field) + if (j < gbiSize - 3) { + GroundVariable* typeVar = NULL; + HASH_FIND_STR(state->variables, inst->args.at[3 + j * 2].as.ref->string, typeVar); + if (typeVar == NULL) { + Ground.Log.Error("unknown struct type for field in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + if (typeVar->value.type.type != GroundType_Struct) { + Ground.Log.Error("field type is not a struct in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + currentType = &typeVar->value.as.Struct; + } + } + + // Resolve the output variable + GroundArg* outputArg = &inst->args.at[inst->args.len - 1]; + GroundVariable* outputItem = NULL; + HASH_FIND_STR(state->variables, outputArg->as.ref->string, outputItem); + if (outputItem == NULL) { + outputItem = malloc(sizeof(GroundVariable)); + if (outputItem == NULL) { + Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + outputItem->value = Ground.New.Value.Int(0); + strncpy(outputItem->name, outputArg->as.ref->string, 2047); + outputItem->_offset = size++; + HASH_ADD_STR(state->variables, name, outputItem); + } + outputArg->_offset = outputItem->_offset; + newInst.args.at[gbiSize - 1] = outputArg->_offset; + + gbp->at[i] = newInst; + gbp->len++; continue; } diff --git a/src/New/BytecodeValue.c b/src/New/BytecodeValue.c index cd70002..a5cbca0 100644 --- a/src/New/BytecodeValue.c +++ b/src/New/BytecodeValue.c @@ -1,5 +1,6 @@ #include "../../include/ground.h" #include +#include static inline GroundBytecodeFunction doFunction(GroundFunction* function) { GroundBytecodeFunction bf = { @@ -47,6 +48,9 @@ static inline GroundBytecodeFunction doFunction(GroundFunction* function) { // Now convert the program to bytecode GroundBytecode bytecode = Ground.New.Bytecode(function->program.ground, &state); *bf.program.ground = bytecode.program; + for (GroundSize i = 0; i < bytecode.heap.len; i++) { + Ground.Free.BytecodeValue(&bytecode.heap.heap[i]); + } free(bytecode.heap.heap); return bf; @@ -65,10 +69,12 @@ static inline GroundBytecodeStruct doStruct(GroundStruct* gs) { if (gbs.size >= gbs.capacity) { GroundBytecodeValue* tmp = malloc(sizeof(GroundBytecodeValue) * gbs.capacity * 2); if (tmp == NULL) { - Ground.Log.Error("malloc failedd in Ground.New.BytecodeValue -> doStruct"); + Ground.Log.Error("malloc failed in Ground.New.BytecodeValue -> doStruct"); Ground.Flags.error = true; return gbs; } + memcpy(tmp, gbs.values, sizeof(GroundBytecodeValue) * gbs.size); + free(gbs.values); gbs.values = tmp; gbs.capacity *= 2; } @@ -92,10 +98,12 @@ static inline GroundBytecodeObject doObject(GroundObject* go) { if (gbo.size >= gbo.capacity) { GroundBytecodeValue* tmp = malloc(sizeof(GroundBytecodeValue) * gbo.capacity * 2); if (tmp == NULL) { - Ground.Log.Error("malloc failedd in Ground.New.BytecodeValue -> doStruct"); + Ground.Log.Error("malloc failed in Ground.New.BytecodeValue -> doObject"); Ground.Flags.error = true; return gbo; } + memcpy(tmp, gbo.values, sizeof(GroundBytecodeValue) * gbo.size); + free(gbo.values); gbo.values = tmp; gbo.capacity *= 2; } diff --git a/src/Program/preprocess.c b/src/Program/preprocess.c index 7f08d61..73a9fd3 100644 --- a/src/Program/preprocess.c +++ b/src/Program/preprocess.c @@ -231,9 +231,10 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt } case GroundInstruction_ENDSTRUCT: { parsing = false; + break; } default: { - Ground.Log.Error("invalid instruction inside struct in "); + Ground.Log.Error("invalid instruction inside struct in Ground.Program.Preprocess -> doStruct"); } }