1 Commits

Author SHA1 Message Date
b6e2a594e9 Auto-append .grnd file extension in use command 2026-03-16 15:29:59 +11:00
40 changed files with 1118 additions and 3501 deletions

1
.gitignore vendored
View File

@@ -3,4 +3,3 @@ build
ground
groundc
.idea
Makefile

View File

@@ -1,7 +1,6 @@
CC = gcc
AR = ar
CFLAGS += -Wall -Wextra -Isrc/include -Iinclude -ggdb
LDFLAGS += -ldl -rdynamic
CFLAGS = -Wall -Wextra -Isrc/include -Iinclude
LDFLAGS = -ldl -rdynamic
# Install paths
PREFIX ?= /usr/local
@@ -18,7 +17,6 @@ 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
@@ -29,13 +27,9 @@ 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 library
# Default target: build standalone executable
.PHONY: all
all: both
DEFINE_TRAM:
$(eval CFLAGS += -DGROUND_COMPILE_WITH_TRAM)
$(eval LDFLAGS += -ltram)
all: executable
# Build standalone executable
.PHONY: executable
@@ -43,16 +37,12 @@ executable: $(EXECUTABLE)
# Build shared library
.PHONY: library
library: $(SHARED_LIB) $(STATIC_LIB) $(HEADERS)
library: $(SHARED_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)
@@ -61,10 +51,6 @@ $(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 $< $@
@@ -94,7 +80,6 @@ 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,14 +348,6 @@ 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,7 +38,6 @@ 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,14 +16,11 @@
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, CALLMETHOD, STRUCT, ENDSTRUCT, INIT,
GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, LICENSE, ERRORCMD, THROW, CATCH
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
} GroundInstType;
typedef enum GroundValueType {
INT, DOUBLE, STRING, CHAR, BOOL, LIST, FUNCTION, STRUCTVAL, CUSTOM, ERROR, NONE, ANY
INT, DOUBLE, STRING, CHAR, BOOL, LIST, FUNCTION, STRUCTVAL, CUSTOM, ERROR, NONE
} GroundValueType;
typedef enum GroundArgType {
@@ -212,7 +209,6 @@ 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

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

View File

@@ -1,181 +0,0 @@
#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

@@ -1,16 +0,0 @@
#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

View File

@@ -1,546 +0,0 @@
#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");
}

View File

