Compare commits
18 Commits
05733c693c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 65cf5580ac | |||
| 57e2f2eea9 | |||
| 8fe67ddc2b | |||
| 7020ac3744 | |||
| 5dcbac3166 | |||
| 4d5cdb5b6b | |||
| 8b04bf39b1 | |||
| 7a53105eca | |||
| 8e4267d500 | |||
| 0d199fedd7 | |||
| 05eddd9a0e | |||
| 7619b897e0 | |||
| 71dfbb5855 | |||
| f2fbeb3a68 | |||
| 6929f055de | |||
| b507d79804 | |||
| 3a1cefffd7 | |||
| 820fd618a3 |
46
README.md
46
README.md
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -118,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,
|
||||
@@ -305,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 {
|
||||
@@ -386,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);
|
||||
@@ -544,6 +567,7 @@ struct _Ground {
|
||||
char* (*Value)(GroundValue* value);
|
||||
|
||||
char* (*BytecodeValue)(GroundBytecodeValue* value);
|
||||
char* (*Bytecode)(GroundBytecode* bytecode);
|
||||
} Stringify;
|
||||
|
||||
struct {
|
||||
|
||||
13
meson.build
13
meson.build
@@ -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',
|
||||
|
||||
@@ -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 = ¤t->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 = ¤t->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 = ¤t->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 = ¤t->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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,5 +8,6 @@ void _GroundFreeObject(GroundObject* in) {
|
||||
HASH_DEL(in->fields, s);
|
||||
|
||||
Ground.Free.Value(&s->value);
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@ void _GroundFreeStruct(GroundStruct* in) {
|
||||
HASH_DEL(in->fields, s);
|
||||
|
||||
Ground.Free.Value(&s->value);
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "../../include/ground.h"
|
||||
#include <uthash.h>
|
||||
|
||||
static inline void doLabels(GroundProgram* program, GroundState* state) {
|
||||
|
||||
@@ -105,67 +106,509 @@ static inline GroundSize doOffsets(GroundProgram* gp, GroundState* state, Ground
|
||||
|
||||
// Set offset for each struct field
|
||||
GroundObjectField *s, *tmp;
|
||||
GroundSize i = 0;
|
||||
GroundSize fi = 0;
|
||||
HASH_ITER(hh, gs->fields, s, tmp) {
|
||||
s->offset = i++;
|
||||
s->offset = fi++;
|
||||
}
|
||||
|
||||
// Now go through and add offsets to everything
|
||||
for (GroundSize j = 0; j < inst->args.len; j++) {
|
||||
// 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
|
||||
GroundArg* arg = &inst->args.at[j];
|
||||
arg = &inst->args.at[j];
|
||||
GroundObjectField* field = NULL;
|
||||
HASH_FIND_STR(state->variables, arg->as.ref->string, field);
|
||||
HASH_FIND_STR(gs->fields, arg->as.ref->string, field);
|
||||
if (field == NULL) {
|
||||
Ground.Log.Error("unexpected null struct field in Ground.New.Bytecode -> doOffsets");
|
||||
Ground.Flags.error = true;
|
||||
return 0;
|
||||
}
|
||||
arg->_offset = field->offset;
|
||||
// 2nd arg offset is already set, but what we do next depends on it
|
||||
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
|
||||
switch (inst->args.at[j]._offset) {
|
||||
GroundSize op = inst->args.at[j]._offset;
|
||||
newInst.args.at[j] = op;
|
||||
switch (op) {
|
||||
case 0: case 3: break; // these do not require extra values
|
||||
case 1: // but these do, so we process them
|
||||
case 2: { // it's safe to assume that literals will have
|
||||
j++; // been dealt with accordingly
|
||||
case 2: {
|
||||
j++;
|
||||
arg = &inst->args.at[j];
|
||||
GroundVariable* item = NULL;
|
||||
HASH_FIND_STR(state->variables, arg->as.ref->string, item);
|
||||
|
||||
if (item == NULL) {
|
||||
if (arg->type == GroundArg_Value) {
|
||||
// Literal value: create a constant variable for it
|
||||
item = malloc(sizeof(GroundVariable));
|
||||
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->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);
|
||||
}
|
||||
}
|
||||
|
||||
arg->_offset = item->_offset;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -112,7 +112,7 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
|
||||
GroundStruct gs = Ground.New.Struct();
|
||||
|
||||
// The aim is to compile an instruction which looks like this:
|
||||
// struct &field OP $value &field OP $value &field OP $value...
|
||||
// struct &structName &field OP $value &field OP $value &field OP $value...
|
||||
// where:
|
||||
// &field is the field to initialise
|
||||
// OP is a numeric identifier:
|
||||
@@ -132,6 +132,7 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
|
||||
|
||||
// Parse struct body
|
||||
bool parsing = true;
|
||||
GroundSize fieldCount = 0;
|
||||
while (parsing) {
|
||||
(*i)++;
|
||||
if (*i >= program->len) {
|
||||
@@ -152,10 +153,16 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
|
||||
}
|
||||
|
||||
snprintf(field->name, 2047, "%s", instruction->args.at[0].as.ref->string);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -176,31 +183,24 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
|
||||
}
|
||||
|
||||
snprintf(field->name, 2047, "%s", instruction->args.at[0].as.ref->string);
|
||||
field->offset = fieldCount++;
|
||||
|
||||
// If we know the type now, add it to the instruction
|
||||
if (strcmp(field->name, "int") == 0) {
|
||||
char* typeName = instruction->args.at[1].as.ref->string;
|
||||
if (strcmp(typeName, "int") == 0) {
|
||||
field->value = Ground.New.Value.Int(0);
|
||||
break;
|
||||
}
|
||||
if (strcmp(field->name, "double") == 0) {
|
||||
} else if (strcmp(typeName, "double") == 0) {
|
||||
field->value = Ground.New.Value.Double(0.0);
|
||||
break;
|
||||
}
|
||||
if (strcmp(field->name, "string") == 0) {
|
||||
} else if (strcmp(typeName, "string") == 0) {
|
||||
field->value = Ground.New.Value.String(Ground.New.String(""));
|
||||
break;
|
||||
}
|
||||
if (strcmp(field->name, "char") == 0) {
|
||||
} else if (strcmp(typeName, "char") == 0) {
|
||||
field->value = Ground.New.Value.Char(0);
|
||||
break;
|
||||
}
|
||||
if (strcmp(field->name, "bool") == 0) {
|
||||
} else if (strcmp(typeName, "bool") == 0) {
|
||||
field->value = Ground.New.Value.Bool(false);
|
||||
break;
|
||||
}
|
||||
if (strcmp(field->name, "function") == 0) {
|
||||
} else if (strcmp(typeName, "function") == 0) {
|
||||
field->value = Ground.New.Value.Function(Ground.New.Function(state));
|
||||
break;
|
||||
} else {
|
||||
field->value = Ground.New.Value.Int(0);
|
||||
}
|
||||
|
||||
HASH_ADD_STR(gs.fields, name, field);
|
||||
@@ -222,6 +222,38 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
|
||||
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)));
|
||||
@@ -229,19 +261,22 @@ static void doStruct(GroundProgram* program, GroundProgram* newProgram, GroundSt
|
||||
newProgram->len--;
|
||||
|
||||
// Add 3 to signal addition of closure
|
||||
Ground.Instruction.append(&output, (GroundArg) {.type = GroundArg_DirectRef, ._offset = 1});
|
||||
Ground.Instruction.append(&output, (GroundArg) {.type = GroundArg_DirectRef, ._offset = 3});
|
||||
break;
|
||||
}
|
||||
case GroundInstruction_ENDSTRUCT: {
|
||||
parsing = false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Ground.Log.Error("invalid instruction inside struct in ");
|
||||
Ground.Log.Error("invalid instruction inside struct in Ground.Program.Preprocess -> doStruct");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ground.Program.append(newProgram, output);
|
||||
|
||||
GroundVariable* variable = malloc(sizeof(GroundVariable));
|
||||
if (variable == NULL) {
|
||||
Ground.Log.Error("malloc failed in Ground.Program.Preprocess -> doStruct");
|
||||
|
||||
316
src/Stringify/Bytecode.c
Normal file
316
src/Stringify/Bytecode.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ char* _GroundStringifyProgram(GroundProgram* program) {
|
||||
}
|
||||
|
||||
APPEND_ESTR(estr, arg);
|
||||
APPEND_ESTR(estr, " ");
|
||||
APPEND_ESTR(estr, "\n");
|
||||
free(arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
577
src/cli/debugger.c
Normal file
577
src/cli/debugger.c
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
11
x86_64-w64-mingw32.txt
Normal 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'
|
||||
Reference in New Issue
Block a user