58 Commits

Author SHA1 Message Date
932846362d Add segfault handling to Ground-dependent programs 2026-05-05 08:34:25 +10:00
39b6a49a9c Add segfault handler 2026-05-05 08:31:14 +10:00
159d86b76d Calling functions nesed inside objects 2026-05-05 08:25:33 +10:00
a2b0924018 Modify fields inside fields 2026-05-04 17:58:52 +10:00
9edb6b51ec Remove flag that I accidently set 2026-05-04 13:19:48 +10:00
5f7f2ea152 Start work on moving to Tram 2026-05-04 13:10:37 +10:00
6c293f7c3f Merge pull request 'Fix Subtraction Bug' (#31) from DiamondNether90/cground:master into master
Reviewed-on: ground/ground#31
2026-04-27 11:28:14 +10:00
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 2750 additions and 769 deletions

View File

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

View File

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

View File

@@ -16,11 +16,14 @@
typedef enum GroundInstType { 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; } GroundInstType;
typedef enum GroundValueType { 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; } GroundValueType;
typedef enum GroundArgType { typedef enum GroundArgType {
@@ -209,6 +212,7 @@ GroundStruct groundCreateStruct();
void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field); void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field);
GroundObjectField* groundFindField(GroundObject head, const char *id); GroundObjectField* groundFindField(GroundObject head, const char *id);
GroundVariable* groundFindVariable(GroundScope* gs, const char *id);
// Run function returned by Ground code // Run function returned by Ground code
// Use argc to set count of args, accepts that amount of GroundValue's after // 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 <stdio.h>
#include <stdlib.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) { GroundStruct fileStruct = {};
if (args.size < 1 || args.values[0].type != STRING) {
return groundCreateValue(NONE);
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; if (S_IFREG != (st.st_mode & S_IFMT)) {
FILE* f = fopen(path, "r"); switch (st.st_mode & S_IFMT) {
if (!f) { case S_IFSOCK:
ERROR("Failed to open file for reading", "FileError"); 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); // opens the given file in read and write mode, the file must exist or openning the file will fail
long fsize = ftell(f); FILE* file = fopen(filePath, "r+");
fseek(f, 0, SEEK_SET);
if (file == NULL) {
char* content = malloc(fsize + 1); ERROR("Unkown error while openning file!", "GenericFileError");
if (!content) {
fclose(f);
ERROR("Failed to allocate memory for file reading", "FileError");
} }
fread(content, 1, fsize, f); // seek to end of file to get size
fclose(f); fseek(file, 0, SEEK_END);
content[fsize] = 0; int64_t size = ftell(file);
rewind(file);
GroundValue val = groundCreateValue(STRING, content);
free(content); GroundObjectField *handleField = groundFindField(*value.data.customVal, "fileHandle");
return val; 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) { GroundValue fileStructRead(GroundScope* scope, List args) {
if (args.size < 2 || args.values[0].type != STRING || args.values[1].type != STRING) { GroundVariable* handleField = groundFindVariable(scope, "fileHandle");
return groundCreateValue(BOOL, 0); if (handleField == NULL) {
ERROR("A field called \"fileHandle\" was not found", "FieldNotFound");
} }
FILE* file = (FILE*)handleField->value.data.intVal;
char* path = args.values[0].data.stringVal;
char* content = args.values[1].data.stringVal; GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
FILE* f = fopen(path, "w"); ERROR("A field called \"size\" was not found", "FieldNotFound");
if (!f) {
ERROR("Failed to open file for writing", "FileError");
} }
int64_t size = sizeField->value.data.intVal;
fprintf(f, "%s", content);
fclose(f); // allocate buffer to read file into
char* buffer = malloc(size + 1);
return groundCreateValue(BOOL, 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) { void ground_init(GroundScope* scope) {
groundAddNativeFunction(scope, "file_Read", native_file_read, STRING, 1, STRING, "path"); fileStruct = groundCreateStruct();
groundAddNativeFunction(scope, "file_Write", native_file_write, BOOL, 2, STRING, "path", STRING, "content");
} 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,20 @@
#include "types.h" #include "types.h"
#include "interpreter.h" #ifdef GROUND_COMPILE_WITH_TRAM
#include <tram.h>
#endif
char* compileGroundProgram(GroundProgram* program); typedef struct GroundCompilerVariable {
[[noreturn]] void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where, int whereLine); GroundValueType type;
char id[MAX_ID_LEN];
typedef struct VariableInfo { UT_hash_handle hh;
char name[256]; } GroundCompilerVariable;
int offset;
} VariableInfo;
typedef struct VariableTable {
VariableInfo* vars;
size_t count;
size_t capacity;
} VariableTable;
typedef struct GroundState {
GroundCompilerVariable* variables;
GroundLabel* labels;
} GroundState;
void compileGroundProgram(GroundProgram* program, char* name);
#ifdef GROUND_COMPILE_WITH_TRAM
void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program, GroundState* state);
#endif

View File

@@ -5,6 +5,43 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <signal.h>
char* getFileContents(const char* filename) {
// https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c
FILE* fp;
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() { GroundProgram groundCreateProgram() {
return (GroundProgram) { return (GroundProgram) {
@@ -89,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;
@@ -115,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 = {
@@ -151,9 +191,35 @@ void groundAddValueToScope(GroundScope* gs, const char* name, GroundValue value)
void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field) { void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field) {
addFieldToStruct(gstruct, name, field); addFieldToStruct(gstruct, name, field);
} }
GroundFunction* createGroundFunction();
void addArgsToGroundFunction(GroundFunction* function, GroundValueType type, char* name);
char* groundCompileProgram(GroundProgram* program) { void groundAddFunctionToStruct(GroundStruct* gstruct, char* name, NativeGroundFunction fn, GroundValueType returnType, int argCount, ...) {
return compileGroundProgram(program); 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, ...) { GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) {
@@ -167,6 +233,14 @@ GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) {
// Seems to crash some functions // Seems to crash some functions
// GroundScope callScope = copyGroundScope(&function->closure); // 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 = { GroundScope callScope = {
.labels = malloc(sizeof(GroundLabel*)), .labels = malloc(sizeof(GroundLabel*)),
.variables = malloc(sizeof(GroundVariable*)), .variables = malloc(sizeof(GroundVariable*)),
@@ -196,3 +270,9 @@ GroundObjectField* groundFindField(GroundObject head, const char *id) {
HASH_FIND_STR(head.fields, id, s); HASH_FIND_STR(head.fields, id, s);
return 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

@@ -4,46 +4,18 @@
#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>
char* getFileContents(const char* filename) { void segfaultHandle(int signal);
// 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"); char* getFileContents(const char* filename);
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;
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
signal(SIGSEGV, segfaultHandle);
if (argc == 1) { if (argc == 1) {
exit(repl()); exit(repl());
} }
@@ -111,17 +83,22 @@ int main(int argc, char** argv) {
} }
if (compile) { if (compile) {
char* compiled = compileGroundProgram(&program); #ifdef GROUND_COMPILE_WITH_TRAM
printf("%s\n", compiled); 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) { } else if (writeBytecode) {
serializeProgramToFile(outFileName, &program); serializeProgramToFile(outFileName, &program);
} else { } else {
GroundVariable* variables = NULL; GroundVariable* variables = NULL;
GroundLabel* labels = NULL; GroundLabel* labels = NULL;
GroundCatch* catches = NULL;
GroundScope scope; GroundScope scope;
scope.variables = &variables; scope.variables = &variables;
scope.labels = &labels; scope.labels = &labels;
scope.catches = &catches;
scope.isMainScope = true; scope.isMainScope = true;
addVariable(scope.variables, "CMDLINE_ARGS", createListGroundValue(groundArgs)); addVariable(scope.variables, "CMDLINE_ARGS", createListGroundValue(groundArgs));
interpretGroundProgram(&program, &scope); interpretGroundProgram(&program, &scope);

View File

@@ -128,6 +128,9 @@ static GroundArg parseArgument(const char* token) {
else if (token[0] == '-') { else if (token[0] == '-') {
// Could be type reference or negative number // Could be type reference or negative number
if (strlen(token) > 1 && !isdigit(token[1])) { 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) // Type reference (e.g., -int, -string)
return createRefGroundArg(TYPEREF, token + 1); 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, "endfun") == 0) return ENDFUN;
if (strcmp(inst, "pusharg") == 0) return PUSHARG; if (strcmp(inst, "pusharg") == 0) return PUSHARG;
if (strcmp(inst, "call") == 0) return CALL; if (strcmp(inst, "call") == 0) return CALL;
if (strcmp(inst, "callmethod") == 0) return CALLMETHOD;
if (strcmp(inst, "struct") == 0) return STRUCT; if (strcmp(inst, "struct") == 0) return STRUCT;
if (strcmp(inst, "endstruct") == 0) return ENDSTRUCT; if (strcmp(inst, "endstruct") == 0) return ENDSTRUCT;
if (strcmp(inst, "init") == 0) return INIT; 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, "extern") == 0) return EXTERN;
if (strcmp(inst, "drop") == 0) return DROP; if (strcmp(inst, "drop") == 0) return DROP;
if (strcmp(inst, "license") == 0) return LICENSE; 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; if (strcmp(inst, "PAUSE") == 0) return PAUSE;
fprintf(stderr, "Error: Unknown instruction: %s\n", inst); fprintf(stderr, "Error: Unknown instruction: %s\n", inst);

View File

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

View File

@@ -29,11 +29,14 @@ void wasm_print(const char* str);
#endif #endif
typedef enum GroundInstType { 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; } GroundInstType;
typedef enum GroundValueType { 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; } GroundValueType;
typedef enum GroundArgType { typedef enum GroundArgType {
@@ -107,11 +110,19 @@ typedef struct GroundVariable {
char id[MAX_ID_LEN]; char id[MAX_ID_LEN];
GroundValue value; GroundValue value;
UT_hash_handle hh; UT_hash_handle hh;
bool freed;
} 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;

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