51 Commits

Author SHA1 Message Date
abe4e9d6f0 Fix conflict 2026-04-27 11:21:08 +10:00
80ee95b918 refactor to facilitate solstice stuff 2026-04-20 19:05:10 +10:00
fca9dd63a0 Allow static linking to Ground 2026-04-17 12:57:47 +10:00
54a0f6058a fixed some issues with the collections lib 2026-04-17 10:09:40 +10:00
2c5c64671c Merge branch 'master' of https://chookspace.com/ground/ground 2026-04-16 17:41:50 +10:00
538cf4c666 fixing bugs with lists and strings 2026-04-16 17:41:37 +10:00
da15a1eff7 most basic tests lib on the planet 2026-04-16 13:10:54 +10:00
731d018857 Fix building without Tram 2026-04-15 15:03:29 +10:00
e4b5aafe35 Add catch support 2026-04-15 10:37:14 +10:00
ca85550c92 Start work on throw and catch 2026-04-14 10:50:27 +10:00
124245f492 Merge branch 'master' of https://chookspace.com/ground/ground 2026-04-13 18:49:10 +10:00
1c752c03db fix tiny bug 2026-04-13 18:48:54 +10:00
7f47491491 Duplicator support 2026-04-13 18:27:52 +10:00
90c91f7c4c Merge branch 'master' of https://chookspace.com/ground/ground 2026-04-13 18:06:39 +10:00
a428b01531 wrote string library 2026-04-13 18:06:33 +10:00
6bcf9d43c8 augh 2026-04-13 13:42:08 +10:00
9a6efed426 Band-aid fix for null type 2026-04-13 13:37:42 +10:00
cca98e3e5d Merge branch 'master' of https://chookspace.com/ground/ground 2026-04-13 12:10:06 +10:00
fb11e75c2a added destructors to List and Hashmap in the collections lib 2026-04-13 12:09:49 +10:00
a38d84ec8c Extlibs can call extlib functions i hope 2026-04-13 11:19:46 +10:00
68bd7f065a I HATE THE GROUND C LIB API RAHHHHHH 2026-04-13 10:44:23 +10:00
90a72a8dd7 stupid fileio bug 2026-04-13 06:47:31 +10:00
9e6a0d5a83 updated fileio and collections libs
rewrote fileio lib to use structs and methods. i also updated collections lib to fix bugs and be more consistent
2026-04-13 06:34:25 +10:00
aa0c71a47b added solstice support to the collections lib 2026-04-12 09:47:27 +10:00
f6c59a61d3 Merge branch 'master' of https://chookspace.com/ground/ground 2026-04-12 07:50:10 +10:00
c64c590abb handle some more memory allocation errors 2026-04-12 07:50:03 +10:00
2d02625e37 Destructor fixes 2026-04-11 20:41:43 +10:00
3427df2643 Merge branches 'master' and 'master' of https://chookspace.com/ground/ground 2026-04-11 19:58:35 +10:00
753e695538 made the collections lib use the "any" type 2026-04-11 19:57:35 +10:00
ead5cdc299 Fix -any again 2026-04-11 19:52:24 +10:00
8a3093c30f added hashmaps and fixed some small issues with lists 2026-04-11 19:47:12 +10:00
a1ad1fe139 fix any again 2026-04-11 19:33:58 +10:00
93c6989890 Fix 2026-04-11 19:26:55 +10:00
7b893dd931 I think any should work now 2026-04-11 19:24:46 +10:00
5b01784011 Destructor 2026-04-11 18:53:28 +10:00
e0bc9261a3 changed __ptr to private_ptr in collections lib 2026-04-11 17:14:21 +10:00
607f95a9c9 added more list functions 2026-04-11 17:04:54 +10:00
5ed3542bd7 Merge branch 'master' of https://chookspace.com/ground/ground 2026-04-11 16:06:40 +10:00
482f85af62 core of lists done 2026-04-11 16:06:34 +10:00
cb6b6a564c Add any type for extlibs 2026-04-11 13:19:04 +10:00
e5776b16dd groundFindVariable function 2026-04-11 13:09:32 +10:00
18ac18bc6d Tram is now an optional dependency 2026-04-11 12:31:37 +10:00
fcf9a13fa1 groundAddFunctionToStruct interface 2026-04-11 12:18:57 +10:00
6f6239f495 Update syntax.md 2026-04-11 10:45:33 +10:00
1342a54771 Methods inside objects 2026-04-11 10:42:53 +10:00
490e782bbf Continue work on compiler 2026-04-11 09:49:14 +10:00
f5c4468d50 Notify of list deprecation 2026-04-11 09:44:48 +10:00
1939ed019b fix makefile 2026-04-10 20:25:38 +10:00
01d3c0ed98 Stuff, also compiler is under construction 2026-04-10 17:43:51 +10:00
c2d6e2fcad Update the header again 2026-04-09 16:04:06 +10:00
c9f6fa5f8a Merge pull request 'Unit tests' (#26) from DiamondNether90/cground:master into master
Reviewed-on: ground/ground#26
2026-04-05 18:21:54 +10:00
21 changed files with 2664 additions and 755 deletions

View File

@@ -1,6 +1,7 @@
CC = gcc
CFLAGS = -Wall -Wextra -Isrc/include -Iinclude
LDFLAGS = -ldl -rdynamic
AR = ar
CFLAGS += -Wall -Wextra -Isrc/include -Iinclude -ggdb
LDFLAGS += -ldl -rdynamic
# Install paths
PREFIX ?= /usr/local
@@ -17,6 +18,7 @@ OBJ_DIR = $(BUILD_DIR)/obj
# Output names
EXECUTABLE = $(BIN_DIR)/ground
SHARED_LIB = $(LIB_DIR)/libgroundvm.so
STATIC_LIB = $(LIB_DIR)/libgroundvm.a
HEADERS = $(INC_DIR)/groundvm.h $(INC_DIR)/groundext.h
# Source files
@@ -27,9 +29,13 @@ EXE_SOURCES = $(wildcard $(SRC_DIR)/*.c)
LIB_OBJECTS = $(LIB_SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/lib_%.o)
EXE_OBJECTS = $(EXE_SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/exe_%.o)
# Default target: build standalone executable
# Default target: build library
.PHONY: all
all: executable
all: both
DEFINE_TRAM:
$(eval CFLAGS += -DGROUND_COMPILE_WITH_TRAM)
$(eval LDFLAGS += -ltram)
# Build standalone executable
.PHONY: executable
@@ -37,12 +43,16 @@ executable: $(EXECUTABLE)
# Build shared library
.PHONY: library
library: $(SHARED_LIB) $(HEADERS)
library: $(SHARED_LIB) $(STATIC_LIB) $(HEADERS)
# Build both
.PHONY: both
both: executable library
# both, with tram
.PHONY: tram_both
tram_both: DEFINE_TRAM both
# Link executable
$(EXECUTABLE): $(EXE_OBJECTS) | $(BIN_DIR)
$(CC) $(EXE_OBJECTS) -o $@ $(LDFLAGS)
@@ -51,6 +61,10 @@ $(EXECUTABLE): $(EXE_OBJECTS) | $(BIN_DIR)
$(SHARED_LIB): $(LIB_OBJECTS) | $(LIB_DIR)
$(CC) -shared $(LIB_OBJECTS) -o $@ $(LDFLAGS)
# Build static library
$(STATIC_LIB): $(LIB_OBJECTS) | $(LIB_DIR)
$(AR) rcs $(STATIC_LIB) $(LIB_OBJECTS)
# Copy header for library distribution
$(INC_DIR)/%.h: include/%.h | $(INC_DIR)
cp $< $@
@@ -80,6 +94,7 @@ install: both
mkdir -p $(DESTDIR)$(PREFIX)/include
cp $(EXECUTABLE) $(DESTDIR)$(PREFIX)/bin/
cp $(SHARED_LIB) $(DESTDIR)$(PREFIX)/lib/
cp $(STATIC_LIB) $(DESTDIR)$(PREFIX)/lib/
cp $(HEADERS) $(DESTDIR)$(PREFIX)/include/
echo "$(DESTDIR)$(PREFIX)/lib" | tee $(DESTDIR)/etc/ld.so.conf.d/cground.conf
ldconfig

View File

@@ -348,6 +348,14 @@ Calls a function. After the function reference, a variable amount of value refer
The last argument must be a direct reference which symbolises where to store the function's return value.
#### callmethod &object !methodName $arg1 $arg2 $arg3... &variable
Calls a method stored inside an object. The object can use the "self" variable to refer to itself, and manipulate its fields.
The first argument must be a direct reference to the object, and the second argument must be a function reference to the method name.
The last argument must be a direct reference which symbolises where to store the method's return value.
### Libraries

View File

@@ -38,6 +38,7 @@ typedef GroundValue (*NativeGroundFunction)(GroundScope* scope, List args);
* ...: A sequence of (GroundValueType type, char* name) for each argument.
*/
void groundAddNativeFunction(GroundScope* scope, char* name, NativeGroundFunction fn, GroundValueType returnType, int argCount, ...);
void groundAddFunctionToStruct(GroundStruct* gstruct, char* name, NativeGroundFunction fn, GroundValueType returnType, int argCount, ...);
#ifdef __cplusplus
}

View File

@@ -16,11 +16,14 @@
typedef enum GroundInstType {
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND,
GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, AND, OR, XOR, NEG,
SHIFT, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, CALLMETHOD, STRUCT, ENDSTRUCT, INIT,
GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, LICENSE, ERRORCMD, THROW, CATCH
} GroundInstType;
typedef enum GroundValueType {
INT, DOUBLE, STRING, CHAR, BOOL, LIST, FUNCTION, STRUCTVAL, CUSTOM, ERROR, NONE
INT, DOUBLE, STRING, CHAR, BOOL, LIST, FUNCTION, STRUCTVAL, CUSTOM, ERROR, NONE, ANY
} GroundValueType;
typedef enum GroundArgType {
@@ -209,6 +212,7 @@ GroundStruct groundCreateStruct();
void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field);
GroundObjectField* groundFindField(GroundObject head, const char *id);
GroundVariable* groundFindVariable(GroundScope* gs, const char *id);
// Run function returned by Ground code
// Use argc to set count of args, accepts that amount of GroundValue's after

View File

@@ -0,0 +1,10 @@
#include <groundext.h>
#include <groundvm.h>
#include "list.h"
#include "hashmap.h"
void ground_init(GroundScope* scope) {
initLists(scope);
initHashmaps(scope);
}

181
libs/collections/hashmap.c Normal file
View File

@@ -0,0 +1,181 @@
#include "hashmap.h"
#include <groundext.h>
#include <groundvm.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <uthash.h>
#include <stdio.h>
GroundStruct hashmapStruct = {};
GroundValue hashmapStructSet(GroundScope* scope, List args) {
char* key = args.values[0].data.stringVal;
GroundValue value = args.values[1];
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal;
HashmapItem* newItem = malloc(sizeof(HashmapItem));
newItem->key = strdup(key);
newItem->value = value;
HASH_ADD_KEYPTR(hh, keys, newItem->key, strlen(newItem->key), newItem);
ptrField->value.data.intVal = (long long)keys;
return groundCreateValue(INT, 0);
}
GroundValue hashmapStructGet(GroundScope* scope, List args) {
char* query = args.values[0].data.stringVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal;
HashmapItem* out;
HASH_FIND_STR(keys, query, out);
if (out == NULL) {
ERROR("Key was not found in hashmap (tip: use hashmap.getOr if you want to specify a fallback value)", "KeyNotFound");
}
return out->value;
}
GroundValue hashmapStructGetOr(GroundScope* scope, List args) {
char* query = args.values[0].data.stringVal;
GroundValue fallback = args.values[1];
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal;
HashmapItem* out;
HASH_FIND_STR(keys, query, out);
if (out == NULL)
return fallback;
return out->value;
}
GroundValue hashmapStructRemove(GroundScope* scope, List args) {
char* query = args.values[0].data.stringVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal;
HashmapItem* out;
HASH_FIND_STR(keys, query, out);
if (out == NULL) {
ERROR("Key was not found in hashmap (tip: use hashmap.removeIfPresent if you want to avoid errors)", "KeyNotFound");
}
HASH_DEL(keys, out);
free(out->key);
free(out);
return groundCreateValue(INT, 0);
}
GroundValue hashmapStructRemoveIfPresent(GroundScope* scope, List args) {
char* query = args.values[0].data.stringVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal;
HashmapItem* out;
HASH_FIND_STR(keys, query, out);
if (out == NULL) {
return groundCreateValue(BOOL, false);
}
HASH_DEL(keys, out);
free(out->key);
free(out);
return groundCreateValue(BOOL, true);
}
GroundValue hashmapStructConstructor(GroundScope* scope, List args) {
GroundValue value = groundCreateValue(CUSTOM, &hashmapStruct);
value.type = CUSTOM;
return value;
}
GroundValue destroyHashmapStruct(GroundScope* scope, List args) {
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal;
HashmapItem* current, *temp;
HASH_ITER(hh, keys, current, temp) {
HASH_DEL(keys, current);
free(current->key);
free(current);
}
return groundCreateValue(NONE);
}
GroundValue duplicateHashmapStruct(GroundScope* scope, List args) {
GroundObject* self = args.values[0].data.customVal;
GroundValue newSelf = groundCreateValue(CUSTOM, &hashmapStruct);
HashmapItem* src, *dst = NULL, *item, *tmp;
HASH_ITER(hh, src, item, tmp) {
HashmapItem *copy = malloc(sizeof(HashmapItem));
*copy = *item;
HASH_ADD_KEYPTR(hh, dst, copy->key, strlen(copy->key), copy);
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
ptrField->value.data.intVal = (int64_t)src;
return newSelf;
}
void initHashmaps(GroundScope* scope) {
hashmapStruct = groundCreateStruct();
groundAddFieldToStruct(&hashmapStruct, "ptr", groundCreateValue(INT, 0));
groundAddFunctionToStruct(&hashmapStruct, "set", hashmapStructSet, INT, 2, STRING, "key", ANY, "value"); // set a key in the hashmap
groundAddFunctionToStruct(&hashmapStruct, "get", hashmapStructGet, ANY, 1, STRING, "key"); // get a key in the hashmap, throws KeyNotFound if the key does not exist
groundAddFunctionToStruct(&hashmapStruct, "getOr", hashmapStructGetOr, ANY, 2, STRING, "key", ANY, "default"); // get a key in the hashmap, or fallback to a default value
groundAddFunctionToStruct(&hashmapStruct, "remove", hashmapStructRemove, INT, 1, STRING, "key"); // remove a key from the hashmap, throws KeyNotFound if the key does not exist
groundAddFunctionToStruct(&hashmapStruct, "removeIfPresent", hashmapStructRemoveIfPresent, BOOL, 1, STRING, "key"); // remove a key from the hashmap, returns true if the key was removed or false if it didn't exist
groundAddFunctionToStruct(&hashmapStruct, "destructor", destroyHashmapStruct, ANY, 0);
groundAddFunctionToStruct(&hashmapStruct, "duplicator", duplicateHashmapStruct, ANY, 1, CUSTOM);
groundAddNativeFunction(scope, "newHashmap", hashmapStructConstructor, CUSTOM, 0);
groundAddNativeFunction(scope, "Hashmap_SOLS_CONSTRUCTOR", hashmapStructConstructor, CUSTOM, 0);
}

View File

@@ -0,0 +1,16 @@
#ifndef HASHMAP_H
#define HASHMAP_H
#include <groundext.h>
#include <groundvm.h>
#include <uthash.h>
typedef struct {
GroundValue value;
char* key;
UT_hash_handle hh;
} HashmapItem;
void initHashmaps(GroundScope* scope);
#endif

546
libs/collections/list.c Normal file
View File

@@ -0,0 +1,546 @@
#include "list.h"
#include <groundext.h>
#include <groundvm.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const uint8_t STARTING_ELEMENTS = 2;
GroundStruct listStruct = {};
GroundValue appendToListStruct(GroundScope* scope, List args) {
GroundValue newValue = args.values[0];
// grab fields we need from struct
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
GroundVariable* memSizeField = groundFindVariable(scope, "memSize");
if (memSizeField == NULL) {
ERROR("A field called \"memSize\" was not found", "FieldNotFound");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
if (capacityField == NULL) {
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
int64_t capacity = capacityField->value.data.intVal;
int64_t size = sizeField->value.data.intVal;
if (size+1 > capacity) {
if (capacity == 0) capacity = 1;
else capacity *= 2;
capacityField->value.data.intVal = capacity;
int64_t newSize = sizeof(GroundValue) * capacity;
memSizeField->value.data.intVal = newSize;
GroundValue* newItems = realloc(items, newSize);
if (newItems == NULL) {
ERROR("Failed to allocate memory when increasing list size!", "AllocFail");
}
items = newItems;
ptrField->value.data.intVal = (int64_t)items;
}
items[size] = newValue;
sizeField->value.data.intVal = size + 1;
return groundCreateValue(INT, 0);
}
GroundValue listStructAt(GroundScope* scope, List args) {
int64_t index = args.values[0].data.intVal;
if (index < 0) {
ERROR("Attempt to access list at negative index", "OutOfBounds");
}
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
if (index >= size) {
char buffer[512];
sprintf(buffer, "Attempt to access list at index %ld when list is of length %ld", index, size);
ERROR(buffer, "OutOfBounds");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
return items[index];
}
GroundValue clearListStruct(GroundScope* scope, List args) {
// grab fields we need from struct
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
GroundVariable* memSizeField = groundFindVariable(scope, "memSize");
if (memSizeField == NULL) {
ERROR("A field called \"memSize\" was not found", "FieldNotFound");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
if (capacityField == NULL) {
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
GroundValue* newBuffer = calloc(STARTING_ELEMENTS, sizeof(GroundValue));
if (newBuffer == NULL) {
ERROR("Failed to allocate memory when clearing list!", "AllocFail");
}
sizeField->value.data.intVal = 0;
ptrField->value.data.intVal = (int64_t)newBuffer;
capacityField->value.data.intVal = STARTING_ELEMENTS;
memSizeField->value.data.intVal = sizeof(GroundValue) * STARTING_ELEMENTS;
return groundCreateValue(INT, 0);
}
GroundValue insertIntoListStruct(GroundScope* scope, List args) {
GroundValue value = args.values[0];
int64_t index = args.values[1].data.intVal;
if (index < 0) {
ERROR("Attempt to insert element into list at negative index", "OutOfBounds");
}
// grab fields we need from struct
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
GroundVariable* memSizeField = groundFindVariable(scope, "memSize");
if (memSizeField == NULL) {
ERROR("A field called \"memSize\" was not found", "FieldNotFound");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
if (capacityField == NULL) {
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
int64_t capacity = capacityField->value.data.intVal;
if (sizeField->value.data.intVal <= index) {
sizeField->value.data.intVal = index+1;
} else {
sizeField->value.data.intVal++;
}
// check if we have enough space to insert the element
if (capacity <= sizeField->value.data.intVal+1) {
// not enough space, double size
capacity = sizeField->value.data.intVal + 1;
capacityField->value.data.intVal = capacity;
}
// allocate new buffer
GroundValue* newBuffer = calloc(capacity, sizeof(GroundValue));
if (newBuffer == NULL) {
ERROR("Failed to allocate memory when increasing list size!", "AllocFail");
}
ptrField->value.data.intVal = (int64_t)newBuffer;
memSizeField->value.data.intVal = sizeof(GroundValue) * capacity;
// copy elements from 0 to i-1 into new buffer
memcpy(newBuffer, items, sizeof(GroundValue) * index);
// insert element at i
newBuffer[index] = value;
// copy elements 0 to i+1 into new buffer
memcpy(newBuffer + (index+1), items + index, sizeof(GroundValue) * (sizeField->value.data.intVal - index));
// free older buffer
free(items);
// retur null
return groundCreateValue(INT, 0);
}
GroundValue listStructDelete(GroundScope* scope, List args) {
int64_t index = args.values[0].data.intVal;
if (index < 0) {
ERROR("Attempt to remove element into list at negative index", "OutOfBounds");
}
// grab fields we need from struct
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
GroundVariable* memSizeField = groundFindVariable(scope, "memSize");
if (memSizeField == NULL) {
ERROR("A field called \"memSizvoid initLists(GroundScope* scope);e\" was not found", "FieldNotFound");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
if (capacityField == NULL) {
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
int64_t capacity = capacityField->value.data.intVal;
if (sizeField->value.data.intVal < index) {
char buffer[512];
sprintf(buffer, "Attempt to delete element at index %ld when list is of size %ld", index, sizeField->value.data.intVal);
ERROR(buffer, "OutOfBounds");
} else {
sizeField->value.data.intVal--;
}
int64_t size = sizeField->value.data.intVal;
// check if we have enough space to insert the element
if (capacity > size+1) {
// too much space, make list smaller
capacity = size + 1;
capacityField->value.data.intVal = capacity;
}
// allocate new buffer
GroundValue* newBuffer = calloc(capacity, sizeof(GroundValue));
if (newBuffer == NULL) {
ERROR("Failed to allocate memory when decreasing list size!", "AllocFail");
}
ptrField->value.data.intVal = (int64_t)newBuffer;
memSizeField->value.data.intVal = sizeof(GroundValue) * capacity;
// copy elements from 0 to i-1 into new buffer
if (index > 0)
memcpy(newBuffer, items, sizeof(GroundValue) * index);
// copy elements 0 to i+1 into new buffer
if (index < size)
memcpy(newBuffer + index, items + index + 1, sizeof(GroundValue) * (size - index));
// free older buffer
free(items);
// retur null
return groundCreateValue(INT, 0);
}
GroundValue listStructSet(GroundScope* scope, List args) {
GroundValue value = args.values[0];
int64_t index = args.values[1].data.intVal;
if (index < 0) {
ERROR("Attempt to set element in list at negative index", "OutOfBounds");
}
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
if (index >= size) {
insertIntoListStruct(scope, args);
} else {
items[index] = value;
}
return groundCreateValue(INT, 0);
}
GroundValue listStructIsEmpty(GroundScope* scope, List args) {
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
return groundCreateValue(BOOL, size <= 0);
}
bool areGroundValuesEqual(GroundValue a, GroundValue b) {
if (a.type != b.type) {
if (a.type == INT && b.type == DOUBLE) {
return a.data.intVal == b.data.doubleVal;
} else if (a.type == DOUBLE && b.type == INT) {
return a.data.doubleVal == b.data.intVal;
}
} else {
switch (a.type) {
case INT:
return a.data.intVal == b.data.intVal;
case DOUBLE:
return a.data.doubleVal == b.data.doubleVal;
case BOOL:
return a.data.boolVal == b.data.boolVal;
case CHAR:
return a.data.charVal == b.data.charVal;
case STRING:
return strcmp(a.data.stringVal, b.data.stringVal) == 0;
default:
return false;
}
}
return false;
}
GroundValue listStructContains(GroundScope* scope, List args) {
GroundValue targetValue = args.values[0];
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
for (int64_t i = 0; i < size; i++) {
if (areGroundValuesEqual(items[i], targetValue)) {
return groundCreateValue(BOOL, true);
}
}
return groundCreateValue(BOOL, false);
}
GroundValue reverseListStruct(GroundScope* scope, List args) {
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
if (capacityField == NULL) {
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
int64_t capacity = capacityField->value.data.intVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
GroundValue* newBuffer = calloc(capacity, sizeof(GroundValue));
if (newBuffer == NULL) {
ERROR("Failed to allocate memory when reversing list!", "AllocFail");
}
int z = 0;
for (int i = size - 1; i >= 0; i--, z++) {
newBuffer[z] = items[i];
}
ptrField->value.data.intVal = (int64_t)newBuffer;
free(items);
return groundCreateValue(INT, 0);
}
GroundValue findListStruct(GroundScope* scope, List args) {
GroundValue targetValue = args.values[0];
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
for (int64_t i = 0; i < size; i++) {
if (areGroundValuesEqual(items[i], targetValue)) {
return groundCreateValue(INT, i);
}
}
return groundCreateValue(INT, (int64_t)-1);
}
GroundValue reserveListStruct(GroundScope* scope, List args) {
int64_t amount = args.values[0].data.intVal;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
if (capacityField == NULL) {
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
int64_t capacity = capacityField->value.data.intVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
GroundVariable* memSizeField = groundFindVariable(scope, "memSize");
if (memSizeField == NULL) {
ERROR("A field called \"memSize\" was not found", "FieldNotFound");
}
if (capacity > amount) {
return groundCreateValue(BOOL, false);
}
capacityField->value.data.intVal = amount;
items = realloc(items, sizeof(GroundValue) * amount);
if (items == NULL) {
ERROR("Failed to allocate memory when reserving list space!", "AllocFail");
}
memSizeField->value.data.intVal = sizeof(GroundValue) * amount;
return groundCreateValue(BOOL, true);
}
GroundValue listStructConstructor(GroundScope* scope, List args) {
int64_t startingCapacity = args.values[0].data.intVal;
if (startingCapacity < 1) {
ERROR("List can't be less than 1 element in capacity on initialization!", "OutOfBounds");
}
GroundValue value = groundCreateValue(CUSTOM, &listStruct);
GroundObjectField *sizeField = groundFindField(*value.data.customVal, "size");
GroundObjectField *capacityField = groundFindField(*value.data.customVal, "capacity");
GroundObjectField *memSizeField = groundFindField(*value.data.customVal, "memSize");
GroundObjectField *ptrField = groundFindField(*value.data.customVal, "ptr");
GroundValue* items = calloc(STARTING_ELEMENTS, sizeof(GroundValue));
if (items == NULL) {
ERROR("Failed to allocate memory while creating list!", "AllocFail");
}
sizeField->value.data.intVal = 0;
capacityField->value.data.intVal = startingCapacity;
memSizeField->value.data.intVal = sizeof(GroundValue) * startingCapacity;
ptrField->value.data.intVal = (int64_t)items;
value.type = CUSTOM;
return value;
}
GroundValue destroyListStruct(GroundScope* scope, List args) {
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
//free((GroundValue*)ptrField->value.data.intVal);
return groundCreateValue(NONE);
}
GroundValue duplicateListStruct(GroundScope* scope, List args) {
GroundObject* self = args.values[0].data.customVal;
GroundObjectField *sizeField = groundFindField(*self, "size");
GroundObjectField *capacityField = groundFindField(*self, "capacity");
GroundObjectField *memSizeField = groundFindField(*self, "memSize");
GroundObjectField *ptrField = groundFindField(*self, "ptr");
GroundValue newSelf = groundCreateValue(CUSTOM, &listStruct);
newSelf.type = CUSTOM;
GroundObjectField *newSizeField = groundFindField(*newSelf.data.customVal, "size");
GroundObjectField *newCapacityField = groundFindField(*newSelf.data.customVal, "capacity");
GroundObjectField *newMemSizeField = groundFindField(*newSelf.data.customVal, "memSize");
GroundObjectField *newPtrField = groundFindField(*newSelf.data.customVal, "ptr");
newSizeField->value.data.intVal = sizeField->value.data.intVal;
newCapacityField->value.data.intVal = capacityField->value.data.intVal;
newMemSizeField->value.data.intVal = memSizeField->value.data.intVal;
groundAddValueToScope(scope, "size", sizeField->value);
groundAddValueToScope(scope, "capacity", capacityField->value);
groundAddValueToScope(scope, "memSize", memSizeField->value);
GroundValue* newPtr = calloc(capacityField->value.data.intVal, sizeof(GroundValue));
if (!newPtr) {
ERROR("Failed to allocate memory while calling copy constructor of List!", "AllocFail");
}
memcpy(newPtr, (GroundValue*)ptrField->value.data.intVal, sizeField->value.data.intVal * sizeof(GroundValue));
newPtrField->value.data.intVal = (int64_t)newPtr;
return newSelf;
}
void initLists(GroundScope* scope) {
listStruct = groundCreateStruct();
groundAddFieldToStruct(&listStruct, "size", groundCreateValue(INT, 0)); // number of elements
groundAddFieldToStruct(&listStruct, "memSize", groundCreateValue(INT, sizeof(GroundValue) * STARTING_ELEMENTS)); // number of bytes allocated
groundAddFieldToStruct(&listStruct, "capacity", groundCreateValue(INT, STARTING_ELEMENTS)); // number of elements that can fit in the currently allocated space
groundAddFieldToStruct(&listStruct, "ptr", groundCreateValue(INT, 0)); // pointer to internal list struct
groundAddFunctionToStruct(&listStruct, "init", listStructConstructor, INT, 1, INT, "startingCapacity"); // init struct (ground constructor)
groundAddFunctionToStruct(&listStruct, "append", appendToListStruct, INT, 1, ANY, "value"); // append item to end of list
groundAddFunctionToStruct(&listStruct, "insert", insertIntoListStruct, INT, 2, ANY, "value", INT, "index"); // insert value at index
groundAddFunctionToStruct(&listStruct, "remove", listStructDelete, INT, 1, INT, "index"); // delete value at index
groundAddFunctionToStruct(&listStruct, "at", listStructAt, ANY, 1, INT, "index"); // get value at index
groundAddFunctionToStruct(&listStruct, "clear", clearListStruct, INT, 0); // clear list
groundAddFunctionToStruct(&listStruct, "set", listStructSet, INT, 2, ANY, "value", INT, "index"); // replace a value at an index
groundAddFunctionToStruct(&listStruct, "isEmpty", listStructIsEmpty, BOOL, 0); // returns true if list is empty, otherwise false
groundAddFunctionToStruct(&listStruct, "contains", listStructContains, BOOL, 1, ANY, "value"); // returns true if value is in list
groundAddFunctionToStruct(&listStruct, "reverse", reverseListStruct, INT, 0); // return list struct in reverse order
groundAddFunctionToStruct(&listStruct, "find", findListStruct, INT, 1, ANY, "value"); // return index of value in list, if not found, returns -1
groundAddFunctionToStruct(&listStruct, "reserve", reserveListStruct, BOOL, 1, INT, "amount"); // ensure list capacity >= amount. returns true if the list's capacity was expanded
groundAddFunctionToStruct(&listStruct, "destructor", destroyListStruct, ANY,0);
groundAddFunctionToStruct(&listStruct, "duplicator", duplicateListStruct, CUSTOM, 1, CUSTOM, "self");
groundAddNativeFunction(scope, "newList", listStructConstructor, CUSTOM, 1, INT, "startingCapacity");
groundAddNativeFunction(scope, "List_SOLS_CONSTRUCTOR", listStructConstructor, CUSTOM, 1, INT, "startingCapacity");
}

13
libs/collections/list.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef LIST_H
#define LIST_H
#include <groundext.h>
#include <groundvm.h>
#include <stddef.h>
extern const uint8_t STARTING_ELEMENTS;
GroundValue createList(int64_t initialCapacity);
void initLists(GroundScope* scope);
#endif

View File

@@ -1,57 +1,258 @@
#include <groundext.h>
#include <groundvm.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "groundext.h"
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
GroundValue native_file_read(GroundScope* scope, List args) {
if (args.size < 1 || args.values[0].type != STRING) {
return groundCreateValue(NONE);
GroundStruct fileStruct = {};
GroundValue fileStructConstructor(GroundScope* scope, List args) {
GroundValue value = groundCreateValue(CUSTOM, &fileStruct);
char* filePath = args.values[0].data.stringVal;
// do a check to make sure the file is actually a file first
struct stat st;
if (0 != lstat(filePath, &st)) {
// lstat failed, file probably doesn't exist
char buffer[256];
sprintf(buffer, "The file \"%s\" does not exist!", filePath);
ERROR(buffer, "FileNotFound");
}
char* path = args.values[0].data.stringVal;
FILE* f = fopen(path, "r");
if (!f) {
ERROR("Failed to open file for reading", "FileError");
if (S_IFREG != (st.st_mode & S_IFMT)) {
switch (st.st_mode & S_IFMT) {
case S_IFSOCK:
ERROR("Attempt to open socket as file!", "NotAFile");
case S_IFBLK:
ERROR("Attempt to open block device as file!", "NotAFile");
case S_IFDIR:
ERROR("Attempt to open directory as file!", "NotAFile");
case S_IFCHR:
ERROR("Attempt to open character device as file!", "NotAFile");
case S_IFIFO:
ERROR("Attempt to open FIFO (pipe) as file!", "NotAFile");
default:
break;
}
}
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
char* content = malloc(fsize + 1);
if (!content) {
fclose(f);
ERROR("Failed to allocate memory for file reading", "FileError");
// opens the given file in read and write mode, the file must exist or openning the file will fail
FILE* file = fopen(filePath, "r+");
if (file == NULL) {
ERROR("Unkown error while openning file!", "GenericFileError");
}
fread(content, 1, fsize, f);
fclose(f);
content[fsize] = 0;
GroundValue val = groundCreateValue(STRING, content);
free(content);
return val;
// seek to end of file to get size
fseek(file, 0, SEEK_END);
int64_t size = ftell(file);
rewind(file);
GroundObjectField *handleField = groundFindField(*value.data.customVal, "fileHandle");
GroundObjectField *sizeField = groundFindField(*value.data.customVal, "size");
GroundObjectField *filePathField = groundFindField(*value.data.customVal, "filePath");
handleField->value.data.intVal = (int64_t)file;
sizeField->value.data.intVal = size;
filePathField->value.data.stringVal = filePath;
value.type = CUSTOM;
return value;
}
GroundValue native_file_write(GroundScope* scope, List args) {
if (args.size < 2 || args.values[0].type != STRING || args.values[1].type != STRING) {
return groundCreateValue(BOOL, 0);
GroundValue fileStructRead(GroundScope* scope, List args) {
GroundVariable* handleField = groundFindVariable(scope, "fileHandle");
if (handleField == NULL) {
ERROR("A field called \"fileHandle\" was not found", "FieldNotFound");
}
char* path = args.values[0].data.stringVal;
char* content = args.values[1].data.stringVal;
FILE* f = fopen(path, "w");
if (!f) {
ERROR("Failed to open file for writing", "FileError");
FILE* file = (FILE*)handleField->value.data.intVal;
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
fprintf(f, "%s", content);
fclose(f);
return groundCreateValue(BOOL, 1);
int64_t size = sizeField->value.data.intVal;
// allocate buffer to read file into
char* buffer = malloc(size + 1);
if (buffer == NULL) {
ERROR("Failed to allocate memory for buffer while reading file!", "AllocFail");
}
// read file
size_t readSize = fread(buffer, 1, size, file);
buffer[readSize] = 0; // null terminate buffer
return groundCreateValue(STRING, buffer);
}
GroundValue fileStructWrite(GroundScope* scope, List args) {
char* buffer = args.values[0].data.stringVal;
GroundVariable* handleField = groundFindVariable(scope, "fileHandle");
if (handleField == NULL) {
ERROR("A field called \"fileHandle\" was not found", "FieldNotFound");
}
FILE* file = (FILE*)handleField->value.data.intVal;
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
GroundVariable* tellField = groundFindVariable(scope, "tell");
if (tellField == NULL) {
ERROR("A field called \"tell\" was not found", "FieldNotFound");
}
ftruncate(fileno(file), 0);
rewind(file);
fprintf(file, "%s", buffer);
sizeField->value.data.intVal = strlen(buffer);
tellField->value.data.intVal = strlen(buffer);
return groundCreateValue(INT, 0);
}
GroundValue fileStructAppend(GroundScope* scope, List args) {
char* buffer = args.values[0].data.stringVal;
GroundVariable* handleField = groundFindVariable(scope, "fileHandle");
if (handleField == NULL) {
ERROR("A field called \"fileHandle\" was not found", "FieldNotFound");
}
FILE* file = (FILE*)handleField->value.data.intVal;
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
GroundVariable* tellField = groundFindVariable(scope, "tell");
if (tellField == NULL) {
ERROR("A field called \"tell\" was not found", "FieldNotFound");
}
fprintf(file, "%s", buffer);
sizeField->value.data.intVal = size + strlen(buffer);
tellField->value.data.intVal = tellField->value.data.intVal + strlen(buffer);
return groundCreateValue(INT, 0);
}
GroundValue fileStructFlush(GroundScope* scope, List args) {
GroundVariable* handleField = groundFindVariable(scope, "fileHandle");
if (handleField == NULL) {
ERROR("A field called \"fileHandle\" was not found", "FieldNotFound");
}
FILE* file = (FILE*)handleField->value.data.intVal;
fflush(file);
return groundCreateValue(INT, 0);
}
GroundValue fileStructSeek(GroundScope* scope, List args) {
int64_t offset = args.values[0].data.intVal;
if (offset < 0) {
ERROR("Can't seek to negative offset!", "OutOfBounds");
}
GroundVariable* handleField = groundFindVariable(scope, "fileHandle");
if (handleField == NULL) {
ERROR("A field called \"fileHandle\" was not found", "FieldNotFound");
}
FILE* file = (FILE*)handleField->value.data.intVal;
GroundVariable* tellField = groundFindVariable(scope, "tell");
if (tellField == NULL) {
ERROR("A field called \"tell\" was not found", "FieldNotFound");
}
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
if (offset >= size) {
ERROR("Attempt to seek past file size!", "OutOfBounds");
}
int result = fseek(file, offset, SEEK_SET);
if (result != 0) {
char buffer[100];
sprintf(buffer, "fseek failed with error code: \"%d\"", result);
ERROR(buffer, "SeekError");
}
tellField->value.data.intVal = offset;
return groundCreateValue(INT, 0);
}
GroundValue fileStructSeekEnd(GroundScope* scope, List args) {
int64_t offset = args.values[0].data.intVal;
if (offset < 0) {
ERROR("Can't seek to negative offset!", "OutOfBounds");
}
GroundVariable* handleField = groundFindVariable(scope, "fileHandle");
if (handleField == NULL) {
ERROR("A field called \"fileHandle\" was not found", "FieldNotFound");
}
FILE* file = (FILE*)handleField->value.data.intVal;
GroundVariable* tellField = groundFindVariable(scope, "tell");
if (tellField == NULL) {
ERROR("A field called \"tell\" was not found", "FieldNotFound");
}
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
if (offset >= size) {
ERROR("Attempt to seek past file size!", "OutOfBounds");
}
int result = fseek(file, offset, SEEK_END);
if (result != 0) {
char buffer[100];
sprintf(buffer, "fseek failed with error code: \"%d\"", result);
ERROR(buffer, "SeekError");
}
tellField->value.data.intVal = size - offset;
return groundCreateValue(INT, 0);
}
void ground_init(GroundScope* scope) {
groundAddNativeFunction(scope, "file_Read", native_file_read, STRING, 1, STRING, "path");
groundAddNativeFunction(scope, "file_Write", native_file_write, BOOL, 2, STRING, "path", STRING, "content");
}
fileStruct = groundCreateStruct();
groundAddFieldToStruct(&fileStruct, "fileHandle", groundCreateValue(INT, 0));
groundAddFieldToStruct(&fileStruct, "filePath", groundCreateValue(STRING, ""));
groundAddFieldToStruct(&fileStruct, "size", groundCreateValue(INT, 0));
groundAddFieldToStruct(&fileStruct, "tell", groundCreateValue(INT, 0));
groundAddFunctionToStruct(&fileStruct, "read", fileStructRead, STRING, 0); // reads the file and returns the contents as a string
// readLines function implemented in solstice
groundAddFunctionToStruct(&fileStruct, "write", fileStructWrite, INT, 1, STRING, "buffer"); // wipes the file before writing the buffer to the file
groundAddFunctionToStruct(&fileStruct, "append", fileStructAppend, INT, 1, STRING, "buffer"); // appends the buffer to end of the contents in the file
groundAddFunctionToStruct(&fileStruct, "flush", fileStructFlush, INT, 0); // flush file buffer (not THAT useful tbh)
groundAddFunctionToStruct(&fileStruct, "seek", fileStructSeek, INT, 1, INT, "offset"); // jump to offset relative to start of file
groundAddFunctionToStruct(&fileStruct, "seekEnd", fileStructSeekEnd, INT, 1, INT, "offset"); // jump to offset relative to end of file
groundAddNativeFunction(scope, "openFile", fileStructConstructor, CUSTOM, 1, STRING, "filePath");
groundAddNativeFunction(scope, "File_SOLS_CONSTRUCTOR", fileStructConstructor, CUSTOM, 1, STRING, "filePath");
}

455
libs/string/string.c Normal file
View File

@@ -0,0 +1,455 @@
#include <ctype.h>
#include <groundext.h>
#include <groundvm.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
GroundValue stringUpper(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
char* uppercase = malloc(sizeof(char) * (strlen(string) + 1));
if (!uppercase)
ERROR("Failed to allocate memory while getting uppercase of string!", "AllocFail");
for (size_t i = 0; i < strlen(string); i++) {
uppercase[i] = toupper(string[i]);
}
return groundCreateValue(STRING, uppercase);
}
GroundValue stringLower(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
char* lowercase = malloc(sizeof(char) * (strlen(string) + 1));
if (!lowercase)
ERROR("Failed to allocate memory while getting uppercase of string!", "AllocFail");
for (size_t i = 0; i < strlen(string); i++) {
lowercase[i] = tolower(string[i]);
}
return groundCreateValue(STRING, lowercase);
}
GroundValue stringStartsWith(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
char* prefix = args.values[1].data.stringVal;
return groundCreateValue(BOOL, strncmp(prefix, string, strlen(prefix)) == 0);
}
GroundValue stringEndsWith(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
char* suffix = args.values[1].data.stringVal;
size_t strLen = strlen(string);
size_t suffixLen = strlen(suffix);
if (suffixLen > strLen) return groundCreateValue(BOOL, false); // can't start with suffix if suffix is longer
return groundCreateValue(
BOOL,
strcmp(string + strLen - suffixLen, suffix) == 0
);
}
GroundValue stringSubstring(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
int64_t start = args.values[1].data.intVal;
int64_t end = args.values[2].data.intVal;
if (end < start) {
ERROR("End can't be less than start when getting substring!", "EndBeforeStart");
}
size_t inputLen = strlen(string);
if (start >= inputLen) {
ERROR("Start is outside string!", "OutOfBounds");
} else if (end >= inputLen || end < 0) {
ERROR("End is outside string!", "OutOfBounds");
}
char* buffer = malloc(inputLen + 1);
if (!buffer) {
ERROR("Failed to allocate memory while getting substring of string!", "AllocFail");
}
// string_Substring("Hello!", 1, 3) -> "ell"
// amount = 3 - 1 + 1 = 3
strncpy(buffer, string + start, end - start + 1);
buffer[end - start + 1] = 0;
return groundCreateValue(STRING, buffer);
}
GroundValue stringCharAt(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
int64_t index = args.values[1].data.intVal;
if (index < 0)
ERROR("Index can't be less than 0!", "OutOfBounds");
int64_t stringLen = strlen(string);
if (index >= stringLen) {
char buffer[256];
sprintf(buffer, "Attempt to get char at index %ld when string is of length %ld.", index, stringLen);
ERROR(buffer, "OutOfBounds");
}
char buffer[2] = {string[index], 0};
return groundCreateValue(STRING, buffer);
}
GroundValue stringFind(GroundScope* scope, List args) {
char* haystack = args.values[0].data.stringVal;
char* needle = args.values[1].data.stringVal;
char* result = strstr(haystack, needle);
if (!result) {
return groundCreateValue(INT, (int64_t)-1);
}
return groundCreateValue(INT, result - haystack);
}
GroundValue stringFindLast(GroundScope* scope, List args) {
char* haystack = args.values[0].data.stringVal;
char* needle = args.values[1].data.stringVal;
if (!*needle) {
return groundCreateValue(INT, (int64_t)strlen(haystack)-1);
}
const char* p = haystack;
while (*p) p++;
const char* q = needle;
while (*q) q++;
bool found = false;
while (!found && (p - haystack) >= (q - needle)) {
const char* s = p;
const char* t = q;
while (t != needle && s != haystack && *(s - 1) == *(t - 1)) {
s--;
t--;
}
found = (t == needle);
if (found) p = s;
else p--;
}
return groundCreateValue(INT, (int64_t)(found ? p - haystack : -1));
}
GroundValue stringContains(GroundScope* scope, List args) {
char* haystack = args.values[0].data.stringVal;
char* needle = args.values[1].data.stringVal;
char* result = strstr(haystack, needle);
return groundCreateValue(BOOL, result != NULL);
}
GroundValue stringCount(GroundScope* scope, List args) {
char* haystack = args.values[0].data.stringVal;
char* needle = args.values[1].data.stringVal;
int64_t count = 0;
const char* tmp = haystack;
while ((tmp = strstr(tmp, needle))) {
count++;
tmp++;
}
return groundCreateValue(INT, count);
}
GroundValue stringTrim(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
// trip whitespace from start
char* start = string;
while (*start && isspace((unsigned char)*start)) start++;
// trim whitespace from end
char* end = string + strlen(string);
while (end > start && isspace((unsigned char)*(end - 1))) end--;
size_t len = end - start;
char* buffer = malloc(len + 1);
if (!buffer)
ERROR("Failed to allocate memory while trimming string!", "AllocFail");
memcpy(buffer, start, len);
buffer[len] = 0;
return groundCreateValue(STRING, buffer);
}
GroundValue stringTrimLeft(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
// trip whitespace from start
char* start = string;
while (*start && isspace((unsigned char)*start)) start++;
size_t len = strlen(start);
char* buffer = malloc(len + 1);
if (!buffer)
ERROR("Failed to allocate memory while trimming string!", "AllocFail");
memcpy(buffer, start, len);
buffer[len] = 0;
return groundCreateValue(STRING, buffer);
}
GroundValue stringTrimRight(GroundScope* scope, List args) {
char* str = args.values[0].data.stringVal;
char* end = str + strlen(str);
while (end > str && isspace((unsigned char)*(end - 1))) {
end--;
}
size_t len = end - str;
char* buffer = malloc(len + 1);
if (!buffer)
ERROR("Failed to allocate memory while trimming string!", "AllocFail");
memcpy(buffer, str, len);
buffer[len] = '\0';
return groundCreateValue(STRING, buffer);
}
GroundValue stringReplace(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
char* from = args.values[1].data.stringVal;
char* to = args.values[2].data.stringVal;
// === THANK YOU TO https://creativeandcritical.net FOR THEIR repl_str() FUNCTION === \\
/* Adjust each of the below values to suit your needs. */
/* Increment positions cache size initially by this number. */
size_t cache_sz_inc = 16;
/* Thereafter, each time capacity needs to be increased,
* multiply the increment by this factor. */
const size_t cache_sz_inc_factor = 3;
/* But never increment capacity by more than this number. */
const size_t cache_sz_inc_max = 1048576;
char *pret, *ret = NULL;
const char *pstr2, *pstr = string;
size_t i, count = 0;
#if (__STDC_VERSION__ >= 199901L)
uintptr_t *pos_cache_tmp, *pos_cache = NULL;
#else
ptrdiff_t *pos_cache_tmp, *pos_cache = NULL;
#endif
size_t cache_sz = 0;
size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from);
/* Find all matches and cache their positions. */
while ((pstr2 = strstr(pstr, from)) != NULL) {
count++;
/* Increase the cache size when necessary. */
if (cache_sz < count) {
cache_sz += cache_sz_inc;
pos_cache_tmp = realloc(pos_cache, sizeof(*pos_cache) * cache_sz);
if (pos_cache_tmp == NULL) {
goto end_repl_str;
} else pos_cache = pos_cache_tmp;
cache_sz_inc *= cache_sz_inc_factor;
if (cache_sz_inc > cache_sz_inc_max) {
cache_sz_inc = cache_sz_inc_max;
}
}
pos_cache[count-1] = pstr2 - string;
pstr = pstr2 + fromlen;
}
orglen = pstr - string + strlen(pstr);
/* Allocate memory for the post-replacement string. */
if (count > 0) {
tolen = strlen(to);
retlen = orglen + (tolen - fromlen) * count;
} else retlen = orglen;
ret = malloc(retlen + 1);
if (ret == NULL) {
goto end_repl_str;
}
if (count == 0) {
/* If no matches, then just duplicate the string. */
strcpy(ret, string);
} else {
/* Otherwise, duplicate the string whilst performing
* the replacements using the position cache. */
pret = ret;
memcpy(pret, string, pos_cache[0]);
pret += pos_cache[0];
for (i = 0; i < count; i++) {
memcpy(pret, to, tolen);
pret += tolen;
pstr = string + pos_cache[i] + fromlen;
cpylen = (i == count-1 ? orglen : pos_cache[i+1]) - pos_cache[i] - fromlen;
memcpy(pret, pstr, cpylen);
pret += cpylen;
}
ret[retlen] = '\0';
}
end_repl_str:
/* Free the cache and return the post-replacement string,
* which will be NULL in the event of an error. */
free(pos_cache);
return groundCreateValue(STRING, ret);
}
GroundValue stringRepeat(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
int64_t times = args.values[1].data.intVal;
if (times < 0)
ERROR("Can't repeat string negative number of times!", "ValueError");
char* out = malloc(strlen(string) * times + 1);
if (!out)
ERROR("Failed to allocate memory while repeating string!", "AllocFail");
strcpy(out, string);
while (--times > 0) {
strcat(out, string);
}
return groundCreateValue(STRING, out);
}
GroundValue stringReverse(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
size_t stringLen = strlen(string);
char* out = malloc(stringLen + 1);
if (!out)
ERROR("Failed to allocate memory while reversing string!", "AllocFail");
unsigned int i = 0;
while (stringLen > 0) out[i++] = string[stringLen-- - 1];
out[i] = 0;
return groundCreateValue(STRING, out);
}
GroundValue stringIsAlpha(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
const char* p = string;
bool isAlpha = true;
while (*p) {
if (!isalpha(*p)) {
isAlpha = false;
break;
}
p++;
}
return groundCreateValue(BOOL, isAlpha);
}
GroundValue stringIsDigit(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
const char* p = string;
bool isDigit = true;
while (*p) {
if (!isdigit(*p)) {
isDigit = false;
break;
}
p++;
}
return groundCreateValue(BOOL, isDigit);
}
GroundValue stringIsAlnum(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
const char* p = string;
bool isAlnum = true;
while (*p) {
if (!isalnum(*p)) {
isAlnum = false;
break;
}
p++;
}
return groundCreateValue(BOOL, isAlnum);
}
GroundValue stringIsSpace(GroundScope* scope, List args) {
char* string = args.values[0].data.stringVal;
const char* p = string;
bool isSpace = true;
while (*p) {
if (!isspace(*p)) {
isSpace = false;
break;
}
p++;
}
return groundCreateValue(BOOL, isSpace);
}
void ground_init(GroundScope* scope) {
// modify string
groundAddNativeFunction(scope, "string_Upper", stringUpper, STRING, 1, STRING, "str");
groundAddNativeFunction(scope, "string_Lower", stringLower, STRING, 1, STRING, "str");
groundAddNativeFunction(scope, "string_Trim", stringTrim, STRING, 1, STRING, "str");
groundAddNativeFunction(scope, "string_TrimLeft", stringTrimLeft, STRING, 1, STRING, "str");
groundAddNativeFunction(scope, "string_TrimRight", stringTrimRight, STRING, 1, STRING, "str");
groundAddNativeFunction(scope, "string_Substring", stringSubstring, STRING, 3, STRING, "str", INT, "start", INT, "end");
groundAddNativeFunction(scope, "string_Replace", stringReplace, STRING, 3, STRING, "str", STRING, "from", STRING, "to");
groundAddNativeFunction(scope, "string_Repeat", stringRepeat, STRING, 2, STRING, "str", INT, "times");
groundAddNativeFunction(scope, "string_Reverse", stringReverse, STRING, 1, STRING, "str");
// check for substring
groundAddNativeFunction(scope, "string_StartsWith", stringStartsWith, BOOL, 2, STRING, "str", STRING, "prefix");
groundAddNativeFunction(scope, "string_EndsWith", stringEndsWith, BOOL, 2, STRING, "str", STRING, "suffix");
groundAddNativeFunction(scope, "string_Contains", stringContains, BOOL, 2, STRING, "haystack", STRING, "needle");
// character classification
groundAddNativeFunction(scope, "string_IsAlpha", stringIsAlpha, BOOL, 1, STRING, "str");
groundAddNativeFunction(scope, "string_IsDigit", stringIsDigit, BOOL, 1, STRING, "str");
groundAddNativeFunction(scope, "string_IsAlnum", stringIsAlnum, BOOL, 1, STRING, "str");
groundAddNativeFunction(scope, "string_IsSpace", stringIsSpace, BOOL, 1, STRING, "str");
// get char
groundAddNativeFunction(scope, "string_CharAt", stringCharAt, STRING, 2, STRING, "str", INT, "index");
// find index substring
groundAddNativeFunction(scope, "string_Find", stringFind, INT, 2, STRING, "haystack", STRING, "needle");
groundAddNativeFunction(scope, "string_FindLast", stringFindLast, INT, 2, STRING, "haystack", STRING, "needle");
groundAddNativeFunction(scope, "string_Count", stringCount, INT, 2, STRING, "haystack", STRING, "needle");
}

50
libs/tests/tests.c Normal file
View File

@@ -0,0 +1,50 @@
#include "uthash.h"
#include <stdio.h>
#include <groundvm.h>
#include <groundext.h>
#include <ansii.h>
GroundValue testsRun(GroundScope* scope, List args) {
GroundObject* testsStruct = args.values[0].data.customVal;
int totalTests = 0, successful = 0, fail = 0;
GroundObjectField* entry, *temp;
HASH_ITER(hh, testsStruct->fields, entry, temp) {
if (entry) {
totalTests++;
if (entry->value.type != FUNCTION) {
ERROR("The given tests struct can't have any struct fields, only methods!", "WrongType");
}
GroundValue success = groundRunFunction(entry->value.data.fnVal, 0);
if (success.type != BOOL) {
ERROR("Your tests should return a boolean.", "ReturnTypeMismatch");
}
if (success.data.boolVal) {
successful++;
printf(ESC_GREEN_FG ESC_BOLD ".");
} else {
fail++;
printf(ESC_RED_FG ESC_BOLD ".");
}
}
}
printf(ESC_RESET "\n\n/============================\\\n");
printf("- Total : %d\n", totalTests);
printf("- Successful: %d\n", successful);
printf("- Fail : %d\n", fail);
printf("\\============================/\n");
return groundCreateValue(INT, 0);
}
void ground_init(GroundScope* scope) {
groundAddNativeFunction(scope, "tests_Run", testsRun, INT, 1, CUSTOM, "testsStruct");
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,9 @@
#include "types.h"
#include "interpreter.h"
char* compileGroundProgram(GroundProgram* program);
[[noreturn]] void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where, int whereLine);
typedef struct VariableInfo {
char name[256];
int offset;
} VariableInfo;
typedef struct VariableTable {
VariableInfo* vars;
size_t count;
size_t capacity;
} VariableTable;
#ifdef GROUND_COMPILE_WITH_TRAM
#include <tram.h>
#endif
void compileGroundProgram(GroundProgram* program, char* name);
#ifdef GROUND_COMPILE_WITH_TRAM
void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program);
#endif

View File

@@ -5,6 +5,42 @@
#include <stdlib.h>
#include <stdarg.h>
char* getFileContents(const char* filename) {
// https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c
FILE* fp;
long lSize;
char* file;
fp = fopen(filename, "rb");
if (!fp) {
perror(filename);
exit(1);
}
fseek(fp, 0L, SEEK_END);
lSize = ftell(fp);
rewind(fp);
file = calloc(1, lSize + 1);
if (!file) {
fclose(fp);
fprintf(stderr, "memory allocation fail when reading file %s\n", filename);
exit(1);
}
if (1!=fread(file, lSize, 1, fp)) {
fclose(fp);
free(file);
fputs("couldn't read entire file", stderr);
exit(1);
}
// we done
fclose(fp);
return file;
}
GroundProgram groundCreateProgram() {
return (GroundProgram) {
@@ -151,9 +187,35 @@ void groundAddValueToScope(GroundScope* gs, const char* name, GroundValue value)
void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field) {
addFieldToStruct(gstruct, name, field);
}
GroundFunction* createGroundFunction();
void addArgsToGroundFunction(GroundFunction* function, GroundValueType type, char* name);
char* groundCompileProgram(GroundProgram* program) {
return compileGroundProgram(program);
void groundAddFunctionToStruct(GroundStruct* gstruct, 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++) {
GroundValueType type = va_arg(args, GroundValueType);
char* argName = va_arg(args, char*);
addArgsToGroundFunction(gf, type, argName);
}
va_end(args);
GroundValue gv = createFunctionGroundValue(gf);
addFieldToStruct(gstruct, name, gv);
}
void groundCompileProgram(GroundProgram* program) {
#ifdef GROUND_COMPILE_WITH_TRAM
compileGroundProgram(program, "program.gexe");
#else
(void)program;
printf("This version of Ground has been compiled without Tram, so compilation is not supported.\n");
#endif
}
GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) {
@@ -167,6 +229,14 @@ GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) {
// Seems to crash some functions
// GroundScope callScope = copyGroundScope(&function->closure);
if (function->isNative) {
List argsList = createList();
for (size_t i = 0; i < argc; i++) {
appendToList(&argsList, va_arg(args, GroundValue));
}
return function->nativeFn(NULL, argsList);
}
GroundScope callScope = {
.labels = malloc(sizeof(GroundLabel*)),
.variables = malloc(sizeof(GroundVariable*)),
@@ -196,3 +266,9 @@ GroundObjectField* groundFindField(GroundObject head, const char *id) {
HASH_FIND_STR(head.fields, id, s);
return s;
}
GroundVariable* groundFindVariable(GroundScope* gs, const char *id) {
GroundVariable* var;
HASH_FIND_STR(*gs->variables, id, var);
return var;
}

File diff suppressed because one or more lines are too long

View File

@@ -7,41 +7,7 @@
#include <stdio.h>
#include <string.h>
char* getFileContents(const char* filename) {
// https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c
FILE* fp;
long lSize;
char* file;
fp = fopen(filename, "rb");
if (!fp) {
perror(filename);
exit(1);
}
fseek(fp, 0L, SEEK_END);
lSize = ftell(fp);
rewind(fp);
file = calloc(1, lSize + 1);
if (!file) {
fclose(fp);
fputs("memory allocation fail when reading file", stderr);
exit(1);
}
if (1!=fread(file, lSize, 1, fp)) {
fclose(fp);
free(file);
fputs("couldn't read entire file", stderr);
exit(1);
}
// we done
fclose(fp);
return file;
}
char* getFileContents(const char* filename);
int main(int argc, char** argv) {
if (argc == 1) {
@@ -111,17 +77,22 @@ int main(int argc, char** argv) {
}
if (compile) {
char* compiled = compileGroundProgram(&program);
printf("%s\n", compiled);
#ifdef GROUND_COMPILE_WITH_TRAM
compileGroundProgram(&program, "program.gexe");
#else
printf("This version of Ground has been compiled without Tram, so compilation is not supported.\n");
#endif
} else if (writeBytecode) {
serializeProgramToFile(outFileName, &program);
} else {
GroundVariable* variables = NULL;
GroundLabel* labels = NULL;
GroundCatch* catches = NULL;
GroundScope scope;
scope.variables = &variables;
scope.labels = &labels;
scope.catches = &catches;
scope.isMainScope = true;
addVariable(scope.variables, "CMDLINE_ARGS", createListGroundValue(groundArgs));
interpretGroundProgram(&program, &scope);

View File

@@ -128,6 +128,9 @@ static GroundArg parseArgument(const char* token) {
else if (token[0] == '-') {
// Could be type reference or negative number
if (strlen(token) > 1 && !isdigit(token[1])) {
if (strcmp(token, "-any") == 0) {
printf("Note: please avoid using \"any\" type outside of extlibs.\n");
}
// Type reference (e.g., -int, -string)
return createRefGroundArg(TYPEREF, token + 1);
}
@@ -191,6 +194,7 @@ static GroundInstType getInstructionType(const char* inst) {
if (strcmp(inst, "endfun") == 0) return ENDFUN;
if (strcmp(inst, "pusharg") == 0) return PUSHARG;
if (strcmp(inst, "call") == 0) return CALL;
if (strcmp(inst, "callmethod") == 0) return CALLMETHOD;
if (strcmp(inst, "struct") == 0) return STRUCT;
if (strcmp(inst, "endstruct") == 0) return ENDSTRUCT;
if (strcmp(inst, "init") == 0) return INIT;
@@ -200,6 +204,8 @@ static GroundInstType getInstructionType(const char* inst) {
if (strcmp(inst, "extern") == 0) return EXTERN;
if (strcmp(inst, "drop") == 0) return DROP;
if (strcmp(inst, "license") == 0) return LICENSE;
if (strcmp(inst, "throw") == 0) return THROW;
if (strcmp(inst, "catch") == 0) return CATCH;
if (strcmp(inst, "PAUSE") == 0) return PAUSE;
fprintf(stderr, "Error: Unknown instruction: %s\n", inst);

View File

@@ -16,6 +16,8 @@ void wasm_print(const char* str) {
}
#endif
bool groundDisableTypeChecking = false;
GroundValue createIntGroundValue(int64_t in) {
GroundValue gv;
gv.data.intVal = in;
@@ -248,6 +250,10 @@ void printGroundValue(GroundValue* gv) {
printf(" }>");
break;
}
case ANY: {
printf("<any>");
break;
}
case ERROR: {
printf("<error type: %s, what: %s>", gv->data.errorVal.type, gv->data.errorVal.what);
break;
@@ -356,6 +362,10 @@ void printGroundArg(GroundArg* ga) {
break;
}
case TYPEREF: {
if (ga->value.refName == NULL) {
printf("-any");
break;
}
printf("-%s", ga->value.refName);
break;
}
@@ -531,6 +541,9 @@ void printGroundInstruction(GroundInstruction* gi) {
case CALL:
printf("call");
break;
case CALLMETHOD:
printf("callmethod");
break;
case STRUCT:
printf("struct");
break;
@@ -554,8 +567,23 @@ void printGroundInstruction(GroundInstruction* gi) {
case ERRORCMD:
printf("error");
break;
default:
printf("FIXME");
case GETFIELD:
printf("getfield");
break;
case SETFIELD:
printf("setfield");
break;
case PAUSE:
printf("pause");
break;
case LICENSE:
printf("license");
break;
case THROW:
printf("throw");
break;
case CATCH:
printf("catch");
break;
}
if (gi->type != CREATELABEL) printf(" ");
@@ -726,6 +754,8 @@ GroundError createGroundError(char* what, char* type, GroundInstruction* where,
}
bool checkFnTypes(GroundValue* left, GroundFunctionArgs* right) {
if (groundDisableTypeChecking) return true;
if (left->type == ANY || right->type == ANY) return true;
if (left->type != right->type) {
return false;
}
@@ -746,6 +776,7 @@ bool checkFnTypes(GroundValue* left, GroundFunctionArgs* right) {
}
bool checkTypes(GroundValue* left, GroundValue* right) {
if (groundDisableTypeChecking) return true;
if (left->type != right->type) {
return false;
}
@@ -769,10 +800,12 @@ GroundScope copyGroundScope(GroundScope* scope) {
GroundScope newScope = {
.labels = malloc(sizeof(GroundLabel*)),
.variables = malloc(sizeof(GroundVariable*)),
.catches = malloc(sizeof(GroundCatch*)),
.isMainScope = false
};
*newScope.variables = NULL;
*newScope.labels = NULL;
*newScope.catches = NULL;
if (scope == NULL) {
printf("oh no scope is null\n");

View File

@@ -29,11 +29,14 @@ void wasm_print(const char* str);
#endif
typedef enum GroundInstType {
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, AND, OR, XOR, NEG, SHIFT, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, LICENSE, ERRORCMD
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND,
GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, AND, OR, XOR, NEG,
SHIFT, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, CALLMETHOD, STRUCT, ENDSTRUCT, INIT,
GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, LICENSE, ERRORCMD, THROW, CATCH
} GroundInstType;
typedef enum GroundValueType {
INT, DOUBLE, STRING, CHAR, BOOL, LIST, FUNCTION, STRUCTVAL, CUSTOM, ERROR, NONE
INT, DOUBLE, STRING, CHAR, BOOL, LIST, FUNCTION, STRUCTVAL, CUSTOM, ERROR, NONE, ANY
} GroundValueType;
typedef enum GroundArgType {
@@ -107,11 +110,19 @@ typedef struct GroundVariable {
char id[MAX_ID_LEN];
GroundValue value;
UT_hash_handle hh;
bool freed;
} 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;

13
tests/catch.grnd Normal file
View File

@@ -0,0 +1,13 @@
fun !throws -int
throw "MyError" "longer error message"
endfun
catch "MyError" %catcher
call !throws &res
end 0
@catcher
println "ruh roh"
end 1