3 Commits

21 changed files with 521 additions and 183 deletions

View File

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

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

18
libs/datetime/datetime.c Normal file
View File

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

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

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

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,6 @@
#include "compiler.h" #include "compiler.h"
#include "types.h" #include "types.h"
#include "serialize.h" #include "serialize.h"
#include "repl.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -44,8 +43,9 @@ char* getFileContents(const char* filename) {
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
if (argc == 1) { if (argc < 2) {
exit(repl()); printf("Usage: %s <file> [-c] [--compile] [-h] [--help]\n", argv[0]);
exit(1);
} }
bool compile = false; bool compile = false;
@@ -71,7 +71,7 @@ int main(int argc, char** argv) {
printf(" -w <output> or --writebytecode <output>: Outputs binary Ground bytecode"); printf(" -w <output> or --writebytecode <output>: Outputs binary Ground bytecode");
printf(" -b <output> or --bytecode <output>: Inputs binary Ground bytecode"); printf(" -b <output> or --bytecode <output>: Inputs binary Ground bytecode");
exit(0); 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) { if (compile) {
printf("Cannot choose both bytecode and compilation"); printf("Cannot choose both bytecode and compilation");
exit(1); exit(1);

View File

@@ -194,7 +194,6 @@ static GroundInstType getInstructionType(const char* inst) {
if (strcmp(inst, "use") == 0) return USE; if (strcmp(inst, "use") == 0) return USE;
if (strcmp(inst, "extern") == 0) return EXTERN; if (strcmp(inst, "extern") == 0) return EXTERN;
if (strcmp(inst, "drop") == 0) return DROP; if (strcmp(inst, "drop") == 0) return DROP;
if (strcmp(inst, "license") == 0) return LICENSE;
if (strcmp(inst, "PAUSE") == 0) return PAUSE; if (strcmp(inst, "PAUSE") == 0) return PAUSE;
fprintf(stderr, "Error: Unknown instruction: %s\n", inst); fprintf(stderr, "Error: Unknown instruction: %s\n", inst);

View File

@@ -1,58 +0,0 @@
/*
* Ground REPL (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("");
// 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);
DESTROY_ESTR(programString);
}
}

View File

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

View File

@@ -545,7 +545,6 @@ void printGroundInstruction(GroundInstruction* gi) {
} }
if (gi->type != CREATELABEL) printf(" "); if (gi->type != CREATELABEL) printf(" ");
for (size_t i = 0; i < gi->args.length; i++) { 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) { if (gi->args.args[i].type == VALUE && gi->args.args[i].value.value.type == STRING) {
printf("\""); printf("\"");
printGroundArg(&gi->args.args[i]); printGroundArg(&gi->args.args[i]);
@@ -553,6 +552,7 @@ void printGroundInstruction(GroundInstruction* gi) {
} else { } else {
printGroundArg(&gi->args.args[i]); printGroundArg(&gi->args.args[i]);
} }
printf(" ");
} }
} }
@@ -565,12 +565,12 @@ List createList() {
void appendToList(List* list, GroundValue value) { void appendToList(List* list, GroundValue value) {
if (list == NULL) { 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); exit(EXIT_FAILURE);
} }
GroundValue* ptr = realloc(list->values, (list->size + 1) * sizeof(GroundValue)); GroundValue* ptr = realloc(list->values, (list->size + 1) * sizeof(GroundValue));
if (ptr == NULL) { 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); exit(EXIT_FAILURE);
} }
list->size++; list->size++;
@@ -580,7 +580,7 @@ void appendToList(List* list, GroundValue value) {
ListAccess getListAt(List* list, size_t idx) { ListAccess getListAt(List* list, size_t idx) {
if (list == NULL) { 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); exit(EXIT_FAILURE);
} }
if (idx < list->size) { if (idx < list->size) {
@@ -598,7 +598,7 @@ ListAccess getListAt(List* list, size_t idx) {
ListAccessStatus setListAt(List* list, size_t idx, GroundValue value) { ListAccessStatus setListAt(List* list, size_t idx, GroundValue value) {
if (list == NULL) { 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); exit(EXIT_FAILURE);
} }
if (idx < list->size) { if (idx < list->size) {

View File

@@ -29,7 +29,7 @@ void wasm_print(const char* str);
#endif #endif
typedef enum GroundInstType { typedef enum GroundInstType {
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, LICENSE, ERRORCMD IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD
} GroundInstType; } GroundInstType;
typedef enum GroundValueType { typedef enum GroundValueType {
@@ -83,7 +83,7 @@ typedef struct GroundError {
typedef struct GroundValue { typedef struct GroundValue {
GroundValueType type; GroundValueType type;
struct GroundStruct* customType; struct GroundStruct* customType;
union { struct {
int64_t intVal; int64_t intVal;
double doubleVal; double doubleVal;
char* stringVal; char* stringVal;

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,2 +1,11 @@
fun !dingle -int
endfun
set &x 5 set &x 5
set &y "dingle"
PAUSE 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 endfun
# Main program # Main program
println "Computing fib(20) recursively..." println "Computing fib(30) recursively..."
call !fib 20 $fib &answer call !fib 30 $fib &answer
println "Result:" $answer println "Result:" $answer
end end

View File

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

View File

@@ -1,81 +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" ]];
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