16 Commits

13 changed files with 853 additions and 136 deletions

View File

@@ -3,9 +3,9 @@
#define MAX_ID_LEN 64
/*
* groundvm.h
* groundvm.h
* Provides an interface for external programs wanting to run Ground code.
*
*
*/
#include <stdint.h>
@@ -125,17 +125,6 @@ typedef struct GroundFunctionArgs {
char* name;
} GroundFunctionArgs;
/*
* Represents a Ground function.
*/
typedef struct GroundFunction {
GroundFunctionArgs* args;
size_t argSize;
GroundValueType returnType;
GroundProgram program;
size_t startLine;
} GroundFunction;
/*
* Field for a GroundStruct
*/
@@ -181,12 +170,35 @@ typedef struct GroundVariable {
UT_hash_handle hh;
} GroundVariable;
typedef struct GroundCatch {
char id[MAX_ID_LEN];
GroundLabel* label;
UT_hash_handle hh;
} GroundCatch;
typedef struct GroundScope {
GroundLabel** labels;
GroundVariable** variables;
GroundCatch** catches;
bool isMainScope;
} GroundScope;
typedef GroundValue (*NativeGroundFunction)(struct GroundScope* scope, List args);
/*
* Represents a Ground function.
*/
typedef struct GroundFunction {
GroundFunctionArgs* args;
size_t argSize;
GroundValueType returnType;
GroundProgram program;
size_t startLine;
bool isNative;
GroundScope closure;
NativeGroundFunction nativeFn;
} GroundFunction;
#ifdef __cplusplus
extern "C" {
#endif
@@ -194,7 +206,7 @@ extern "C" {
GroundProgram groundCreateProgram();
void groundAddInstructionToProgram(GroundProgram* program, GroundInstruction instruction);
GroundValue groundRunProgram(GroundProgram* program);
void groundPrintProgram(GroundProgram* program);
void groundPrintProgram(GroundProgram* program);
char* groundCompileProgram(GroundProgram* program);
GroundInstruction groundCreateInstruction(GroundInstType type);

45
libs/_box/_box.c Normal file
View File

@@ -0,0 +1,45 @@
#include <groundext.h>
#include <groundvm.h>
#include <stdlib.h>
GroundValue boxGet(GroundScope* scope, List args) {
GroundVariable* varptr = groundFindVariable(scope, "ptr");
GroundValue* ptr = (GroundValue*)varptr->value.data.intVal;
if (ptr == NULL) {
ERROR("No value in box", "ValueError");
}
return *ptr;
}
GroundValue boxSet(GroundScope* scope, List args) {
GroundVariable* varptr = groundFindVariable(scope, "ptr");
GroundValue** ptr = (GroundValue**)&varptr->value.data.intVal;
if (*ptr == NULL) {
*ptr = malloc(sizeof(GroundValue));
if (*ptr == NULL) {
ERROR("Could not allocate memory", "AllocError");
}
}
**ptr = args.values[0];
return groundCreateValue(NONE);
}
GroundValue boxFree(GroundScope* scope, List args) {
GroundVariable* varptr = groundFindVariable(scope, "ptr");
GroundValue** ptr = (GroundValue**)&varptr->value.data.intVal;
if (*ptr != NULL) {
free(*ptr);
*ptr = 0;
}
return groundCreateValue(NONE);
}
void ground_init(GroundScope* scope) {
GroundStruct boxStruct = groundCreateStruct();
groundAddFieldToStruct(&boxStruct, "ptr", groundCreateValue(INT, 0));
groundAddFunctionToStruct(&boxStruct, "get", boxGet, ANY, 0);
groundAddFunctionToStruct(&boxStruct, "set", boxSet, ANY, 1, ANY, "item");
groundAddFunctionToStruct(&boxStruct, "free", boxFree, ANY, 0);
groundAddValueToScope(scope, "_box", groundCreateValue(STRUCTVAL, boxStruct));
}

202
libs/allocator/allocator.c Normal file
View File

@@ -0,0 +1,202 @@
// allocator.c - library to safely, dynamically allocate memory inside of Ground
#include <groundext.h>
#include <groundvm.h>
#include <stdbool.h>
#include <uthash.h>
GroundStruct allocatorStruct;
// callmethod &allocator !allocate $size &
GroundValue groundAllocatorAllocate(GroundScope* scope, List args) {
int64_t size = args.values[0].data.intVal;
if (size < 1) {
ERROR("Cannot allocate with size of < 1", "AllocSizeError");
}
GroundValue* ptr = malloc(sizeof(GroundValue) * size);
if (ptr == NULL) {
ERROR("Failed to allocate memory", "AllocError");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("Failed to find field for pointer", "ValueError");
}
ptrField->value.type = INT;
ptrField->value.data.intVal = (int64_t)ptr;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
if (capacityField == NULL) {
ERROR("Failed to find field for capacity", "ValueError");
}
capacityField->value.type = INT;
capacityField->value.data.intVal = size;
return groundCreateValue(INT, 0);
}
// callmethod &allocator !addCapacity $addedCapacity &
GroundValue groundAllocatorAddCapacity(GroundScope* scope, List args) {
int64_t addedCapacity = args.values[0].data.intVal;
if (addedCapacity < 0) {
ERROR("Cannot shrink capacity", "AllocSizeError");
}
GroundVariable* currentCapacity = groundFindVariable(scope, "capacity");
if (currentCapacity == NULL) {
ERROR("Failed to find field for capacity", "ValueError");
}
if (currentCapacity->value.data.intVal < 1) {
return groundAllocatorAllocate(scope, args);
}
currentCapacity->value.data.intVal += addedCapacity;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("Failed to find field for pointer", "ValueError");
}
GroundValue* ptr = (GroundValue*)ptrField->value.data.intVal;
if (ptr == NULL) {
return groundAllocatorAllocate(scope, args);
}
GroundValue* newPtr = realloc(ptr, sizeof(GroundValue) * currentCapacity->value.data.intVal);
if (newPtr == NULL) {
ERROR("Failed to allocate more memory for pointer", "AllocError");
}
ptrField->value.data.intVal = (int64_t)newPtr;
return groundCreateValue(INT, 0);
}
// callmethod &allocator !getAtOffset $offset &
GroundValue groundAllocatorGetAtOffset(GroundScope* scope, List args) {
int64_t offset = args.values[0].data.intVal;
GroundVariable* currentCapacity = groundFindVariable(scope, "capacity");
if (currentCapacity == NULL) {
ERROR("Failed to find field for capacity", "ValueError");
}
if (offset >= currentCapacity->value.data.intVal || offset < 0) {
ERROR("Offset is out of bounds for current capacity", "OutOfBounds");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("Failed to find field for pointer", "ValueError");
}
GroundValue* ptr = (GroundValue*)ptrField->value.data.intVal;
if (ptr == NULL) {
ERROR("Cannot get offset of null pointer", "NullPointerError");
}
return ptr[offset];
}
GroundValue copyGroundValue(const GroundValue* gv);
// callmethod &allocator !setAtOffset $offset $value &
GroundValue groundAllocatorSetAtOffset(GroundScope* scope, List args) {
int64_t offset = args.values[0].data.intVal;
GroundValue value = copyGroundValue(&args.values[1]);
GroundVariable* currentCapacity = groundFindVariable(scope, "capacity");
if (currentCapacity == NULL) {
ERROR("Failed to find field for capacity", "ValueError");
}
if (offset >= currentCapacity->value.data.intVal || offset < 0) {
ERROR("Offset is out of bounds for current capacity", "OutOfBounds");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("Failed to find field for pointer", "ValueError");
}
GroundValue* ptr = (GroundValue*)ptrField->value.data.intVal;
if (ptr == NULL) {
ERROR("Cannot set offset of null pointer", "NullPointerError");
}
ptr[offset] = value;
return groundCreateValue(INT, 0);
}
GroundValue groundAllocatorDestructor(GroundScope* scope, List args) {
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("Failed to find field for pointer", "ValueError");
}
GroundValue* ptr = (GroundValue*)ptrField->value.data.intVal;
if (ptr == NULL) {
ERROR("Cannot set offset of null pointer", "NullPointerError");
}
free(ptr);
ptrField->value.data.intVal = 0;
GroundVariable* currentCapacity = groundFindVariable(scope, "capacity");
if (currentCapacity == NULL) {
ERROR("Failed to find field for capacity", "ValueError");
}
currentCapacity->value.data.intVal = 0;
return groundCreateValue(INT, 0);
}
GroundValue groundAllocatorDuplicator(GroundScope* scope, List args) {
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("Failed to find field for pointer", "ValueError");
}
GroundValue* ptr = (GroundValue*)ptrField->value.data.intVal;
GroundVariable* currentCapacity = groundFindVariable(scope, "capacity");
if (currentCapacity == NULL) {
ERROR("Failed to find field for capacity", "ValueError");
}
GroundValue newAllocator = groundCreateValue(CUSTOM, &allocatorStruct);
GroundObjectField* newPtrField = groundFindField(*newAllocator.data.customVal, "ptr");
if (newPtrField == NULL) {
ERROR("Failed to find field for pointer", "ValueError");
}
GroundValue* newPtr;
if (ptr == NULL) {
newPtr = NULL;
} else {
newPtr = malloc(sizeof(GroundValue) * currentCapacity->value.data.intVal);
if (newPtr == NULL) {
ERROR("Failed to allocate new pointer in duplicator", "AllocError");
}
memcpy(newPtr, ptr, sizeof(GroundValue) * currentCapacity->value.data.intVal);
}
newPtrField->value.data.intVal = (int64_t)newPtr;
GroundObjectField* newCapacity = groundFindField(*newAllocator.data.customVal, "capacity");
if (newCapacity == NULL) {
ERROR("Failed to find field for capacity", "ValueError");
}
newCapacity->value.data.intVal = currentCapacity->value.data.intVal;
return newAllocator;
}
void ground_init(GroundScope* scope) {
allocatorStruct = groundCreateStruct();
groundAddFieldToStruct(&allocatorStruct, "ptr", groundCreateValue(INT, 0)); // private
groundAddFieldToStruct(&allocatorStruct, "capacity", groundCreateValue(INT, 0)); // protected
groundAddFunctionToStruct(&allocatorStruct, "allocate", groundAllocatorAllocate, ANY, 1, INT, "amount");
groundAddFunctionToStruct(&allocatorStruct, "addCapacity", groundAllocatorAddCapacity, ANY, 1, INT, "amount");
groundAddFunctionToStruct(&allocatorStruct, "getAtOffset", groundAllocatorGetAtOffset, ANY, 1, INT, "offset");
groundAddFunctionToStruct(&allocatorStruct, "setAtOffset", groundAllocatorSetAtOffset, ANY, 2, INT, "offset", ANY, "value");
groundAddFunctionToStruct(&allocatorStruct, "destructor", groundAllocatorDestructor, INT, 0);
groundAddFunctionToStruct(&allocatorStruct, "duplicator", groundAllocatorDuplicator, CUSTOM, 1, CUSTOM, "self");
GroundValue structVal = groundCreateValue(STRUCTVAL, allocatorStruct);
groundAddValueToScope(scope, "Allocator", structVal);
}