@@ -1,13 +0,0 @@
#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,422 +0,0 @@
#include "date_functions.h"
#include <stdio.h>
GroundValue tmToGroundValue(struct tm t) {
GroundStruct timeStruct = groundCreateStruct();
groundAddFieldToStruct(&timeStruct, "year", groundCreateValue(INT, t.tm_year + 1900));
groundAddFieldToStruct(&timeStruct, "month", groundCreateValue(INT, t.tm_mon + 1));
groundAddFieldToStruct(&timeStruct, "day", groundCreateValue(INT, t.tm_mday));
groundAddFieldToStruct(&timeStruct, "hour", groundCreateValue(INT, t.tm_hour));
groundAddFieldToStruct(&timeStruct, "minute", groundCreateValue(INT, t.tm_min));
groundAddFieldToStruct(&timeStruct, "second", groundCreateValue(INT, t.tm_sec));
groundAddFieldToStruct(&timeStruct, "weekDay", groundCreateValue(INT, t.tm_wday));
groundAddFieldToStruct(&timeStruct, "yearDay", groundCreateValue(INT, t.tm_yday + 1));
groundAddFieldToStruct(&timeStruct, "isDaylightSavingsTime", groundCreateValue(BOOL, t.tm_isdst));
GroundValue value = groundCreateValue(CUSTOM, &timeStruct);
value.type = CUSTOM;
return value;
}
GroundValue datetime_Now(GroundScope* scope, List args) {
time_t now = time(NULL);
struct tm t = {0};
localtime_r(&now, &t);
// return a -datetime struct
return tmToGroundValue(t);
}
GroundValue datetime_FromEpochLocal(GroundScope* scope, List args) {
struct tm t = {0};
time_t ts = args.values[0].data.doubleVal;
localtime_r(&ts, &t);
return tmToGroundValue(t);
}
GroundValue datetime_FromEpochUTC(GroundScope* scope, List args) {
struct tm t = {0};
time_t ts = args.values[0].data.doubleVal;
gmtime_r(&ts, &t);
return tmToGroundValue(t);
}
GroundValue datetime_ToEpochLocal(GroundScope* scope, List args) {
GroundObject obj = *args.values[0].data.customVal;
// check args
GroundObjectField* year = groundFindField(obj, "year");
if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError");
GroundObjectField* month = groundFindField(obj, "month");
if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError");
GroundObjectField* day = groundFindField(obj, "day");
if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError");
GroundObjectField* hour = groundFindField(obj, "hour");
if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError");
GroundObjectField* minute = groundFindField(obj, "minute");
if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError");
GroundObjectField* second = groundFindField(obj, "second");
if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError");
GroundObjectField* weekDay = groundFindField(obj, "weekDay");
if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError");
GroundObjectField* yearDay = groundFindField(obj, "yearDay");
if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError");
GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime");
if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError");
// construct tm struct from our ground struct
struct tm t = {
.tm_sec = second->value.data.intVal,
.tm_min = minute->value.data.intVal,
.tm_hour = hour->value.data.intVal,
.tm_mday = day->value.data.intVal,
.tm_mon = month->value.data.intVal - 1,
.tm_year = year->value.data.intVal - 1900,
.tm_wday = 0,
.tm_yday = 0,
.tm_isdst = -1
};
time_t ts = mktime(&t);
return groundCreateValue(INT, (long long)ts);
}
GroundValue datetime_ToEpochUTC(GroundScope* scope, List args) {
GroundObject obj = *args.values[0].data.customVal;
// check args
GroundObjectField* year = groundFindField(obj, "year");
if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError");
GroundObjectField* month = groundFindField(obj, "month");
if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError");
GroundObjectField* day = groundFindField(obj, "day");
if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError");
GroundObjectField* hour = groundFindField(obj, "hour");
if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError");
GroundObjectField* minute = groundFindField(obj, "minute");
if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError");
GroundObjectField* second = groundFindField(obj, "second");
if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError");
GroundObjectField* weekDay = groundFindField(obj, "weekDay");
if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError");
GroundObjectField* yearDay = groundFindField(obj, "yearDay");
if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError");
GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime");
if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError");
// construct tm struct from our ground struct
struct tm t = {
.tm_sec = second->value.data.intVal,
.tm_min = minute->value.data.intVal,
.tm_hour = hour->value.data.intVal,
.tm_mday = day->value.data.intVal,
.tm_mon = month->value.data.intVal - 1,
.tm_year = year->value.data.intVal - 1900,
.tm_wday = 0,
.tm_yday = 0,
.tm_isdst = -1
};
time_t ts = timegm(&t);
return groundCreateValue(INT, (long long)ts);
}
GroundValue formatDatetimeObj(GroundObject obj, char* formatString) {
// check args
GroundObjectField* year = groundFindField(obj, "year");
if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError");
GroundObjectField* month = groundFindField(obj, "month");
if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError");
GroundObjectField* day = groundFindField(obj, "day");
if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError");
GroundObjectField* hour = groundFindField(obj, "hour");
if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError");
GroundObjectField* minute = groundFindField(obj, "minute");
if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError");
GroundObjectField* second = groundFindField(obj, "second");
if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError");
GroundObjectField* weekDay = groundFindField(obj, "weekDay");
if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError");
GroundObjectField* yearDay = groundFindField(obj, "yearDay");
if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError");
GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime");
if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError");
// construct tm struct from our ground struct
struct tm t = {
.tm_sec = second->value.data.intVal,
.tm_min = minute->value.data.intVal,
.tm_hour = hour->value.data.intVal,
.tm_mday = day->value.data.intVal,
.tm_mon = month->value.data.intVal - 1,
.tm_year = year->value.data.intVal - 1900,
.tm_wday = 0,
.tm_yday = 0,
.tm_isdst = -1
};
mktime(&t); // normalise time
// create a buffer for the string
char* buffer = (char*)malloc(128);
strftime(buffer, 128, formatString, &t);
// return ground version of string
return groundCreateValue(STRING, buffer);
}
GroundValue datetime_Format(GroundScope* scope, List args) {
return formatDatetimeObj(
*args.values[0].data.customVal,
args.values[1].data.stringVal
);
}
GroundValue datetime_FromFormatted(GroundScope* scope, List args) {
char* timeString = args.values[0].data.stringVal;
char* formatString = args.values[1].data.stringVal;
struct tm t = {0};
t.tm_isdst = -1;
char* result = strptime(timeString, formatString, &t);
if (result == NULL) {
ERROR("Time string does not match format!", "ValueError");
}
mktime(&t);
return tmToGroundValue(t);
}
GroundValue datetime_ToISO8601UTC(GroundScope* scope, List args) {
GroundObject obj = *args.values[0].data.customVal;
// check args
GroundObjectField* year = groundFindField(obj, "year");
if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError");
GroundObjectField* month = groundFindField(obj, "month");
if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError");
GroundObjectField* day = groundFindField(obj, "day");
if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError");
GroundObjectField* hour = groundFindField(obj, "hour");
if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError");
GroundObjectField* minute = groundFindField(obj, "minute");
if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError");
GroundObjectField* second = groundFindField(obj, "second");
if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError");
GroundObjectField* weekDay = groundFindField(obj, "weekDay");
if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError");
GroundObjectField* yearDay = groundFindField(obj, "yearDay");
if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError");
GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime");
if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError");
// construct tm struct from our ground struct
struct tm t = {
.tm_sec = second->value.data.intVal,
.tm_min = minute->value.data.intVal,
.tm_hour = hour->value.data.intVal,
.tm_mday = day->value.data.intVal,
.tm_mon = month->value.data.intVal - 1,
.tm_year = year->value.data.intVal - 1900,
.tm_wday = 0,
.tm_yday = 0,
.tm_isdst = -1
};
time_t ts = mktime(&t);
gmtime_r(&ts, &t);
char* buffer = (char*)malloc(128);
strftime(buffer, 128, "%Y-%m-%dT%H:%M:%SZ", &t);
return groundCreateValue(STRING, buffer);
}
GroundValue datetime_ToISO8601Local(GroundScope* scope, List args) {
GroundObject obj = *args.values[0].data.customVal;
// check args
GroundObjectField* year = groundFindField(obj, "year");
if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError");
GroundObjectField* month = groundFindField(obj, "month");
if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError");
GroundObjectField* day = groundFindField(obj, "day");
if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError");
GroundObjectField* hour = groundFindField(obj, "hour");
if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError");
GroundObjectField* minute = groundFindField(obj, "minute");
if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError");
GroundObjectField* second = groundFindField(obj, "second");
if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError");
GroundObjectField* weekDay = groundFindField(obj, "weekDay");
if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError");
GroundObjectField* yearDay = groundFindField(obj, "yearDay");
if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError");
GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime");
if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError");
// construct tm struct from our ground struct
struct tm t = {
.tm_sec = second->value.data.intVal,
.tm_min = minute->value.data.intVal,
.tm_hour = hour->value.data.intVal,
.tm_mday = day->value.data.intVal,
.tm_mon = month->value.data.intVal - 1,
.tm_year = year->value.data.intVal - 1900,
.tm_wday = 0,
.tm_yday = 0,
.tm_isdst = -1
};
time_t ts = mktime(&t);
localtime_r(&ts, &t);
char* buffer = (char*)malloc(128);
strftime(buffer, 128, "%Y-%m-%dT%H:%M:%S%z", &t);
return groundCreateValue(STRING, buffer);
}
GroundValue datetime_Diff(GroundScope* scope, List args) {
GroundObject obj1 = *args.values[0].data.customVal;
GroundObject obj2 = *args.values[1].data.customVal;
// check first timedate object
GroundObjectField* year = groundFindField(obj1, "year");
if (year == NULL || year->value.type != INT) ERROR("Object 1 does not have year field as int", "ValueError");
GroundObjectField* month = groundFindField(obj1, "month");
if (month == NULL || month->value.type != INT) ERROR("Object 1 does not have month field as int", "ValueError");
GroundObjectField* day = groundFindField(obj1, "day");
if (day == NULL || day->value.type != INT) ERROR("Object 1 does not have day field as int", "ValueError");
GroundObjectField* hour = groundFindField(obj1, "hour");
if (hour == NULL || hour->value.type != INT) ERROR("Object 1 does not have hour field as int", "ValueError");
GroundObjectField* minute = groundFindField(obj1, "minute");
if (minute == NULL || minute->value.type != INT) ERROR("Object 1 does not have minute field as int", "ValueError");
GroundObjectField* second = groundFindField(obj1, "second");
if (second == NULL || second->value.type != INT) ERROR("Object 1 does not have second field as int", "ValueError");
GroundObjectField* weekDay = groundFindField(obj1, "weekDay");
if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object 1 does not have weekDay field as int", "ValueError");
GroundObjectField* yearDay = groundFindField(obj1, "yearDay");
if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object 1 does not have yearDay field as int", "ValueError");
GroundObjectField* isDaylightSavingsTime = groundFindField(obj1, "isDaylightSavingsTime");
if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object 1 does not have isDaylightSavingsTime field as bool", "ValueError");
// construct tm struct from our ground struct
struct tm t1 = {
.tm_sec = second->value.data.intVal,
.tm_min = minute->value.data.intVal,
.tm_hour = hour->value.data.intVal,
.tm_mday = day->value.data.intVal,
.tm_mon = month->value.data.intVal - 1,
.tm_year = year->value.data.intVal - 1900,
.tm_wday = 0,
.tm_yday = 0,
.tm_isdst = -1
};
// check second timedate object
year = groundFindField(obj2, "year");
if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError");
month = groundFindField(obj2, "month");
if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError");
day = groundFindField(obj2, "day");
if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError");
hour = groundFindField(obj2, "hour");
if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError");
minute = groundFindField(obj2, "minute");
if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError");
second = groundFindField(obj2, "second");
if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError");
weekDay = groundFindField(obj2, "weekDay");
if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError");
yearDay = groundFindField(obj2, "yearDay");
if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError");
isDaylightSavingsTime = groundFindField(obj2, "isDaylightSavingsTime");
if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError");
// construct tm struct from our ground struct
struct tm t2 = {
.tm_sec = second->value.data.intVal,
.tm_min = minute->value.data.intVal,
.tm_hour = hour->value.data.intVal,
.tm_mday = day->value.data.intVal,
.tm_mon = month->value.data.intVal - 1,
.tm_year = year->value.data.intVal - 1900,
.tm_wday = 0,
.tm_yday = 0,
.tm_isdst = -1
};
time_t ts1 = mktime(&t1);
time_t ts2 = mktime(&t2);
return groundCreateValue(INT, ts2 - ts1);
}
GroundValue datetime_Add(GroundScope* scope, List args) {
GroundObject obj = *args.values[0].data.customVal;
long long secs = args.values[1].data.intVal;
long long mins = args.values[2].data.intVal;
long long hours = args.values[3].data.intVal;
long long days = args.values[4].data.intVal;
// check args
GroundObjectField* year = groundFindField(obj, "year");
if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError");
GroundObjectField* month = groundFindField(obj, "month");
if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError");
GroundObjectField* day = groundFindField(obj, "day");
if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError");
GroundObjectField* hour = groundFindField(obj, "hour");
if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError");
GroundObjectField* minute = groundFindField(obj, "minute");
if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError");
GroundObjectField* second = groundFindField(obj, "second");
if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError");
GroundObjectField* weekDay = groundFindField(obj, "weekDay");
if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError");
GroundObjectField* yearDay = groundFindField(obj, "yearDay");
if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError");
GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime");
if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError");
// construct tm struct from our ground struct
struct tm t = {
.tm_sec = second->value.data.intVal,
.tm_min = minute->value.data.intVal,
.tm_hour = hour->value.data.intVal,
.tm_mday = day->value.data.intVal,
.tm_mon = month->value.data.intVal - 1,
.tm_year = year->value.data.intVal - 1900,
.tm_wday = 0,
.tm_yday = 0,
.tm_isdst = -1
};
time_t base = mktime(&t);
long long totalSeconds =
secs +
mins * 60 +
hours * 3600 +
days * 86400;
base += totalSeconds;
struct tm newT;
localtime_r(&base, &newT);
return tmToGroundValue(newT);
}

