Start working on CALLMETHOD
This commit is contained in:
@@ -127,6 +127,7 @@ struct GroundError {
|
|||||||
// --- Program structure types definitions ---
|
// --- Program structure types definitions ---
|
||||||
|
|
||||||
enum GroundTypeType {
|
enum GroundTypeType {
|
||||||
|
GroundType_Undefined,
|
||||||
GroundType_Int, GroundType_Double, GroundType_Char, GroundType_Bool,
|
GroundType_Int, GroundType_Double, GroundType_Char, GroundType_Bool,
|
||||||
GroundType_String, GroundType_List,
|
GroundType_String, GroundType_List,
|
||||||
GroundType_Function, GroundType_Struct, GroundType_Object,
|
GroundType_Function, GroundType_Struct, GroundType_Object,
|
||||||
|
|||||||
@@ -888,6 +888,137 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
|
|||||||
return CONTINUE;
|
return CONTINUE;
|
||||||
}
|
}
|
||||||
CALLMETHOD: {
|
CALLMETHOD: {
|
||||||
|
// bytecode: object_offset, field0_offset, ..., fieldN_offset, method_offset, arg0_offset, ..., argM_offset, output_offset
|
||||||
|
GroundSize len = instruction->args.len;
|
||||||
|
|
||||||
|
// Navigate the object through field path to find the containing object and method
|
||||||
|
GroundBytecodeValue* current = HEAP_GET(heap, instruction->args.at[0]);
|
||||||
|
|
||||||
|
// Walk the field path to find the containing object
|
||||||
|
GroundSize fieldPairsCount = 0;
|
||||||
|
for (GroundSize fi = 1; fi < len - 1; fi++) {
|
||||||
|
if (current->type.type != GroundType_Object) break;
|
||||||
|
GroundBytecodeValue* next = ¤t->as.Object.values[instruction->args.at[fi]];
|
||||||
|
if (next->type.type == GroundType_Function) {
|
||||||
|
// This is the method
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current = next;
|
||||||
|
fieldPairsCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The method is at index 1 + fieldPairsCount
|
||||||
|
GroundSize methodFieldIdx = 1 + fieldPairsCount;
|
||||||
|
GroundSize methodArgCount = len - methodFieldIdx - 2; // subtract method and output
|
||||||
|
|
||||||
|
if (current->type.type != GroundType_Object) {
|
||||||
|
Ground.Log.Error("CALLMETHOD on non-object value in Ground.Bytecode.Instruction.execute");
|
||||||
|
Ground.Flags.error = true;
|
||||||
|
return CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GroundBytecodeFunction* method = ¤t->as.Object.values[instruction->args.at[methodFieldIdx]].as.Function;
|
||||||
|
|
||||||
|
if (method->isNativeFunction) {
|
||||||
|
// Native method handling (same as CALL native path but with self appended)
|
||||||
|
ffi_type* argTypes[methodArgCount + 1];
|
||||||
|
void* args[methodArgCount + 1];
|
||||||
|
|
||||||
|
// Self is the first arg for native methods
|
||||||
|
argTypes[0] = ffiTypeFromGroundType(&(GroundType){.type = GroundType_Object});
|
||||||
|
args[0] = current;
|
||||||
|
|
||||||
|
for (GroundSize i = 0; i < methodArgCount; i++) {
|
||||||
|
GroundBytecodeValue* val = HEAP_GET(heap, instruction->args.at[methodFieldIdx + 1 + i]);
|
||||||
|
argTypes[i + 1] = ffiTypeFromGroundType(&val->type);
|
||||||
|
switch (val->type.type) {
|
||||||
|
case GroundType_Int: args[i + 1] = &val->as.Int; break;
|
||||||
|
case GroundType_Double: args[i + 1] = &val->as.Double; break;
|
||||||
|
case GroundType_Char: args[i + 1] = &val->as.Char; break;
|
||||||
|
case GroundType_Bool: args[i + 1] = &val->as.Bool; break;
|
||||||
|
case GroundType_String: args[i + 1] = &val->as.String.cstr; break;
|
||||||
|
default: args[i + 1] = val; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ffi_cif cif;
|
||||||
|
ffi_status status = ffi_prep_cif(
|
||||||
|
&cif, FFI_DEFAULT_ABI, methodArgCount + 1,
|
||||||
|
ffiTypeFromGroundType(method->returnType), argTypes
|
||||||
|
);
|
||||||
|
|
||||||
|
if (status != FFI_OK) {
|
||||||
|
Ground.Log.Error("ffi CIF preparation failed in Ground.Bytecode.Instruction.execute");
|
||||||
|
Ground.Flags.error = true;
|
||||||
|
return CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
union {
|
||||||
|
GroundInt Int;
|
||||||
|
GroundDouble Double;
|
||||||
|
GroundChar Char;
|
||||||
|
GroundBool Bool;
|
||||||
|
char* String;
|
||||||
|
} result;
|
||||||
|
|
||||||
|
ffi_call(&cif, method->program.native, &result, args);
|
||||||
|
|
||||||
|
switch (method->returnType->type) {
|
||||||
|
case GroundType_Int: HEAP_SET(heap, instruction->args.at[len - 1], Ground.New.BytecodeValue(Ground.New.Value.Int(result.Int))); break;
|
||||||
|
case GroundType_Char: HEAP_SET(heap, instruction->args.at[len - 1], Ground.New.BytecodeValue(Ground.New.Value.Char(result.Char))); break;
|
||||||
|
case GroundType_Double: HEAP_SET(heap, instruction->args.at[len - 1], Ground.New.BytecodeValue(Ground.New.Value.Double(result.Double))); break;
|
||||||
|
case GroundType_Bool: HEAP_SET(heap, instruction->args.at[len - 1], Ground.New.BytecodeValue(Ground.New.Value.Bool(result.Bool))); break;
|
||||||
|
case GroundType_String: HEAP_SET(heap, instruction->args.at[len - 1], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(result.String)))); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method->closure == NULL) {
|
||||||
|
Ground.Log.Error("unexpected NULL closure in Ground.Bytecode.Instruction.execute");
|
||||||
|
Ground.Flags.error = true;
|
||||||
|
return CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total args for method: methodArgCount explicit + 1 self
|
||||||
|
GroundSize totalArgs = methodArgCount + 1;
|
||||||
|
GroundSize heapSize = totalArgs + method->closure->len;
|
||||||
|
|
||||||
|
GroundBytecodeHeap newHeap = { malloc(sizeof(GroundBytecodeValue) * heapSize), heapSize, heapSize };
|
||||||
|
if (newHeap.heap == NULL) {
|
||||||
|
Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute");
|
||||||
|
Ground.Flags.error = true;
|
||||||
|
return CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load explicit method arguments into the heap
|
||||||
|
for (GroundSize i = 0; i < methodArgCount; i++) {
|
||||||
|
newHeap.heap[i] = Ground.Copy.BytecodeValue(HEAP_GET(heap, instruction->args.at[methodFieldIdx + 1 + i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Self (the containing object) is the last argument
|
||||||
|
newHeap.heap[methodArgCount] = Ground.Copy.BytecodeValue(current);
|
||||||
|
|
||||||
|
// Copy closure variables after arguments
|
||||||
|
for (GroundSize i = totalArgs; i < newHeap.len; i++) {
|
||||||
|
newHeap.heap[i] = Ground.Copy.BytecodeValue(&method->closure->heap[i - totalArgs]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the method
|
||||||
|
GroundBytecodeValue result = Ground.Bytecode.Program.execute(method->program.ground, &newHeap);
|
||||||
|
|
||||||
|
GroundBytecodeValue resultCopy = Ground.Copy.BytecodeValue(&result);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
for (GroundSize i = 0; i < newHeap.len; i++) {
|
||||||
|
Ground.Free.BytecodeValue(&newHeap.heap[i]);
|
||||||
|
}
|
||||||
|
free(newHeap.heap);
|
||||||
|
|
||||||
|
// Store the result
|
||||||
|
HEAP_SET(heap, instruction->args.at[len - 1], resultCopy);
|
||||||
|
|
||||||
return CONTINUE;
|
return CONTINUE;
|
||||||
}
|
}
|
||||||
STRUCT: {
|
STRUCT: {
|
||||||
|
|||||||
@@ -457,6 +457,158 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
case GroundInstruction_CALLMETHOD: {
|
case GroundInstruction_CALLMETHOD: {
|
||||||
|
// callmethod &object -type (&field -type)... !methodName $args... &output
|
||||||
|
// Bytecode: object_offset, field0_offset, ..., fieldN_offset, method_offset, arg0_offset, ..., argM_offset, output_offset
|
||||||
|
GroundInstruction* inst = &gp->at[i];
|
||||||
|
|
||||||
|
// Find the methodName (FunctionRef) to determine the field path length
|
||||||
|
GroundSize methodIdx = 0;
|
||||||
|
for (GroundSize j = 0; j < inst->args.len; j++) {
|
||||||
|
if (inst->args.at[j].type == GroundArg_FunctionRef) {
|
||||||
|
methodIdx = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GroundSize fieldPairs = (methodIdx - 2) / 2;
|
||||||
|
GroundSize methodArgs = inst->args.len - methodIdx - 2;
|
||||||
|
GroundSize gbiSize = 1 + fieldPairs + 1 + methodArgs + 1;
|
||||||
|
|
||||||
|
GroundBytecodeInstruction newInst = {
|
||||||
|
.type = GroundInstruction_CALLMETHOD,
|
||||||
|
.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 object 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 object (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 || var->value.type.type != GroundType_Struct) {
|
||||||
|
Ground.Log.Error("unknown struct name 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 < fieldPairs; j++) {
|
||||||
|
GroundArg* fieldArg = &inst->args.at[2 + (j * 2)];
|
||||||
|
GroundObjectField* field;
|
||||||
|
HASH_FIND_STR(currentType->fields, fieldArg->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
|
||||||
|
GroundVariable* typeVar = NULL;
|
||||||
|
HASH_FIND_STR(state->variables, inst->args.at[3 + j * 2].as.ref->string, typeVar);
|
||||||
|
if (typeVar == NULL || 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 method name to a field offset
|
||||||
|
GroundArg* methodArg = &inst->args.at[methodIdx];
|
||||||
|
GroundObjectField* methodField;
|
||||||
|
HASH_FIND_STR(currentType->fields, methodArg->as.ref->string, methodField);
|
||||||
|
if (methodField == NULL) {
|
||||||
|
Ground.Log.Error("unknown method in struct in Ground.New.Bytecode -> doOffsets");
|
||||||
|
Ground.Flags.error = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
newInst.args.at[1 + fieldPairs] = methodField->offset;
|
||||||
|
|
||||||
|
// Resolve the method arguments
|
||||||
|
for (GroundSize j = 0; j < methodArgs; j++) {
|
||||||
|
GroundArg* arg = &inst->args.at[methodIdx + 1 + j];
|
||||||
|
if (arg->type == GroundArg_Value) {
|
||||||
|
GroundVariable* constItem = malloc(sizeof(GroundVariable));
|
||||||
|
if (constItem == NULL) {
|
||||||
|
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets");
|
||||||
|
Ground.Flags.error = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
constItem->value = Ground.Copy.Value(&arg->as.value);
|
||||||
|
if (Ground.Flags.error) return 0;
|
||||||
|
constItem->_offset = size++;
|
||||||
|
snprintf(constItem->name, sizeof(constItem->name) - 1, "_._.ground_internal_constant_%zu", constItem->_offset);
|
||||||
|
HASH_ADD_STR(state->variables, name, constItem);
|
||||||
|
newInst.args.at[2 + fieldPairs + j] = constItem->_offset;
|
||||||
|
} else {
|
||||||
|
GroundVariable* argItem = NULL;
|
||||||
|
HASH_FIND_STR(state->variables, arg->as.ref->string, argItem);
|
||||||
|
if (argItem == NULL) {
|
||||||
|
argItem = malloc(sizeof(GroundVariable));
|
||||||
|
if (argItem == NULL) {
|
||||||
|
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets");
|
||||||
|
Ground.Flags.error = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
argItem->value = Ground.New.Value.Int(0);
|
||||||
|
strncpy(argItem->name, arg->as.ref->string, 2047);
|
||||||
|
argItem->_offset = size++;
|
||||||
|
HASH_ADD_STR(state->variables, name, argItem);
|
||||||
|
}
|
||||||
|
arg->_offset = argItem->_offset;
|
||||||
|
newInst.args.at[2 + fieldPairs + j] = argItem->_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,15 +222,40 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
|
|||||||
case GroundInstruction_FUN: {
|
case GroundInstruction_FUN: {
|
||||||
// Parse the function itself
|
// Parse the function itself
|
||||||
doFunction(program, newProgram, state, i);
|
doFunction(program, newProgram, state, i);
|
||||||
|
|
||||||
|
// Add hidden "self" parameter to the method
|
||||||
|
GroundVariable* fnVar = NULL;
|
||||||
|
HASH_FIND_STR(state->variables, newProgram->at[newProgram->len - 1].args.at[0].as.ref->string, fnVar);
|
||||||
|
if (fnVar != NULL && fnVar->value.type.type == GroundType_Function) {
|
||||||
|
Ground.Function.appendArg(&fnVar->value.as.Function, "self");
|
||||||
|
|
||||||
|
GroundVariable* selfVar = malloc(sizeof(GroundVariable));
|
||||||
|
if (selfVar != NULL) {
|
||||||
|
snprintf(selfVar->name, 2047, "self");
|
||||||
|
selfVar->value = Ground.New.Value.Int(0);
|
||||||
|
HASH_ADD_STR(fnVar->value.as.Function.closure->variables, name, selfVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add it to the struct
|
||||||
|
GroundObjectField* field = malloc(sizeof(GroundObjectField));
|
||||||
|
if (field == NULL) {
|
||||||
|
Ground.Log.Error("malloc failed in Ground.Program.preprocess -> doStruct");
|
||||||
|
Ground.Flags.error = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(field->name, 2047, "%s", instruction->args.at[0].as.ref->string);
|
||||||
|
field->offset = fieldCount++;
|
||||||
|
|
||||||
|
HASH_ADD_STR(gs.fields, name, field);
|
||||||
|
|
||||||
// Read and remove the emitted instruction
|
// Read and remove the emitted instruction
|
||||||
GroundInstruction* inst = &newProgram->at[newProgram->len - 1];
|
GroundInstruction* inst = &newProgram->at[newProgram->len - 1];
|
||||||
Ground.Instruction.append(&output, Ground.New.Arg.FunctionRef(Ground.Copy.Identifier(inst->args.at[0].as.ref)));
|
Ground.Instruction.append(&output, Ground.New.Arg.FunctionRef(Ground.Copy.Identifier(inst->args.at[0].as.ref)));
|
||||||
Ground.Free.Instruction(inst);
|
Ground.Free.Instruction(inst);
|
||||||
newProgram->len--;
|
newProgram->len--;
|
||||||
|
|
||||||
// TODO do the rest of this
|
|
||||||
// for some reason I haven't done it yet?? oh well who cares
|
|
||||||
|
|
||||||
// Add 3 to signal addition of closure
|
// Add 3 to signal addition of closure
|
||||||
Ground.Instruction.append(&output, (GroundArg) {.type = GroundArg_DirectRef, ._offset = 3});
|
Ground.Instruction.append(&output, (GroundArg) {.type = GroundArg_DirectRef, ._offset = 3});
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user