168
libs/socket/socket.c Normal file
View File

@@ -0,0 +1,168 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <groundvm.h>
#include <groundext.h>
#define BACKLOG 16
#define CHUNK_SIZE 4096
typedef struct {
int fd;
struct sockaddr_in addr;
GroundFunction* function;
} client_t;
typedef struct {
char* data;
size_t len;
size_t cap;
} buf_t;
static void buf_init(buf_t *b) {
b->data = malloc(CHUNK_SIZE);
b->len = 0;
b->cap = CHUNK_SIZE;
}
static int buf_append(buf_t *b, const char *src, size_t n) {
if (b->len + n > b->cap) {
size_t new_cap = b->cap;
while (new_cap < b->len + n) new_cap *= 2;
char *p = realloc(b->data, new_cap);
if (!p) return -1;
b->data = p;
b->cap = new_cap;
}
memcpy(b->data + b->len, src, n);
b->len += n;
return 0;
}
static void buf_free(buf_t *b) {
free(b->data);
b->data = NULL;
b->len = b->cap = 0;
}
static void handle(int fd, const char *data, size_t len, GroundFunction* function) {
GroundValue value = groundRunFunction(function, 1, groundCreateValue(STRING, data));
if (value.type == ERROR) {
printf(" error type: %s\n", value.data.errorVal.type);
printf(" error what: %s\n", value.data.errorVal.what);
send(fd, "Server Error", 12, 0);
return;
}
if (value.type != STRING) {
printf(" wrong type: %d\n", value.type);
send(fd, "Server Error", 12, 0);
return;
}
size_t resplen = strlen(value.data.stringVal);
send(fd, value.data.stringVal, resplen, 0);
}
static void *client_thread(void *arg) {
client_t *client = arg;
int fd = client->fd;
GroundFunction* function = client->function;
// ...
free(client);
buf_t buf;
buf_init(&buf);
char chunk[CHUNK_SIZE];
while (1) {
ssize_t n = recv(fd, chunk, sizeof(chunk), 0);
if (n < 0) { perror("recv"); break; }
if (n == 0) break; // client disconnected
if (buf_append(&buf, chunk, n) < 0) {
fprintf(stderr, "[fd=%d] out of memory\n", fd);
break;
}
// handle every complete line we've accumulated
while (1) {
char* newline = memchr(buf.data, '\n', buf.len);
if (!newline) break;
size_t msg_len = newline - buf.data;
// null terminate and handle
char saved = buf.data[msg_len];
buf.data[msg_len] = '\0';
handle(fd, buf.data, msg_len, function);
buf.data[msg_len] = saved;
// shift the buffer left past the consumed message
size_t consumed = msg_len + 1;
memmove(buf.data, buf.data + consumed, buf.len - consumed);
buf.len -= consumed;
}
}
buf_free(&buf);
close(fd);
printf("[fd=%d] disconnected\n", fd);
return NULL;
}
GroundValue socket_Listen(GroundScope* scope, List args) {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) { perror("socket"); ERROR("Failed to open socket (see above error for detail", "SocketFail"); }
int64_t port = args.values[1].data.intVal;
GroundFunction* function = args.values[0].data.fnVal;
int opt = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(port),
.sin_addr = { .s_addr = INADDR_ANY },
};
if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind"); ERROR("Failed to bind to socket (see above error for detail", "SocketFail");
}
if (listen(listen_fd, BACKLOG) < 0) {
perror("listen"); ERROR("Failed to listen on socket (see above error for detail)", "SocketFail");
}
while (1) {
client_t *client = malloc(sizeof(client_t));
socklen_t addrlen = sizeof(client->addr);
client->fd = accept(listen_fd, (struct sockaddr *)&client->addr, &addrlen);
client->function = function;
if (client->fd < 0) {
perror("accept");
free(client);
continue;
}
pthread_t tid;
if (pthread_create(&tid, NULL, client_thread, client) != 0) {
perror("pthread_create");
close(client->fd);
free(client);
continue;
}
pthread_detach(tid);
}
close(listen_fd);
return groundCreateValue(INT, 0);
}
void ground_init(GroundScope* scope) {
groundAddNativeFunction(scope, "socket_Listen", socket_Listen, ANY, 2, FUNCTION, "handler", INT, "port");
}