View File

@@ -1,18 +0,0 @@
#pragma once
#define _XOPEN_SOURCE // to make gcc happy lol
#include <groundext.h>
#include <groundvm.h>
#include <time.h>
GroundValue datetime_Now(GroundScope* scope, List args);
GroundValue datetime_Format(GroundScope* scope, List args);
GroundValue datetime_FromFormatted(GroundScope* scope, List args);
GroundValue datetime_ToISO8601UTC(GroundScope* scope, List args);
GroundValue datetime_ToISO8601Local(GroundScope* scope, List args);
GroundValue datetime_FromEpochLocal(GroundScope* scope, List args);
GroundValue datetime_FromEpochUTC(GroundScope* scope, List args);
GroundValue datetime_ToEpochLocal(GroundScope* scope, List args);
GroundValue datetime_ToEpochUTC(GroundScope* scope, List args);
GroundValue datetime_Diff(GroundScope* scope, List args);
GroundValue datetime_Add(GroundScope* scope, List args);

View File

@@ -1,18 +0,0 @@
#include "time_functions.h"
#include "date_functions.h"
void ground_init(GroundScope* scope) {
groundAddNativeFunction(scope, "datetime_NowEpoch", datetime_NowEpoch, DOUBLE, 0);
groundAddNativeFunction(scope, "datetime_Now", datetime_Now, CUSTOM, 0);
groundAddNativeFunction(scope, "datetime_Format", datetime_Format, STRING, 2, CUSTOM, "datetime", STRING, "format");
groundAddNativeFunction(scope, "datetime_FromFormatted", datetime_FromFormatted, CUSTOM, 2, STRING, "datetimeString", STRING, "format");
groundAddNativeFunction(scope, "datetime_ToISO8601UTC", datetime_ToISO8601UTC, STRING, 1, CUSTOM, "datetime");
groundAddNativeFunction(scope, "datetime_ToISO8601Local", datetime_ToISO8601Local, STRING, 1, CUSTOM, "datetime");
groundAddNativeFunction(scope, "datetime_FromEpochUTC", datetime_FromEpochUTC, CUSTOM, 1, DOUBLE, "epoch");
groundAddNativeFunction(scope, "datetime_FromEpochLocal", datetime_FromEpochLocal, CUSTOM, 1, DOUBLE, "epoch");
groundAddNativeFunction(scope, "datetime_ToEpochUTC", datetime_ToEpochUTC, INT, 1, CUSTOM, "datetime");
groundAddNativeFunction(scope, "datetime_ToEpochLocal", datetime_ToEpochLocal, INT, 1, CUSTOM, "datetime");
groundAddNativeFunction(scope, "datetime_Diff", datetime_Diff, INT, 2, CUSTOM, "datetime1", CUSTOM, "datetime2");
groundAddNativeFunction(scope, "datetime_Add", datetime_Add, CUSTOM, 5, CUSTOM, "datetime", INT, "seconds", INT, "minutes", INT, "hours", INT, "days");
}

