#include "../../include/ground.h" #include #include static void writeProgram(FILE* f, GroundBytecodeProgram* program) { uint64_t count = program->len; fwrite(&count, 8, 1, f); for (GroundSize i = 0; i < count; i++) { uint8_t type = (uint8_t)program->at[i].type; fwrite(&type, 1, 1, f); uint64_t argCount = program->at[i].args.len; fwrite(&argCount, 8, 1, f); if (argCount > 0) { fwrite(program->at[i].args.at, sizeof(GroundSize), argCount, f); } } } static void writeValue(FILE* f, GroundBytecodeValue* v) { switch (v->type.type) { case GroundType_Int: { uint8_t tag = 0; fwrite(&tag, 1, 1, f); fwrite(&v->as.Int, 8, 1, f); break; } case GroundType_Double: { uint8_t tag = 1; fwrite(&tag, 1, 1, f); fwrite(&v->as.Double, 8, 1, f); break; } case GroundType_Char: { uint8_t tag = 2; fwrite(&tag, 1, 1, f); fwrite(&v->as.Char, 1, 1, f); break; } case GroundType_Bool: { uint8_t tag = 3; fwrite(&tag, 1, 1, f); fwrite(&v->as.Bool, 1, 1, f); break; } case GroundType_String: { uint8_t tag = 4; fwrite(&tag, 1, 1, f); uint64_t len = v->as.String.len; fwrite(&len, 8, 1, f); fwrite(v->as.String.cstr, 1, len, f); break; } case GroundType_Function: { uint8_t tag = 5; fwrite(&tag, 1, 1, f); uint8_t isNative = v->as.Function.isNativeFunction ? 1 : 0; fwrite(&isNative, 1, 1, f); uint64_t argCount = v->as.Function.args.count; fwrite(&argCount, 8, 1, f); for (GroundSize i = 0; i < argCount; i++) { char* name = v->as.Function.args.at[i].as.id != NULL ? v->as.Function.args.at[i].as.id->string : ""; uint64_t len = strlen(name); fwrite(&len, 8, 1, f); fwrite(name, 1, len, f); } if (!v->as.Function.isNativeFunction && v->as.Function.program.ground != NULL) { writeProgram(f, v->as.Function.program.ground); } else { uint64_t zero = 0; fwrite(&zero, 8, 1, f); } // closures are generated at runtime, not persisted break; } case GroundType_List: { uint8_t tag = 6; fwrite(&tag, 1, 1, f); uint64_t count = v->as.List.count; fwrite(&count, 8, 1, f); for (GroundSize i = 0; i < count; i++) { writeValue(f, &v->as.List.at[i]); } break; } case GroundType_Struct: { uint8_t tag = 7; fwrite(&tag, 1, 1, f); uint64_t size = v->as.Struct.size; fwrite(&size, 8, 1, f); for (GroundSize i = 0; i < size; i++) { writeValue(f, &v->as.Struct.values[i]); } break; } case GroundType_Object: { uint8_t tag = 8; fwrite(&tag, 1, 1, f); uint64_t size = v->as.Object.size; fwrite(&size, 8, 1, f); for (GroundSize i = 0; i < size; i++) { writeValue(f, &v->as.Object.values[i]); } break; } case GroundType_Undefined: { uint8_t tag = 9; fwrite(&tag, 1, 1, f); break; } case GroundType_CoreType: { uint8_t tag = 10; fwrite(&tag, 1, 1, f); uint8_t coreType = (uint8_t)v->as.CoreType; fwrite(&coreType, 1, 1, f); break; } default: { uint8_t tag = 0xFF; fwrite(&tag, 1, 1, f); break; } } } void _GroundBytecodeSave(GroundBytecode* bytecode, const char* path) { FILE* f = fopen(path, "wb"); if (f == NULL) { Ground.Log.Error("failed to open file for writing in Ground.Bytecode.save"); Ground.Flags.error = true; return; } fwrite("GRNDbc\0", 7, 1, f); uint32_t version = 1; fwrite(&version, 4, 1, f); uint64_t count = bytecode->heap.len; fwrite(&count, 8, 1, f); for (GroundSize i = 0; i < count; i++) { writeValue(f, &bytecode->heap.heap[i]); } writeProgram(f, &bytecode->program); fclose(f); }