101
libs/threading/thread.c Normal file
View File

@@ -0,0 +1,101 @@
#include "thread.h"
#include <stdarg.h>
#include <stddef.h>
#include <groundext.h>
#include <groundvm.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
GroundStruct threadStruct = {};
typedef struct {
GroundFunction* func;
GroundObject* funcArgsListStruct;
} ThreadArgs;
GroundScope copyGroundScope(GroundScope* scope);
GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope);
void* threadMain(void* threadArgs) {
ThreadArgs* realArgs = (ThreadArgs*)threadArgs;
GroundFunction func = *realArgs->func;
GroundObjectField* listSizeField = groundFindField(*realArgs->funcArgsListStruct, "size");
if (listSizeField == NULL) {
return NULL;
}
int64_t listSize = listSizeField->value.data.intVal;
GroundValue* listPtr = (GroundValue*)groundFindField(*realArgs->funcArgsListStruct, "ptr")->value.data.intVal;
// put args from list into func args
va_list list;
GroundScope funcScope = copyGroundScope(&func.closure);
for (int64_t i = 0; i < listSize; i++) {
groundAddValueToScope(&funcScope, func.args[i].name, listPtr[i]);
}
// run func
free(realArgs);
interpretGroundProgram(&func.program, &funcScope);
return NULL;
}
GroundValue threadStructStart(GroundScope* scope, List args) {
GroundVariable* funcField = groundFindVariable(scope, "fn");
GroundVariable* argsField = groundFindVariable(scope, "args");
pthread_t newThread;
ThreadArgs *threadArgs = malloc(sizeof(ThreadArgs));
threadArgs->func = funcField->value.data.fnVal;
threadArgs->funcArgsListStruct = (GroundObject*)argsField->value.data.customVal;
pthread_create(&newThread, NULL, threadMain, threadArgs);
return groundCreateValue(INT, 0);
}
GroundValue threadStructConstructor(GroundScope* scope, List args) {
GroundValue value = groundCreateValue(CUSTOM, &threadStruct);
GroundValue function = args.values[0];
GroundValue threadFuncArgs = args.values[1];
GroundObjectField* funcField = groundFindField(*value.data.customVal, "fn");
GroundObjectField* argsField = groundFindField(*value.data.customVal, "args");
funcField->value = function;
argsField->value = threadFuncArgs;
value.type = CUSTOM;
return value;
}
void initThreadStruct(GroundScope* scope) {
threadStruct = groundCreateStruct();
groundAddFieldToStruct(&threadStruct, "fn", groundCreateValue(FUNCTION, 0));
groundAddFieldToStruct(&threadStruct, "args", groundCreateValue(INT, 0));
groundAddFieldToStruct(&threadStruct, "pthread", groundCreateValue(INT, 0));
groundAddFunctionToStruct(&threadStruct, "start", threadStructStart, INT, 0);
groundAddNativeFunction(
scope,
"Thread_SOLS_CONSTRUCTOR",
threadStructConstructor,
CUSTOM,
2,
FUNCTION,
"function",
CUSTOM,
"args"
);
}