View File

@@ -1,12 +0,0 @@
#include "time_functions.h"
GroundValue datetime_NowEpoch(GroundScope* scope, List args) {
// grab time from system clock
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
// convert it to secs and return it
double secsSinceEpoch = (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
return groundCreateValue(DOUBLE, secsSinceEpoch);
}

View File

@@ -1,5 +0,0 @@
#pragma once
#include <groundext.h>
#include <time.h>
GroundValue datetime_NowEpoch(GroundScope* scope, List args);

View File

@@ -1,258 +1,57 @@
#include <groundext.h>
#include <groundvm.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "groundext.h"
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");
GroundValue native_file_read(GroundScope* scope, List args) {
if (args.size < 1 || args.values[0].type != STRING) {
return groundCreateValue(NONE);
}
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;
}
char* path = args.values[0].data.stringVal;
FILE* f = fopen(path, "r");
if (!f) {
ERROR("Failed to open file for 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+");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
if (file == NULL) {
ERROR("Unkown error while openning file!", "GenericFileError");
char* content = malloc(fsize + 1);
if (!content) {
fclose(f);
ERROR("Failed to allocate memory for file reading", "FileError");
}
// seek to end of file to get size
fseek(file, 0, SEEK_END);
int64_t size = ftell(file);
rewind(file);
fread(content, 1, fsize, f);
fclose(f);
content[fsize] = 0;
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 val = groundCreateValue(STRING, content);
free(content);
return val;
}
GroundValue fileStructRead(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;
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
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");
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);
}
// read file
size_t readSize = fread(buffer, 1, size, file);
buffer[readSize] = 0; // null terminate buffer
char* path = args.values[0].data.stringVal;
char* content = args.values[1].data.stringVal;
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");
FILE* f = fopen(path, "w");
if (!f) {
ERROR("Failed to open file for writing", "FileError");
}
GroundVariable* tellField = groundFindVariable(scope, "tell");
if (tellField == NULL) {
ERROR("A field called \"tell\" was not found", "FieldNotFound");
}
fprintf(f, "%s", content);
fclose(f);
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);
return groundCreateValue(BOOL, 1);
}
void ground_init(GroundScope* scope) {
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");
groundAddNativeFunction(scope, "file_Read", native_file_read, STRING, 1, STRING, "path");
groundAddNativeFunction(scope, "file_Write", native_file_write, BOOL, 2, STRING, "path", STRING, "content");
}

View File

