Compare commits

...

20 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
05733c693c Keep working on structs 2026-07-05 18:01:09 +10:00
338241519e Start work on preprocessing structs 2026-07-05 15:08:35 +10:00
28 changed files with 2709 additions and 226 deletions

View File

@@ -13,6 +13,33 @@ Install with:
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.
## 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.
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
#define GROUND_H
@@ -10,7 +20,6 @@
#ifdef _WIN32
// TODO: check if this works
#include <minwindef.h>
#define PATH_MAX MAX_PATH
#else
#include <limits.h>
#endif
@@ -105,12 +114,6 @@ struct GroundStruct {
GroundObjectField* fields;
};
struct GroundObjectField {
char name[2048];
GroundValue* value;
UT_hash_handle hh;
};
struct GroundObject {
GroundObjectField* fields;
GroundStruct* type;
@@ -124,6 +127,7 @@ struct GroundError {
// --- Program structure types definitions ---
enum GroundTypeType {
GroundType_Undefined,
GroundType_Int, GroundType_Double, GroundType_Char, GroundType_Bool,
GroundType_String, GroundType_List,
GroundType_Function, GroundType_Struct, GroundType_Object,
@@ -159,6 +163,13 @@ struct GroundValue {
GroundType type;
};
struct GroundObjectField {
char name[2048];
GroundValue value;
GroundSize offset;
UT_hash_handle hh;
};
enum GroundArgType {
GroundArg_Value, GroundArg_ValueRef, GroundArg_DirectRef,
@@ -304,8 +315,17 @@ struct GroundBytecodeFunction {
// Only for native function
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 {
union {
@@ -385,10 +405,14 @@ struct _Ground {
bool error;
} Flags;
/**
* @brief Functions to create new instances of Ground-specific types. Use Ground.Free to free when no longer needed.
*/
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 {
GroundValue (*Int) (GroundInt in);
GroundValue (*Double) (GroundDouble in);
@@ -543,6 +567,7 @@ struct _Ground {
char* (*Value)(GroundValue* value);
char* (*BytecodeValue)(GroundBytecodeValue* value);
char* (*Bytecode)(GroundBytecode* bytecode);
} Stringify;
struct {

View File

@@ -115,15 +115,22 @@ sources = files(
'src/Stringify/Value.c',
'src/Stringify/BytecodeValue.c',
'src/Stringify/Bytecode.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(
'src/cli/main.c'
'src/cli/main.c',
'src/cli/debugger.c'
)
incdir = include_directories('include')
@@ -132,7 +139,7 @@ libffi = dependency('libffi', version : '>=3.0.0')
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,
name : 'ground',

View File

@@ -1,5 +1,7 @@
#include "../../../include/ground.h"
#ifndef _WIN32
#include "../../linenoise/linenoise.h"
#endif
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
@@ -8,39 +10,13 @@
#include <ffi.h>
#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 JUMP(idx) (struct GroundExecutionResult) { .type = _GER_JUMP, .as.line = idx }
#define END(res) (struct GroundExecutionResult) { .type = _GER_END, .as.end = res }
#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) {
switch (type->type) {
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 );
}
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("");
HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(input))));
free(input);
return CONTINUE;
#endif
}
PRINT: {
for (GroundSize i = 0; i < instruction->args.len; i++) {
printValue(HEAP_GET(heap, instruction->args.at[i]));
printf(" ");
printf("%s ", Ground.Stringify.BytecodeValue(HEAP_GET(heap, instruction->args.at[i])));
}
return CONTINUE;
}
PRINTLN: {
for (GroundSize i = 0; i < instruction->args.len; i++) {
printValue(HEAP_GET(heap, instruction->args.at[i]));
printf(" ");
printf("%s ", Ground.Stringify.BytecodeValue(HEAP_GET(heap, instruction->args.at[i])));
}
printf("\n");
return CONTINUE;
@@ -109,7 +89,6 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
SET: {
if (instruction->args.at[0] != 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);
}
return CONTINUE;
@@ -733,6 +712,9 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
return CONTINUE;
}
} else {
for (GroundSize i = 0; i < function->closure->len; i++) {
Ground.Free.BytecodeValue(&function->closure->heap[i]);
}
free(function->closure->heap);
}
@@ -744,11 +726,6 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute");
Ground.Flags.error = true;
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++) {
@@ -911,10 +888,248 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
return CONTINUE;
}
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;
}
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;
}
ENDSTRUCT: {
@@ -922,12 +1137,86 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
return CONTINUE;
}
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;
}
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;
}
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;
}
USE: {

View File

@@ -138,6 +138,89 @@ static GroundBytecodeValue readValue(FILE* f) {
v.as.Function.closure = NULL;
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: {
Ground.Log.Error("unknown value type in Ground.Bytecode.load");
Ground.Flags.error = true;
@@ -173,7 +256,7 @@ GroundBytecode _GroundBytecodeLoad(const char* path) {
uint64_t count;
fread(&count, 8, 1, f);
if (count > 0) {
bc.heap.heap = malloc(sizeof(GroundValue) * count);
bc.heap.heap = malloc(sizeof(GroundBytecodeValue) * count);
if (bc.heap.heap == NULL) {
Ground.Log.Error("malloc failed in Ground.Bytecode.load");
Ground.Flags.error = true;

View File

@@ -76,6 +76,48 @@ static void writeValue(FILE* f, GroundBytecodeValue* v) {
// closures are generated at runtime, not persisted
break;
}
case GroundType_List: {
uint8_t tag = 6;
fwrite(&tag, 1, 1, f);
uint64_t count = v->as.List.count;
fwrite(&count, 8, 1, f);
for (GroundSize i = 0; i < count; i++) {
writeValue(f, &v->as.List.at[i]);
}
break;
}
case GroundType_Struct: {
uint8_t tag = 7;
fwrite(&tag, 1, 1, f);
uint64_t size = v->as.Struct.size;
fwrite(&size, 8, 1, f);
for (GroundSize i = 0; i < size; i++) {
writeValue(f, &v->as.Struct.values[i]);
}
break;
}
case GroundType_Object: {
uint8_t tag = 8;
fwrite(&tag, 1, 1, f);
uint64_t size = v->as.Object.size;
fwrite(&size, 8, 1, f);
for (GroundSize i = 0; i < size; i++) {
writeValue(f, &v->as.Object.values[i]);
}
break;
}
case GroundType_Undefined: {
uint8_t tag = 9;
fwrite(&tag, 1, 1, f);
break;
}
case GroundType_CoreType: {
uint8_t tag = 10;
fwrite(&tag, 1, 1, f);
uint8_t coreType = (uint8_t)v->as.CoreType;
fwrite(&coreType, 1, 1, f);
break;
}
default: {
uint8_t tag = 0xFF;
fwrite(&tag, 1, 1, f);

View File

@@ -1,4 +1,5 @@
#include "../../include/ground.h"
#include <string.h>
GroundBytecodeValue _GroundCopyBytecodeValue(GroundBytecodeValue* value) {
GroundBytecodeValue newValue = *value;
@@ -7,6 +8,7 @@ GroundBytecodeValue _GroundCopyBytecodeValue(GroundBytecodeValue* value) {
case GroundType_Double:
case GroundType_Bool:
case GroundType_Char:
case GroundType_CoreType:
break;
case GroundType_String: {
@@ -14,7 +16,161 @@ GroundBytecodeValue _GroundCopyBytecodeValue(GroundBytecodeValue* value) {
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;
}

View File

@@ -22,14 +22,7 @@ GroundObject _GroundCopyObject(GroundObject* in) {
strncpy(item->name, s->name, 2047);
item->value = malloc(sizeof(GroundValue));
if (item->value == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.Object()");
Ground.Flags.error = true;
return object;
}
*item->value = Ground.Copy.Value(s->value);
item->value = Ground.Copy.Value(&s->value);
if (Ground.Flags.error) {
return object;

View File

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

View File

@@ -21,14 +21,7 @@ GroundStruct _GroundCopyStruct(GroundStruct* in) {
strncpy(item->name, s->name, 2047);
item->value = malloc(sizeof(GroundValue));
if (item->value == NULL) {
Ground.Log.Error("malloc failed in Ground.Copy.Struct()");
Ground.Flags.error = true;
return gs;
}
*item->value = Ground.Copy.Value(s->value);
item->value = Ground.Copy.Value(&s->value);
if (Ground.Flags.error) {
return gs;

View File

@@ -3,7 +3,7 @@
void _GroundFreeArg(GroundArg* in) {
if (in->type == GroundArg_Value) {
Ground.Free.Value(&in->as.value);
} else {
} else if (in->type != GroundArg_DirectRef && in->type != GroundArg_Label && in->type != GroundArg_LineRef) {
Ground.Free.Identifier(in->as.ref);
}
}

View File

@@ -2,27 +2,56 @@
void _GroundFreeBytecodeValue(GroundBytecodeValue* in) {
// TODO: Implement all this
switch (in->type.type) {
case GroundType_String: {
Ground.Free.String(&in->as.String);
break;
}
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;
}
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;
}
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;
}
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;
}

View File

@@ -1,7 +1,5 @@
#include "../../include/ground.h"
#include <stdlib.h>
void _GroundFreeObject(GroundObject* in) {
GroundObjectField *s, *tmp;
@@ -9,7 +7,7 @@ void _GroundFreeObject(GroundObject* in) {
HASH_DEL(in->fields, s);
Ground.Free.Value(s->value);
free(s->value);
Ground.Free.Value(&s->value);
free(s);
}
}

View File

@@ -1,7 +1,5 @@
#include "../../include/ground.h"
#include <stdlib.h>
void _GroundFreeStruct(GroundStruct* in) {
GroundObjectField *s, *tmp;
@@ -9,7 +7,7 @@ void _GroundFreeStruct(GroundStruct* in) {
HASH_DEL(in->fields, s);
Ground.Free.Value(s->value);
free(s->value);
Ground.Free.Value(&s->value);
free(s);
}
}

View File

@@ -1,4 +1,5 @@
#include "../../include/ground.h"
#include <uthash.h>
static inline void doLabels(GroundProgram* program, GroundState* state) {
@@ -13,7 +14,7 @@ static inline void doLabels(GroundProgram* program, GroundState* state) {
if (label == NULL) {
label = malloc(sizeof(GroundLabel));
if (label == NULL) {
Ground.Log.Error("malloc failed in Ground.Internal.run()");
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doLabels");
Ground.Flags.error = true;
return;
}
@@ -33,7 +34,7 @@ static inline void doLabels(GroundProgram* program, GroundState* state) {
GroundSize* line = Ground.State.findLabel(state, arg->as.ref->string);
if (line == NULL) {
char buf[2048];
snprintf(buf, 2047, "couldn't find label '%s' (instruction JUMP at %zu) in Ground.Internal.Run()", arg->as.ref->string, i);
snprintf(buf, 2047, "couldn't find label '%s' (instruction JUMP at %zu) in Ground.New.Bytecode -> doLabels", arg->as.ref->string, i);
Ground.Log.Error(buf);
Ground.Flags.error = true;
return;
@@ -46,7 +47,7 @@ static inline void doLabels(GroundProgram* program, GroundState* state) {
GroundSize* line = Ground.State.findLabel(state, arg->as.ref->string);
if (line == NULL) {
char buf[2048];
snprintf(buf, 2047, "couldn't find label '%s' (instruction IF at %zu) in Ground.Internal.Run()", arg->as.ref->string, i);
snprintf(buf, 2047, "couldn't find label '%s' (instruction IF at %zu) in Ground.New.Bytecode -> doLabels", arg->as.ref->string, i);
Ground.Log.Error(buf);
Ground.Flags.error = true;
return;
@@ -59,7 +60,7 @@ static inline void doLabels(GroundProgram* program, GroundState* state) {
GroundSize* line = Ground.State.findLabel(state, arg->as.ref->string);
if (line == NULL) {
char buf[2048];
snprintf(buf, 2047, "couldn't find label '%s' (instruction CATCH at %zu) in Ground.Internal.Run()", arg->as.ref->string, i);
snprintf(buf, 2047, "couldn't find label '%s' (instruction CATCH at %zu) in Ground.New.Bytecode -> doLabels", arg->as.ref->string, i);
Ground.Log.Error(buf);
Ground.Flags.error = true;
return;
@@ -85,6 +86,534 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground
}
for (GroundSize i = 0; i < gp->len; i++) {
// Handle instructions which deal with struct fields
switch (gp->at[i].type) {
case GroundInstruction_STRUCT: {
GroundInstruction* inst = &gp->at[i];
GroundVariable* gsv = NULL;
HASH_FIND_STR(state->variables, inst->args.at[0].as.ref->string, gsv);
if (gsv == NULL) {
Ground.Log.Error("unexpected null variable in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true;
return 0;
}
if (gsv->value.type.type != GroundType_Struct) {
Ground.Log.Error("unexpected null variable in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true;
return 0;
}
GroundStruct* gs = &gsv->value.as.Struct;
// Set offset for each struct field
GroundObjectField *s, *tmp;
GroundSize fi = 0;
HASH_ITER(hh, gs->fields, s, tmp) {
s->offset = fi++;
}
// Create bytecode instruction
GroundBytecodeInstruction newInst = {
.type = GroundInstruction_STRUCT,
.args = {
.at = malloc(sizeof(GroundSize) * inst->args.len),
.capacity = inst->args.len,
.len = inst->args.len
}
};
if (newInst.args.at == NULL) {
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true;
return 0;
}
// Resolve the struct type variable to a heap offset
GroundArg* arg = &inst->args.at[0];
GroundVariable* structItem = NULL;
HASH_FIND_STR(state->variables, arg->as.ref->string, structItem);
if (structItem == NULL) {
structItem = malloc(sizeof(GroundVariable));
if (structItem == NULL) {
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true;
return 0;
}
structItem->value = Ground.New.Value.Int(0);
strncpy(structItem->name, arg->as.ref->string, 2047);
structItem->_offset = size++;
HASH_ADD_STR(state->variables, name, structItem);
}
newInst.args.at[0] = structItem->_offset;
// Now go through and add offsets to everything (skip struct name at index 0)
for (GroundSize j = 1; j < inst->args.len; j++) {
// struct field name
arg = &inst->args.at[j];
GroundObjectField* field = NULL;
HASH_FIND_STR(gs->fields, arg->as.ref->string, field);
if (field == NULL) {
Ground.Log.Error("unexpected null struct field in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true;
return 0;
}
newInst.args.at[j] = field->offset;
// 2nd arg is the op code, what we do next depends on it
j++;
// 0 -> none
// 1 -> init
// 2 -> set
// 3 -> attach closure to function
GroundSize op = inst->args.at[j]._offset;
newInst.args.at[j] = op;
switch (op) {
case 0: case 3: break; // these do not require extra values
case 1: // but these do, so we process them
case 2: {
j++;
arg = &inst->args.at[j];
GroundVariable* item = NULL;
if (arg->type == GroundArg_Value) {
// Literal value: create a constant variable for it
item = malloc(sizeof(GroundVariable));
if (item == NULL) {
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true;
return 0;
}
item->value = Ground.Copy.Value(&arg->as.value);
item->_offset = size++;
snprintf(item->name, sizeof(item->name) - 1, "_._.ground_internal_constant_%zu", item->_offset);
HASH_ADD_STR(state->variables, name, item);
} else {
// Variable reference: look it up or create it
HASH_FIND_STR(state->variables, arg->as.ref->string, item);
if (item == NULL) {
item = malloc(sizeof(GroundVariable));
if (item == NULL) {
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true;
return 0;
}
item->value = Ground.New.Value.Int(0);
strncpy(item->name, arg->as.ref->string, 2047);
item->_offset = size++;
HASH_ADD_STR(state->variables, name, item);
}
}
newInst.args.at[j] = item->_offset;
}
}
}
gbp->at[i] = newInst;
gbp->len++;
continue;
}
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;
}
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;
}
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;
}
default: break;
}
for (GroundSize j = 0; j < gp->at[i].args.len; j++) {
GroundArg* arg = &gp->at[i].args.at[j];
@@ -96,7 +625,7 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground
if (arg->type == GroundArg_Value) {
GroundVariable* item = malloc(sizeof(GroundVariable));
if (item == NULL) {
Ground.Log.Error("malloc failed in Ground.Internal.run()");
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true;
return 0;
}
@@ -115,7 +644,7 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground
if (item == NULL) {
item = malloc(sizeof(GroundVariable));
if (item == NULL) {
Ground.Log.Error("malloc failed in Ground.Internal.run()");
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true;
return 0;
}
@@ -137,7 +666,7 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground
};
if (inst.args.at == NULL) {
Ground.Log.Error("malloc failed in Ground.Internal.run()");
Ground.Log.Error("malloc failed in Ground.New.Bytecode -> doOffsets");
Ground.Flags.error = true;
return 0;
}

View File

@@ -1,5 +1,6 @@
#include "../../include/ground.h"
#include <uthash.h>
#include <string.h>
static inline GroundBytecodeFunction doFunction(GroundFunction* function) {
GroundBytecodeFunction bf = {
@@ -47,11 +48,72 @@ static inline GroundBytecodeFunction doFunction(GroundFunction* function) {
// Now convert the program to bytecode
GroundBytecode bytecode = Ground.New.Bytecode(function->program.ground, &state);
*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);
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 bv = {
@@ -89,11 +151,11 @@ GroundBytecodeValue _GroundNewBytecodeValue(GroundValue value) {
break;
}
case GroundType_Struct: {
// TODO - convert to bytecode struct
bv.as.Struct = doStruct(&value.as.Struct);
break;
}
case GroundType_Object: {
// TODO - convert to bytecode object
bv.as.Object = doObject(&value.as.Object);
break;
}
case GroundType_CoreType: {

View File

@@ -22,14 +22,7 @@ GroundObject _GroundNewObject(GroundStruct* in) {
strncpy(item->name, s->name, 2047);
item->value = malloc(sizeof(GroundValue));
if (item->value == NULL) {
Ground.Log.Error("malloc failed in Ground.New.Object()");
Ground.Flags.error = true;
return object;
}
*item->value = Ground.Copy.Value(s->value);
item->value = Ground.Copy.Value(&s->value);
if (Ground.Flags.error) {
return object;

View File

@@ -1,5 +1,296 @@
#include "../../include/ground.h"
static void doFunction(GroundProgram* program, GroundProgram* newProgram, GroundState* state, GroundSize* i) {
GroundFunction function = Ground.New.Function(state);
// Parse signature
GroundInstruction* sig = &program->at[*i];
char* id = sig->args.at[0].as.ref->string;
for (GroundSize j = 1; j < sig->args.len; j++) {
GroundArg* arg = &sig->args.at[j];
// Ignore type annotations, this can be taken care of in another function
if (arg->type == GroundArg_TypeRef) {
continue;
}
char* argId = arg->as.ref->string;
Ground.Function.appendArg(&function, argId);
GroundVariable* argVar = malloc(sizeof(GroundVariable));
if (argVar == NULL) {
Ground.Flags.error = true;
Ground.Log.Error("malloc failed in Ground.Program.Preprocess");
return;
}
snprintf(argVar->name, 2047, "%s", argId);
argVar->value = Ground.New.Value.Int(0);
HASH_ADD_STR(function.closure->variables, name, argVar);
}
// Add instructions to function
GroundSize funCount = 1;
GroundSize structCount = 0;
for (;;) {
(*i)++;
if (*i >= program->len) {
Ground.Flags.error = true;
Ground.Log.Error("expecting ENDFUN instruction, reached end of program in Ground.Program.Preprocess");
return;
}
GroundInstruction* inst = &program->at[*i];
switch (inst->type) {
case GroundInstruction_FUN: {
funCount++;
break;
}
case GroundInstruction_ENDFUN: {
if (funCount < 1) {
Ground.Flags.error = true;
Ground.Log.Error("extra ENDFUN instruction (expecting ENDSTRUCT before) in Ground.Program.Preprocess");
return;
}
funCount--;
break;
}
case GroundInstruction_STRUCT: {
structCount++;
break;
}
case GroundInstruction_ENDSTRUCT: {
if (structCount < 1) {
Ground.Flags.error = true;
Ground.Log.Error("extra ENDSTRUCT instruction (expecting ENDFUN before) in Ground.Program.Preprocess");
return;
}
structCount--;
break;
}
default: break;
}
if (funCount == 0 && structCount == 0) {
// We done
break;
}
Ground.Function.appendInstruction(&function, *inst);
}
// Preprocess function's body
GroundProgram functionBody = Ground.Program.preprocess(function.program.ground, function.closure);
if (Ground.Flags.error) {
return;
}
Ground.Free.Program(function.program.ground);
*function.program.ground = functionBody;
GroundVariable* var = malloc(sizeof(GroundVariable));
if (var == NULL) {
Ground.Flags.error = true;
Ground.Log.Error("malloc failed in Ground.Program.Preprocess");
return;
}
snprintf(var->name, 2047, "%s", id);
var->value = Ground.New.Value.Function(function);
HASH_ADD_STR(state->variables, name, var);
// Add instruction to attach closure to function
GroundInstruction inst = Ground.New.Instruction(GroundInstruction_FUN);
Ground.Instruction.append(&inst, Ground.New.Arg.FunctionRef(sig->args.at[0].as.ref));
Ground.Program.append(newProgram, inst);
}
static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundState* state, GroundSize* i);
static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundState* state, GroundSize* i) {
GroundIdentifier* structName = Ground.Copy.Identifier(program->at[*i].args.at[0].as.ref);
GroundStruct gs = Ground.New.Struct();
// The aim is to compile an instruction which looks like this:
// 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)
// Functions will be moved to the end of the struct, and a closure will be attached to all of them
//
// Oh yeah, we also need to make a GroundStruct value
GroundSize structCount = 0;
GroundInstruction output = Ground.New.Instruction(GroundInstruction_STRUCT);
Ground.Instruction.append(&output, Ground.New.Arg.TypeRef(structName));
// Parse struct body
bool parsing = true;
GroundSize fieldCount = 0;
while (parsing) {
(*i)++;
if (*i >= program->len) {
Ground.Flags.error = true;
Ground.Log.Error("expecting ENDFUN instruction, reached end of program in Ground.Program.Preprocess");
return;
}
GroundInstruction* instruction = &program->at[*i];
switch (instruction->type) {
case GroundInstruction_SET: {
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 we know the value type now, add it to the instruction
if (instruction->args.at[1].type == GroundArg_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;
}
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;
}
case GroundInstruction_INIT: {
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 we know the type now, add it to the instruction
char* typeName = instruction->args.at[1].as.ref->string;
if (strcmp(typeName, "int") == 0) {
field->value = Ground.New.Value.Int(0);
} else if (strcmp(typeName, "double") == 0) {
field->value = Ground.New.Value.Double(0.0);
} else if (strcmp(typeName, "string") == 0) {
field->value = Ground.New.Value.String(Ground.New.String(""));
} else if (strcmp(typeName, "char") == 0) {
field->value = Ground.New.Value.Char(0);
} else if (strcmp(typeName, "bool") == 0) {
field->value = Ground.New.Value.Bool(false);
} else if (strcmp(typeName, "function") == 0) {
field->value = Ground.New.Value.Function(Ground.New.Function(state));
} else {
field->value = Ground.New.Value.Int(0);
}
HASH_ADD_STR(gs.fields, name, field);
// init &field -type -> &field 1 -type
Ground.Instruction.append(&output, Ground.Copy.Arg(&instruction->args.at[0]));
Ground.Instruction.append(&output, (GroundArg) {.type = GroundArg_DirectRef, ._offset = 1});
Ground.Instruction.append(&output, Ground.Copy.Arg(&instruction->args.at[1]));
break;
}
case GroundInstruction_STRUCT: {
// TODO structs in structs
break;
}
case GroundInstruction_EXTERN: {
// TODO external functions as struct members
break;
}
case GroundInstruction_FUN: {
// Parse the function itself
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
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.Free.Instruction(inst);
newProgram->len--;
// Add 3 to signal addition of closure
Ground.Instruction.append(&output, (GroundArg) {.type = GroundArg_DirectRef, ._offset = 3});
break;
}
case GroundInstruction_ENDSTRUCT: {
parsing = false;
break;
}
default: {
Ground.Log.Error("invalid instruction inside struct in Ground.Program.Preprocess -> doStruct");
}
}
}
Ground.Program.append(newProgram, output);
GroundVariable* variable = malloc(sizeof(GroundVariable));
if (variable == NULL) {
Ground.Log.Error("malloc failed in Ground.Program.Preprocess -> doStruct");
Ground.Flags.error = true;
return;
}
snprintf(variable->name, 2047, "%s", structName->string);
variable->value = Ground.New.Value.Struct(gs);
HASH_ADD_STR(state->variables, name, variable);
}
GroundProgram _GroundProgramPreprocess(GroundProgram* program, GroundState* state) {
GroundProgram newProgram = Ground.New.Program();
@@ -7,114 +298,13 @@ GroundProgram _GroundProgramPreprocess(GroundProgram* program, GroundState* stat
for (GroundSize i = 0; i < program->len; i++) {
switch (program->at[i].type) {
case GroundInstruction_FUN: {
GroundFunction function = Ground.New.Function(state);
// Parse signature
GroundInstruction* sig = &program->at[i];
char* id = sig->args.at[0].as.ref->string;
for (GroundSize j = 1; j < sig->args.len; j++) {
GroundArg* arg = &sig->args.at[j];
// Ignore type annotations, this can be taken care of in another function
if (arg->type == GroundArg_TypeRef) {
continue;
}
char* argId = arg->as.ref->string;
Ground.Function.appendArg(&function, argId);
GroundVariable* argVar = malloc(sizeof(GroundVariable));
if (argVar == NULL) {
Ground.Flags.error = true;
Ground.Log.Error("malloc failed in Ground.Program.Preprocess");
return newProgram;
}
snprintf(argVar->name, 2047, "%s", argId);
argVar->value = Ground.New.Value.Int(0);
HASH_ADD_STR(function.closure->variables, name, argVar);
}
// Add instructions to function
GroundSize funCount = 1;
GroundSize structCount = 0;
for (;;) {
i++;
if (i >= program->len) {
Ground.Flags.error = true;
Ground.Log.Error("expecting ENDFUN instruction, reached end of program in Ground.Program.Preprocess");
return newProgram;
}
GroundInstruction* inst = &program->at[i];
switch (inst->type) {
case GroundInstruction_FUN: {
funCount++;
break;
}
case GroundInstruction_ENDFUN: {
if (funCount < 1) {
Ground.Flags.error = true;
Ground.Log.Error("extra ENDFUN instruction (expecting ENDSTRUCT before) in Ground.Program.Preprocess");
return newProgram;
}
funCount--;
break;
}
case GroundInstruction_STRUCT: {
structCount++;
break;
}
case GroundInstruction_ENDSTRUCT: {
if (structCount < 1) {
Ground.Flags.error = true;
Ground.Log.Error("extra ENDSTRUCT instruction (expecting ENDFUN before) in Ground.Program.Preprocess");
return newProgram;
}
structCount--;
break;
}
default: break;
}
if (funCount == 0 && structCount == 0) {
// We done
break;
}
Ground.Function.appendInstruction(&function, *inst);
}
// Preprocess function's body
GroundProgram functionBody = Ground.Program.preprocess(function.program.ground, function.closure);
if (Ground.Flags.error) {
return newProgram;
}
Ground.Free.Program(function.program.ground);
*function.program.ground = functionBody;
GroundVariable* var = malloc(sizeof(GroundVariable));
if (var == NULL) {
Ground.Flags.error = true;
Ground.Log.Error("malloc failed in Ground.Program.Preprocess");
return newProgram;
}
snprintf(var->name, 2047, "%s", id);
var->value = Ground.New.Value.Function(function);
HASH_ADD_STR(state->variables, name, var);
// Add instruction to attach closure to function
GroundInstruction inst = Ground.New.Instruction(GroundInstruction_FUN);
Ground.Instruction.append(&inst, Ground.New.Arg.FunctionRef(sig->args.at[0].as.ref));
Ground.Program.append(&newProgram, inst);
doFunction(program, &newProgram, state, &i);
if (Ground.Flags.error) return newProgram;
break;
}
case GroundInstruction_STRUCT: {
// TODO: Preprocess structs
doStruct(program, &newProgram, state, &i);
if (Ground.Flags.error) return newProgram;
break;
}
default: {

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/estr.h"
#include <inttypes.h>
char* _GroundStringifyBytecodeValue(GroundBytecodeValue* value) {
@@ -50,13 +51,101 @@ char* _GroundStringifyBytecodeValue(GroundBytecodeValue* value) {
// TODO implement list stringification
}
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: {
// 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: {
// 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, " ");
free(arg);
}
if (!estr.shouldBeFreed) {
char* result = strdup(estr.str);
return result;
}
return estr.str;
}

View File

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

View File

@@ -50,7 +50,14 @@ char* _GroundStringifyValue(GroundValue* value) {
// TODO implement list stringification
}
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: {
// TODO implement struct stringification
@@ -58,6 +65,26 @@ char* _GroundStringifyValue(GroundValue* value) {
case GroundType_Object: {
// 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;
}
}

View File

@@ -5,8 +5,8 @@ void _GroundStructAddField(GroundStruct* gs, const char* id, GroundValue value)
HASH_FIND_STR(gs->fields, id, field);
if (field != NULL) {
Ground.Free.Value(field->value);
*field->value = Ground.Copy.Value(&value);
Ground.Free.Value(&field->value);
field->value = Ground.Copy.Value(&value);
strncpy(field->name, id, 2047);
} else {
@@ -18,16 +18,11 @@ void _GroundStructAddField(GroundStruct* gs, const char* id, GroundValue value)
return;
}
field->value = malloc(sizeof(GroundValue));
if (field->value == NULL) {
Ground.Log.Error("malloc failed in Ground.Struct.addField()");
Ground.Flags.error = true;
return;
}
*field->value = Ground.Copy.Value(&value);
field->value = Ground.Copy.Value(&value);
strncpy(field->name, id, 2047);
HASH_ADD_STR(gs->fields, name, field);
}
}

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 <stdio.h>
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: {
@@ -81,8 +96,21 @@ int main(int argc, char** argv) {
break;
}
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;
}
case ARGS_HELP: {

View File

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