forked from ground/ground
Compare commits
16 Commits
abe4e9d6f0
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f6803b44c8 | |||
| c25df7918b | |||
| 491d6a2c71 | |||
| 3db779cb3e | |||
| ed27cb0d1b | |||
| bcbab3afb3 | |||
| 12d2ff6e24 | |||
| 7ff709f9b7 | |||
| 60054fe6f3 | |||
| 932846362d | |||
| 39b6a49a9c | |||
| 159d86b76d | |||
| a2b0924018 | |||
| 9edb6b51ec | |||
| 5f7f2ea152 | |||
| 6c293f7c3f |
@@ -3,9 +3,9 @@
|
|||||||
#define MAX_ID_LEN 64
|
#define MAX_ID_LEN 64
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* groundvm.h
|
* groundvm.h
|
||||||
* Provides an interface for external programs wanting to run Ground code.
|
* Provides an interface for external programs wanting to run Ground code.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -125,17 +125,6 @@ typedef struct GroundFunctionArgs {
|
|||||||
char* name;
|
char* name;
|
||||||
} GroundFunctionArgs;
|
} 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
|
* Field for a GroundStruct
|
||||||
*/
|
*/
|
||||||
@@ -181,12 +170,35 @@ typedef struct GroundVariable {
|
|||||||
UT_hash_handle hh;
|
UT_hash_handle hh;
|
||||||
} GroundVariable;
|
} GroundVariable;
|
||||||
|
|
||||||
|
typedef struct GroundCatch {
|
||||||
|
char id[MAX_ID_LEN];
|
||||||
|
GroundLabel* label;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
} GroundCatch;
|
||||||
|
|
||||||
typedef struct GroundScope {
|
typedef struct GroundScope {
|
||||||
GroundLabel** labels;
|
GroundLabel** labels;
|
||||||
GroundVariable** variables;
|
GroundVariable** variables;
|
||||||
|
GroundCatch** catches;
|
||||||
bool isMainScope;
|
bool isMainScope;
|
||||||
} GroundScope;
|
} 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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -194,7 +206,7 @@ extern "C" {
|
|||||||
GroundProgram groundCreateProgram();
|
GroundProgram groundCreateProgram();
|
||||||
void groundAddInstructionToProgram(GroundProgram* program, GroundInstruction instruction);
|
void groundAddInstructionToProgram(GroundProgram* program, GroundInstruction instruction);
|
||||||
GroundValue groundRunProgram(GroundProgram* program);
|
GroundValue groundRunProgram(GroundProgram* program);
|
||||||
void groundPrintProgram(GroundProgram* program);
|
void groundPrintProgram(GroundProgram* program);
|
||||||
char* groundCompileProgram(GroundProgram* program);
|
char* groundCompileProgram(GroundProgram* program);
|
||||||
|
|
||||||
GroundInstruction groundCreateInstruction(GroundInstType type);
|
GroundInstruction groundCreateInstruction(GroundInstType type);
|
||||||
|
|||||||
45
libs/_box/_box.c
Normal file
45
libs/_box/_box.c
Normal 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
202
libs/allocator/allocator.c
Normal 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
168
libs/socket/socket.c
Normal 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
101
libs/threading/thread.c
Normal 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
8
libs/threading/thread.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef TASK_H
|
||||||
|
#define TASK_H
|
||||||
|
|
||||||
|
#include <groundext.h>
|
||||||
|
|
||||||
|
void initThreadStruct(GroundScope* scope);
|
||||||
|
|
||||||
|
#endif
|
||||||
6
libs/threading/threading.c
Normal file
6
libs/threading/threading.c
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include "thread.h"
|
||||||
|
#include <groundext.h>
|
||||||
|
|
||||||
|
void ground_init(GroundScope* scope) {
|
||||||
|
initThreadStruct(scope);
|
||||||
|
}
|
||||||
@@ -376,6 +376,34 @@ bool checkForErrors(GroundProgram* program) {
|
|||||||
return true;
|
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) {
|
void compileGroundProgram(GroundProgram* program, char* name) {
|
||||||
|
|
||||||
if (!checkForErrors(program)) {
|
if (!checkForErrors(program)) {
|
||||||
@@ -383,13 +411,23 @@ void compileGroundProgram(GroundProgram* program, char* name) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GroundState state = createGroundState();
|
||||||
|
|
||||||
Tram_Program tramProgram = Tram_Program_Create();
|
Tram_Program tramProgram = Tram_Program_Create();
|
||||||
|
|
||||||
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("main")});
|
||||||
Tram_Program_AddInstruction(&tramProgram, Tram_Instruction_Create(Tram_InstructionType_CreateLabel, parameters));
|
Tram_Program_AddInstruction(&tramProgram, Tram_Instruction_Create(Tram_InstructionType_CreateLabel, parameters));
|
||||||
|
|
||||||
|
// Resolve labels
|
||||||
for (size_t i = 0; i < program->size; i++) {
|
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);
|
Tram_Compiler* compiler = Tram_Compiler_Create(tramProgram);
|
||||||
@@ -406,46 +444,7 @@ void compileGroundProgram(GroundProgram* program, char* name) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct GroundCompilerVariable {
|
void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program, GroundState* state) {
|
||||||
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) {
|
|
||||||
switch (instruction->type) {
|
switch (instruction->type) {
|
||||||
case IF:
|
case IF:
|
||||||
break;
|
break;
|
||||||
@@ -542,7 +541,7 @@ void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* prog
|
|||||||
case EXTERN:
|
case EXTERN:
|
||||||
break;
|
break;
|
||||||
case CREATELABEL: {
|
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));
|
Tram_Program_AddInstruction(program, Tram_Instruction_Create(Tram_InstructionType_CreateLabel, parameters));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -557,4 +556,4 @@ void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* prog
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3,7 +3,18 @@
|
|||||||
#include <tram.h>
|
#include <tram.h>
|
||||||
#endif
|
#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);
|
void compileGroundProgram(GroundProgram* program, char* name);
|
||||||
#ifdef GROUND_COMPILE_WITH_TRAM
|
#ifdef GROUND_COMPILE_WITH_TRAM
|
||||||
void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program);
|
void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program, GroundState* state);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
char* getFileContents(const char* filename) {
|
char* getFileContents(const char* filename) {
|
||||||
// https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c
|
// https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c
|
||||||
FILE* fp;
|
FILE* fp;
|
||||||
@@ -125,7 +126,7 @@ GroundValue groundCreateValue(GroundValueType type, ...) {
|
|||||||
gv.type = CUSTOM;
|
gv.type = CUSTOM;
|
||||||
gv.data.customVal = malloc(sizeof(GroundObject));
|
gv.data.customVal = malloc(sizeof(GroundObject));
|
||||||
*gv.data.customVal = createObject(*gstruct);
|
*gv.data.customVal = createObject(*gstruct);
|
||||||
|
|
||||||
// Deep copy the struct definition so it stays valid
|
// Deep copy the struct definition so it stays valid
|
||||||
gv.customType = malloc(sizeof(GroundStruct));
|
gv.customType = malloc(sizeof(GroundStruct));
|
||||||
gv.customType->size = gstruct->size;
|
gv.customType->size = gstruct->size;
|
||||||
@@ -151,7 +152,10 @@ GroundValue groundCreateValue(GroundValueType type, ...) {
|
|||||||
return gv;
|
return gv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void segfaultHandle(int signal);
|
||||||
|
|
||||||
GroundValue groundRunProgram(GroundProgram* program) {
|
GroundValue groundRunProgram(GroundProgram* program) {
|
||||||
|
signal(SIGSEGV, segfaultHandle);
|
||||||
GroundVariable* variables = NULL;
|
GroundVariable* variables = NULL;
|
||||||
GroundLabel* labels = NULL;
|
GroundLabel* labels = NULL;
|
||||||
GroundScope scope = {
|
GroundScope scope = {
|
||||||
@@ -226,8 +230,7 @@ GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) {
|
|||||||
return createErrorGroundValue(createGroundError("Null function passed to groundRunFunction", "valueError", NULL, NULL));
|
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) {
|
if (function->isNative) {
|
||||||
List argsList = createList();
|
List argsList = createList();
|
||||||
@@ -237,14 +240,6 @@ GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) {
|
|||||||
return function->nativeFn(NULL, argsList);
|
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) {
|
if (argc != function->argSize) {
|
||||||
return createErrorGroundValue(createGroundError("Too few or too many arguments for function", "callError", NULL, NULL));
|
return createErrorGroundValue(createGroundError("Too few or too many arguments for function", "callError", NULL, NULL));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,10 @@ char* getFileContents(const char* filename);
|
|||||||
exit(1);
|
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* findLabel(GroundLabel* head, const char *id) {
|
||||||
GroundLabel *item;
|
GroundLabel *item;
|
||||||
HASH_FIND_STR(head, id, item);
|
HASH_FIND_STR(head, id, item);
|
||||||
@@ -157,14 +161,11 @@ void deleteVariable(GroundVariable** head, GroundVariable *item) {
|
|||||||
if (item == NULL) {
|
if (item == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (strcmp(item->id, "self") == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (item->freed) {
|
if (item->freed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
item->freed = true;
|
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");
|
GroundObjectField* destructor = findField(*item->value.data.customVal, "destructor");
|
||||||
if (destructor != NULL) {
|
if (destructor != NULL) {
|
||||||
if (destructor->value.type == FUNCTION) {
|
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);
|
runtimeError(FIXME, "Failed to allocate memory for scope variables", NULL, -1);
|
||||||
}
|
}
|
||||||
*scope.variables = NULL;
|
*scope.variables = NULL;
|
||||||
|
*scope.catches = NULL;
|
||||||
addVariable(scope.variables, "self", item->value);
|
addVariable(scope.variables, "self", item->value);
|
||||||
GroundInstruction instruction = createGroundInstruction(CALLMETHOD);
|
GroundInstruction instruction = createGroundInstruction(CALLMETHOD);
|
||||||
addArgToInstruction(&instruction, createRefGroundArg(DIRREF, "self"));
|
addArgToInstruction(&instruction, createRefGroundArg(DIRREF, "self"));
|
||||||
addArgToInstruction(&instruction, createRefGroundArg(FNREF, "destructor"));
|
addArgToInstruction(&instruction, createRefGroundArg(FNREF, "destructor"));
|
||||||
addArgToInstruction(&instruction, createRefGroundArg(DIRREF, ""));
|
addArgToInstruction(&instruction, createRefGroundArg(DIRREF, ""));
|
||||||
interpretGroundInstruction(instruction, &scope);
|
interpretGroundInstruction(instruction, &scope);
|
||||||
|
|
||||||
|
GroundVariable *var, *varTmp;
|
||||||
|
HASH_ITER(hh, *scope.variables, var, varTmp) {
|
||||||
|
deleteVariable(scope.variables, var);
|
||||||
|
}
|
||||||
|
free(scope.variables);
|
||||||
|
free(scope.catches);
|
||||||
} else {
|
} else {
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Custom type destructor must be a function", NULL, -1);
|
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);
|
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* createGroundFunction() {
|
||||||
GroundFunction* gf = malloc(sizeof(GroundFunction));
|
GroundFunction* gf = malloc(sizeof(GroundFunction));
|
||||||
gf->argSize = 0;
|
gf->argSize = 0;
|
||||||
@@ -318,14 +333,14 @@ GroundDebugInstruction parseDebugInstruction(char* in) {
|
|||||||
|
|
||||||
free(instruction);
|
free(instruction);
|
||||||
return gdi;
|
return gdi;
|
||||||
}
|
}
|
||||||
|
|
||||||
void groundAddNativeFunction(GroundScope* scope, char* name, NativeGroundFunction fn, GroundValueType returnType, int argCount, ...) {
|
void groundAddNativeFunction(GroundScope* scope, char* name, NativeGroundFunction fn, GroundValueType returnType, int argCount, ...) {
|
||||||
GroundFunction* gf = createGroundFunction();
|
GroundFunction* gf = createGroundFunction();
|
||||||
gf->isNative = true;
|
gf->isNative = true;
|
||||||
gf->nativeFn = fn;
|
gf->nativeFn = fn;
|
||||||
gf->returnType = returnType;
|
gf->returnType = returnType;
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, argCount);
|
va_start(args, argCount);
|
||||||
for(int i = 0; i < argCount; i++) {
|
for(int i = 0; i < argCount; i++) {
|
||||||
@@ -334,7 +349,7 @@ void groundAddNativeFunction(GroundScope* scope, char* name, NativeGroundFunctio
|
|||||||
addArgsToGroundFunction(gf, type, argName);
|
addArgsToGroundFunction(gf, type, argName);
|
||||||
}
|
}
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
addVariable(scope->variables, name, createFunctionGroundValue(gf));
|
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);
|
GroundVariable* variable = findVariable(*scope->variables, in->args.args[i].value.refName);
|
||||||
if (variable) {
|
if (variable) {
|
||||||
// If there is a duplicator, call it (except when returning and getting fields)
|
// 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;
|
GroundObject* obj = variable->value.data.customVal;
|
||||||
GroundObjectField* duplicator = findField(*obj, "duplicator");
|
GroundObjectField* duplicator = findField(*obj, "duplicator");
|
||||||
if (duplicator != NULL) {
|
if (duplicator != NULL) {
|
||||||
@@ -627,6 +645,9 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
|||||||
GroundValue gv = createFunctionGroundValue(function);
|
GroundValue gv = createFunctionGroundValue(function);
|
||||||
|
|
||||||
addVariable(scope.variables, name, gv);
|
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].type == STRUCT) {
|
||||||
if (in->instructions[i].args.length < 1) {
|
if (in->instructions[i].args.length < 1) {
|
||||||
@@ -669,6 +690,11 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
|||||||
*gv.data.structVal = parseStruct(&gp, &scope, errorOffset);
|
*gv.data.structVal = parseStruct(&gp, &scope, errorOffset);
|
||||||
|
|
||||||
addVariable(scope.variables, name, gv);
|
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) {
|
if (in->instructions[i].type == PAUSE || instructionsToPause == 0) {
|
||||||
printf("Paused execution\n");
|
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);
|
runtimeError(FIXME, "Failed to read input from console with fgets", NULL, -1);
|
||||||
}
|
}
|
||||||
GroundDebugInstruction gdi = parseDebugInstruction(buffer);
|
GroundDebugInstruction gdi = parseDebugInstruction(buffer);
|
||||||
|
|
||||||
bool shouldBreak = false;
|
bool shouldBreak = false;
|
||||||
switch (gdi.type) {
|
switch (gdi.type) {
|
||||||
case CONTINUE: {
|
case CONTINUE: {
|
||||||
@@ -697,7 +723,7 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case DUMP: {
|
case DUMP: {
|
||||||
if (scope.variables == NULL) {
|
if (scope.variables == NULL) {
|
||||||
printf("Can't access variables");
|
printf("Can't access variables");
|
||||||
break;
|
break;
|
||||||
@@ -763,7 +789,7 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
|||||||
printf("Unknown instruction (type \"help\" for help)");
|
printf("Unknown instruction (type \"help\" for help)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (shouldBreak) {
|
if (shouldBreak) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -822,7 +848,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
* These instructions are for controlling how the program is executed.
|
* These instructions are for controlling how the program is executed.
|
||||||
* Instructions:
|
* Instructions:
|
||||||
* if, jump, end
|
* if, jump, end
|
||||||
*/
|
*/
|
||||||
case IF: {
|
case IF: {
|
||||||
if (in->args.length < 2) {
|
if (in->args.length < 2) {
|
||||||
runtimeError(TOO_FEW_ARGS, "Expecting 2 arguments", in, currentInstruction);
|
runtimeError(TOO_FEW_ARGS, "Expecting 2 arguments", in, currentInstruction);
|
||||||
@@ -912,7 +938,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
char buffer[256];
|
char buffer[256];
|
||||||
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
|
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
|
||||||
buffer[strcspn(buffer, "\n")] = '\0';
|
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 {
|
} else {
|
||||||
runtimeError(FIXME, "Failed to read input from console with fgets", in, currentInstruction);
|
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) {
|
switch (in->args.args[0].value.value.type) {
|
||||||
case INT: {
|
case INT: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("int"));
|
addVariableString(scope, in->args.args[1].value.refName, "int");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DOUBLE: {
|
case DOUBLE: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("double"));
|
addVariableString(scope, in->args.args[1].value.refName, "double");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case STRING: {
|
case STRING: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("string"));
|
addVariableString(scope, in->args.args[1].value.refName, "string");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CHAR: {
|
case CHAR: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("char"));
|
addVariableString(scope, in->args.args[1].value.refName, "char");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BOOL: {
|
case BOOL: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("bool"));
|
addVariableString(scope, in->args.args[1].value.refName, "bool");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LIST: {
|
case LIST: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("list"));
|
addVariableString(scope, in->args.args[1].value.refName, "list");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CUSTOM: {
|
case CUSTOM: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("custom"));
|
addVariableString(scope, in->args.args[1].value.refName, "custom");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FUNCTION: {
|
case FUNCTION: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("function"));
|
addVariableString(scope, in->args.args[1].value.refName, "function");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case STRUCTVAL: {
|
case STRUCTVAL: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("struct"));
|
addVariableString(scope, in->args.args[1].value.refName, "struct");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ANY: {
|
case ANY: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("any"));
|
addVariableString(scope, in->args.args[1].value.refName, "any");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NONE: {
|
case NONE: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("none"));
|
addVariableString(scope, in->args.args[1].value.refName, "none");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ERROR: {
|
case ERROR: {
|
||||||
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("error"));
|
addVariableString(scope, in->args.args[1].value.refName, "error");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1058,9 +1084,11 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
if (in->args.args[i].type != VALUE) {
|
if (in->args.args[i].type != VALUE) {
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for all args after arg 1", in, currentInstruction);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case SETLISTAT: {
|
case SETLISTAT: {
|
||||||
@@ -1091,7 +1119,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
if (value->type != LIST) {
|
if (value->type != LIST) {
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
|
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) {
|
switch (status) {
|
||||||
case LIST_OKAY:
|
case LIST_OKAY:
|
||||||
break;
|
break;
|
||||||
@@ -1198,7 +1226,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
if (value->type != LIST) {
|
if (value->type != LIST) {
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
|
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;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1361,11 +1389,12 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
if (right->type != STRING) {
|
if (right->type != STRING) {
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a String for arg 2", in, currentInstruction);
|
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);
|
strcpy(newString, left->data.stringVal);
|
||||||
strcat(newString, right->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) {
|
else if (left->type == INT || left->type == DOUBLE) {
|
||||||
if (right->type != INT && right->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);
|
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);
|
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
||||||
|
freeGroundValue(&returnValue);
|
||||||
}
|
}
|
||||||
free(argsList.values);
|
free(argsList.values);
|
||||||
} else {
|
} else {
|
||||||
@@ -1992,8 +2022,12 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
HASH_ITER(hh, *newScope.variables, var, tmp) {
|
HASH_ITER(hh, *newScope.variables, var, tmp) {
|
||||||
deleteVariable(newScope.variables, var);
|
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);
|
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
||||||
|
freeGroundValue(&returnValue);
|
||||||
currentInstruction = currentCurrentInstruction;
|
currentInstruction = currentCurrentInstruction;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2005,9 +2039,6 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
if (in->args.args[0].type != DIRREF) {
|
if (in->args.args[0].type != DIRREF) {
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to an object for arg 1", in, currentInstruction);
|
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) {
|
if (in->args.args[in->args.length - 1].type != DIRREF) {
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef as the last arg", in, currentInstruction);
|
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) {
|
if (fnvar == NULL) {
|
||||||
runtimeError(UNKNOWN_VARIABLE, "Method not found inside specified object", in, currentInstruction);
|
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;
|
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);
|
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);
|
runtimeError(TOO_MANY_ARGS, "Incorrect amount of arguments provided for function", in, currentInstruction);
|
||||||
}
|
}
|
||||||
if (function->isNative) {
|
if (function->isNative) {
|
||||||
List argsList = createList();
|
List argsList = createList();
|
||||||
for (size_t i = 0; i < function->argSize; i++) {
|
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);
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
|
||||||
}
|
}
|
||||||
if (function->nativeFn) {
|
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);
|
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
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 = {
|
GroundScope newscope = {
|
||||||
@@ -2081,30 +2157,46 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
if (scope->isMainScope) {
|
if (scope->isMainScope) {
|
||||||
throwError(&returnValue.data.errorVal);
|
throwError(&returnValue.data.errorVal);
|
||||||
}
|
}
|
||||||
|
GroundVariable *var, *varTmp;
|
||||||
|
HASH_ITER(hh, *newscope.variables, var, varTmp) {
|
||||||
|
deleteVariable(newscope.variables, var);
|
||||||
|
}
|
||||||
|
free(newscope.variables);
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
if (function->returnType != ANY && returnValue.type != function->returnType) {
|
if (function->returnType != ANY && returnValue.type != function->returnType) {
|
||||||
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from native function", in, currentInstruction);
|
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);
|
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
||||||
|
freeGroundValue(&returnValue);
|
||||||
|
|
||||||
// Copy back modified variables
|
// Copy back modified variables
|
||||||
HASH_ITER(hh, obj->data.customVal->fields, el, tmp) {
|
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);
|
free(argsList.values);
|
||||||
} else {
|
} else {
|
||||||
GroundScope newScope = copyGroundScope(&function->closure);
|
GroundScope newScope = copyGroundScope(&function->closure);
|
||||||
for (size_t i = 0; i < function->argSize; i++) {
|
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);
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
|
||||||
}
|
}
|
||||||
//if (in->args.args[i + 1].value.value.type != function->args[i].type) {
|
//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);
|
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
|
// Add the object to the scope
|
||||||
addVariable(newScope.variables, "self", *obj);
|
addVariable(newScope.variables, "self", *obj);
|
||||||
@@ -2112,12 +2204,20 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
currentInstruction = function->startLine;
|
currentInstruction = function->startLine;
|
||||||
GroundValue returnValue = interpretGroundProgram(&function->program, &newScope);
|
GroundValue returnValue = interpretGroundProgram(&function->program, &newScope);
|
||||||
if (returnValue.type == ERROR) {
|
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;
|
return returnValue;
|
||||||
}
|
}
|
||||||
if (function->returnType != ANY && returnValue.type != function->returnType) {
|
if (function->returnType != ANY && returnValue.type != function->returnType) {
|
||||||
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from function", in, currentInstruction);
|
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);
|
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
||||||
|
freeGroundValue(&returnValue);
|
||||||
currentInstruction = currentCurrentInstruction;
|
currentInstruction = currentCurrentInstruction;
|
||||||
|
|
||||||
// Copy out the object
|
// Copy out the object
|
||||||
@@ -2125,7 +2225,17 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
if (objvar == NULL) {
|
if (objvar == NULL) {
|
||||||
runtimeError(UNKNOWN_VARIABLE, "self not found in scope, did you drop the object?", in, currentInstruction);
|
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;
|
break;
|
||||||
@@ -2325,7 +2435,10 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
addVariable(scope->variables, in->args.args[0].value.refName, gv);
|
addVariable(scope->variables, in->args.args[0].value.refName, gv);
|
||||||
|
if (gv.type == CUSTOM) {
|
||||||
|
gv.customType = NULL;
|
||||||
|
}
|
||||||
|
freeGroundValue(&gv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2363,21 +2476,18 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// setfield &obj &field $value
|
// setfield &obj &field $value
|
||||||
|
// also allows setting nested fields with:
|
||||||
|
// setfield &obj &field1 &field2 &field3 ... $value
|
||||||
case SETFIELD: {
|
case SETFIELD: {
|
||||||
if (in->args.length < 3) {
|
if (in->args.length < 3) {
|
||||||
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
|
runtimeError(TOO_FEW_ARGS, "Expecting 3 or more args", in, currentInstruction);
|
||||||
}
|
|
||||||
if (in->args.length > 3) {
|
|
||||||
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
|
|
||||||
}
|
}
|
||||||
if (in->args.args[0].type != DIRREF) {
|
if (in->args.args[0].type != DIRREF) {
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to an Object for arg 1", in, currentInstruction);
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to an Object for arg 1", in, currentInstruction);
|
||||||
}
|
}
|
||||||
if (in->args.args[1].type != DIRREF) {
|
// Check everything is a DIRREF wihle looping through the fields
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
|
if (in->args.args[in->args.length - 1].type != VALUE) {
|
||||||
}
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for last arg", in, currentInstruction);
|
||||||
if (in->args.args[2].type != VALUE) {
|
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 3", in, currentInstruction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
|
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
|
||||||
@@ -2392,11 +2502,28 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
if (field == NULL) {
|
if (field == NULL) {
|
||||||
runtimeError(UNKNOWN_VARIABLE, "Struct does not contain that field", in, currentInstruction);
|
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);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
src/main.c
11
src/main.c
@@ -4,12 +4,16 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
#include "repl.h"
|
#include "repl.h"
|
||||||
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
void segfaultHandle(int signal);
|
||||||
|
|
||||||
char* getFileContents(const char* filename);
|
char* getFileContents(const char* filename);
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
exit(repl());
|
exit(repl());
|
||||||
}
|
}
|
||||||
@@ -17,6 +21,7 @@ int main(int argc, char** argv) {
|
|||||||
bool compile = false;
|
bool compile = false;
|
||||||
bool writeBytecode = false;
|
bool writeBytecode = false;
|
||||||
bool readBytecode = false;
|
bool readBytecode = false;
|
||||||
|
bool segfaultHandler = true;
|
||||||
char* fileName = NULL;
|
char* fileName = NULL;
|
||||||
char* outFileName = NULL;
|
char* outFileName = NULL;
|
||||||
List groundArgs = createList();
|
List groundArgs = createList();
|
||||||
@@ -28,6 +33,9 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
compile = true;
|
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) {
|
else if (strcmp("--help", argv[i]) == 0 || strcmp("-h", argv[i]) == 0) {
|
||||||
printf("GroundVM help\n");
|
printf("GroundVM help\n");
|
||||||
printf("Usage: %s <file> [-c] [--compile] [-h] [--help] [-w <output>] [--writeBytecode <output>] [-b] [--bytecode]\n", argv[0]);
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (segfaultHandler) signal(SIGSEGV, segfaultHandle);
|
||||||
|
|
||||||
|
|
||||||
GroundProgram program;
|
GroundProgram program;
|
||||||
|
|
||||||
if (readBytecode) {
|
if (readBytecode) {
|
||||||
|
|||||||
44
src/types.c
44
src/types.c
@@ -89,14 +89,11 @@ GroundValue copyGroundValue(const GroundValue* gv) {
|
|||||||
case CHAR: newGv.data.charVal = gv->data.charVal; break;
|
case CHAR: newGv.data.charVal = gv->data.charVal; break;
|
||||||
case BOOL: newGv.data.boolVal = gv->data.boolVal; break;
|
case BOOL: newGv.data.boolVal = gv->data.boolVal; break;
|
||||||
case STRING:
|
case STRING:
|
||||||
/*
|
|
||||||
if (gv->data.stringVal != NULL) {
|
if (gv->data.stringVal != NULL) {
|
||||||
newGv.data.stringVal = strdup(gv->data.stringVal);
|
newGv.data.stringVal = strdup(gv->data.stringVal);
|
||||||
} else {
|
} else {
|
||||||
newGv.data.stringVal = NULL;
|
newGv.data.stringVal = NULL;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
newGv.data.stringVal = gv->data.stringVal;
|
|
||||||
break;
|
break;
|
||||||
case LIST: {
|
case LIST: {
|
||||||
List newList = createList();
|
List newList = createList();
|
||||||
@@ -109,6 +106,10 @@ GroundValue copyGroundValue(const GroundValue* gv) {
|
|||||||
}
|
}
|
||||||
case FUNCTION: newGv.data.fnVal = gv->data.fnVal; break;
|
case FUNCTION: newGv.data.fnVal = gv->data.fnVal; break;
|
||||||
case STRUCTVAL: {
|
case STRUCTVAL: {
|
||||||
|
if (gv->data.structVal == NULL) {
|
||||||
|
newGv.data.structVal = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
newGv.data.structVal = malloc(sizeof(GroundStruct));
|
newGv.data.structVal = malloc(sizeof(GroundStruct));
|
||||||
if (newGv.data.structVal == NULL) {
|
if (newGv.data.structVal == NULL) {
|
||||||
printf("Couldn't allocate memory for GroundStruct copy\n");
|
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));
|
newGv.data.structVal->fields = malloc(gv->data.structVal->size * sizeof(GroundStructField));
|
||||||
if (newGv.data.structVal->fields == NULL && gv->data.structVal->size > 0) {
|
if (newGv.data.structVal->fields == NULL && gv->data.structVal->size > 0) {
|
||||||
printf("Couldn't allocate memory for GroundStruct fields copy\n");
|
printf("Couldn't allocate memory for GroundStruct fields copy\n");
|
||||||
|
printf("%p, %zu\n", newGv.data.structVal->fields, newGv.data.structVal->size);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,6 +168,30 @@ GroundValue copyGroundValue(const GroundValue* gv) {
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
case NONE:
|
||||||
default: {
|
default: {
|
||||||
|
|
||||||
@@ -268,7 +294,7 @@ void printGroundValue(GroundValue* gv) {
|
|||||||
void freeGroundValue(GroundValue* gv) {
|
void freeGroundValue(GroundValue* gv) {
|
||||||
if (gv->type == STRING && gv->data.stringVal != NULL) {
|
if (gv->type == STRING && gv->data.stringVal != NULL) {
|
||||||
// leak some memory for now
|
// leak some memory for now
|
||||||
// free(gv->data.stringVal);
|
free(gv->data.stringVal);
|
||||||
gv->data.stringVal = NULL;
|
gv->data.stringVal = NULL;
|
||||||
}
|
}
|
||||||
if (gv->type == LIST && gv->data.listVal.values != NULL) {
|
if (gv->type == LIST && gv->data.listVal.values != NULL) {
|
||||||
@@ -280,10 +306,15 @@ void freeGroundValue(GroundValue* gv) {
|
|||||||
list->values = NULL;
|
list->values = NULL;
|
||||||
gv->data.listVal = createList();
|
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;
|
GroundStruct* gstruct = gv->data.structVal;
|
||||||
freeGroundStruct(gstruct);
|
if (gstruct->fields != NULL) {
|
||||||
|
freeGroundStruct(gstruct);
|
||||||
|
free(gstruct->fields);
|
||||||
|
gstruct->fields = NULL;
|
||||||
|
}
|
||||||
free(gstruct);
|
free(gstruct);
|
||||||
|
gv->data.structVal = NULL;
|
||||||
}
|
}
|
||||||
if (gv->type == CUSTOM && gv->data.customVal != NULL) {
|
if (gv->type == CUSTOM && gv->data.customVal != NULL) {
|
||||||
freeGroundObject(gv->data.customVal);
|
freeGroundObject(gv->data.customVal);
|
||||||
@@ -645,6 +676,7 @@ ListAccessStatus setListAt(List* list, size_t idx, GroundValue value) {
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (idx < list->size) {
|
if (idx < list->size) {
|
||||||
|
freeGroundValue(&list->values[idx]);
|
||||||
list->values[idx] = value;
|
list->values[idx] = value;
|
||||||
return LIST_OKAY;
|
return LIST_OKAY;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user