@@ -1,23 +1,35 @@
#include <groundext.h>
#include <groundvm.h>
#include <curl/curl.h>
#include <groundvm.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
CURL* curlHandle = NULL;
GroundStruct template;
const char* VALID_REQUEST_TYPES[] = {
"GET",
"POST"
};
bool curl_init = false;
typedef struct {
char* data;
size_t size;
} ResponseBuffer;
typedef struct {
CURL* handle;
bool connected;
char* url;
} WebSocketConnection;
// Global storage for WebSocket connections (simple implementation)
#define MAX_WS_CONNECTIONS 10
WebSocketConnection ws_connections[MAX_WS_CONNECTIONS] = {0};
void curl_setup() {
if (!curl_init) {
curl_global_init(CURL_GLOBAL_ALL);
curl_init = true;
}
}
// Proper write callback that accumulates data
size_t write_callback(void* ptr, size_t size, size_t nmemb, void* userdata) {
size_t total_size = size * nmemb;
ResponseBuffer* buffer = (ResponseBuffer*)userdata;
@@ -36,117 +48,295 @@ size_t write_callback(void* ptr, size_t size, size_t nmemb, void* userdata) {
return total_size;
}
GroundValue ground_request(GroundScope* scope, List args) {
// GET request
GroundValue get_request(GroundScope* scope, List args) {
curl_setup();
CURL* handle = curl_easy_init();
GroundObject obj = *args.values[0].data.customVal;
// check arg types
GroundObjectField* address = groundFindField(obj, "address");
if (address == NULL || address->value.type != STRING) {
ERROR("Object does not have address field as string", "ValueError");
if (!handle) {
ERROR("CURL failed to init", "GenericRequestError");
}
GroundObjectField* type = groundFindField(obj, "type");
if (type == NULL || type->value.type != STRING) {
ERROR("Object does not have type field as string", "ValueError");
}
GroundObjectField* userAgent = groundFindField(obj, "userAgent");
if (userAgent == NULL || userAgent->value.type != STRING) {
ERROR("Object does not have userAgent field as string", "ValueError");
}
GroundObjectField* httpHeaders = groundFindField(obj, "httpHeaders");
if (httpHeaders == NULL || httpHeaders->value.type != LIST) {
ERROR("Object does not have httpHeaders field as list<string>", "ValueError");
}
GroundObjectField* verbose = groundFindField(obj, "verbose");
if (verbose == NULL || verbose->value.type != BOOL) {
ERROR("Object does not have verbose field as bool", "ValueError");
}
// check request type string
bool requestTypeOk = false;
for (int i = 0; i < sizeof(VALID_REQUEST_TYPES)/sizeof(VALID_REQUEST_TYPES[0]); i++) {
if (strcmp(type->value.data.stringVal, VALID_REQUEST_TYPES[i]) == 0) {
requestTypeOk = true;
break;
}
}
if (!requestTypeOk)
ERROR("Invalid request type! Choices: GET, POST", "ValueError");
ResponseBuffer buffer = {0};
buffer.data = malloc(1);
buffer.data[0] = '\0';
buffer.size = 0;
// set curl params
curl_easy_setopt(curlHandle, CURLOPT_URL, address->value.data.stringVal);
curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void*)&buffer);
curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, userAgent->value.data.stringVal);
curl_easy_setopt(curlHandle, CURLOPT_VERBOSE, verbose->value.data.boolVal);
curl_easy_setopt(handle, CURLOPT_URL, args.values[0].data.stringVal);
curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buffer);
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
// append headers to http request
struct curl_slist* httpHeaderList = NULL;
GroundValue* httpHeaderStrings = httpHeaders->value.data.listVal.values;
for (int i = 0; i < httpHeaders->value.data.listVal.size; i++) {
httpHeaderList = curl_slist_append(httpHeaderList, httpHeaderStrings[i].data.stringVal);
}
curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, httpHeaderList);
CURLcode result = curl_easy_perform(handle);
CURLcode curlStatus = curl_easy_perform(curlHandle);
curl_slist_free_all(httpHeaderList); // dont want em' memory leaks :P - spooopy
// check for any curl errors
if (curlStatus != CURLE_OK) {
char curlErrorMsg[255] = {0};
snprintf(curlErrorMsg, 255, "%s\n", curl_easy_strerror(curlStatus));
ERROR(curlErrorMsg, "CurlError");
if (result != CURLE_OK) {
char buf[256];
snprintf(buf, sizeof(buf) - 1, "Curl request failed: %s\n", curl_easy_strerror(result));
ERROR(buf, "ActionRequestError");
}
// get the http response code
long httpCode = 0;
curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &httpCode);
curl_easy_cleanup(handle);
// create return value and return it
GroundValue value = groundCreateValue(CUSTOM, &template);
GroundValue ret = groundCreateValue(STRING, buffer.data);
return ret;
}
GroundObjectField* status = groundFindField(*value.data.customVal, "statusCode");
status->value.data.intVal = httpCode;
// POST request
GroundValue post_request(GroundScope* scope, List args) {
curl_setup();
CURL* handle = curl_easy_init();
GroundObjectField* data = groundFindField(*value.data.customVal, "data");
data->value.data.stringVal = buffer.data;
if (!handle) {
ERROR("CURL failed to init", "GenericRequestError");
}
value.type = CUSTOM;
ResponseBuffer buffer = {0};
buffer.data = malloc(1);
buffer.data[0] = '\0';
buffer.size = 0;
return value;
curl_easy_setopt(handle, CURLOPT_URL, args.values[0].data.stringVal);
curl_easy_setopt(handle, CURLOPT_POST, 1L);
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, args.values[1].data.stringVal);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buffer);
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
CURLcode result = curl_easy_perform(handle);
if (result != CURLE_OK) {
free(buffer.data);
char buf[256];
snprintf(buf, sizeof(buf) - 1, "Curl request failed: %s\n", curl_easy_strerror(result));
curl_easy_cleanup(handle);
ERROR(buf, "ActionRequestError");
}
curl_easy_cleanup(handle);
GroundValue ret = groundCreateValue(STRING, buffer.data);
return ret;
}
// ============== WebSocket Functions ==============
// Find an empty slot for a new WebSocket connection
int find_ws_slot() {
for (int i = 0; i < MAX_WS_CONNECTIONS; i++) {
if (!ws_connections[i].connected) {
return i;
}
}
return -1;
}
// WebSocket Connect - returns connection ID as INT
GroundValue ws_connect(GroundScope* scope, List args) {
curl_setup();
int slot = find_ws_slot();
if (slot == -1) {
ERROR("Maximum WebSocket connections reached", "OverflowError");
}
CURL* handle = curl_easy_init();
if (!handle) {
ERROR("CURL failed to init", "GenericWSError");
}
curl_easy_setopt(handle, CURLOPT_URL, args.values[0].data.stringVal);
curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 2L); // WebSocket mode
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 10L);
CURLcode result = curl_easy_perform(handle);
if (result != CURLE_OK) {
char buf[256];
snprintf(buf, sizeof(buf) - 1, "Curl WebSocket failed: %s\n", curl_easy_strerror(result));
curl_easy_cleanup(handle);
ERROR(buf, "ActionWSError");
}
ws_connections[slot].handle = handle;
ws_connections[slot].connected = true;
ws_connections[slot].url = strdup(args.values[0].data.stringVal);
return groundCreateValue(INT, (int64_t)slot);
}
// WebSocket Send - sends a text message, returns bytes sent as INT
GroundValue ws_send(GroundScope* scope, List args) {
int conn_id = (int)args.values[0].data.intVal;
if (conn_id < 0 || conn_id >= MAX_WS_CONNECTIONS || !ws_connections[conn_id].connected) {
ERROR("Invalid WebSocket connection ID", "GenericWSError");
}
CURL* handle = ws_connections[conn_id].handle;
const char* message = args.values[1].data.stringVal;
size_t sent;
CURLcode result = curl_ws_send(handle, message, strlen(message), &sent, 0, CURLWS_TEXT);
if (result != CURLE_OK) {
char buf[256];
snprintf(buf, sizeof(buf) - 1, "WebSocket send failed: %s\n", curl_easy_strerror(result));
ERROR(buf, "ActionWSError");
}
return groundCreateValue(INT, (int64_t)sent);
}
// WebSocket Receive - receives a message (blocking)
GroundValue ws_receive(GroundScope* scope, List args) {
int conn_id = (int)args.values[0].data.intVal;
if (conn_id < 0 || conn_id >= MAX_WS_CONNECTIONS || !ws_connections[conn_id].connected) {
ERROR("Invalid WebSocket connection ID", "GenericWSError");
}
CURL* handle = ws_connections[conn_id].handle;
char buffer[4096];
size_t received;
const struct curl_ws_frame* meta;
CURLcode result = curl_ws_recv(handle, buffer, sizeof(buffer) - 1, &received, &meta);
if (result != CURLE_OK) {
if (result == CURLE_AGAIN) {
// No data available right now
return groundCreateValue(STRING, "");
}
char buf[256];
snprintf(buf, sizeof(buf) - 1, "WebSocket receive failed: %s\n", curl_easy_strerror(result));
ERROR(buf, "ActionWSError");
}
buffer[received] = '\0';
// Handle different frame types
if (meta->flags & CURLWS_CLOSE) {
printf("WebSocket close frame received\n");
ws_connections[conn_id].connected = false;
return groundCreateValue(STRING, "[CLOSED]");
}
if (meta->flags & CURLWS_PING) {
// Automatically respond to ping with pong
curl_ws_send(handle, "", 0, &received, 0, CURLWS_PONG);
return groundCreateValue(STRING, "[PING]");
}
char* data = malloc(received + 1);
memcpy(data, buffer, received);
data[received] = '\0';
return groundCreateValue(STRING, data);
}
// WebSocket Receive with timeout (non-blocking version)
GroundValue ws_receive_timeout(GroundScope* scope, List args) {
int conn_id = (int)args.values[0].data.intVal;
long timeout_ms = (long)args.values[1].data.intVal;
if (conn_id < 0 || conn_id >= MAX_WS_CONNECTIONS || !ws_connections[conn_id].connected) {
ERROR("Invalid WebSocket connection ID", "GenericWSError");
}
CURL* handle = ws_connections[conn_id].handle;
// Set socket to non-blocking mode
curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms);
char buffer[4096];
size_t received;
const struct curl_ws_frame* meta;
CURLcode result = curl_ws_recv(handle, buffer, sizeof(buffer) - 1, &received, &meta);
if (result == CURLE_AGAIN || result == CURLE_OPERATION_TIMEDOUT) {
return groundCreateValue(STRING, "");
}
if (result != CURLE_OK) {
char buf[256];
snprintf(buf, sizeof(buf) - 1, "WebSocket receive failed: %s\n", curl_easy_strerror(result));
ERROR(buf, "ActionWSError");
}
buffer[received] = '\0';
if (meta->flags & CURLWS_CLOSE) {
ws_connections[conn_id].connected = false;
return groundCreateValue(STRING, "[CLOSED]");
}
if (meta->flags & CURLWS_PING) {
curl_ws_send(handle, "", 0, &received, 0, CURLWS_PONG);
return groundCreateValue(STRING, "[PING]");
}
char* data = malloc(received + 1);
memcpy(data, buffer, received);
data[received] = '\0';
return groundCreateValue(STRING, data);
}
// WebSocket Close - properly close a connection, returns BOOL success
GroundValue ws_close(GroundScope* scope, List args) {
int conn_id = (int)args.values[0].data.intVal;
if (conn_id < 0 || conn_id >= MAX_WS_CONNECTIONS || !ws_connections[conn_id].connected) {
ERROR("Invalid or already connected websocket", "GenericWSError");
}
CURL* handle = ws_connections[conn_id].handle;
size_t sent;
// Send close frame
curl_ws_send(handle, "", 0, &sent, 0, CURLWS_CLOSE);
// Cleanup
curl_easy_cleanup(handle);
free(ws_connections[conn_id].url);
ws_connections[conn_id].handle = NULL;
ws_connections[conn_id].connected = false;
ws_connections[conn_id].url = NULL;
return groundCreateValue(BOOL, true);
}
// WebSocket Send Binary data, returns bytes sent as INT
GroundValue ws_send_binary(GroundScope* scope, List args) {
int conn_id = (int)args.values[0].data.intVal;
if (conn_id < 0 || conn_id >= MAX_WS_CONNECTIONS || !ws_connections[conn_id].connected) {
ERROR("Invalid WebSocket connection ID", "GenericWSError");
}
CURL* handle = ws_connections[conn_id].handle;
const char* data = args.values[1].data.stringVal;
size_t sent;
CURLcode result = curl_ws_send(handle, data, strlen(data), &sent, 0, CURLWS_BINARY);
if (result != CURLE_OK) {
char buf[256];
snprintf(buf, sizeof(buf) - 1, "WebSocket binary send failed: %s\n", curl_easy_strerror(result));
ERROR(buf, "ActionWSError");
}
return groundCreateValue(INT, (int64_t)sent);
}
void ground_init(GroundScope* scope) {
// init libcurl
curl_global_init(CURL_GLOBAL_ALL);
curlHandle = curl_easy_init();
// HTTP Functions
groundAddNativeFunction(scope, "request_Get", get_request, STRING, 1, STRING, "url");
groundAddNativeFunction(scope, "request_Post", post_request, STRING, 2, STRING, "url", STRING, "data");
groundAddValueToScope(scope, "requestInitSuccess", groundCreateValue(BOOL, curlHandle != NULL));
if (curlHandle == NULL) {
return;
}
// create struct
template = groundCreateStruct();
groundAddFieldToStruct(&template, "statusCode", groundCreateValue(INT, 200));
groundAddFieldToStruct(&template, "data", groundCreateValue(STRING, ""));
groundAddFieldToStruct(&template, "ok", groundCreateValue(BOOL, 1));
// create struct that users can pass to this library
GroundStruct requestStruct = groundCreateStruct();
groundAddFieldToStruct(&requestStruct, "address", groundCreateValue(STRING, ""));
groundAddFieldToStruct(&requestStruct, "type", groundCreateValue(STRING, "GET"));
groundAddFieldToStruct(&requestStruct, "userAgent", groundCreateValue(STRING, "Ground/1.0"));
groundAddFieldToStruct(&requestStruct, "httpHeaders", groundCreateValue(LIST, 0));
groundAddFieldToStruct(&requestStruct, "verbose", groundCreateValue(BOOL, 0));
groundAddValueToScope(scope, "request", groundCreateValue(STRUCTVAL, requestStruct));
// create functions
groundAddNativeFunction(scope, "request_Request", ground_request, CUSTOM, 1, CUSTOM, "requestInfo");
// WebSocket Functions
groundAddNativeFunction(scope, "ws_Connect", ws_connect, INT, 1, STRING, "url");
groundAddNativeFunction(scope, "ws_Send", ws_send, INT, 2, INT, "conn_id", STRING, "message");
groundAddNativeFunction(scope, "ws_Receive", ws_receive, STRING, 1, INT, "conn_id");
groundAddNativeFunction(scope, "ws_ReceiveTimeout", ws_receive_timeout, STRING, 2, INT, "conn_id", INT, "timeout_ms");
groundAddNativeFunction(scope, "ws_Close", ws_close, BOOL, 1, INT, "conn_id");
groundAddNativeFunction(scope, "ws_SendBinary", ws_send_binary, INT, 2, INT, "conn_id", STRING, "data");
}

