bug fixes and getfield implementation beta

This commit is contained in:
2026-07-13 11:49:59 +10:00
parent 6929f055de
commit f2fbeb3a68
11 changed files with 371 additions and 23 deletions

View File

@@ -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.

View File

@@ -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);

View File

@@ -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 = &current->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: {

View File

@@ -1,4 +1,5 @@
#include "../../include/ground.h"
#include <string.h>
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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -8,5 +8,6 @@ void _GroundFreeObject(GroundObject* in) {
HASH_DEL(in->fields, s);
Ground.Free.Value(&s->value);
free(s);
}
}

View File

@@ -8,5 +8,6 @@ void _GroundFreeStruct(GroundStruct* in) {
HASH_DEL(in->fields, s);
Ground.Free.Value(&s->value);
free(s);
}
}

View File

@@ -1,4 +1,5 @@
#include "../../include/ground.h"
#include <uthash.h>
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;
}

View File

@@ -1,5 +1,6 @@
#include "../../include/ground.h"
#include <uthash.h>
#include <string.h>
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;
}

View File

@@ -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");
}
}