Compare commits

...

18 Commits

Author SHA1 Message Date
65cf5580ac Better stringification of bytecode values 2026-07-28 19:41:32 +10:00
57e2f2eea9 Remove something 2026-07-28 19:29:09 +10:00
8fe67ddc2b Add more stuff to the debugger 2026-07-28 18:40:33 +10:00
7020ac3744 Start working on debugger 2026-07-28 18:01:06 +10:00
5dcbac3166 Remove debug prints, fix offset dumping 2026-07-28 08:13:06 +10:00
4d5cdb5b6b Stuff 2026-07-27 21:37:08 +10:00
8b04bf39b1 updates 2026-07-27 20:22:42 +10:00
7a53105eca Start working on CALLMETHOD 2026-07-16 14:39:39 +10:00
8e4267d500 memory leak be gone 2026-07-14 12:29:23 +10:00
0d199fedd7 fix the clanker's code 2026-07-14 11:35:54 +10:00
05eddd9a0e hfguidshfiouhowfonweoinfvodnsbdifbisdb 2026-07-13 16:47:41 +10:00
7619b897e0 ITS A HYDRA I FIXED A BUG BUT ANOTHER ONE POPPED UP 2026-07-13 14:45:02 +10:00
71dfbb5855 ASDUIFBUIJDBSUIFBI STUPID BUG ONFGIUODSDNDIOFNOISNFIONADOSIFNI 2026-07-13 14:40:56 +10:00
f2fbeb3a68 bug fixes and getfield implementation beta 2026-07-13 11:49:59 +10:00
6929f055de object initialisation 2026-07-08 12:06:18 +10:00
b507d79804 Fix Meson build file 2026-07-06 16:15:41 +10:00
3a1cefffd7 windows building support 2026-07-06 14:17:22 +10:00
820fd618a3 le objects now go to the the bytecode 2026-07-05 18:25:49 +10:00
24 changed files with 2375 additions and 102 deletions

View File

@@ -13,6 +13,33 @@ Install with:
meson install -C builddir meson install -C builddir
``` ```
# Cross Compiling for Windows
We all hate Windows, right? As much as we hate it, people still use it, so it's nice to be able to run our good software on their crappy platform.
You'll need the cross compiler (install command is for Arch and derivatives):
```sh
sudo pacman -S mingw-w64-gcc
```
First, cross compile libffi for Windows (look up a guide) and install it to `/usr/x86_64-w64-mingw32/`.
Cross compile for Windows with the following commands:
```sh
meson setup --cross-file x86_64-w64-mingw32.txt -Ddefault_library=static --prefix=/usr/x86_64-w64-mingw32/ windows-builddir
meson compile -C windows-builddir
```
If you'd like it to be installed as a library to link with (for example with Solstice), run
```sh
meson install -C windows-builddir
```
This will install it to `/usr/x86_64-w64-mingw32/`, for convenient usage with other Windows cross compilation projects.
You may need to run `ldconfig` or reboot to update your ld cache. You may need to run `ldconfig` or reboot to update your ld cache.
## Bytecode Format ## Bytecode Format
@@ -26,3 +53,22 @@ Under the hood, Ground uses a bytecode format to store things.
`extern` no longer loads a dynamic library from /usr/lib/ground, it is now used to detail a system shared library to open. `extern` no longer loads a dynamic library from /usr/lib/ground, it is now used to detail a system shared library to open.
Format: `extern "libName" "symbolName" !functionName -returnType -argType...` Format: `extern "libName" "symbolName" !functionName -returnType -argType...`
### `getfield`, `setfield`, `callmethod`
Due to the new bytecode format, these instructions go from this format
```
getfield $parent &fields... &output
setfield &parent &fields... $input
callmethod &parent &fields... !functionName $args... &output
```
to this format:
```
getfield $parent -type (&field -type)... &output
setfield &parent -type (&field -type)... $input
callmethod &parent -type (&field -type)... !functionName $args... &output
```
so offsets for instructions can be computed before runtime.

View File