View File

@@ -1,455 +0,0 @@
#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");
}

View File

@@ -1,50 +0,0 @@
#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,9 +1,18 @@
#include "types.h"
#ifdef GROUND_COMPILE_WITH_TRAM
#include <tram.h>
#endif
#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;
void compileGroundProgram(GroundProgram* program, char* name);
#ifdef GROUND_COMPILE_WITH_TRAM
void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program);
#endif

View File

@@ -6,42 +6,6 @@
#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) {
.instructions = malloc(sizeof(GroundInstruction)),
@@ -187,35 +151,9 @@ 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);
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
char* groundCompileProgram(GroundProgram* program) {
return compileGroundProgram(program);
}
GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) {
@@ -229,14 +167,6 @@ 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*)),
@@ -266,9 +196,3 @@ 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

@@ -3,15 +3,49 @@
#include "compiler.h"
#include "types.h"
#include "serialize.h"
#include "repl.h"
#include <stdio.h>
#include <string.h>
char* getFileContents(const char* filename);
char* getFileContents(const char* filename) {
// https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c
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;
}
int main(int argc, char** argv) {
if (argc == 1) {
exit(repl());
if (argc < 2) {
printf("Usage: %s <file> [-c] [--compile] [-h] [--help]\n", argv[0]);
exit(1);
}
bool compile = false;
@@ -37,7 +71,7 @@ int main(int argc, char** argv) {
printf(" -w <output> or --writebytecode <output>: Outputs binary Ground bytecode");
printf(" -b <output> or --bytecode <output>: Inputs binary Ground bytecode");
exit(0);
} else if (strcmp("--writeBytecode", argv[i]) == 0 || strcmp("-w", argv[i]) == 0) {
} else if (strcmp("--writebytecode", argv[i]) == 0 || strcmp("-w", argv[i]) == 0) {
if (compile) {
printf("Cannot choose both bytecode and compilation");
exit(1);
@@ -77,22 +111,17 @@ int main(int argc, char** argv) {
}
if (compile) {
#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
char* compiled = compileGroundProgram(&program);
printf("%s\n", compiled);
} 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,9 +128,6 @@ 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);
}
@@ -179,11 +176,6 @@ static GroundInstType getInstructionType(const char* inst) {
if (strcmp(inst, "not") == 0) return NOT;
if (strcmp(inst, "greater") == 0) return GREATER;
if (strcmp(inst, "lesser") == 0) return LESSER;
if (strcmp(inst, "and") == 0) return AND;
if (strcmp(inst, "or") == 0) return OR;
if (strcmp(inst, "xor") == 0) return XOR;
if (strcmp(inst, "neg") == 0) return NEG;
if (strcmp(inst, "shift") == 0) return SHIFT;
if (strcmp(inst, "stoi") == 0) return STOI;
if (strcmp(inst, "stod") == 0) return STOD;
if (strcmp(inst, "ctoi") == 0) return CTOI;
@@ -194,7 +186,6 @@ 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;
@@ -203,9 +194,6 @@ static GroundInstType getInstructionType(const char* inst) {
if (strcmp(inst, "use") == 0) return USE;
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

@@ -1,60 +0,0 @@
/*
* Ground REPL (shows when ground is ran without any arguments)
* Copyright (C) 2026 DiamondNether90
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "interpreter.h"
#include "include/estr.h"
#include "parser.h"
#define BUFFER_SIZE 1024
int repl() {
printf("Ground REPL\n");
printf("Copyright (C) DiamondNether90 2026\n");
printf("Distributed under the GNU GPL-3.0 license; type \"license\" for more info\n");
char input[BUFFER_SIZE];
GroundVariable* variables = NULL;
GroundLabel* labels = NULL;
GroundScope scope;
scope.variables = &variables;
scope.labels = &labels;
scope.isMainScope = true;
while (true) {
Estr programString = CREATE_ESTR("");
*scope.labels = NULL;
// Get program
printf(">>> ");
while (true) {
fgets(input, BUFFER_SIZE, stdin);
if (strcmp(input, "\n") == 0) {
break;
}
APPEND_ESTR(programString, input);
printf("...");
}
// Interpret program
GroundProgram program = createGroundProgram();
program = parseFile(programString.str);
interpretGroundProgram(&program, &scope);
freeGroundProgram(&program);
DESTROY_ESTR(programString);
}
}

View File

@@ -1 +0,0 @@
int repl();

View File

@@ -16,8 +16,6 @@ void wasm_print(const char* str) {
}
#endif
bool groundDisableTypeChecking = false;
GroundValue createIntGroundValue(int64_t in) {
GroundValue gv;
gv.data.intVal = in;
@@ -89,14 +87,11 @@ GroundValue copyGroundValue(const GroundValue* gv) {
case CHAR: newGv.data.charVal = gv->data.charVal; break;
case BOOL: newGv.data.boolVal = gv->data.boolVal; break;
case STRING:
/*
if (gv->data.stringVal != NULL) {
newGv.data.stringVal = strdup(gv->data.stringVal);
} else {
newGv.data.stringVal = NULL;
}
*/
newGv.data.stringVal = gv->data.stringVal;
break;
case LIST: {
List newList = createList();
@@ -250,10 +245,6 @@ 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;
@@ -267,8 +258,7 @@ void printGroundValue(GroundValue* gv) {
void freeGroundValue(GroundValue* gv) {
if (gv->type == STRING && gv->data.stringVal != NULL) {
// leak some memory for now
// free(gv->data.stringVal);
free(gv->data.stringVal);
gv->data.stringVal = NULL;
}
if (gv->type == LIST && gv->data.listVal.values != NULL) {
@@ -362,10 +352,6 @@ void printGroundArg(GroundArg* ga) {
break;
}
case TYPEREF: {
if (ga->value.refName == NULL) {
printf("-any");
break;
}
printf("-%s", ga->value.refName);
break;
}
@@ -496,21 +482,6 @@ void printGroundInstruction(GroundInstruction* gi) {
case LESSER:
printf("lesser");
break;
case AND:
printf("and");
break;
case OR:
printf("or");
break;
case XOR:
printf("xor");
break;
case NEG:
printf("neg");
break;
case SHIFT:
printf("shift");
break;
case STOI:
printf("stoi");
break;
@@ -541,9 +512,6 @@ void printGroundInstruction(GroundInstruction* gi) {
case CALL:
printf("call");
break;
case CALLMETHOD:
printf("callmethod");
break;
case STRUCT:
printf("struct");
break;
@@ -567,28 +535,12 @@ void printGroundInstruction(GroundInstruction* gi) {
case ERRORCMD:
printf("error");
break;
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");
default:
printf("FIXME");
break;
}
if (gi->type != CREATELABEL) printf(" ");
for (size_t i = 0; i < gi->args.length; i++) {
if (i != 0) printf(" ");
if (gi->args.args[i].type == VALUE && gi->args.args[i].value.value.type == STRING) {
printf("\"");
printGroundArg(&gi->args.args[i]);
@@ -596,6 +548,7 @@ void printGroundInstruction(GroundInstruction* gi) {
} else {
printGroundArg(&gi->args.args[i]);
}
printf(" ");
}
}
@@ -608,12 +561,12 @@ List createList() {
void appendToList(List* list, GroundValue value) {
if (list == NULL) {
printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/ground\n");
printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/cground\n");
exit(EXIT_FAILURE);
}
GroundValue* ptr = realloc(list->values, (list->size + 1) * sizeof(GroundValue));
if (ptr == NULL) {
printf("There was an error allocating memory for a list.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/ground\n");
printf("There was an error allocating memory for a list.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/cground\n");
exit(EXIT_FAILURE);
}
list->size++;
@@ -623,7 +576,7 @@ void appendToList(List* list, GroundValue value) {
ListAccess getListAt(List* list, size_t idx) {
if (list == NULL) {
printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/ground\n");
printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/cground\n");
exit(EXIT_FAILURE);
}
if (idx < list->size) {
@@ -641,7 +594,7 @@ ListAccess getListAt(List* list, size_t idx) {
ListAccessStatus setListAt(List* list, size_t idx, GroundValue value) {
if (list == NULL) {
printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/ground\n");
printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/cground\n");
exit(EXIT_FAILURE);
}
if (idx < list->size) {
@@ -754,8 +707,6 @@ 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;
}
@@ -776,7 +727,6 @@ bool checkFnTypes(GroundValue* left, GroundFunctionArgs* right) {
}
bool checkTypes(GroundValue* left, GroundValue* right) {
if (groundDisableTypeChecking) return true;
if (left->type != right->type) {
return false;
}
@@ -800,12 +750,10 @@ 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,14 +29,11 @@ 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, CALLMETHOD, STRUCT, ENDSTRUCT, INIT,
GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, LICENSE, ERRORCMD, THROW, CATCH
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
} GroundInstType;
typedef enum GroundValueType {
INT, DOUBLE, STRING, CHAR, BOOL, LIST, FUNCTION, STRUCTVAL, CUSTOM, ERROR, NONE, ANY
INT, DOUBLE, STRING, CHAR, BOOL, LIST, FUNCTION, STRUCTVAL, CUSTOM, ERROR, NONE
} GroundValueType;
typedef enum GroundArgType {
@@ -110,19 +107,11 @@ 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;

View File

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

View File

@@ -1,15 +1,27 @@
set &x 5
PAUSE
fun !dingle -function -int &a
PAUSE
fun !capture -int -int &b
PAUSE
add $a $b &tmp
add $tmp $x &tmp
return $tmp
endfun
return $capture
endfun
set &x 10
call !dingle 3 &result
PAUSE
call !result 5 &y
println $y $x
println $y

View File

@@ -1,4 +1,4 @@
set &x "dingus"
println $x
PAUSE
drop &x
println $x
PAUSE

View File

@@ -1,2 +0,0 @@
set &x 10
end $x

View File

@@ -1,4 +0,0 @@
fun !lib_PrintHello -int
println "Hello"
return 0
endfun

View File

@@ -1,6 +1,5 @@
# A cool list
setlist &favWords "hello" "there" "general"
listappend &favWords "kenobi"
setlist &favWords "hello" "there" "general" "kenobi"
println $favWords
set &count 0

View File

@@ -1,2 +1,11 @@
fun !dingle -int
endfun
set &x 5
set &y "dingle"
PAUSE
println "continuing"
println "step through here"
println "step again"
println "and again"

View File

@@ -18,7 +18,7 @@ fun !fib -int -int &n -function &fib
endfun
# Main program
println "Computing fib(20) recursively..."
call !fib 20 $fib &answer
println "Computing fib(30) recursively..."
call !fib 30 $fib &answer
println "Result:" $answer
end

View File

@@ -2,10 +2,10 @@ input &str
getstrsize $str &size
set &idx 0
@loop
getstrcharat $str $idx &char
println $char
add 1 $idx &idx
equal $idx $size &cond
if $cond %loopend
jump %loop
getstrcharat $str $idx &char
println $char
add 1 $idx &idx
equal $idx $size &cond
if $cond %loopend
jump %loop
@loopend

View File

@@ -1,82 +0,0 @@
#!/usr/bin/env bash
echo "" > log.txt
for f in *.grnd; do
[ -e "$f" ] || continue # skip if no files match
# Files to skip over
if [[ "$f" == "lib.grnd" ]] ||
[[ "$f" == "string.grnd" ]] ||
[[ "$f" == "test.grnd" ]] ||
[[ "$f" == "to1000.grnd" ]] ||
[[ "$f" == "uhoh.grnd" ]] ||
[[ "$f" == "pause.grnd" ]];
then continue
fi
echo "Running $f"
ground "$f" > log.txt
FILE="log.txt"
FAILED="\033[31mFailed\n\033[0m"
if [[ "$f" == "closure.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "13 10\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "convs.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "32\n12\n3.140000\na\n97\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "drop.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "dingus\nGround runtime error:\n ErrorType: UnknownVariable\n ErrorInstruction: println \$x\n ErrorLine: 4\n"));
then printf "\033[31mFailed\n\033[0m"
exit 1
fi
elif [[ "$f" == "error.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "Ground runtime error:\n ErrorType: Hello\n ErrorContext: [1, 2, 3, Hi!]\n ErrorInstruction: error \"Hello\" [1, 2, 3, Hi!] 1\n ErrorLine: 2\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "fib.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "Fibonacci result: 7540113804746346429\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "function.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "dingle\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "list.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "[hello, there, general, kenobi]\nhello\nthere\ngeneral\nkenobi\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "recursivefib.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "Computing fib(20) recursively...\nResult: 6765\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "simple.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "dingus\ndinglefart\n5.840000\n464773025\n5164.120000\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "struct.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "53\n32\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "use.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "Hello\n"));
then printf $FAILED
exit 1
fi
else
printf "\033[31mCould not find test case\n\033[0m"
exit 1
fi
done
rm log.txt
printf "\033[32mAll tests passed!\n\033[0m"
exit 0

View File

@@ -1,2 +0,0 @@
use "lib"
call !lib_PrintHello &tmp