diff --git a/src/Bytecode/Instruction/execute.c b/src/Bytecode/Instruction/execute.c index a2e2dbf..b50b1e2 100644 --- a/src/Bytecode/Instruction/execute.c +++ b/src/Bytecode/Instruction/execute.c @@ -913,7 +913,24 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns GroundBytecodeValue* type = HEAP_GET(heap, instruction->args.at[i]); switch (type->type.type) { case GroundType_Struct: { - // TODO nested structs + GroundBytecodeStruct* innerStruct = &type->as.Struct; + GroundBytecodeObject nestedObject = { + .size = innerStruct->size, + .capacity = innerStruct->size, + .values = malloc(sizeof(GroundBytecodeValue) * innerStruct->size) + }; + if (nestedObject.values == NULL) { + Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute"); + Ground.Flags.error = true; + return CONTINUE; + } + for (GroundSize k = 0; k < innerStruct->size; k++) { + nestedObject.values[k] = Ground.Copy.BytecodeValue(&innerStruct->values[k]); + } + gbs->values[offset] = (GroundBytecodeValue) { + .as.Object = nestedObject, + .type = {GroundType_Object} + }; break; } case GroundType_CoreType: { @@ -1054,6 +1071,22 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns return CONTINUE; } SETFIELD: { + // args: [parent_offset, field0_offset, ..., fieldF_offset, value_offset] + GroundBytecodeValue* current = HEAP_GET(heap, instruction->args.at[0]); + + for (GroundSize i = 1; i < instruction->args.len - 1; i++) { + if (current->type.type != GroundType_Object) { + Ground.Log.Error("SETFIELD on non-object value in Ground.Bytecode.Instruction.execute"); + Ground.Flags.error = true; + return CONTINUE; + } + current = ¤t->as.Object.values[instruction->args.at[i]]; + } + + GroundBytecodeValue* value = HEAP_GET(heap, instruction->args.at[instruction->args.len - 1]); + Ground.Free.BytecodeValue(current); + *current = Ground.Copy.BytecodeValue(value); + return CONTINUE; } USE: { diff --git a/src/Free/Arg.c b/src/Free/Arg.c index 87ace8d..1e5f811 100644 --- a/src/Free/Arg.c +++ b/src/Free/Arg.c @@ -3,7 +3,7 @@ void _GroundFreeArg(GroundArg* in) { if (in->type == GroundArg_Value) { Ground.Free.Value(&in->as.value); - } else { + } else if (in->type != GroundArg_DirectRef && in->type != GroundArg_Label && in->type != GroundArg_LineRef) { Ground.Free.Identifier(in->as.ref); } } diff --git a/src/New/Bytecode.c b/src/New/Bytecode.c index 0211ed3..4b0a854 100644 --- a/src/New/Bytecode.c +++ b/src/New/Bytecode.c @@ -106,15 +106,49 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground // Set offset for each struct field GroundObjectField *s, *tmp; - GroundSize i = 0; + GroundSize fi = 0; HASH_ITER(hh, gs->fields, s, tmp) { - s->offset = i++; + s->offset = fi++; } + // Create bytecode instruction + GroundBytecodeInstruction newInst = { + .type = GroundInstruction_STRUCT, + .args = { + .at = malloc(sizeof(GroundSize) * inst->args.len), + .capacity = inst->args.len, + .len = inst->args.len + } + }; + + if (newInst.args.at == NULL) { + Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + + // Resolve the struct type variable to a heap offset + GroundArg* arg = &inst->args.at[0]; + GroundVariable* structItem = NULL; + HASH_FIND_STR(state->variables, arg->as.ref->string, structItem); + if (structItem == NULL) { + structItem = malloc(sizeof(GroundVariable)); + if (structItem == NULL) { + Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + structItem->value = Ground.New.Value.Int(0); + strncpy(structItem->name, arg->as.ref->string, 2047); + structItem->_offset = size++; + HASH_ADD_STR(state->variables, name, structItem); + } + newInst.args.at[0] = structItem->_offset; + // Now go through and add offsets to everything (skip struct name at index 0) for (GroundSize j = 1; j < inst->args.len; j++) { // struct field name - GroundArg* arg = &inst->args.at[j]; + arg = &inst->args.at[j]; GroundObjectField* field = NULL; HASH_FIND_STR(gs->fields, arg->as.ref->string, field); if (field == NULL) { @@ -122,40 +156,61 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground Ground.Flags.error = true; return 0; } - arg->_offset = field->offset; - // 2nd arg offset is already set, but what we do next depends on it + newInst.args.at[j] = field->offset; + // 2nd arg is the op code, what we do next depends on it j++; // 0 -> none // 1 -> init // 2 -> set // 3 -> attach closure to function - switch (inst->args.at[j]._offset) { + GroundSize op = inst->args.at[j]._offset; + newInst.args.at[j] = op; + switch (op) { case 0: case 3: break; // these do not require extra values case 1: // but these do, so we process them - case 2: { // it's safe to assume that literals will have - j++; // been dealt with accordingly + case 2: { + j++; arg = &inst->args.at[j]; GroundVariable* item = NULL; - HASH_FIND_STR(state->variables, arg->as.ref->string, item); - if (item == NULL) { + if (arg->type == GroundArg_Value) { + // Literal value: create a constant variable for it 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->value = Ground.Copy.Value(&arg->as.value); item->_offset = size++; + snprintf(item->name, sizeof(item->name) - 1, "_._.ground_internal_constant_%zu", item->_offset); HASH_ADD_STR(state->variables, name, item); + } else { + // Variable reference: look it up or create it + 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[j] = item->_offset; } } } + gbp->at[i] = newInst; + gbp->len++; + continue; } case GroundInstruction_GETFIELD: { @@ -274,6 +329,114 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground continue; } case GroundInstruction_SETFIELD: { + // setfield $parent -type (&field -type)... &field $value + // Bytecode: parent_offset, field0_offset, ..., fieldF_offset, value_offset + GroundInstruction* inst = &gp->at[i]; + GroundSize gbiSize = 2 + ((inst->args.len - 2) / 2); + + GroundBytecodeInstruction newInst = { + .type = GroundInstruction_SETFIELD, + .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); + } + + newInst.args.at[0] = item->_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 value variable + GroundArg* valueArg = &inst->args.at[inst->args.len - 1]; + GroundVariable* valueItem = NULL; + HASH_FIND_STR(state->variables, valueArg->as.ref->string, valueItem); + if (valueItem == NULL) { + valueItem = malloc(sizeof(GroundVariable)); + if (valueItem == NULL) { + Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets"); + Ground.Flags.error = true; + return 0; + } + valueItem->value = Ground.New.Value.Int(0); + strncpy(valueItem->name, valueArg->as.ref->string, 2047); + valueItem->_offset = size++; + HASH_ADD_STR(state->variables, name, valueItem); + } + newInst.args.at[gbiSize - 1] = valueItem->_offset; + + gbp->at[i] = newInst; + gbp->len++; continue; } diff --git a/src/Program/preprocess.c b/src/Program/preprocess.c index 86b1f45..f3cfee2 100644 --- a/src/Program/preprocess.c +++ b/src/Program/preprocess.c @@ -246,6 +246,8 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt } + Ground.Program.append(newProgram, output); + GroundVariable* variable = malloc(sizeof(GroundVariable)); if (variable == NULL) { Ground.Log.Error("malloc failed in Ground.Program.Preprocess -> doStruct"); diff --git a/src/Stringify/BytecodeValue.c b/src/Stringify/BytecodeValue.c index 284a4af..8bbfbd9 100644 --- a/src/Stringify/BytecodeValue.c +++ b/src/Stringify/BytecodeValue.c @@ -65,11 +65,11 @@ char* _GroundStringifyBytecodeValue(GroundBytecodeValue* value) { } case GroundType_Object: { Estr str = CREATE_ESTR("as.Struct.size; i++) { + for (GroundSize i = 0; i < value->as.Object.size; i++) { if (i != 0) { APPEND_ESTR(str, ", "); } - char* field = Ground.Stringify.BytecodeValue(&value->as.Struct.values[i]); + char* field = Ground.Stringify.BytecodeValue(&value->as.Object.values[i]); APPEND_ESTR(str, field); free(field); }