8
libs/threading/thread.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef TASK_H
#define TASK_H
#include <groundext.h>
void initThreadStruct(GroundScope* scope);
#endif

View File

@@ -0,0 +1,6 @@
#include "thread.h"
#include <groundext.h>
void ground_init(GroundScope* scope) {
initThreadStruct(scope);
}

View File

@@ -376,6 +376,34 @@ bool checkForErrors(GroundProgram* program) {
return true;
}
GroundState createGroundState() {
GroundState state = {
.variables = NULL,
.labels = NULL
};
return state;
}
GroundCompilerVariable* cFindVariable(GroundState* state, const char* id) {
GroundCompilerVariable* var;
HASH_FIND_STR(state->variables, id, var);
return var;
}
void cDeleteVariable(GroundState* state, const char* id) {
GroundCompilerVariable* var;
HASH_FIND_STR(state->variables, id, var);
HASH_DEL(state->variables, var);
}
GroundCompilerVariable* cAddVariable(GroundState* state, const char* id, GroundValueType type) {
GroundCompilerVariable* var = malloc(sizeof(GroundCompilerVariable));
snprintf(var->id, MAX_ID_LEN, "%s", id);
var->type = type;
HASH_ADD_STR(state->variables, id, var);
return var;
}
void compileGroundProgram(GroundProgram* program, char* name) {
if (!checkForErrors(program)) {
@@ -383,13 +411,23 @@ void compileGroundProgram(GroundProgram* program, char* name) {
return;
}
GroundState state = createGroundState();
Tram_Program tramProgram = Tram_Program_Create();
Tram_ParameterList parameters = Tram_ParameterList_Create(1, (Tram_Parameter[]){Tram_Parameter_Variable("main")});
Tram_Program_AddInstruction(&tramProgram, Tram_Instruction_Create(Tram_InstructionType_CreateLabel, parameters));
// Resolve labels
for (size_t i = 0; i < program->size; i++) {
compileGroundInstruction(&program->instructions[i], &tramProgram);
if (program->instructions[i].type == CREATELABEL) {
Tram_ParameterList parameters = Tram_ParameterList_Create(1, (Tram_Parameter[]){Tram_Parameter_Variable(program->instructions[i].args.args[0].value.refName)});
Tram_Program_AddInstruction(&tramProgram, Tram_Instruction_Create(Tram_InstructionType_CreateLabel, parameters));
}
}
for (size_t i = 0; i < program->size; i++) {
compileGroundInstruction(&program->instructions[i], &tramProgram, &state);
}
Tram_Compiler* compiler = Tram_Compiler_Create(tramProgram);
@@ -406,46 +444,7 @@ void compileGroundProgram(GroundProgram* program, char* name) {
}
typedef struct GroundCompilerVariable {
GroundValueType type;
char id[MAX_ID_LEN];
UT_hash_handle hh;
} GroundCompilerVariable;
typedef struct GroundState {
GroundCompilerVariable* variables;
GroundLabel* labels;
} GroundState;
GroundState createGroundState() {
GroundState state = {
.variables = NULL,
.labels = NULL
};
return state;
}
GroundCompilerVariable* cFindVariable(GroundState* state, const char* id) {
GroundCompilerVariable* var;
HASH_FIND_STR(state->variables, id, var);
return var;
}
GroundCompilerVariable* cDeleteVariable(GroundState* state, const char* id) {
GroundCompilerVariable* var;
HASH_FIND_STR(state->variables, id, var);
HASH_DEL(state->variables, var);
}
GroundCompilerVariable* cAddVariable(GroundState* state, const char* id, GroundValueType type) {
GroundCompilerVariable* var = malloc(sizeof(GroundCompilerVariable));
snprintf(var->id, MAX_ID_LEN, "%s", id);
var->type = type;
HASH_ADD_STR(state->variables, id, var);
return var;
}
void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program) {
void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program, GroundState* state) {
switch (instruction->type) {
case IF:
break;
@@ -542,7 +541,7 @@ void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* prog
case EXTERN:
break;
case CREATELABEL: {
Tram_ParameterList parameters = Tram_ParameterList_Create(1, (Tram_Parameter[]){Tram_Parameter_Variable("main")});
Tram_ParameterList parameters = Tram_ParameterList_Create(1, (Tram_Parameter[]){Tram_Parameter_Variable(instruction->args.args[0].value.refName)});
Tram_Program_AddInstruction(program, Tram_Instruction_Create(Tram_InstructionType_CreateLabel, parameters));
break;
}
@@ -557,4 +556,4 @@ void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* prog
}
}
#endif
#endif

View File

@@ -3,7 +3,18 @@
#include <tram.h>
#endif
typedef struct GroundCompilerVariable {
GroundValueType type;
char id[MAX_ID_LEN];
UT_hash_handle hh;
} GroundCompilerVariable;
typedef struct GroundState {
GroundCompilerVariable* variables;
GroundLabel* labels;
} GroundState;
void compileGroundProgram(GroundProgram* program, char* name);
#ifdef GROUND_COMPILE_WITH_TRAM
void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program);
void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program, GroundState* state);
#endif

View File