@@ -1,3 +1,13 @@
/**
* @file ground.h
* @brief Defines the interface for the GroundVM.
*
* This header has two main sections: the types, and the interface. The types are the layout of the data that the GroundVM uses, whereas the interface declares all the functions which GroundVM provides for interfacing with Ground programs. The interface resides entirely in the struct _Ground, which has pointers to implementation functions.
*
* @author Maxwell Jeffress
* @date 2026-07-12
*/
#ifndef GROUND_H #ifndef GROUND_H
#define GROUND_H #define GROUND_H
@@ -10,7 +20,6 @@
#ifdef _WIN32 #ifdef _WIN32
// TODO: check if this works // TODO: check if this works
#include <minwindef.h> #include <minwindef.h>
#define PATH_MAX MAX_PATH
#else #else
#include <limits.h> #include <limits.h>
#endif #endif
@@ -118,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,
@@ -305,8 +315,17 @@ struct GroundBytecodeFunction {
// Only for native function // Only for native function
GroundType* returnType; GroundType* returnType;
}; };
struct GroundBytecodeStruct {};
struct GroundBytecodeObject {}; struct GroundBytecodeStruct {
GroundBytecodeValue* values;
size_t size;
size_t capacity;
};
struct GroundBytecodeObject {
GroundBytecodeValue* values;
size_t size;
size_t capacity;
};
struct GroundBytecodeValue { struct GroundBytecodeValue {
union { union {
@@ -386,10 +405,14 @@ struct _Ground {
bool error; bool error;
} Flags; } Flags;
/**
* @brief Functions to create new instances of Ground-specific types. Use Ground.Free to free when no longer needed.
*/
struct { struct {
// Creates a new value of the specified type. /**
// Returns a GroundValue * @brief Creates a new GroundValue, which wraps the underlying value for the VM. Copies the input value.
*/
struct { struct {
GroundValue (*Int) (GroundInt in); GroundValue (*Int) (GroundInt in);
GroundValue (*Double) (GroundDouble in); GroundValue (*Double) (GroundDouble in);
@@ -544,6 +567,7 @@ struct _Ground {
char* (*Value)(GroundValue* value); char* (*Value)(GroundValue* value);
char* (*BytecodeValue)(GroundBytecodeValue* value); char* (*BytecodeValue)(GroundBytecodeValue* value);
char* (*Bytecode)(GroundBytecode* bytecode);
} Stringify; } Stringify;
struct { struct {

View File

@@ -115,15 +115,22 @@ sources = files(
'src/Stringify/Value.c', 'src/Stringify/Value.c',
'src/Stringify/BytecodeValue.c', 'src/Stringify/BytecodeValue.c',
'src/Stringify/Bytecode.c',
'src/Struct/addField.c', 'src/Struct/addField.c',
'src/linenoise/linenoise.c' #'src/linenoise/linenoise.c'
) )
if host_machine.system() != 'windows'
sources += ['src/linenoise/linenoise.c']
endif
cli_sources = files( cli_sources = files(
'src/cli/main.c' 'src/cli/main.c',
'src/cli/debugger.c'
) )
incdir = include_directories('include') incdir = include_directories('include')
@@ -132,7 +139,7 @@ libffi = dependency('libffi', version : '>=3.0.0')
install_headers('include/ground.h') install_headers('include/ground.h')
lib = shared_library('ground', sources, include_directories : incdir, dependencies : libffi, install : true) lib = both_libraries('ground', sources, include_directories : incdir, dependencies : libffi, install : true)
pkg.generate(lib, pkg.generate(lib,
name : 'ground', name : 'ground',

View File

@@ -1,5 +1,7 @@
#include "../../../include/ground.h" #include "../../../include/ground.h"
#ifndef _WIN32
#include "../../linenoise/linenoise.h" #include "../../linenoise/linenoise.h"
#endif
#include <stdint.h> #include <stdint.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
@@ -8,39 +10,13 @@
#include <ffi.h> #include <ffi.h>
#define HEAP_GET(heap, idx) (&(heap)->heap[(idx)]) #define HEAP_GET(heap, idx) (&(heap)->heap[(idx)])
#define HEAP_SET(heap, idx, val) ((heap)->heap[(idx)] = (val)) #define HEAP_SET(heap, idx, val) ({Ground.Free.BytecodeValue(&(heap)->heap[idx]); ((heap)->heap[(idx)] = (val));})
#define CONTINUE (struct GroundExecutionResult) { _GER_CONTINUE } #define CONTINUE (struct GroundExecutionResult) { _GER_CONTINUE }
#define JUMP(idx) (struct GroundExecutionResult) { .type = _GER_JUMP, .as.line = idx } #define JUMP(idx) (struct GroundExecutionResult) { .type = _GER_JUMP, .as.line = idx }
#define END(res) (struct GroundExecutionResult) { .type = _GER_END, .as.end = res } #define END(res) (struct GroundExecutionResult) { .type = _GER_END, .as.end = res }
#define RETURN(val) (struct GroundExecutionResult) { .type = _GER_RETURN, .as.value = val } #define RETURN(val) (struct GroundExecutionResult) { .type = _GER_RETURN, .as.value = val }
static void printValue(GroundBytecodeValue* val) {
switch (val->type.type) {
case GroundType_Int:
printf("%" PRId64, val->as.Int);
break;
case GroundType_Double:
printf("%f", val->as.Double);
break;
case GroundType_Bool:
printf(val->as.Bool ? "true" : "false");
break;
case GroundType_Char:
printf("%c", val->as.Char);
break;
case GroundType_String:
printf("%s", val->as.String.cstr);
break;
case GroundType_Function:
printf("<function>");
break;
default:
printf("<fixme opt=%d>", val->type.type);
break;
}
}
static ffi_type* ffiTypeFromGroundType(GroundType* type) { static ffi_type* ffiTypeFromGroundType(GroundType* type) {
switch (type->type) { switch (type->type) {
case GroundType_Int: return &ffi_type_sint64; case GroundType_Int: return &ffi_type_sint64;
@@ -86,22 +62,26 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
return END( HEAP_GET(heap, instruction->args.at[0])->as.Int ); return END( HEAP_GET(heap, instruction->args.at[0])->as.Int );
} }
INPUT: { INPUT: {
#ifdef _WIN32
// TODO implement line input on Windows
HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(""))));
return CONTINUE;
#else
char* input = linenoise(""); char* input = linenoise("");
HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(input)))); HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(input))));
free(input); free(input);
return CONTINUE; return CONTINUE;
#endif
} }
PRINT: { PRINT: {
for (GroundSize i = 0; i < instruction->args.len; i++) { for (GroundSize i = 0; i < instruction->args.len; i++) {
printValue(HEAP_GET(heap, instruction->args.at[i])); printf("%s ", Ground.Stringify.BytecodeValue(HEAP_GET(heap, instruction->args.at[i])));
printf(" ");
} }
return CONTINUE; return CONTINUE;
} }
PRINTLN: { PRINTLN: {
for (GroundSize i = 0; i < instruction->args.len; i++) { for (GroundSize i = 0; i < instruction->args.len; i++) {
printValue(HEAP_GET(heap, instruction->args.at[i])); printf("%s ", Ground.Stringify.BytecodeValue(HEAP_GET(heap, instruction->args.at[i])));
printf(" ");
} }
printf("\n"); printf("\n");
return CONTINUE; return CONTINUE;
@@ -109,7 +89,6 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
SET: { SET: {
if (instruction->args.at[0] != instruction->args.at[1]) { if (instruction->args.at[0] != instruction->args.at[1]) {
GroundBytecodeValue copy = Ground.Copy.BytecodeValue(HEAP_GET(heap, instruction->args.at[1])); GroundBytecodeValue copy = Ground.Copy.BytecodeValue(HEAP_GET(heap, instruction->args.at[1]));
Ground.Free.BytecodeValue(&heap->heap[instruction->args.at[0]]);
HEAP_SET(heap, instruction->args.at[0], copy); HEAP_SET(heap, instruction->args.at[0], copy);
} }
return CONTINUE; return CONTINUE;
@@ -733,6 +712,9 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
return CONTINUE; return CONTINUE;
} }
} else { } else {
for (GroundSize i = 0; i < function->closure->len; i++) {
Ground.Free.BytecodeValue(&function->closure->heap[i]);
}
free(function->closure->heap); free(function->closure->heap);
} }
@@ -744,11 +726,6 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute"); Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute");
Ground.Flags.error = true; Ground.Flags.error = true;
return CONTINUE; return CONTINUE;
} else {
// forget all values in previous closure
for (GroundSize i = 0; i < function->closure->len; i++) {
Ground.Free.BytecodeValue(&function->closure->heap[i]);
}
} }
for (GroundSize i = 0; i < heap->len; i++) { for (GroundSize i = 0; i < heap->len; i++) {
@@ -911,10 +888,248 @@ 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 = &current->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 = &current->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: {
// no-op GroundBytecodeStruct* gbs = &HEAP_GET(heap, instruction->args.at[0])->as.Struct;
// struct &structName &field OP $value &field OP $value &field OP $value...
// where:
// &field is the field to initialise
// OP is a numeric identifier:
// 0 -> none
// 1 -> init
// 2 -> set
// 3 -> attach closure to function
// $value is either a type reference (OP=0), a value reference (OP=1), or none (OP=2)
for (GroundSize i = 1; i < instruction->args.len; i++) {
GroundSize offset = instruction->args.at[i];
i++;
GroundSize op = instruction->args.at[i];
switch (op) {
case 0: break;
case 1: {
i++;
GroundBytecodeValue* type = HEAP_GET(heap, instruction->args.at[i]);
switch (type->type.type) {
case GroundType_Struct: {
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: {
switch (type->as.CoreType) {
case GroundType_Int: {
gbs->values[offset] = Ground.New.BytecodeValue(Ground.New.Value.Int(0));
break;
}
case GroundType_Double: {
gbs->values[offset] = Ground.New.BytecodeValue(Ground.New.Value.Double(0.0));
break;
}
case GroundType_String: {
gbs->values[offset] = Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String("")));
break;
}
case GroundType_Char: {
gbs->values[offset] = Ground.New.BytecodeValue(Ground.New.Value.Char(0));
break;
}
case GroundType_Bool: {
gbs->values[offset] = Ground.New.BytecodeValue(Ground.New.Value.Bool(false));
break;
}
default: break; // TODO implement all the other stuff
}
}
}
break;
}
case 2: {
i++;
gbs->values[offset] = Ground.Copy.BytecodeValue(HEAP_GET(heap, instruction->args.at[i]));
break;
}
case 3: {
GroundBytecodeFunction* function = &gbs->values[offset].as.Function;
if (function->closure == NULL) {
function->closure = malloc(sizeof(GroundBytecodeHeap));
if (function->closure == NULL) {
Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute");
Ground.Flags.error = true;
return CONTINUE;
}
} else {
for (GroundSize i = 0; i < function->closure->len; i++) {
Ground.Free.BytecodeValue(&function->closure->heap[i]);
}
free(function->closure->heap);
}
function->closure->capacity = heap->capacity;
function->closure->len = heap->len;
function->closure->heap = malloc(sizeof(GroundBytecodeValue) * heap->capacity);
if (function->closure->heap == NULL) {
Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute");
Ground.Flags.error = true;
return CONTINUE;
}
for (GroundSize i = 0; i < heap->len; i++) {
function->closure->heap[i] = Ground.Copy.BytecodeValue(&heap->heap[i]);
}
break;
}
}
}
return CONTINUE; return CONTINUE;
} }
ENDSTRUCT: { ENDSTRUCT: {
@@ -922,12 +1137,86 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
return CONTINUE; return CONTINUE;
} }
INIT: { INIT: {
GroundBytecodeValue* type = HEAP_GET(heap, instruction->args.at[1]);
switch (type->type.type) {
case GroundType_Struct: {
// copy the contents of the struct into the object
GroundBytecodeObject object = {
.size = type->as.Struct.size,
.capacity = type->as.Struct.size,
.values = malloc(sizeof(GroundBytecodeValue) * type->as.Struct.size)
};
for (GroundSize i = 0; i < type->as.Struct.size; i++) {
object.values[i] = Ground.Copy.BytecodeValue(&type->as.Struct.values[i]);
}
HEAP_SET(heap, instruction->args.at[0], ((GroundBytecodeValue) {.as.Object = object, .type = {GroundType_Object}}));
break;
}
case GroundType_CoreType: {
switch (type->as.CoreType) {
case GroundType_Int: {
HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.Int(0)));
break;
}
case GroundType_Double: {
HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.Double(0.0)));
break;
}
case GroundType_String: {
HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(""))));
break;
}
case GroundType_Char: {
HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.Char(0)));
break;
}
case GroundType_Bool: {
HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.Bool(false)));
break;
}
default: break; // TODO implement all the other stuff
}
}
default: break; // should not be reached
}
return CONTINUE; return CONTINUE;
} }
GETFIELD: { GETFIELD: {
// args: [parent_offset, field0_offset, ..., fieldF_offset, output_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("GETFIELD on non-object value in Ground.Bytecode.Instruction.execute");
Ground.Flags.error = true;
return CONTINUE;
}
current = &current->as.Object.values[instruction->args.at[i]];
}
GroundBytecodeValue* output = HEAP_GET(heap, instruction->args.at[instruction->args.len - 1]);
Ground.Free.BytecodeValue(output);
*output = Ground.Copy.BytecodeValue(current);
return CONTINUE; return CONTINUE;
} }
SETFIELD: { 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 = &current->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; return CONTINUE;
} }
USE: { USE: {

View File

@@ -138,6 +138,89 @@ static GroundBytecodeValue readValue(FILE* f) {
v.as.Function.closure = NULL; v.as.Function.closure = NULL;
break; break;
} }
case 6: {
v.type.type = GroundType_List;
uint64_t count;
fread(&count, 8, 1, f);
v.as.List.count = count;
v.as.List.capacity = count;
if (count > 0) {
v.as.List.at = malloc(sizeof(GroundBytecodeValue) * count);
if (v.as.List.at == NULL) {
Ground.Log.Error("malloc failed in Ground.Bytecode.load");
Ground.Flags.error = true;
return v;
}
for (uint64_t i = 0; i < count; i++) {
v.as.List.at[i] = readValue(f);
if (Ground.Flags.error) {
return v;
}
}
} else {
v.as.List.at = NULL;
}
break;
}
case 7: {
v.type.type = GroundType_Struct;
uint64_t size;
fread(&size, 8, 1, f);
v.as.Struct.size = size;
v.as.Struct.capacity = size;
if (size > 0) {
v.as.Struct.values = malloc(sizeof(GroundBytecodeValue) * size);
if (v.as.Struct.values == NULL) {
Ground.Log.Error("malloc failed in Ground.Bytecode.load");
Ground.Flags.error = true;
return v;
}
for (uint64_t i = 0; i < size; i++) {
v.as.Struct.values[i] = readValue(f);
if (Ground.Flags.error) {
return v;
}
}
} else {
v.as.Struct.values = NULL;
}
break;
}
case 8: {
v.type.type = GroundType_Object;
uint64_t size;
fread(&size, 8, 1, f);
v.as.Object.size = size;
v.as.Object.capacity = size;
if (size > 0) {
v.as.Object.values = malloc(sizeof(GroundBytecodeValue) * size);
if (v.as.Object.values == NULL) {
Ground.Log.Error("malloc failed in Ground.Bytecode.load");
Ground.Flags.error = true;
return v;
}
for (uint64_t i = 0; i < size; i++) {
v.as.Object.values[i] = readValue(f);
if (Ground.Flags.error) {
return v;
}
}
} else {
v.as.Object.values = NULL;
}
break;
}
case 9: {
v.type.type = GroundType_Undefined;
break;
}
case 10: {
v.type.type = GroundType_CoreType;
uint8_t coreType;
fread(&coreType, 1, 1, f);
v.as.CoreType = (enum GroundTypeType)coreType;
break;
}
default: { default: {
Ground.Log.Error("unknown value type in Ground.Bytecode.load"); Ground.Log.Error("unknown value type in Ground.Bytecode.load");
Ground.Flags.error = true; Ground.Flags.error = true;
@@ -173,7 +256,7 @@ GroundBytecode _GroundBytecodeLoad(const char* path) {
uint64_t count; uint64_t count;
fread(&count, 8, 1, f); fread(&count, 8, 1, f);
if (count > 0) { if (count > 0) {
bc.heap.heap = malloc(sizeof(GroundValue) * count); bc.heap.heap = malloc(sizeof(GroundBytecodeValue) * count);
if (bc.heap.heap == NULL) { if (bc.heap.heap == NULL) {
Ground.Log.Error("malloc failed in Ground.Bytecode.load"); Ground.Log.Error("malloc failed in Ground.Bytecode.load");
Ground.Flags.error = true; Ground.Flags.error = true;

View File

@@ -76,6 +76,48 @@ static void writeValue(FILE* f, GroundBytecodeValue* v) {
// closures are generated at runtime, not persisted // closures are generated at runtime, not persisted
break; 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: { default: {
uint8_t tag = 0xFF; uint8_t tag = 0xFF;
fwrite(&tag, 1, 1, f); fwrite(&tag, 1, 1, f);

View File

@@ -1,4 +1,5 @@
#include "../../include/ground.h" #include "../../include/ground.h"
#include <string.h>
GroundBytecodeValue _GroundCopyBytecodeValue(GroundBytecodeValue* value) { GroundBytecodeValue _GroundCopyBytecodeValue(GroundBytecodeValue* value) {
GroundBytecodeValue newValue = *value; GroundBytecodeValue newValue = *value;
@@ -7,6 +8,7 @@ GroundBytecodeValue _GroundCopyBytecodeValue(GroundBytecodeValue* value) {
case GroundType_Double: case GroundType_Double:
case GroundType_Bool: case GroundType_Bool:
case GroundType_Char: case GroundType_Char:
case GroundType_CoreType:
break; break;
case GroundType_String: { case GroundType_String: {
@@ -14,7 +16,161 @@ GroundBytecodeValue _GroundCopyBytecodeValue(GroundBytecodeValue* value) {
break; break;
} }
// TODO: Implement copying for everything else case GroundType_Object: {
GroundBytecodeObject* src = &value->as.Object;
GroundBytecodeObject dst = {
.size = src->size,
.capacity = src->capacity,
};
if (src->capacity > 0) {
dst.values = malloc(sizeof(GroundBytecodeValue) * src->capacity);
if (dst.values == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue");
Ground.Flags.error = true;
break;
}
for (GroundSize i = 0; i < src->size; i++) {
dst.values[i] = Ground.Copy.BytecodeValue(&src->values[i]);
}
}
newValue.as.Object = dst;
break;
}
case GroundType_Struct: {
GroundBytecodeStruct* src = &value->as.Struct;
GroundBytecodeStruct dst = {
.size = src->size,
.capacity = src->capacity,
};
if (src->capacity > 0) {
dst.values = malloc(sizeof(GroundBytecodeValue) * src->capacity);
if (dst.values == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue");
Ground.Flags.error = true;
break;
}
for (GroundSize i = 0; i < src->size; i++) {
dst.values[i] = Ground.Copy.BytecodeValue(&src->values[i]);
}
}
newValue.as.Struct = dst;
break;
}
case GroundType_List: {
GroundBytecodeList* src = &value->as.List;
GroundBytecodeList dst = {
.count = src->count,
.capacity = src->capacity,
};
if (src->capacity > 0) {
dst.at = malloc(sizeof(GroundBytecodeValue) * src->capacity);
if (dst.at == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue");
Ground.Flags.error = true;
break;
}
for (GroundSize i = 0; i < src->count; i++) {
dst.at[i] = Ground.Copy.BytecodeValue(&src->at[i]);
}
}
newValue.as.List = dst;
break;
}
case GroundType_Function: {
GroundBytecodeFunction* src = &value->as.Function;
GroundBytecodeFunction dst = *src;
if (src->args.at != NULL && src->args.count > 0) {
dst.args.at = malloc(sizeof(GroundFunctionArg) * src->args.count);
if (dst.args.at == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue");
Ground.Flags.error = true;
break;
}
memcpy(dst.args.at, src->args.at, sizeof(GroundFunctionArg) * src->args.count);
} else {
dst.args.at = NULL;
}
if (!src->isNativeFunction && src->program.ground != NULL) {
dst.program.ground = malloc(sizeof(GroundBytecodeProgram));
if (dst.program.ground == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue");
Ground.Flags.error = true;
free(dst.args.at);
break;
}
*dst.program.ground = *src->program.ground;
if (src->program.ground->at != NULL && src->program.ground->len > 0) {
dst.program.ground->at = malloc(sizeof(GroundBytecodeInstruction) * src->program.ground->len);
if (dst.program.ground->at == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue");
Ground.Flags.error = true;
free(dst.program.ground);
free(dst.args.at);
break;
}
for (GroundSize i = 0; i < src->program.ground->len; i++) {
dst.program.ground->at[i] = src->program.ground->at[i];
if (src->program.ground->at[i].args.at != NULL && src->program.ground->at[i].args.len > 0) {
dst.program.ground->at[i].args.at = malloc(sizeof(GroundSize) * src->program.ground->at[i].args.len);
if (dst.program.ground->at[i].args.at == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue");
Ground.Flags.error = true;
break;
}
memcpy(dst.program.ground->at[i].args.at, src->program.ground->at[i].args.at, sizeof(GroundSize) * src->program.ground->at[i].args.len);
}
}
}
}
if (src->closure != NULL) {
dst.closure = malloc(sizeof(GroundBytecodeHeap));
if (dst.closure == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue");
Ground.Flags.error = true;
break;
}
dst.closure->capacity = src->closure->capacity;
dst.closure->len = src->closure->len;
if (src->closure->heap != NULL && src->closure->len > 0) {
dst.closure->heap = malloc(sizeof(GroundBytecodeValue) * src->closure->capacity);
if (dst.closure->heap == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue");
Ground.Flags.error = true;
free(dst.closure);
break;
}
for (GroundSize i = 0; i < src->closure->len; i++) {
if (src->closure->heap[i].type.type == GroundType_Function) {
dst.closure->heap[i] = src->closure->heap[i];
} else {
dst.closure->heap[i] = Ground.Copy.BytecodeValue(&src->closure->heap[i]);
}
}
} else {
dst.closure->heap = NULL;
}
}
if (src->isNativeFunction && src->returnType != NULL) {
dst.returnType = malloc(sizeof(GroundType));
if (dst.returnType == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.BytecodeValue");
Ground.Flags.error = true;
break;
}
*dst.returnType = *src->returnType;
}
newValue.as.Function = dst;
break;
}
default: break; default: break;
} }

View File

@@ -5,7 +5,7 @@ GroundString _GroundCopyString(GroundString* in) {
string.cstr = malloc(in->len + 1); string.cstr = malloc(in->len + 1);
if (string.cstr == NULL) { if (string.cstr == NULL) {
Ground.Flags.error = false; Ground.Flags.error = true;
return string; return string;
} }

View File

@@ -3,7 +3,7 @@
void _GroundFreeArg(GroundArg* in) { void _GroundFreeArg(GroundArg* in) {
if (in->type == GroundArg_Value) { if (in->type == GroundArg_Value) {
Ground.Free.Value(&in->as.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); Ground.Free.Identifier(in->as.ref);
} }
} }

View File

@@ -2,27 +2,56 @@
void _GroundFreeBytecodeValue(GroundBytecodeValue* in) { void _GroundFreeBytecodeValue(GroundBytecodeValue* in) {
// TODO: Implement all this
switch (in->type.type) { switch (in->type.type) {
case GroundType_String: { case GroundType_String: {
Ground.Free.String(&in->as.String); Ground.Free.String(&in->as.String);
break; break;
} }
case GroundType_List: { case GroundType_List: {
// Ground.Free.List(&in->as.List); for (GroundSize i = 0; i < in->as.List.count; i++) {
Ground.Free.BytecodeValue(&in->as.List.at[i]);
}
free(in->as.List.at);
break; break;
} }
case GroundType_Function: { case GroundType_Function: {
// Ground.Free.Function(&in->as.Function); GroundBytecodeFunction* fn = &in->as.Function;
if (fn->closure != NULL) {
for (GroundSize i = 0; i < fn->closure->len; i++) {
if (fn->closure->heap[i].type.type != GroundType_Function) {
Ground.Free.BytecodeValue(&fn->closure->heap[i]);
}
}
free(fn->closure->heap);
free(fn->closure);
}
free(fn->args.at);
if (!fn->isNativeFunction && fn->program.ground != NULL) {
for (GroundSize i = 0; i < fn->program.ground->len; i++) {
free(fn->program.ground->at[i].args.at);
}
free(fn->program.ground->at);
free(fn->program.ground);
}
if (fn->isNativeFunction && fn->returnType != NULL) {
free(fn->returnType);
}
break; break;
} }
case GroundType_Struct: { case GroundType_Struct: {
// Ground.Free.Struct(&in->as.Struct); GroundBytecodeStruct* gbs = &in->as.Struct;
for (GroundSize i = 0; i < gbs->size; i++) {
Ground.Free.BytecodeValue(&gbs->values[i]);
}
free(gbs->values);
break; break;
} }
case GroundType_Object: { case GroundType_Object: {
// Ground.Free.Object(&in->as.Object); GroundBytecodeObject* gbo = &in->as.Object;
for (GroundSize i = 0; i < gbo->size; i++) {
Ground.Free.BytecodeValue(&gbo->values[i]);
}
free(gbo->values);
break; break;
} }

View File

@@ -8,5 +8,6 @@ void _GroundFreeObject(GroundObject* in) {
HASH_DEL(in->fields, s); HASH_DEL(in->fields, s);
Ground.Free.Value(&s->value); Ground.Free.Value(&s->value);
free(s);
} }
} }

View File

@@ -8,5 +8,6 @@ void _GroundFreeStruct(GroundStruct* in) {
HASH_DEL(in->fields, s); HASH_DEL(in->fields, s);
Ground.Free.Value(&s->value); Ground.Free.Value(&s->value);
free(s);
} }
} }

View File

@@ -1,4 +1,5 @@
#include "../../include/ground.h" #include "../../include/ground.h"
#include <uthash.h>
static inline void doLabels(GroundProgram* program, GroundState* state) { static inline void doLabels(GroundProgram* program, GroundState* state) {
@@ -105,67 +106,509 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground
// Set offset for each struct field // Set offset for each struct field
GroundObjectField *s, *tmp; GroundObjectField *s, *tmp;
GroundSize i = 0; GroundSize fi = 0;
HASH_ITER(hh, gs->fields, s, tmp) { HASH_ITER(hh, gs->fields, s, tmp) {
s->offset = i++; s->offset = fi++;
} }
// Now go through and add offsets to everything // Create bytecode instruction
for (GroundSize j = 0; j < inst->args.len; j++) { 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 // struct field name
GroundArg* arg = &inst->args.at[j]; arg = &inst->args.at[j];
GroundObjectField* field = NULL; GroundObjectField* field = NULL;
HASH_FIND_STR(state->variables, arg->as.ref->string, field); HASH_FIND_STR(gs->fields, arg->as.ref->string, field);
if (field == NULL) { if (field == NULL) {
Ground.Log.Error("unexpected null struct field in Ground.New.Bytecode -> doOffsets"); Ground.Log.Error("unexpected null struct field in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true; Ground.Flags.error = true;
return 0; return 0;
} }
arg->_offset = field->offset; newInst.args.at[j] = field->offset;
// 2nd arg offset is already set, but what we do next depends on it // 2nd arg is the op code, what we do next depends on it
j++; j++;
// 0 -> none // 0 -> none
// 1 -> init // 1 -> init
// 2 -> set // 2 -> set
// 3 -> attach closure to function // 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 0: case 3: break; // these do not require extra values
case 1: // but these do, so we process them case 1: // but these do, so we process them
case 2: { // it's safe to assume that literals will have case 2: {
j++; // been dealt with accordingly j++;
arg = &inst->args.at[j]; arg = &inst->args.at[j];
GroundVariable* item = NULL; 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)); item = malloc(sizeof(GroundVariable));
if (item == NULL) { if (item == NULL) {
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets"); Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true; Ground.Flags.error = true;
return 0; return 0;
} }
item->value = Ground.New.Value.Int(0); item->value = Ground.Copy.Value(&arg->as.value);
strncpy(item->name, arg->as.ref->string, 2047);
item->_offset = size++; item->_offset = size++;
snprintf(item->name, sizeof(item->name) - 1, "_._.ground_internal_constant_%zu", item->_offset);
HASH_ADD_STR(state->variables, name, item); 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; continue;
} }
case GroundInstruction_GETFIELD: { case GroundInstruction_GETFIELD: {
// getfield $parent -type (&field -type)... &field &output
// The last field does not need a type annotation.
// Bytecode: parent_offset, field0_offset, ..., fieldF_offset, output_offset
GroundInstruction* inst = &gp->at[i];
GroundSize gbiSize = 2 + ((inst->args.len - 2) / 2);
GroundBytecodeInstruction newInst = {
.type = GroundInstruction_GETFIELD,
.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);
}
arg->_offset = item->_offset;
newInst.args.at[0] = arg->_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 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;
} }
case GroundInstruction_SETFIELD: { 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];
if (valueArg->type == GroundArg_Value) {
GroundVariable* 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.Copy.Value(&valueArg->as.value);
if (Ground.Flags.error) return 0;
item->_offset = size++;
snprintf(item->name, sizeof(item->name) - 1, "_._.ground_internal_constant_%zu", item->_offset);
HASH_ADD_STR(state->variables, name, item);
valueArg->_offset = item->_offset;
newInst.args.at[gbiSize - 1] = item->_offset;
} else {
valueArg->_offset = item->_offset;
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; 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;
} }

View File

@@ -1,5 +1,6 @@
#include "../../include/ground.h" #include "../../include/ground.h"
#include <uthash.h> #include <uthash.h>
#include <string.h>
static inline GroundBytecodeFunction doFunction(GroundFunction* function) { static inline GroundBytecodeFunction doFunction(GroundFunction* function) {
GroundBytecodeFunction bf = { GroundBytecodeFunction bf = {
@@ -47,11 +48,72 @@ static inline GroundBytecodeFunction doFunction(GroundFunction* function) {
// Now convert the program to bytecode // Now convert the program to bytecode
GroundBytecode bytecode = Ground.New.Bytecode(function->program.ground, &state); GroundBytecode bytecode = Ground.New.Bytecode(function->program.ground, &state);
*bf.program.ground = bytecode.program; *bf.program.ground = bytecode.program;
for (GroundSize i = 0; i < bytecode.heap.len; i++) {
Ground.Free.BytecodeValue(&bytecode.heap.heap[i]);
}
free(bytecode.heap.heap); free(bytecode.heap.heap);
return bf; return bf;
} }
static inline GroundBytecodeStruct doStruct(GroundStruct* gs) {
GroundBytecodeStruct gbs = {
.values = malloc(sizeof(GroundBytecodeValue) * 16),
.capacity = 16,
.size = 0
};
GroundObjectField *s, *tmp;
HASH_ITER(hh, gs->fields, s, tmp) {
if (gbs.size >= gbs.capacity) {
GroundBytecodeValue* tmp = malloc(sizeof(GroundBytecodeValue) * gbs.capacity * 2);
if (tmp == NULL) {
Ground.Log.Error("malloc failed in Ground.New.BytecodeValue -> doStruct");
Ground.Flags.error = true;
return gbs;
}
memcpy(tmp, gbs.values, sizeof(GroundBytecodeValue) * gbs.size);
free(gbs.values);
gbs.values = tmp;
gbs.capacity *= 2;
}
gbs.values[gbs.size] = Ground.New.BytecodeValue(s->value);
gbs.size++;
}
return gbs;
}
static inline GroundBytecodeObject doObject(GroundObject* go) {
GroundBytecodeObject gbo = {
.values = malloc(sizeof(GroundBytecodeValue) * 16),
.capacity = 16,
.size = 0
};
GroundObjectField *s, *tmp;
HASH_ITER(hh, go->fields, s, tmp) {
if (gbo.size >= gbo.capacity) {
GroundBytecodeValue* tmp = malloc(sizeof(GroundBytecodeValue) * gbo.capacity * 2);
if (tmp == NULL) {
Ground.Log.Error("malloc failed in Ground.New.BytecodeValue -> doObject");
Ground.Flags.error = true;
return gbo;
}
memcpy(tmp, gbo.values, sizeof(GroundBytecodeValue) * gbo.size);
free(gbo.values);
gbo.values = tmp;
gbo.capacity *= 2;
}
gbo.values[gbo.size] = Ground.New.BytecodeValue(s->value);
gbo.size++;
}
return gbo;
}
GroundBytecodeValue _GroundNewBytecodeValue(GroundValue value) { GroundBytecodeValue _GroundNewBytecodeValue(GroundValue value) {
GroundBytecodeValue bv = { GroundBytecodeValue bv = {
@@ -89,11 +151,11 @@ GroundBytecodeValue _GroundNewBytecodeValue(GroundValue value) {
break; break;
} }
case GroundType_Struct: { case GroundType_Struct: {
// TODO - convert to bytecode struct bv.as.Struct = doStruct(&value.as.Struct);
break; break;
} }
case GroundType_Object: { case GroundType_Object: {
// TODO - convert to bytecode object bv.as.Object = doObject(&value.as.Object);
break; break;
} }
case GroundType_CoreType: { case GroundType_CoreType: {

View File

@@ -112,7 +112,7 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
GroundStruct gs = Ground.New.Struct(); GroundStruct gs = Ground.New.Struct();
// The aim is to compile an instruction which looks like this: // The aim is to compile an instruction which looks like this:
// struct &field OP $value &field OP $value &field OP $value... // struct &structName &field OP $value &field OP $value &field OP $value...
// where: // where:
// &field is the field to initialise // &field is the field to initialise
// OP is a numeric identifier: // OP is a numeric identifier:
@@ -132,6 +132,7 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
// Parse struct body // Parse struct body
bool parsing = true; bool parsing = true;
GroundSize fieldCount = 0;
while (parsing) { while (parsing) {
(*i)++; (*i)++;
if (*i >= program->len) { if (*i >= program->len) {
@@ -152,10 +153,16 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
} }
snprintf(field->name, 2047, "%s", instruction->args.at[0].as.ref->string); snprintf(field->name, 2047, "%s", instruction->args.at[0].as.ref->string);
field->offset = fieldCount++;
// If we know the value type now, add it to the instruction // If we know the value type now, add it to the instruction
if (instruction->args.at[1].type == GroundArg_Value) { if (instruction->args.at[1].type == GroundArg_Value) {
field->value = Ground.Copy.Value(&instruction->args.at[1].as.value); field->value = Ground.Copy.Value(&instruction->args.at[1].as.value);
HASH_ADD_STR(gs.fields, name, field);
// set &field $value -> &field 2 $value
Ground.Instruction.append(&output, Ground.Copy.Arg(&instruction->args.at[0]));
Ground.Instruction.append(&output, (GroundArg) {.type = GroundArg_DirectRef, ._offset = 2});
Ground.Instruction.append(&output, Ground.Copy.Arg(&instruction->args.at[1]));
break; break;
} }
@@ -176,31 +183,24 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
} }
snprintf(field->name, 2047, "%s", instruction->args.at[0].as.ref->string); snprintf(field->name, 2047, "%s", instruction->args.at[0].as.ref->string);
field->offset = fieldCount++;
// If we know the type now, add it to the instruction // If we know the type now, add it to the instruction
if (strcmp(field->name, "int") == 0) { char* typeName = instruction->args.at[1].as.ref->string;
if (strcmp(typeName, "int") == 0) {
field->value = Ground.New.Value.Int(0); field->value = Ground.New.Value.Int(0);
break; } else if (strcmp(typeName, "double") == 0) {
}
if (strcmp(field->name, "double") == 0) {
field->value = Ground.New.Value.Double(0.0); field->value = Ground.New.Value.Double(0.0);
break; } else if (strcmp(typeName, "string") == 0) {
}
if (strcmp(field->name, "string") == 0) {
field->value = Ground.New.Value.String(Ground.New.String("")); field->value = Ground.New.Value.String(Ground.New.String(""));
break; } else if (strcmp(typeName, "char") == 0) {
}
if (strcmp(field->name, "char") == 0) {
field->value = Ground.New.Value.Char(0); field->value = Ground.New.Value.Char(0);
break; } else if (strcmp(typeName, "bool") == 0) {
}
if (strcmp(field->name, "bool") == 0) {
field->value = Ground.New.Value.Bool(false); field->value = Ground.New.Value.Bool(false);
break; } else if (strcmp(typeName, "function") == 0) {
}
if (strcmp(field->name, "function") == 0) {
field->value = Ground.New.Value.Function(Ground.New.Function(state)); field->value = Ground.New.Value.Function(Ground.New.Function(state));
break; } else {
field->value = Ground.New.Value.Int(0);
} }
HASH_ADD_STR(gs.fields, name, field); HASH_ADD_STR(gs.fields, name, field);
@@ -222,6 +222,38 @@ 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++;
if (fnVar != NULL && fnVar->value.type.type == GroundType_Function) {
field->value = Ground.Copy.Value(&fnVar->value);
}
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)));
@@ -229,19 +261,22 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
newProgram->len--; newProgram->len--;
// Add 3 to signal addition of closure // Add 3 to signal addition of closure
Ground.Instruction.append(&output, (GroundArg) {.type = GroundArg_DirectRef, ._offset = 1}); Ground.Instruction.append(&output, (GroundArg) {.type = GroundArg_DirectRef, ._offset = 3});
break; break;
} }
case GroundInstruction_ENDSTRUCT: { case GroundInstruction_ENDSTRUCT: {
parsing = false; parsing = false;
break;
} }
default: { default: {
Ground.Log.Error("invalid instruction inside struct in "); Ground.Log.Error("invalid instruction inside struct in Ground.Program.Preprocess -> doStruct");
} }
} }
} }
Ground.Program.append(newProgram, output);
GroundVariable* variable = malloc(sizeof(GroundVariable)); GroundVariable* variable = malloc(sizeof(GroundVariable));
if (variable == NULL) { if (variable == NULL) {
Ground.Log.Error("malloc failed in Ground.Program.Preprocess -> doStruct"); Ground.Log.Error("malloc failed in Ground.Program.Preprocess -> doStruct");

316
src/Stringify/Bytecode.c Normal file
View File

@@ -0,0 +1,316 @@
#include "../../include/ground.h"
#include "../include/estr.h"
char* _GroundStringifyBytecode(GroundBytecode* bytecode) {
Estr estr = CREATE_ESTR("");
APPEND_ESTR(estr, "Bytecode Heap:\n")
for (GroundSize i = 0; i < bytecode->heap.len; i++) {
char buf[32];
snprintf(buf, 32, "%zu", i);
char* bv = Ground.Stringify.BytecodeValue(&bytecode->heap.heap[i]);
APPEND_ESTR(estr, " ")
APPEND_ESTR(estr, buf);
APPEND_ESTR(estr, ": ");
APPEND_ESTR(estr, bv);
APPEND_ESTR(estr, "\n");
free(bv);
}
APPEND_ESTR(estr, "Program:\n");
for (GroundSize i = 0; i < bytecode->program.len; i++) {
GroundBytecodeInstruction* instruction = &bytecode->program.at[i];
char buf[32];
snprintf(buf, 32, "%zu", i);
APPEND_ESTR(estr, " ")
APPEND_ESTR(estr, buf);
APPEND_ESTR(estr, ": ");
switch (instruction->type) {
case GroundInstruction_IF: {
APPEND_ESTR(estr, "IF ");
break;
}
case GroundInstruction_JUMP: {
APPEND_ESTR(estr, "JUMP ");
break;
}
case GroundInstruction_END: {
APPEND_ESTR(estr, "END ");
break;
}
case GroundInstruction_INPUT: {
APPEND_ESTR(estr, "INPUT ");
break;
}
case GroundInstruction_PRINT: {
APPEND_ESTR(estr, "PRINT ");
break;
}
case GroundInstruction_PRINTLN: {
APPEND_ESTR(estr, "PRINTLN ");
break;
}
case GroundInstruction_SET: {
APPEND_ESTR(estr, "SET ");
break;
}
case GroundInstruction_GETTYPE: {
APPEND_ESTR(estr, "GETTYPE ");
break;
}
case GroundInstruction_EXISTS: {
APPEND_ESTR(estr, "EXISTS ");
break;
}
case GroundInstruction_SETLIST: {
APPEND_ESTR(estr, "SETLIST ");
break;
}
case GroundInstruction_SETLISTAT: {
APPEND_ESTR(estr, "SETLISTAT ");
break;
}
case GroundInstruction_GETLISTAT: {
APPEND_ESTR(estr, "GETLISTAT ");
break;
}
case GroundInstruction_GETLISTSIZE: {
APPEND_ESTR(estr, "GETLISTSIZE ");
break;
}
case GroundInstruction_LISTAPPEND: {
APPEND_ESTR(estr, "LISTAPPEND ");
break;
}
case GroundInstruction_GETSTRSIZE: {
APPEND_ESTR(estr, "GETSTRSIZE ");
break;
}
case GroundInstruction_GETSTRCHARAT: {
APPEND_ESTR(estr, "GETSTRCHARAT ");
break;
}
case GroundInstruction_ADD: {
APPEND_ESTR(estr, "ADD ");
break;
}
case GroundInstruction_SUBTRACT: {
APPEND_ESTR(estr, "SUBTRACT ");
break;
}
case GroundInstruction_MULTIPLY: {
APPEND_ESTR(estr, "MULTIPLY ");
break;
}
case GroundInstruction_DIVIDE: {
APPEND_ESTR(estr, "DIVIDE ");
break;
}
case GroundInstruction_EQUAL: {
APPEND_ESTR(estr, "EQUAL ");
break;
}
case GroundInstruction_INEQUAL: {
APPEND_ESTR(estr, "INEQUAL ");
break;
}
case GroundInstruction_NOT: {
APPEND_ESTR(estr, "NOT ");
break;
}
case GroundInstruction_GREATER: {
APPEND_ESTR(estr, "GREATER ");
break;
}
case GroundInstruction_LESSER: {
APPEND_ESTR(estr, "LESSER ");
break;
}
case GroundInstruction_AND: {
APPEND_ESTR(estr, "AND ");
break;
}
case GroundInstruction_OR: {
APPEND_ESTR(estr, "OR ");
break;
}
case GroundInstruction_XOR: {
APPEND_ESTR(estr, "XOR ");
break;
}
case GroundInstruction_NEG: {
APPEND_ESTR(estr, "NEG ");
break;
}
case GroundInstruction_SHIFT: {
APPEND_ESTR(estr, "SHIFT ");
break;
}
case GroundInstruction_STOI: {
APPEND_ESTR(estr, "STOI ");
break;
}
case GroundInstruction_STOD: {
APPEND_ESTR(estr, "STOD ");
break;
}
case GroundInstruction_ITOC: {
APPEND_ESTR(estr, "ITOC ");
break;
}
case GroundInstruction_CTOI: {
APPEND_ESTR(estr, "CTOI ");
break;
}
case GroundInstruction_TOSTRING: {
APPEND_ESTR(estr, "TOSTRING ");
break;
}
case GroundInstruction_FUN: {
APPEND_ESTR(estr, "FUN ");
break;
}
case GroundInstruction_RETURN: {
APPEND_ESTR(estr, "RETURN ");
break;
}
case GroundInstruction_ENDFUN: {
APPEND_ESTR(estr, "ENDFUN ");
break;
}
case GroundInstruction_CALL: {
APPEND_ESTR(estr, "CALL ");
break;
}
case GroundInstruction_CALLMETHOD: {
APPEND_ESTR(estr, "CALLMETHOD ");
break;
}
case GroundInstruction_STRUCT: {
APPEND_ESTR(estr, "STRUCT ");
break;
}
case GroundInstruction_ENDSTRUCT: {
APPEND_ESTR(estr, "ENDSTRUCT ");
break;
}
case GroundInstruction_INIT: {
APPEND_ESTR(estr, "INIT ");
break;
}
case GroundInstruction_GETFIELD: {
APPEND_ESTR(estr, "GETFIELD ");
break;
}
case GroundInstruction_SETFIELD: {
APPEND_ESTR(estr, "SETFIELD ");
break;
}
case GroundInstruction_USE: {
APPEND_ESTR(estr, "USE ");
break;
}
case GroundInstruction_EXTERN: {
APPEND_ESTR(estr, "EXTERN ");
break;
}
case GroundInstruction_CREATELABEL: {
APPEND_ESTR(estr, "CREATELABEL ");
break;
}
case GroundInstruction_PAUSE: {
APPEND_ESTR(estr, "PAUSE ");
break;
}
case GroundInstruction_DROP: {
APPEND_ESTR(estr, "DROP ");
break;
}
case GroundInstruction_LICENSE: {
APPEND_ESTR(estr, "LICENSE ");
break;
}
case GroundInstruction_ERROR: {
APPEND_ESTR(estr, "ERROR ");
break;
}
case GroundInstruction_THROW: {
APPEND_ESTR(estr, "THROW ");
break;
}
case GroundInstruction_CATCH: {
APPEND_ESTR(estr, "CATCH ");
break;
}
}
for (GroundSize j = 0; j < instruction->args.len; j++) {
char buf[32];
snprintf(buf, 32, "%zu", instruction->args.at[j]);
APPEND_ESTR(estr, buf);
APPEND_ESTR(estr, " ");
}
APPEND_ESTR(estr, "\n");
}
return estr.str;
}

View File

@@ -1,4 +1,5 @@
#include "../../include/ground.h" #include "../../include/ground.h"
#include "../include/estr.h"
#include <inttypes.h> #include <inttypes.h>
char* _GroundStringifyBytecodeValue(GroundBytecodeValue* value) { char* _GroundStringifyBytecodeValue(GroundBytecodeValue* value) {
@@ -50,13 +51,101 @@ char* _GroundStringifyBytecodeValue(GroundBytecodeValue* value) {
// TODO implement list stringification // TODO implement list stringification
} }
case GroundType_Function: { case GroundType_Function: {
// TODO implement function stringification char* buf = malloc(sizeof("<Function>"));
if (buf == NULL) {
Ground.Flags.error = true;
Ground.Log.Error("malloc failed in Ground.Stringify.Value");
return NULL;
}
sprintf(buf, "<Function>");
return buf;
} }
case GroundType_Struct: { case GroundType_Struct: {
// TODO implement struct stringification Estr str = CREATE_ESTR("<struct fields: { ");
for (GroundSize i = 0; i < value->as.Struct.size; i++) {
char* field = Ground.Stringify.BytecodeValue(&value->as.Struct.values[i]);
if (i != 0) {
APPEND_ESTR(str, ", ");
}
APPEND_ESTR(str, field);
free(field);
}
APPEND_ESTR(str, " }>");
return str.str;
} }
case GroundType_Object: { case GroundType_Object: {
// TODO implement object stringification Estr str = CREATE_ESTR("<object fields: { ");
for (GroundSize i = 0; i < value->as.Object.size; i++) {
if (i != 0) {
APPEND_ESTR(str, ", ");
}
char* field = Ground.Stringify.BytecodeValue(&value->as.Object.values[i]);
APPEND_ESTR(str, field);
free(field);
}
APPEND_ESTR(str, " }>");
return str.str;
}
case GroundType_CoreType: {
Estr str = CREATE_ESTR("<CoreType ");
switch (value->as.CoreType) {
case GroundType_Undefined: {
APPEND_ESTR(str, "Undefined");
break;
}
case GroundType_Int: {
APPEND_ESTR(str, "Int");
break;
}
case GroundType_Double: {
APPEND_ESTR(str, "Double");
break;
}
case GroundType_Char: {
APPEND_ESTR(str, "Char");
break;
}
case GroundType_Bool: {
APPEND_ESTR(str, "Bool");
break;
}
case GroundType_String: {
APPEND_ESTR(str, "String");
break;
}
case GroundType_List: {
APPEND_ESTR(str, "List");
break;
}
case GroundType_Function: {
APPEND_ESTR(str, "Function");
break;
}
case GroundType_Struct: {
APPEND_ESTR(str, "Struct");
break;
}
case GroundType_Object: {
APPEND_ESTR(str, "Object");
break;
}
case GroundType_CoreType: {
APPEND_ESTR(str, "CoreType");
break;
}
}
APPEND_ESTR(str, ">");
return str.str;
}
case GroundType_Undefined: {
char* buf = malloc(sizeof("<Undefined>"));
if (buf == NULL) {
Ground.Flags.error = true;
Ground.Log.Error("malloc failed in Ground.Stringify.Value");
return NULL;
}
sprintf(buf, "<Undefined>");
return buf;
} }
} }

View File

@@ -291,8 +291,13 @@ char* _GroundStringifyInstruction(GroundInstruction* instruction) {
} }
APPEND_ESTR(estr, arg); APPEND_ESTR(estr, arg);
APPEND_ESTR(estr, " ");
free(arg); free(arg);
} }
if (!estr.shouldBeFreed) {
char* result = strdup(estr.str);
return result;
}
return estr.str; return estr.str;
} }

View File

@@ -16,7 +16,7 @@ char* _GroundStringifyProgram(GroundProgram* program) {
} }
APPEND_ESTR(estr, arg); APPEND_ESTR(estr, arg);
APPEND_ESTR(estr, " "); APPEND_ESTR(estr, "\n");
free(arg); free(arg);
} }

View File

@@ -50,7 +50,14 @@ char* _GroundStringifyValue(GroundValue* value) {
// TODO implement list stringification // TODO implement list stringification
} }
case GroundType_Function: { case GroundType_Function: {
// TODO implement function stringification char* buf = malloc(sizeof("<Function>"));
if (buf == NULL) {
Ground.Flags.error = true;
Ground.Log.Error("malloc failed in Ground.Stringify.Value");
return NULL;
}
sprintf(buf, "<Function>");
return buf;
} }
case GroundType_Struct: { case GroundType_Struct: {
// TODO implement struct stringification // TODO implement struct stringification
@@ -58,6 +65,26 @@ char* _GroundStringifyValue(GroundValue* value) {
case GroundType_Object: { case GroundType_Object: {
// TODO implement object stringification // TODO implement object stringification
} }
case GroundType_CoreType: {
char* buf = malloc(sizeof("<CoreType>"));
if (buf == NULL) {
Ground.Flags.error = true;
Ground.Log.Error("malloc failed in Ground.Stringify.Value");
return NULL;
}
sprintf(buf, "<CoreType>");
return buf;
}
case GroundType_Undefined: {
char* buf = malloc(sizeof("<Undefined>"));
if (buf == NULL) {
Ground.Flags.error = true;
Ground.Log.Error("malloc failed in Ground.Stringify.Value");
return NULL;
}
sprintf(buf, "<Undefined>");
return buf;
}
} }

577
src/cli/debugger.c Normal file
View File

@@ -0,0 +1,577 @@
#include "../../include/ground.h"
#include "../linenoise/linenoise.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <inttypes.h>
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;
bool* breakpoints;
GroundSize programLen;
bool hasStarted;
bool hasFinished;
} Debugger;
enum DebuggerStatus {
DBG_DONE, DBG_CONTINUE, DBG_WAIT, DBG_STEP
};
static void printInstructionAt(GroundBytecode* bytecode, GroundSize idx) {
if (idx >= bytecode->program.len) {
printf("Index out of bounds\n");
return;
}
GroundBytecodeInstruction* instruction = &bytecode->program.at[idx];
const char* name = "UNKNOWN";
switch (instruction->type) {
case GroundInstruction_IF: name = "IF"; break;
case GroundInstruction_JUMP: name = "JUMP"; break;
case GroundInstruction_END: name = "END"; break;
case GroundInstruction_INPUT: name = "INPUT"; break;
case GroundInstruction_PRINT: name = "PRINT"; break;
case GroundInstruction_PRINTLN: name = "PRINTLN"; break;
case GroundInstruction_SET: name = "SET"; break;
case GroundInstruction_GETTYPE: name = "GETTYPE"; break;
case GroundInstruction_EXISTS: name = "EXISTS"; break;
case GroundInstruction_SETLIST: name = "SETLIST"; break;
case GroundInstruction_SETLISTAT: name = "SETLISTAT"; break;
case GroundInstruction_GETLISTAT: name = "GETLISTAT"; break;
case GroundInstruction_GETLISTSIZE: name = "GETLISTSIZE"; break;
case GroundInstruction_LISTAPPEND: name = "LISTAPPEND"; break;
case GroundInstruction_GETSTRSIZE: name = "GETSTRSIZE"; break;
case GroundInstruction_GETSTRCHARAT: name = "GETSTRCHARAT"; break;
case GroundInstruction_ADD: name = "ADD"; break;
case GroundInstruction_SUBTRACT: name = "SUBTRACT"; break;
case GroundInstruction_MULTIPLY: name = "MULTIPLY"; break;
case GroundInstruction_DIVIDE: name = "DIVIDE"; break;
case GroundInstruction_EQUAL: name = "EQUAL"; break;
case GroundInstruction_INEQUAL: name = "INEQUAL"; break;
case GroundInstruction_NOT: name = "NOT"; break;
case GroundInstruction_GREATER: name = "GREATER"; break;
case GroundInstruction_LESSER: name = "LESSER"; break;
case GroundInstruction_AND: name = "AND"; break;
case GroundInstruction_OR: name = "OR"; break;
case GroundInstruction_XOR: name = "XOR"; break;
case GroundInstruction_NEG: name = "NEG"; break;
case GroundInstruction_SHIFT: name = "SHIFT"; break;
case GroundInstruction_STOI: name = "STOI"; break;
case GroundInstruction_STOD: name = "STOD"; break;
case GroundInstruction_ITOC: name = "ITOC"; break;
case GroundInstruction_CTOI: name = "CTOI"; break;
case GroundInstruction_TOSTRING: name = "TOSTRING"; break;
case GroundInstruction_FUN: name = "FUN"; break;
case GroundInstruction_RETURN: name = "RETURN"; break;
case GroundInstruction_ENDFUN: name = "ENDFUN"; break;
case GroundInstruction_CALL: name = "CALL"; break;
case GroundInstruction_CALLMETHOD: name = "CALLMETHOD"; break;
case GroundInstruction_STRUCT: name = "STRUCT"; break;
case GroundInstruction_ENDSTRUCT: name = "ENDSTRUCT"; break;
case GroundInstruction_INIT: name = "INIT"; break;
case GroundInstruction_GETFIELD: name = "GETFIELD"; break;
case GroundInstruction_SETFIELD: name = "SETFIELD"; break;
case GroundInstruction_USE: name = "USE"; break;
case GroundInstruction_EXTERN: name = "EXTERN"; break;
case GroundInstruction_CREATELABEL: name = "CREATELABEL"; break;
case GroundInstruction_PAUSE: name = "PAUSE"; break;
case GroundInstruction_DROP: name = "DROP"; break;
case GroundInstruction_LICENSE: name = "LICENSE"; break;
case GroundInstruction_ERROR: name = "ERROR"; break;
case GroundInstruction_THROW: name = "THROW"; break;
case GroundInstruction_CATCH: name = "CATCH"; break;
}
printf(" %zu: %s ", idx, name);
for (GroundSize i = 0; i < instruction->args.len; i++) {
printf("%zu ", instruction->args.at[i]);
}
printf("\n");
}
static inline bool parseValue(const char* str, GroundBytecodeValue* outVal) {
enum GroundTypeType inferredType = GroundType_Undefined;
size_t len = strlen(str);
if (strcmp(str, "true") == 0 || strcmp(str, "false") == 0) {
inferredType = GroundType_Bool;
} else if (len >= 2 && str[0] == '"' && str[len - 1] == '"') {
inferredType = GroundType_String;
} else if (len >= 2 && str[0] == '\'' && str[len - 1] == '\'') {
if (len == 3) {
inferredType = GroundType_Char;
} else {
inferredType = GroundType_String;
}
} else {
// check if double or int
char* endptr;
bool has_dot = false;
for (const char* p = str; *p; p++) {
if (*p == '.' || *p == 'e' || *p == 'E') {
has_dot = true;
break;
}
}
if (has_dot) {
strtod(str, &endptr);
if (endptr != str && *endptr == '\0') {
inferredType = GroundType_Double;
}
} else {
strtoll(str, &endptr, 10);
if (endptr != str && *endptr == '\0') {
inferredType = GroundType_Int;
}
}
}
if (inferredType == GroundType_Undefined) {
inferredType = GroundType_String;
}
outVal->type.type = inferredType;
switch (inferredType) {
case GroundType_Int: {
char* endptr;
outVal->as.Int = strtoll(str, &endptr, 10);
if (endptr == str || *endptr != '\0') {
return false;
}
return true;
}
case GroundType_Double: {
char* endptr;
outVal->as.Double = strtod(str, &endptr);
if (endptr == str || *endptr != '\0') {
return false;
}
return true;
}
case GroundType_Bool: {
if (strcmp(str, "true") == 0 || strcmp(str, "1") == 0) {
outVal->as.Bool = true;
return true;
} else if (strcmp(str, "false") == 0 || strcmp(str, "0") == 0) {
outVal->as.Bool = false;
return true;
}
return false;
}
case GroundType_Char: {
if (str[0] == '\'' && str[1] != '\0' && str[2] == '\'' && str[3] == '\0') {
outVal->as.Char = str[1];
} else {
outVal->as.Char = str[0];
}
return true;
}
case GroundType_String: {
if (len >= 2 && str[0] == '"' && str[len - 1] == '"') {
outVal->as.String.len = len - 2;
outVal->as.String.cstr = malloc(len - 1);
if (outVal->as.String.cstr) {
memcpy(outVal->as.String.cstr, str + 1, len - 2);
outVal->as.String.cstr[len - 2] = '\0';
}
} else if (len >= 2 && str[0] == '\'' && str[len - 1] == '\'') {
outVal->as.String.len = len - 2;
outVal->as.String.cstr = malloc(len - 1);
if (outVal->as.String.cstr) {
memcpy(outVal->as.String.cstr, str + 1, len - 2);
outVal->as.String.cstr[len - 2] = '\0';
}
} else {
outVal->as.String.len = len;
outVal->as.String.cstr = malloc(len + 1);
if (outVal->as.String.cstr) {
strcpy(outVal->as.String.cstr, str);
}
}
return outVal->as.String.cstr != NULL;
}
default:
return false;
}
}
static inline enum DebuggerStatus runCommand(char* input, GroundBytecode* bytecode, Debugger* debugger) {
char* inputCopy = strdup(input);
DebugCommand cmd = parseCommand(input);
if (cmd.command == NULL) {
free(inputCopy);
return DBG_WAIT;
}
if (strcmp(cmd.command, "help") == 0) {
printf(
"Commands: \n"
" help - Show this help message\n"
" start - Start or restart program execution\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"
);
free(inputCopy);
return DBG_WAIT;
}
if (strcmp(cmd.command, "start") == 0) {
debugger->currentInst = 0;
debugger->hasStarted = true;
debugger->hasFinished = false;
printf("Started debugging program. Current instruction:\n");
printInstructionAt(bytecode, debugger->currentInst);
free(inputCopy);
return DBG_WAIT;
}
if (strcmp(cmd.command, "continue") == 0) {
free(inputCopy);
return DBG_CONTINUE;
}
if (strcmp(cmd.command, "step") == 0) {
free(inputCopy);
return DBG_STEP;
}
if (strcmp(cmd.command, "break") == 0) {
if (cmd.args.count < 1) {
printf("Usage: break [idx]\n");
free(inputCopy);
return DBG_WAIT;
}
char* endptr;
GroundSize idx = strtoull(cmd.args.at[0], &endptr, 10);
if (endptr == cmd.args.at[0] || *endptr != '\0') {
printf("Invalid instruction index\n");
free(inputCopy);
return DBG_WAIT;
}
if (idx >= bytecode->program.len) {
printf("Instruction index out of bounds (0 to %zu)\n", bytecode->program.len - 1);
free(inputCopy);
return DBG_WAIT;
}
debugger->breakpoints[idx] = true;
printf("Breakpoint set at instruction %zu\n", idx);
free(inputCopy);
return DBG_WAIT;
}
if (strcmp(cmd.command, "exit") == 0) {
free(inputCopy);
return DBG_DONE;
}
if (strcmp(cmd.command, "dump") == 0) {
char* str = Ground.Stringify.Bytecode(bytecode);
if (str) {
printf("%s", str);
free(str);
}
free(inputCopy);
return DBG_WAIT;
}
if (strcmp(cmd.command, "view") == 0) {
if (cmd.args.count < 1) {
printf("Usage: view [slot]\n");
free(inputCopy);
return DBG_WAIT;
}
char* endptr;
GroundSize slot = strtoull(cmd.args.at[0], &endptr, 10);
if (endptr == cmd.args.at[0] || *endptr != '\0') {
printf("Invalid slot index\n");
free(inputCopy);
return DBG_WAIT;
}
if (slot >= bytecode->heap.len) {
printf("Slot index out of bounds (0 to %zu)\n", bytecode->heap.len - 1);
free(inputCopy);
return DBG_WAIT;
}
char* str = Ground.Stringify.BytecodeValue(&bytecode->heap.heap[slot]);
if (str) {
printf("Slot %zu: %s\n", slot, str);
free(str);
} else {
printf("Slot %zu: (failed to stringify)\n", slot);
}
free(inputCopy);
return DBG_WAIT;
}
if (strcmp(cmd.command, "set") == 0) {
if (cmd.args.count < 2) {
printf("Usage: set [slot] [value]\n");
free(inputCopy);
return DBG_WAIT;
}
char* endptr;
GroundSize slot = strtoull(cmd.args.at[0], &endptr, 10);
if (endptr == cmd.args.at[0] || *endptr != '\0') {
printf("Invalid slot index\n");
free(inputCopy);
return DBG_WAIT;
}
if (slot >= bytecode->heap.len) {
printf("Slot index out of bounds (0 to %zu)\n", bytecode->heap.len - 1);
free(inputCopy);
return DBG_WAIT;
}
char* p = strstr(inputCopy, cmd.args.at[0]);
if (p) {
p += strlen(cmd.args.at[0]);
while (*p == ' ') p++;
GroundBytecodeValue newVal;
memset(&newVal, 0, sizeof(newVal));
if (parseValue(p, &newVal)) {
Ground.Free.BytecodeValue(&bytecode->heap.heap[slot]);
bytecode->heap.heap[slot] = newVal;
char* val_repr = Ground.Stringify.BytecodeValue(&bytecode->heap.heap[slot]);
printf("Slot %zu set to %s\n", slot, val_repr ? val_repr : p);
if (val_repr) free(val_repr);
} else {
printf("Failed to parse value '%s'\n", p);
}
}
free(inputCopy);
return DBG_WAIT;
}
if (strcmp(cmd.command, "copy") == 0) {
if (cmd.args.count < 2) {
printf("Usage: copy [slot1] [slot2]\n");
free(inputCopy);
return DBG_WAIT;
}
char* endptr;
GroundSize slot1 = strtoull(cmd.args.at[0], &endptr, 10);
if (endptr == cmd.args.at[0] || *endptr != '\0') {
printf("Invalid slot1 index\n");
free(inputCopy);
return DBG_WAIT;
}
GroundSize slot2 = strtoull(cmd.args.at[1], &endptr, 10);
if (endptr == cmd.args.at[1] || *endptr != '\0') {
printf("Invalid slot2 index\n");
free(inputCopy);
return DBG_WAIT;
}
if (slot1 >= bytecode->heap.len || slot2 >= bytecode->heap.len) {
printf("Slot index out of bounds (0 to %zu)\n", bytecode->heap.len - 1);
free(inputCopy);
return DBG_WAIT;
}
Ground.Free.BytecodeValue(&bytecode->heap.heap[slot1]);
bytecode->heap.heap[slot1] = Ground.Copy.BytecodeValue(&bytecode->heap.heap[slot2]);
if (Ground.Flags.error) {
printf("Failed to copy slot %zu to slot %zu\n", slot2, slot1);
Ground.Flags.error = false;
} else {
char* val_repr = Ground.Stringify.BytecodeValue(&bytecode->heap.heap[slot1]);
printf("Copied slot %zu to slot %zu. New value: %s\n", slot2, slot1, val_repr ? val_repr : "");
if (val_repr) free(val_repr);
}
free(inputCopy);
return DBG_WAIT;
}
fprintf(stderr, "Unknown command %s\n", cmd.command);
free(inputCopy);
return DBG_WAIT;
}
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;
debugger.currentInst = 0;
debugger.programLen = bytecode->program.len;
debugger.breakpoints = calloc(debugger.programLen, sizeof(bool));
debugger.hasStarted = false;
debugger.hasFinished = false;
for (;;) {
char* input = linenoise("> ");
if (input == NULL) {
free(debugger.breakpoints);
return;
}
if (input[0] != '\0') {
linenoiseHistoryAdd(input);
}
switch (runCommand(input, bytecode, &debugger)) {
case DBG_DONE: {
free(debugger.breakpoints);
free(input);
return;
}
case DBG_CONTINUE: {
if (debugger.hasFinished) {
printf("Program has finished execution. Use 'start' to restart.\n");
break;
}
if (!debugger.hasStarted) {
debugger.hasStarted = true;
debugger.currentInst = 0;
printf("Started debugging program.\n");
}
bool first_step = true;
while (debugger.currentInst < bytecode->program.len) {
if (!first_step && debugger.breakpoints[debugger.currentInst]) {
printf("Breakpoint hit at instruction %zu\n", debugger.currentInst);
printInstructionAt(bytecode, debugger.currentInst);
break;
}
first_step = false;
struct GroundExecutionResult status = Ground.Bytecode.Instruction.execute(&bytecode->program.at[debugger.currentInst], &bytecode->heap);
if (Ground.Flags.error) {
fprintf(stderr, "Errors while debugging: \n");
Ground.Log.printErrors();
Ground.Flags.error = false;
break;
}
switch (status.type) {
case _GER_CONTINUE: {
debugger.currentInst++;
break;
}
case _GER_RETURN: {
char* val_str = Ground.Stringify.BytecodeValue(&status.as.value);
printf("Program returned value: %s\n", val_str ? val_str : "");
if (val_str) free(val_str);
debugger.hasFinished = true;
break;
}
case _GER_JUMP: {
debugger.currentInst = status.as.line;
break;
}
case _GER_END: {
printf("Program exited with code %" PRId64 "\n", status.as.end);
debugger.hasFinished = true;
break;
}
}
if (debugger.hasFinished) {
break;
}
}
if (debugger.currentInst >= bytecode->program.len && !debugger.hasFinished) {
printf("Program reached the end of instructions.\n");
debugger.hasFinished = true;
}
break;
}
case DBG_STEP: {
if (debugger.hasFinished) {
printf("Program has finished execution. Use 'start' to restart.\n");
break;
}
if (!debugger.hasStarted) {
debugger.hasStarted = true;
debugger.currentInst = 0;
printf("Started debugging program.\n");
}
if (debugger.currentInst >= bytecode->program.len) {
printf("Program reached the end of instructions.\n");
debugger.hasFinished = true;
break;
}
struct GroundExecutionResult status = Ground.Bytecode.Instruction.execute(&bytecode->program.at[debugger.currentInst], &bytecode->heap);
if (Ground.Flags.error) {
fprintf(stderr, "Errors while debugging: \n");
Ground.Log.printErrors();
Ground.Flags.error = false;
break;
}
switch (status.type) {
case _GER_CONTINUE: {
debugger.currentInst++;
break;
}
case _GER_RETURN: {
char* val_str = Ground.Stringify.BytecodeValue(&status.as.value);
printf("Program returned value: %s\n", val_str ? val_str : "");
if (val_str) free(val_str);
debugger.hasFinished = true;
break;
}
case _GER_JUMP: {
debugger.currentInst = status.as.line;
break;
}
case _GER_END: {
printf("Program exited with code %" PRId64 "\n", status.as.end);
debugger.hasFinished = true;
break;
}
}
if (debugger.currentInst >= bytecode->program.len && !debugger.hasFinished) {
printf("Program reached the end of instructions.\n");
debugger.hasFinished = true;
} else if (!debugger.hasFinished) {
printInstructionAt(bytecode, debugger.currentInst);
}
break;
}
case DBG_WAIT: {
break;
}
}
free(input);
}
}

