From b2f6883e62d4d8ad68701a69be4885a978ba8c5e Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Sun, 21 Jun 2026 16:13:00 +1000 Subject: [PATCH] stuff --- include/ground.h | 2 + meson.build | 8 ++ src/Bytecode/Instruction/execute.c | 6 +- src/Bytecode/load.c | 141 +++++++++++++++++++++++++++++ src/Bytecode/save.c | 75 +++++++++++++++ src/New/Bytecode.c | 20 ++-- src/cli/main.c | 107 ++++++++++++++++++++++ src/libmain.c | 5 + test/test | Bin 16056 -> 0 bytes test/test.c | 29 ------ 10 files changed, 351 insertions(+), 42 deletions(-) create mode 100644 src/Bytecode/load.c create mode 100644 src/Bytecode/save.c create mode 100644 src/cli/main.c delete mode 100755 test/test delete mode 100644 test/test.c diff --git a/include/ground.h b/include/ground.h index 4863539..840bd81 100644 --- a/include/ground.h +++ b/include/ground.h @@ -425,6 +425,8 @@ struct _Ground { } Log; struct { + void (*save) (GroundBytecode* bytecode, const char* path); + GroundBytecode (*load) (const char* path); struct { void (*execute) (GroundBytecodeProgram* program, GroundBytecodeHeap* heap); void (*optimise) (GroundBytecodeProgram* program); diff --git a/meson.build b/meson.build index f55101d..cf0c7e3 100644 --- a/meson.build +++ b/meson.build @@ -6,6 +6,8 @@ sources = files( 'src/Bytecode/Heap/get.c', 'src/Bytecode/Heap/set.c', + 'src/Bytecode/save.c', + 'src/Bytecode/load.c', 'src/Bytecode/Instruction/execute.c', 'src/Bytecode/Program/execute.c', 'src/Bytecode/Program/optimise.c', @@ -99,6 +101,10 @@ sources = files( 'src/Struct/addField.c' ) +cli_sources = files( + 'src/cli/main.c' +) + incdir = include_directories('include') libffi = dependency('libffi', version : '>=3.0.0') @@ -112,3 +118,5 @@ pkg.generate(lib, version : meson.project_version(), description : 'Ground virtual machine', ) + +cli = executable('ground', cli_sources, include_directories : incdir, install : true, link_with : lib) diff --git a/src/Bytecode/Instruction/execute.c b/src/Bytecode/Instruction/execute.c index 3dfa8a5..10f4224 100644 --- a/src/Bytecode/Instruction/execute.c +++ b/src/Bytecode/Instruction/execute.c @@ -24,10 +24,14 @@ int64_t _GroundBytecodeInstructionExecute(GroundBytecodeInstruction* instruction goto *jumpTable[instruction->type]; IF: { + GroundValue* cond = Ground.Bytecode.Heap.get(heap, instruction->args.at[0]); + if (cond->as.Bool) { + return instruction->args.at[1]; + } return -1; } JUMP: { - return -1; + return instruction->args.at[0]; } END: { return -2; diff --git a/src/Bytecode/load.c b/src/Bytecode/load.c new file mode 100644 index 0000000..aca5995 --- /dev/null +++ b/src/Bytecode/load.c @@ -0,0 +1,141 @@ +#include "../../include/ground.h" +#include +#include + +static GroundValue readValue(FILE* f) { + GroundValue v = {0}; + uint8_t tag; + if (fread(&tag, 1, 1, f) != 1) { + Ground.Log.Error("failed to read value type in Ground.Bytecode.load"); + Ground.Flags.error = true; + return v; + } + + switch (tag) { + case 0: { + v.type.type = GroundType_Int; + fread(&v.as.Int, 8, 1, f); + break; + } + case 1: { + v.type.type = GroundType_Double; + fread(&v.as.Double, 8, 1, f); + break; + } + case 2: { + v.type.type = GroundType_Char; + fread(&v.as.Char, 1, 1, f); + break; + } + case 3: { + v.type.type = GroundType_Bool; + fread(&v.as.Bool, 1, 1, f); + break; + } + case 4: { + v.type.type = GroundType_String; + uint64_t len; + fread(&len, 8, 1, f); + v.as.String.cstr = malloc(len + 1); + if (v.as.String.cstr == NULL) { + Ground.Log.Error("malloc failed in Ground.Bytecode.load"); + Ground.Flags.error = true; + return v; + } + fread(v.as.String.cstr, 1, len, f); + v.as.String.cstr[len] = '\0'; + v.as.String.len = len; + break; + } + default: { + Ground.Log.Error("unknown value type in Ground.Bytecode.load"); + Ground.Flags.error = true; + break; + } + } + + return v; +} + +GroundBytecode _GroundBytecodeLoad(const char* path) { + GroundBytecode bc = {0}; + + FILE* f = fopen(path, "rb"); + if (f == NULL) { + Ground.Log.Error("failed to open file for reading in Ground.Bytecode.load"); + Ground.Flags.error = true; + return bc; + } + + char magic[8]; + memset(magic, 0, 8); + if (fread(magic, 7, 1, f) != 1 || memcmp(magic, "GRNDbc", 6) != 0) { + Ground.Log.Error("bad magic in Ground.Bytecode.load"); + Ground.Flags.error = true; + fclose(f); + return bc; + } + + uint32_t version; + fread(&version, 4, 1, f); + + uint64_t count; + fread(&count, 8, 1, f); + if (count > 0) { + bc.heap.heap = malloc(sizeof(GroundValue) * count); + if (bc.heap.heap == NULL) { + Ground.Log.Error("malloc failed in Ground.Bytecode.load"); + Ground.Flags.error = true; + fclose(f); + return bc; + } + for (uint64_t i = 0; i < count; i++) { + bc.heap.heap[i] = readValue(f); + if (Ground.Flags.error) { + fclose(f); + return bc; + } + } + bc.heap.capacity = count; + bc.heap.len = count; + } + + fread(&count, 8, 1, f); + if (count > 0) { + bc.program.at = malloc(sizeof(GroundBytecodeInstruction) * count); + if (bc.program.at == NULL) { + Ground.Log.Error("malloc failed in Ground.Bytecode.load"); + Ground.Flags.error = true; + fclose(f); + return bc; + } + bc.program.capacity = count; + bc.program.len = count; + + for (uint64_t i = 0; i < count; i++) { + uint8_t type; + fread(&type, 1, 1, f); + bc.program.at[i].type = (enum GroundInstructionType)type; + + uint64_t argCount; + fread(&argCount, 8, 1, f); + if (argCount > 0) { + bc.program.at[i].args.at = malloc(sizeof(GroundSize) * argCount); + if (bc.program.at[i].args.at == NULL) { + Ground.Log.Error("malloc failed in Ground.Bytecode.load"); + Ground.Flags.error = true; + fclose(f); + return bc; + } + fread(bc.program.at[i].args.at, sizeof(GroundSize), argCount, f); + } else { + bc.program.at[i].args.at = NULL; + } + bc.program.at[i].args.len = argCount; + bc.program.at[i].args.capacity = argCount; + } + } + + fclose(f); + return bc; +} diff --git a/src/Bytecode/save.c b/src/Bytecode/save.c new file mode 100644 index 0000000..2dc38bf --- /dev/null +++ b/src/Bytecode/save.c @@ -0,0 +1,75 @@ +#include "../../include/ground.h" +#include + +static void writeValue(FILE* f, GroundValue* 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; + } + 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]); + } + + count = bytecode->program.len; + fwrite(&count, 8, 1, f); + for (GroundSize i = 0; i < count; i++) { + uint8_t type = (uint8_t)bytecode->program.at[i].type; + fwrite(&type, 1, 1, f); + uint64_t argCount = bytecode->program.at[i].args.len; + fwrite(&argCount, 8, 1, f); + fwrite(bytecode->program.at[i].args.at, 8, argCount, f); + } + + fclose(f); +} diff --git a/src/New/Bytecode.c b/src/New/Bytecode.c index 78421ef..eee7654 100644 --- a/src/New/Bytecode.c +++ b/src/New/Bytecode.c @@ -72,12 +72,6 @@ static inline void doLabels(GroundProgram* program, GroundState* state) { } -static inline void addToGroundBytecodeProgram(GroundBytecodeProgram* program, GroundBytecodeInstruction inst) { - if (program->len + 1 >= program->capacity) { - - } -} - /* * Assigns an offset to each variable referenced. */ @@ -111,6 +105,7 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground item->_offset = size++; snprintf(item->name, sizeof(item->name) - 1, "_._.ground_internal_constant_%zu", item->_offset); HASH_ADD_STR(state->variables, name, item); + arg->_offset = item->_offset; continue; } @@ -131,7 +126,6 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground } arg->_offset = item->_offset; - size++; } // Convert to GroundBytecodeInstruction @@ -142,7 +136,11 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground .args.capacity = gp->at[i].args.len }; - if (inst.args.at == NULL) {} + if (inst.args.at == NULL) { + Ground.Log.Error("malloc failed in Ground.Internal.run()"); + Ground.Flags.error = true; + return 0; + } for (GroundSize j = 0; j < gp->at[i].args.len; j++) { inst.args.at[j] = gp->at[i].args.at[j]._offset; @@ -196,12 +194,10 @@ GroundBytecode _GroundNewBytecode(GroundProgram* program, GroundState* state) { GroundVariable *s, *tmp; - // Copy constants into heap - GroundSize i = 0; + // Copy constants into heap at their assigned offsets HASH_ITER(hh, state->variables, s, tmp) { - bytecode.heap.heap[i] = Ground.Copy.Value(&s->value); + bytecode.heap.heap[s->_offset] = Ground.Copy.Value(&s->value); if (Ground.Flags.error) return bytecode; - i++; } diff --git a/src/cli/main.c b/src/cli/main.c new file mode 100644 index 0000000..49ad4e2 --- /dev/null +++ b/src/cli/main.c @@ -0,0 +1,107 @@ +#include "../../include/ground.h" +#include + +enum ArgsAction { + ARGS_HELP, ARGS_EXECUTE, ARGS_DEBUG, ARGS_ASSEMBLE, ARGS_DISASSEMBLE +}; + +typedef struct Args { + enum ArgsAction action; + char* inputFile; + char* outputFile; +} Args; + +Args parseArgs(int argc, char** argv) { + Args args = { + .action = ARGS_EXECUTE, + .inputFile = NULL, + .outputFile = NULL + }; + + if (argc == 1) { + args.action = ARGS_HELP; + return args; + } + + for (int i = 1; i < argc; i++) { + char* arg = argv[i]; + + if (strcmp(arg, "-d") == 0 || strcmp(arg, "--debug") == 0) { + args.action = ARGS_DEBUG; + } else if (strcmp(arg, "-a") == 0 || strcmp(arg, "--assemble") == 0) { + args.action = ARGS_ASSEMBLE; + if (i + i < argc) { + i++; + args.outputFile = argv[i]; + } + } else if (strcmp(arg, "-D") == 0 || strcmp(arg, "--disassemble") == 0) { + args.action = ARGS_DISASSEMBLE; + } else if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { + args.action = ARGS_HELP; + } else { + args.inputFile = arg; + } + + } + + return args; +} + +int main(int argc, char** argv) { + Args args = parseArgs(argc, argv); + + if (args.inputFile == NULL) { + fprintf(stderr, "Please specify a bytecode file\n"); + return 1; + } + + switch (args.action) { + case ARGS_EXECUTE: { + GroundBytecode bc = Ground.Bytecode.load(args.inputFile); + if (Ground.Flags.error) { + fprintf(stderr, "Failed to load bytecode, printing errors...\n"); + Ground.Log.printErrors(); + return 1; + } + + Ground.Bytecode.Program.execute(&bc.program, &bc.heap); + if (Ground.Flags.error) { + fprintf(stderr, "Failed to run program, printing errors...\n"); + Ground.Log.printErrors(); + return 1; + } + break; + } + case ARGS_DEBUG: { + fprintf(stderr, "Not yet implemented"); + break; + } + case ARGS_ASSEMBLE: { + fprintf(stderr, "Not yet implemented"); + break; + } + case ARGS_DISASSEMBLE: { + fprintf(stderr, "Not yet implemented"); + break; + } + case ARGS_HELP: { + printf("GroundVM cli help\n"); + printf("Usage: %s [-h] [--help] [-d] [--debug] [-a ] [--assemble ] [-D] [--disassemble]\n", argv[0]); + printf("Options:\n"); + printf(" -h or --help\n"); + printf(" Shows this help message\n"); + printf(" -d or --debug\n"); + printf(" Interactive debugger for a Ground program\n"); + printf(" -a or --assemble \n"); + printf(" Assembles a .grnd textual representation into a .gb bytecode file\n"); + printf(" Inputs from , outputs to \n"); + printf(" -D or --disassemble\n"); + printf(" Disassembles a .gb program, showing the heap, instructions, and offsets\n"); + printf("If no options are specified, the provided will be executed.\n"); + + break; + } + } + + return 0; +} diff --git a/src/libmain.c b/src/libmain.c index b372c80..4cf8434 100644 --- a/src/libmain.c +++ b/src/libmain.c @@ -100,6 +100,9 @@ void _GroundBytecodeProgramOptimise(GroundBytecodeProgram* program); int64_t _GroundBytecodeInstructionExecute(GroundBytecodeInstruction* instruction, GroundBytecodeHeap* heap); +void _GroundBytecodeSave(GroundBytecode* bytecode, const char* path); +GroundBytecode _GroundBytecodeLoad(const char* path); + void _GroundBytecodeHeapSet(GroundBytecodeHeap* heap, GroundSize idx, GroundValue value); GroundValue* _GroundBytecodeHeapGet(GroundBytecodeHeap* heap, GroundSize idx); @@ -223,6 +226,8 @@ struct _Ground Ground = { }, .Bytecode = { + .save = _GroundBytecodeSave, + .load = _GroundBytecodeLoad, .Program = { .execute = _GroundBytecodeProgramExecute, .optimise = _GroundBytecodeProgramOptimise, diff --git a/test/test b/test/test deleted file mode 100755 index afefbecec22dd47e25c8df703bf3866556be1729..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16056 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 diff --git a/test/test.c b/test/test.c deleted file mode 100644 index c01260d..0000000 --- a/test/test.c +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include - -int main() { - GroundProgram program = Ground.New.Program(); - GroundState state = {NULL, NULL, NULL}; - - // Instruction 1: CREATELABEL start - GroundInstruction label_inst = Ground.New.Instruction(GroundInstruction_CREATELABEL); - Ground.Instruction.append(&label_inst, Ground.New.Arg.LabelRef("start")); - Ground.Program.append(&program, label_inst); - - // Instruction 2: JUMP start - GroundInstruction jump_inst = Ground.New.Instruction(GroundInstruction_JUMP); - Ground.Instruction.append(&jump_inst, Ground.New.Arg.LabelRef("start")); - Ground.Program.append(&program, jump_inst); - - // Execute program - Ground.Program.execute(&program, &state); - - if (Ground.Flags.error) { - printf("Error set during execution!\n"); - Ground.Log.printErrors(); - return 1; - } else { - printf("Success!\n"); - return 0; - } -}