@@ -5,7 +5,8 @@
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
char* getFileContents(const char* filename) {
// https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c
FILE* fp;
@@ -125,7 +126,7 @@ GroundValue groundCreateValue(GroundValueType type, ...) {
gv.type = CUSTOM;
gv.data.customVal = malloc(sizeof(GroundObject));
*gv.data.customVal = createObject(*gstruct);
// Deep copy the struct definition so it stays valid
gv.customType = malloc(sizeof(GroundStruct));
gv.customType->size = gstruct->size;
@@ -151,7 +152,10 @@ GroundValue groundCreateValue(GroundValueType type, ...) {
return gv;
}
void segfaultHandle(int signal);
GroundValue groundRunProgram(GroundProgram* program) {
signal(SIGSEGV, segfaultHandle);
GroundVariable* variables = NULL;
GroundLabel* labels = NULL;
GroundScope scope = {
@@ -226,8 +230,7 @@ GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) {
return createErrorGroundValue(createGroundError("Null function passed to groundRunFunction", "valueError", NULL, NULL));
}
// Seems to crash some functions
// GroundScope callScope = copyGroundScope(&function->closure);
GroundScope callScope = copyGroundScope(&function->closure);
if (function->isNative) {
List argsList = createList();
@@ -237,14 +240,6 @@ GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) {
return function->nativeFn(NULL, argsList);
}
GroundScope callScope = {
.labels = malloc(sizeof(GroundLabel*)),
.variables = malloc(sizeof(GroundVariable*)),
.isMainScope = false
};
*callScope.variables = NULL;
*callScope.labels = NULL;
if (argc != function->argSize) {
return createErrorGroundValue(createGroundError("Too few or too many arguments for function", "callError", NULL, NULL));
}

View File

@@ -119,6 +119,10 @@ char* getFileContents(const char* filename);
exit(1);
}
void segfaultHandle(int signal) {
runtimeError(FIXME, "The Ground interpreter has crashed due to a segmentation fault. Please report this issue as soon as possible, with as much detail as possible.", NULL, currentInstruction);
}
GroundLabel* findLabel(GroundLabel* head, const char *id) {
GroundLabel *item;
HASH_FIND_STR(head, id, item);
@@ -157,14 +161,11 @@ void deleteVariable(GroundVariable** head, GroundVariable *item) {
if (item == NULL) {
return;
}
if (strcmp(item->id, "self") == 0) {
return;
}
if (item->freed) {
return;
}
item->freed = true;
if (item->value.type == CUSTOM) {
if (strcmp(item->id, "self") != 0 && item->value.type == CUSTOM) {
GroundObjectField* destructor = findField(*item->value.data.customVal, "destructor");
if (destructor != NULL) {
if (destructor->value.type == FUNCTION) {
@@ -178,12 +179,20 @@ void deleteVariable(GroundVariable** head, GroundVariable *item) {
runtimeError(FIXME, "Failed to allocate memory for scope variables", NULL, -1);
}
*scope.variables = NULL;
*scope.catches = NULL;
addVariable(scope.variables, "self", item->value);
GroundInstruction instruction = createGroundInstruction(CALLMETHOD);
addArgToInstruction(&instruction, createRefGroundArg(DIRREF, "self"));
addArgToInstruction(&instruction, createRefGroundArg(FNREF, "destructor"));
addArgToInstruction(&instruction, createRefGroundArg(DIRREF, ""));
interpretGroundInstruction(instruction, &scope);
GroundVariable *var, *varTmp;
HASH_ITER(hh, *scope.variables, var, varTmp) {
deleteVariable(scope.variables, var);
}
free(scope.variables);
free(scope.catches);
} else {
runtimeError(ARG_TYPE_MISMATCH, "Custom type destructor must be a function", NULL, -1);
}
@@ -206,6 +215,12 @@ void addVariable(GroundVariable **head, const char *id, GroundValue data) {
HASH_ADD_STR(*head, id, item);
}
void addVariableString(GroundScope* scope, const char* id, const char* str) {
GroundValue tempVal = createStringGroundValue(str);
addVariable(scope->variables, id, tempVal);
freeGroundValue(&tempVal);
}
GroundFunction* createGroundFunction() {
GroundFunction* gf = malloc(sizeof(GroundFunction));
gf->argSize = 0;
@@ -318,14 +333,14 @@ GroundDebugInstruction parseDebugInstruction(char* in) {
free(instruction);
return gdi;
}
}
void groundAddNativeFunction(GroundScope* scope, char* name, NativeGroundFunction fn, GroundValueType returnType, int argCount, ...) {
GroundFunction* gf = createGroundFunction();
gf->isNative = true;
gf->nativeFn = fn;
gf->returnType = returnType;
va_list args;
va_start(args, argCount);
for(int i = 0; i < argCount; i++) {
@@ -334,7 +349,7 @@ void groundAddNativeFunction(GroundScope* scope, char* name, NativeGroundFunctio
addArgsToGroundFunction(gf, type, argName);
}
va_end(args);
addVariable(scope->variables, name, createFunctionGroundValue(gf));
}
@@ -379,7 +394,10 @@ GroundValue resolveInstructionVariables(GroundInstruction* in, GroundScope* scop
GroundVariable* variable = findVariable(*scope->variables, in->args.args[i].value.refName);
if (variable) {
// If there is a duplicator, call it (except when returning and getting fields)
if (variable->value.type == CUSTOM && in->type != RETURN && in->type != GETFIELD) {
if ((variable->value.type == CUSTOM && in->type != RETURN && in->type != GETFIELD)
&& (in->type == SET || in->type == SETFIELD ||
in->type == CALL || in->type == CALLMETHOD
)) {
GroundObject* obj = variable->value.data.customVal;
GroundObjectField* duplicator = findField(*obj, "duplicator");
if (duplicator != NULL) {
@@ -627,6 +645,9 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
GroundValue gv = createFunctionGroundValue(function);
addVariable(scope.variables, name, gv);
// gp instructions are shallow copies shared with function->program; only free the array
free(gp.instructions);
free(name);
}
if (in->instructions[i].type == STRUCT) {
if (in->instructions[i].args.length < 1) {
@@ -669,6 +690,11 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
*gv.data.structVal = parseStruct(&gp, &scope, errorOffset);
addVariable(scope.variables, name, gv);
// addVariable deep-copies gv; free the original here
freeGroundValue(&gv);
// struct gp instructions are deep-copied via copyGroundInstruction; safe to fully free
freeGroundProgram(&gp);
free(name);
}
if (in->instructions[i].type == PAUSE || instructionsToPause == 0) {
printf("Paused execution\n");
@@ -685,7 +711,7 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
runtimeError(FIXME, "Failed to read input from console with fgets", NULL, -1);
}
GroundDebugInstruction gdi = parseDebugInstruction(buffer);
bool shouldBreak = false;
switch (gdi.type) {
case CONTINUE: {
@@ -697,7 +723,7 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
break;
}
case DUMP: {
case DUMP: {
if (scope.variables == NULL) {
printf("Can't access variables");
break;
@@ -763,7 +789,7 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
printf("Unknown instruction (type \"help\" for help)");
break;
}
}
}
if (shouldBreak) {
break;
}
@@ -822,7 +848,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
* These instructions are for controlling how the program is executed.
* Instructions:
* if, jump, end
*/
*/
case IF: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 arguments", in, currentInstruction);
@@ -912,7 +938,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
char buffer[256];
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
buffer[strcspn(buffer, "\n")] = '\0';
addVariable(scope->variables, in->args.args[0].value.refName, createStringGroundValue(buffer));
addVariableString(scope, in->args.args[0].value.refName, buffer);
} else {
runtimeError(FIXME, "Failed to read input from console with fgets", in, currentInstruction);
}
@@ -970,51 +996,51 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
switch (in->args.args[0].value.value.type) {
case INT: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("int"));
addVariableString(scope, in->args.args[1].value.refName, "int");
break;
}
case DOUBLE: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("double"));
addVariableString(scope, in->args.args[1].value.refName, "double");
break;
}
case STRING: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("string"));
addVariableString(scope, in->args.args[1].value.refName, "string");
break;
}
case CHAR: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("char"));
addVariableString(scope, in->args.args[1].value.refName, "char");
break;
}
case BOOL: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("bool"));
addVariableString(scope, in->args.args[1].value.refName, "bool");
break;
}
case LIST: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("list"));
addVariableString(scope, in->args.args[1].value.refName, "list");
break;
}
case CUSTOM: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("custom"));
addVariableString(scope, in->args.args[1].value.refName, "custom");
break;
}
case FUNCTION: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("function"));
addVariableString(scope, in->args.args[1].value.refName, "function");
break;
}
case STRUCTVAL: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("struct"));
addVariableString(scope, in->args.args[1].value.refName, "struct");
break;
}
case ANY: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("any"));
addVariableString(scope, in->args.args[1].value.refName, "any");
break;
}
case NONE: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("none"));
addVariableString(scope, in->args.args[1].value.refName, "none");
break;
}
case ERROR: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("error"));
addVariableString(scope, in->args.args[1].value.refName, "error");
break;
}
}
@@ -1058,9 +1084,11 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (in->args.args[i].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for all args after arg 1", in, currentInstruction);
}
appendToList(&newList, in->args.args[i].value.value);
appendToList(&newList, copyGroundValue(&in->args.args[i].value.value));
}
addVariable(scope->variables, in->args.args[0].value.refName, createListGroundValue(newList));
GroundValue listVal = createListGroundValue(newList);
addVariable(scope->variables, in->args.args[0].value.refName, listVal);
freeGroundValue(&listVal);
break;
}
case SETLISTAT: {
@@ -1091,7 +1119,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (value->type != LIST) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
}
ListAccessStatus status = setListAt(&value->data.listVal, in->args.args[1].value.value.data.intVal, in->args.args[2].value.value);
ListAccessStatus status = setListAt(&value->data.listVal, in->args.args[1].value.value.data.intVal, copyGroundValue(&in->args.args[2].value.value));
switch (status) {
case LIST_OKAY:
break;
@@ -1198,7 +1226,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (value->type != LIST) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
}
appendToList(&value->data.listVal, in->args.args[1].value.value);
appendToList(&value->data.listVal, copyGroundValue(&in->args.args[1].value.value));
break;
}
@@ -1326,7 +1354,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
}
}
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue(buf));
addVariableString(scope, in->args.args[1].value.refName, buf);
break;
}
@@ -1361,11 +1389,12 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (right->type != STRING) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a String for arg 2", in, currentInstruction);
}
char* newString = malloc(strlen(left->data.stringVal) + strlen(right->data.stringVal));
char* newString = malloc(strlen(left->data.stringVal) + strlen(right->data.stringVal) + 1);
strcpy(newString, left->data.stringVal);
strcat(newString, right->data.stringVal);
addVariable(scope->variables, in->args.args[2].value.refName, createStringGroundValue(newString));
addVariableString(scope, in->args.args[2].value.refName, newString);
free(newString);
}
else if (left->type == INT || left->type == DOUBLE) {
if (right->type != INT && right->type != DOUBLE) {
@@ -1964,6 +1993,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from native function", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
freeGroundValue(&returnValue);
}
free(argsList.values);
} else {
@@ -1992,8 +2022,12 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
HASH_ITER(hh, *newScope.variables, var, tmp) {
deleteVariable(newScope.variables, var);
}
free(newScope.variables);
free(newScope.labels);
free(newScope.catches);
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
freeGroundValue(&returnValue);
currentInstruction = currentCurrentInstruction;
}
break;
@@ -2005,9 +2039,6 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to an object for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != FNREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a FunctionRef for arg 2", in, currentInstruction);
}
if (in->args.args[in->args.length - 1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef as the last arg", in, currentInstruction);
}
@@ -2025,32 +2056,77 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (fnvar == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Method not found inside specified object", in, currentInstruction);
}
if (fnvar->value.type != FUNCTION) {
runtimeError(UNKNOWN_VARIABLE, "Provided reference does not reference a method", in, currentInstruction);
size_t fnRefPos = 0;
if (in->args.args[1].type == FNREF) {
if (fnvar == NULL) {
runtimeError(FIXME, "fnvar is null (this should not happen)", in, currentInstruction);
}
if (fnvar->value.type != FUNCTION) {
runtimeError(ARG_TYPE_MISMATCH, "Field is not a method", in, currentInstruction);
}
fnRefPos = 1;
} else for (size_t i = 2; i < in->args.length; i++) {
if (in->args.args[i].type == FNREF) {
if (fnvar == NULL) {
runtimeError(FIXME, "fnvar is null (this should not happen)", in, currentInstruction);
}
if (fnvar->value.type != CUSTOM) {
runtimeError(ARG_TYPE_MISMATCH, "Field is not an object", in, currentInstruction);
}
obj = &fnvar->value;
fnvar = findField(*fnvar->value.data.customVal, in->args.args[i].value.refName);
if (fnvar == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Struct does not contain that field", in, currentInstruction);
}
if (fnvar->value.type != FUNCTION) {
runtimeError(ARG_TYPE_MISMATCH, "Field is not a method", in, currentInstruction);
}
fnRefPos = i;
break;
} else if (in->args.args[i].type == DIRREF) {
if (fnvar == NULL) {
runtimeError(FIXME, "fnvar is null (this should not happen)", in, currentInstruction);
}
if (fnvar->value.type != CUSTOM) {
runtimeError(ARG_TYPE_MISMATCH, "Field is not an object", in, currentInstruction);
}
fnvar = findField(*fnvar->value.data.customVal, in->args.args[i].value.refName);
if (fnvar == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Struct does not contain that field", in, currentInstruction);
}
} else {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef or FnRef for callmethod args", in, currentInstruction);
}
}
if (fnRefPos == 0) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a FunctionRef after field list", in, currentInstruction);
}
GroundFunction* function = fnvar->value.data.fnVal;
if (function->argSize < in->args.length - 3) {
if (function->argSize < in->args.length - fnRefPos - 2) {
runtimeError(TOO_FEW_ARGS, "Incorrect amount of arguments provided for function", in, currentInstruction);
}
if (function->argSize > in->args.length - 3) {
if (function->argSize > in->args.length - fnRefPos - 2) {
runtimeError(TOO_MANY_ARGS, "Incorrect amount of arguments provided for function", in, currentInstruction);
}
if (function->isNative) {
List argsList = createList();
for (size_t i = 0; i < function->argSize; i++) {
if (in->args.args[i + 2].type != VALUE) {
if (in->args.args[i + fnRefPos + 1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
}
if (function->nativeFn) {
if (function->args[i].type != ANY && in->args.args[i + 2].value.value.type != function->args[i].type) {
if (function->args[i].type != ANY && in->args.args[i + fnRefPos + 1].value.value.type != function->args[i].type) {
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
}
} else {
if (function->args[i].type != ANY && !checkFnTypes(&in->args.args[i + 2].value.value, &function->args[i])) {
if (function->args[i].type != ANY && !checkFnTypes(&in->args.args[i + fnRefPos + 1].value.value, &function->args[i])) {
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
}
}
appendToList(&argsList, in->args.args[i + 2].value.value);
appendToList(&argsList, in->args.args[i + fnRefPos + 1].value.value);
}
GroundScope newscope = {
@@ -2081,30 +2157,46 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (scope->isMainScope) {
throwError(&returnValue.data.errorVal);
}
GroundVariable *var, *varTmp;
HASH_ITER(hh, *newscope.variables, var, varTmp) {
deleteVariable(newscope.variables, var);
}
free(newscope.variables);
return returnValue;
}
if (function->returnType != ANY && returnValue.type != function->returnType) {
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from native function", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
freeGroundValue(&returnValue);
// Copy back modified variables
HASH_ITER(hh, obj->data.customVal->fields, el, tmp) {
el->value = findVariable(*newscope.variables, el->id)->value;
GroundVariable* newscopeVar = findVariable(*newscope.variables, el->id);
if (newscopeVar != NULL) {
freeGroundValue(&el->value);
el->value = copyGroundValue(&newscopeVar->value);
}
}
GroundVariable *var, *varTmp;
HASH_ITER(hh, *newscope.variables, var, varTmp) {
deleteVariable(newscope.variables, var);
}
free(newscope.variables);
}
free(argsList.values);
} else {
GroundScope newScope = copyGroundScope(&function->closure);
for (size_t i = 0; i < function->argSize; i++) {
if (in->args.args[i + 2].type != VALUE) {
if (in->args.args[i + fnRefPos + 1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
}
//if (in->args.args[i + 1].value.value.type != function->args[i].type) {
if (function->args[i].type != ANY && !checkFnTypes(&in->args.args[i + 2].value.value, &function->args[i])) {
if (function->args[i].type != ANY && !checkFnTypes(&in->args.args[i + fnRefPos + 1].value.value, &function->args[i])) {
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
}
addVariable(newScope.variables, function->args[i].name, in->args.args[i + 2].value.value);
addVariable(newScope.variables, function->args[i].name, in->args.args[i + fnRefPos + 1].value.value);
}
// Add the object to the scope
addVariable(newScope.variables, "self", *obj);
@@ -2112,12 +2204,20 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
currentInstruction = function->startLine;
GroundValue returnValue = interpretGroundProgram(&function->program, &newScope);
if (returnValue.type == ERROR) {
GroundVariable *var, *varTmp;
HASH_ITER(hh, *newScope.variables, var, varTmp) {
deleteVariable(newScope.variables, var);
}
free(newScope.variables);
free(newScope.labels);
free(newScope.catches);
return returnValue;
}
if (function->returnType != ANY && returnValue.type != function->returnType) {
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from function", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
freeGroundValue(&returnValue);
currentInstruction = currentCurrentInstruction;
// Copy out the object
@@ -2125,7 +2225,17 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (objvar == NULL) {
runtimeError(UNKNOWN_VARIABLE, "self not found in scope, did you drop the object?", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[0].value.refName, objvar->value);
freeGroundValue(obj);
*obj = copyGroundValue(&objvar->value);
// Clean up newScope variables and pointers
GroundVariable *var, *varTmp;
HASH_ITER(hh, *newScope.variables, var, varTmp) {
deleteVariable(newScope.variables, var);
}
free(newScope.variables);
free(newScope.labels);
free(newScope.catches);
}
break;
@@ -2325,7 +2435,10 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
}
}
addVariable(scope->variables, in->args.args[0].value.refName, gv);
if (gv.type == CUSTOM) {
gv.customType = NULL;
}
freeGroundValue(&gv);
break;
}
@@ -2363,21 +2476,18 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
break;
}
// setfield &obj &field $value
// also allows setting nested fields with:
// setfield &obj &field1 &field2 &field3 ... $value
case SETFIELD: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
runtimeError(TOO_FEW_ARGS, "Expecting 3 or more args", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to an Object for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 3", in, currentInstruction);
// Check everything is a DIRREF wihle looping through the fields
if (in->args.args[in->args.length - 1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for last arg", in, currentInstruction);
}
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
@@ -2392,11 +2502,28 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (field == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Struct does not contain that field", in, currentInstruction);
}
if (field->value.type != in->args.args[2].value.value.type) {
for (size_t i = 2; i < in->args.length - 1; i++) {
if (field == NULL) {
runtimeError(FIXME, "Field is null, this should have errored beforehand", in, currentInstruction);
}
if (in->args.args[i].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for field setter args", in, currentInstruction);
}
if (field->value.type != CUSTOM) {
runtimeError(ARG_TYPE_MISMATCH, "Field is not an object", in, currentInstruction);
}
field = findField(*field->value.data.customVal, in->args.args[i].value.refName);
if (field == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Struct does not contain that field", in, currentInstruction);
}
}
if (field->value.type != in->args.args[in->args.length - 1].value.value.type) {
runtimeError(ARG_TYPE_MISMATCH, "Field type and provided type do not match", in, currentInstruction);
}
field->value = copyGroundValue(&in->args.args[2].value.value);
field->value = copyGroundValue(&in->args.args[in->args.length - 1].value.value);
break;
}

View File

@@ -4,12 +4,16 @@
#include "types.h"
#include "serialize.h"
#include "repl.h"
#include <signal.h>
#include <stdio.h>
#include <string.h>
void segfaultHandle(int signal);
char* getFileContents(const char* filename);
int main(int argc, char** argv) {
if (argc == 1) {
exit(repl());
}
@@ -17,6 +21,7 @@ int main(int argc, char** argv) {
bool compile = false;
bool writeBytecode = false;
bool readBytecode = false;
bool segfaultHandler = true;
char* fileName = NULL;
char* outFileName = NULL;
List groundArgs = createList();
@@ -28,6 +33,9 @@ int main(int argc, char** argv) {
}
compile = true;
}
else if (strcmp("--disable-segfault-handler", argv[i]) == 0) {
segfaultHandler = false;
}
else if (strcmp("--help", argv[i]) == 0 || strcmp("-h", argv[i]) == 0) {
printf("GroundVM help\n");
printf("Usage: %s <file> [-c] [--compile] [-h] [--help] [-w <output>] [--writeBytecode <output>] [-b] [--bytecode]\n", argv[0]);
@@ -66,6 +74,9 @@ int main(int argc, char** argv) {
exit(1);
}
if (segfaultHandler) signal(SIGSEGV, segfaultHandle);
GroundProgram program;
if (readBytecode) {

View File

@@ -89,14 +89,11 @@ GroundValue copyGroundValue(const GroundValue* gv) {
case CHAR: newGv.data.charVal = gv->data.charVal; break;
case BOOL: newGv.data.boolVal = gv->data.boolVal; break;
case STRING:
/*
if (gv->data.stringVal != NULL) {
newGv.data.stringVal = strdup(gv->data.stringVal);
} else {
newGv.data.stringVal = NULL;
}
*/
newGv.data.stringVal = gv->data.stringVal;
break;
case LIST: {
List newList = createList();
@@ -109,6 +106,10 @@ GroundValue copyGroundValue(const GroundValue* gv) {
}
case FUNCTION: newGv.data.fnVal = gv->data.fnVal; break;
case STRUCTVAL: {
if (gv->data.structVal == NULL) {
newGv.data.structVal = NULL;
break;
}
newGv.data.structVal = malloc(sizeof(GroundStruct));
if (newGv.data.structVal == NULL) {
printf("Couldn't allocate memory for GroundStruct copy\n");
@@ -119,6 +120,7 @@ GroundValue copyGroundValue(const GroundValue* gv) {
newGv.data.structVal->fields = malloc(gv->data.structVal->size * sizeof(GroundStructField));
if (newGv.data.structVal->fields == NULL && gv->data.structVal->size > 0) {
printf("Couldn't allocate memory for GroundStruct fields copy\n");
printf("%p, %zu\n", newGv.data.structVal->fields, newGv.data.structVal->size);
exit(1);
}
@@ -166,6 +168,30 @@ GroundValue copyGroundValue(const GroundValue* gv) {
}
break;
}
case ERROR: {
const GroundError* err = &gv->data.errorVal;
GroundError newErr;
if (err->what != NULL) {
newErr.what = strdup(err->what);
} else {
newErr.what = NULL;
}
if (err->type != NULL) {
newErr.type = strdup(err->type);
} else {
newErr.type = NULL;
}
if (err->where != NULL) {
newErr.where = malloc(sizeof(GroundInstruction));
*newErr.where = copyGroundInstruction(err->where);
} else {
newErr.where = NULL;
}
newErr.line = err->line;
newErr.hasLine = err->hasLine;
newGv.data.errorVal = newErr;
break;
}
case NONE:
default: {
@@ -268,7 +294,7 @@ void printGroundValue(GroundValue* gv) {
void freeGroundValue(GroundValue* gv) {
if (gv->type == STRING && gv->data.stringVal != NULL) {
// leak some memory for now
// free(gv->data.stringVal);
free(gv->data.stringVal);
gv->data.stringVal = NULL;
}
if (gv->type == LIST && gv->data.listVal.values != NULL) {
@@ -280,10 +306,15 @@ void freeGroundValue(GroundValue* gv) {
list->values = NULL;
gv->data.listVal = createList();
}
if (gv->type == STRUCTVAL && gv->data.structVal->fields != NULL) {
if (gv->type == STRUCTVAL && gv->data.structVal != NULL) {
GroundStruct* gstruct = gv->data.structVal;
freeGroundStruct(gstruct);
if (gstruct->fields != NULL) {
freeGroundStruct(gstruct);
free(gstruct->fields);
gstruct->fields = NULL;
}
free(gstruct);
gv->data.structVal = NULL;
}
if (gv->type == CUSTOM && gv->data.customVal != NULL) {
freeGroundObject(gv->data.customVal);
@@ -645,6 +676,7 @@ ListAccessStatus setListAt(List* list, size_t idx, GroundValue value) {
exit(EXIT_FAILURE);
}
if (idx < list->size) {
freeGroundValue(&list->values[idx]);
list->values[idx] = value;
return LIST_OKAY;
} else {