View File

@@ -1,6 +1,8 @@
#include "../../include/ground.h" #include "../../include/ground.h"
#include <stdio.h> #include <stdio.h>
void runDebugger(GroundBytecode* bytecode);
enum ArgsAction { enum ArgsAction {
ARGS_HELP, ARGS_EXECUTE, ARGS_DEBUG, ARGS_ASSEMBLE, ARGS_DISASSEMBLE ARGS_HELP, ARGS_EXECUTE, ARGS_DEBUG, ARGS_ASSEMBLE, ARGS_DISASSEMBLE
}; };
@@ -73,7 +75,20 @@ int main(int argc, char** argv) {
break; break;
} }
case ARGS_DEBUG: { 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; break;
} }
case ARGS_ASSEMBLE: { case ARGS_ASSEMBLE: {
@@ -81,8 +96,21 @@ int main(int argc, char** argv) {
break; break;
} }
case ARGS_DISASSEMBLE: { case ARGS_DISASSEMBLE: {
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;
}
char* str = Ground.Stringify.Bytecode(&bc);
printf("%s", str);
free(str);
break; break;
} }
case ARGS_HELP: { case ARGS_HELP: {

View File

@@ -121,6 +121,7 @@ char* _GroundStringifyString(GroundString* string);
char* _GroundStringifyValue(GroundValue* value); char* _GroundStringifyValue(GroundValue* value);
char* _GroundStringifyBytecodeValue(GroundBytecodeValue* value); char* _GroundStringifyBytecodeValue(GroundBytecodeValue* value);
char* _GroundStringifyBytecode(GroundBytecode* value);
void* _GroundFFIOpenSharedObject(char* id); void* _GroundFFIOpenSharedObject(char* id);
void* _GroundFFIGetFunction(void* handle, char* id); void* _GroundFFIGetFunction(void* handle, char* id);
@@ -274,6 +275,7 @@ struct _Ground Ground = {
.Value = _GroundStringifyValue, .Value = _GroundStringifyValue,
.BytecodeValue = _GroundStringifyBytecodeValue, .BytecodeValue = _GroundStringifyBytecodeValue,
.Bytecode= _GroundStringifyBytecode,
}, },
.FFI = { .FFI = {

11
x86_64-w64-mingw32.txt Normal file
View File

@@ -0,0 +1,11 @@
[binaries]
c = 'x86_64-w64-mingw32-gcc'
ar = 'x86_64-w64-mingw32-ar'
exe_wrapper = 'wine'
pkg-config = 'pkg-config'
[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'