diff --git a/meson.build b/meson.build index 491038a..97f635a 100644 --- a/meson.build +++ b/meson.build @@ -129,7 +129,8 @@ endif cli_sources = files( - 'src/cli/main.c' + 'src/cli/main.c', + 'src/cli/debugger.c' ) incdir = include_directories('include') diff --git a/src/cli/debugger.c b/src/cli/debugger.c new file mode 100644 index 0000000..b8c0f84 --- /dev/null +++ b/src/cli/debugger.c @@ -0,0 +1,140 @@ +#include "../../include/ground.h" +#include "../linenoise/linenoise.h" + +#include +#include + +typedef struct DebugCommand { + char* command; + struct { + char* at[32]; + GroundSize count; + } args; +} DebugCommand; + +static inline DebugCommand parseCommand(char* input) { + DebugCommand debugCommand; + // split by spaces + const char* delim = " "; + debugCommand.command = strtok(input, delim); + for (GroundSize i = 0; i < 32; i++) { + char* buf = strtok(NULL, delim); + if (buf == NULL) { + debugCommand.args.count = i; + break; + } + debugCommand.args.at[i] = buf; + } + + return debugCommand; +} + +typedef struct Debugger { + + GroundSize currentInst; + +} Debugger; + +enum DebuggerStatus { + DBG_DONE, DBG_CONTINUE, DBG_WAIT, DBG_STEP +}; + +static inline enum DebuggerStatus runCommand(char* input, GroundBytecode* bytecode, Debugger* debugger) { + DebugCommand cmd = parseCommand(input); + if (strcmp(cmd.command, "help") == 0) { + printf( + "Commands: \n" \ + " help - Show this help message\n" \ + " continue - Run the program until next breakpoint, error, or otherwise termination\n" \ + " step - Run one instruction\n" \ + " break [idx] - Sets a breakpoint on the specified index\n" \ + " exit - Stop debugging and exit\n\n" \ + + " dump - Dump current state of VM\n" \ + " view [slot] - Prints the variable in the current slot\n" \ + " set [slot] [value] - Sets the current slot to the specified value\n" \ + " copy [slot] [slot] - Copies a value from the second slot to the first\n" \ + ); + return DBG_CONTINUE; + } + if (strcmp(cmd.command, "continue") == 0) { + return DBG_CONTINUE; + } + if (strcmp(cmd.command, "step") == 0) { + return DBG_STEP; + } + if (strcmp(cmd.command, "break") == 0) { + return DBG_WAIT; + } + if (strcmp(cmd.command, "exit") == 0) { + return DBG_DONE; + } + + if (strcmp(cmd.command, "dump") == 0) { + printf("%s", Ground.Stringify.Bytecode(bytecode)); + return DBG_WAIT; + } + if (strcmp(cmd.command, "view") == 0) { + return DBG_WAIT; + } + if (strcmp(cmd.command, "set") == 0) { + return DBG_WAIT; + } + if (strcmp(cmd.command, "copy") == 0) { + return DBG_WAIT; + } + + fprintf(stderr, "Unknown command %s\n", cmd.command); + return DBG_WAIT; +} + +static inline void runInstruction(GroundBytecodeProgram* program, GroundBytecodeHeap* heap) { + GroundSize i = 0; + while (i < program->len) { + struct GroundExecutionResult status = Ground.Bytecode.Instruction.execute(&program->at[i], heap); + if (Ground.Flags.error) { + fprintf(stderr, "Errors while debugging: \n"); + Ground.Log.printErrors(); + } + + switch (status.type) { + + case _GER_CONTINUE: i++; break; + case _GER_RETURN: return; + case _GER_JUMP: i = status.as.line; break; + case _GER_END: exit(status.as.end); + + } + } +} + +void runDebugger(GroundBytecode* bytecode) { + printf("Ground Debugger\n"); + printf("Type 'help' for a list of commands\n"); + printf("Type 'start' to begin debugging\n"); + printf("Type 'exit' to exit\n"); + + Debugger debugger; + + for (;;) { + char* input = linenoise("> "); + if (input == NULL) { + continue; + } + switch (runCommand(input, bytecode, &debugger)) { + case DBG_DONE: { + return; + } + case DBG_CONTINUE: { + break; + } + case DBG_WAIT: { + break; + } + case DBG_STEP: { + break; + } + } + free(input); + } +} diff --git a/src/cli/main.c b/src/cli/main.c index e181973..e7b0032 100644 --- a/src/cli/main.c +++ b/src/cli/main.c @@ -1,6 +1,8 @@ #include "../../include/ground.h" #include +void runDebugger(GroundBytecode* bytecode); + enum ArgsAction { ARGS_HELP, ARGS_EXECUTE, ARGS_DEBUG, ARGS_ASSEMBLE, ARGS_DISASSEMBLE }; @@ -73,7 +75,20 @@ int main(int argc, char** argv) { break; } case ARGS_DEBUG: { - fprintf(stderr, "Not yet implemented"); + if (args.inputFile == NULL) { + fprintf(stderr, "Please specify a bytecode file\n"); + return 1; + } + + 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; + } + + runDebugger(&bc); + break; } case ARGS_ASSEMBLE: {