From 6596bfcc85331ed3f233fc626f633398f00c9d31 Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Thu, 28 Aug 2025 11:11:59 +1000 Subject: [PATCH] Organisation, documentation, mathlib --- extlibs/compiling.md | 9 ++ extlibs/exec/README.md | 9 ++ extlibs/{ => exec}/exec.cpp | 0 extlibs/exec/ground_lib.h | 193 ++++++++++++++++++++++++++++++ extlibs/file/README.md | 13 ++ extlibs/{ => file}/file.cpp | 0 extlibs/file/ground_lib.h | 193 ++++++++++++++++++++++++++++++ extlibs/math/README.md | 45 +++++++ extlibs/math/ground_lib.h | 193 ++++++++++++++++++++++++++++++ extlibs/math/math.cpp | 79 ++++++++++++ extlibs/request/README.md | 13 ++ extlibs/request/ground_lib.h | 193 ++++++++++++++++++++++++++++++ extlibs/{ => request}/request.cpp | 9 +- 13 files changed, 948 insertions(+), 1 deletion(-) create mode 100644 extlibs/compiling.md create mode 100644 extlibs/exec/README.md rename extlibs/{ => exec}/exec.cpp (100%) create mode 100644 extlibs/exec/ground_lib.h create mode 100644 extlibs/file/README.md rename extlibs/{ => file}/file.cpp (100%) create mode 100644 extlibs/file/ground_lib.h create mode 100644 extlibs/math/README.md create mode 100644 extlibs/math/ground_lib.h create mode 100644 extlibs/math/math.cpp create mode 100644 extlibs/request/README.md create mode 100644 extlibs/request/ground_lib.h rename extlibs/{ => request}/request.cpp (73%) diff --git a/extlibs/compiling.md b/extlibs/compiling.md new file mode 100644 index 0000000..54bd42e --- /dev/null +++ b/extlibs/compiling.md @@ -0,0 +1,9 @@ +## Compiling External Libraries + +On Linux: + +`g++ -shared -fPIC -o filename.so filename.cpp` + +On macOS: + +`g++ -shared -fPIC -o filename.dylib filename.cpp` diff --git a/extlibs/exec/README.md b/extlibs/exec/README.md new file mode 100644 index 0000000..bf10e3f --- /dev/null +++ b/extlibs/exec/README.md @@ -0,0 +1,9 @@ +# exec library + +This library allows executing third party executables via the C++ "system" command. + +## Functions + +### fun -int !exec -string &command + +Runs a command on a system. Returns the status code of that command. diff --git a/extlibs/exec.cpp b/extlibs/exec/exec.cpp similarity index 100% rename from extlibs/exec.cpp rename to extlibs/exec/exec.cpp diff --git a/extlibs/exec/ground_lib.h b/extlibs/exec/ground_lib.h new file mode 100644 index 0000000..7567cf1 --- /dev/null +++ b/extlibs/exec/ground_lib.h @@ -0,0 +1,193 @@ +#ifndef GROUND_LIB_H +#define GROUND_LIB_H + +#include +#include +#include +#include + +// Ground types - must match the interpreter +typedef enum { + GROUND_INT, + GROUND_DOUBLE, + GROUND_BOOL, + GROUND_STRING, + GROUND_CHAR +} GroundType; + +typedef struct { + GroundType type; + union { + int int_val; + double double_val; + int bool_val; + char* string_val; + char char_val; + } data; +} GroundValue; + +// Helper macros for creating GroundValue objects +#define GROUND_INT_VAL(x) ({ GroundValue v; v.type = GROUND_INT; v.data.int_val = (x); v; }) +#define GROUND_DOUBLE_VAL(x) ({ GroundValue v; v.type = GROUND_DOUBLE; v.data.double_val = (x); v; }) +#define GROUND_BOOL_VAL(x) ({ GroundValue v; v.type = GROUND_BOOL; v.data.bool_val = (x) ? 1 : 0; v; }) +#define GROUND_CHAR_VAL(x) ({ GroundValue v; v.type = GROUND_CHAR; v.data.char_val = (x); v; }) + +// Helper function for creating string values +inline GroundValue ground_string_val(const std::string& str) { + GroundValue v; + v.type = GROUND_STRING; + char* result_str = new char[str.length() + 1]; + std::strcpy(result_str, str.c_str()); + v.data.string_val = result_str; + return v; +} + +// Helper function for creating string values from C strings +inline GroundValue ground_cstring_val(const char* str) { + GroundValue v; + v.type = GROUND_STRING; + if (str) { + size_t len = std::strlen(str); + char* result_str = new char[len + 1]; + std::strcpy(result_str, str); + v.data.string_val = result_str; + } else { + v.data.string_val = nullptr; + } + return v; +} + +// Helper macros for type checking +#define IS_INT(v) ((v).type == GROUND_INT) +#define IS_DOUBLE(v) ((v).type == GROUND_DOUBLE) +#define IS_BOOL(v) ((v).type == GROUND_BOOL) +#define IS_STRING(v) ((v).type == GROUND_STRING) +#define IS_CHAR(v) ((v).type == GROUND_CHAR) + +// Helper macros for extracting values +#define GET_INT(v) ((v).data.int_val) +#define GET_DOUBLE(v) ((v).data.double_val) +#define GET_BOOL(v) ((v).data.bool_val != 0) +#define GET_STRING(v) ((v).data.string_val) +#define GET_CHAR(v) ((v).data.char_val) + +// Helper macros for argument validation +#define REQUIRE_ARGS(count) \ + if (arg_count < (count)) { \ + std::cerr << "Error: Expected at least " << (count) << " arguments, got " << arg_count << std::endl; \ + return GROUND_BOOL_VAL(false); \ + } + +#define REQUIRE_TYPE(arg_index, expected_type) \ + if (args[arg_index].type != expected_type) { \ + std::cerr << "Error: Argument " << (arg_index + 1) << " must be of type " << #expected_type << std::endl; \ + return GROUND_BOOL_VAL(false); \ + } + +// Convenience macro for checking both arg count and types +#define VALIDATE_ARGS_1(type1) \ + REQUIRE_ARGS(1); \ + REQUIRE_TYPE(0, type1); + +#define VALIDATE_ARGS_2(type1, type2) \ + REQUIRE_ARGS(2); \ + REQUIRE_TYPE(0, type1); \ + REQUIRE_TYPE(1, type2); + +#define VALIDATE_ARGS_3(type1, type2, type3) \ + REQUIRE_ARGS(3); \ + REQUIRE_TYPE(0, type1); \ + REQUIRE_TYPE(1, type2); \ + REQUIRE_TYPE(2, type3); + +// Function registration helpers +class GroundLibrary { +private: + std::vector function_names; + std::vector function_pointers; + +public: + void register_function(const std::string& name, void* ptr) { + function_names.push_back(name); + function_pointers.push_back(ptr); + } + + const char** get_function_names() { + static std::vector names; + names.clear(); + for (const auto& name : function_names) { + names.push_back(name.c_str()); + } + names.push_back(nullptr); // Null terminator + return names.data(); + } + + void* get_function(const char* name) { + for (size_t i = 0; i < function_names.size(); i++) { + if (function_names[i] == name) { + return function_pointers[i]; + } + } + return nullptr; + } +}; + +// Global library instance +extern GroundLibrary ground_lib_registry; + +// Macro to register functions easily +#define REGISTER_GROUND_FUNCTION(func_name) \ + ground_lib_registry.register_function(#func_name, (void*)func_name) + +// Macro to define the library interface +#define GROUND_LIBRARY_INTERFACE() \ + GroundLibrary ground_lib_registry; \ + extern "C" { \ + const char** ground_get_functions() { \ + return ground_lib_registry.get_function_names(); \ + } \ + void* ground_get_function(const char* name) { \ + return ground_lib_registry.get_function(name); \ + } \ + } + +// Optional initialization macro +#define GROUND_LIBRARY_INIT() \ + extern "C" { \ + void ground_lib_init() { + +#define GROUND_LIBRARY_INIT_END() \ + } \ + } + +// Optional cleanup macro +#define GROUND_LIBRARY_CLEANUP() \ + extern "C" { \ + void ground_lib_cleanup() { + +#define GROUND_LIBRARY_CLEANUP_END() \ + } \ + } + +// Utility function to print GroundValue for debugging +inline void debug_print_ground_value(const GroundValue& v) { + switch (v.type) { + case GROUND_INT: + std::cout << "INT: " << v.data.int_val << std::endl; + break; + case GROUND_DOUBLE: + std::cout << "DOUBLE: " << v.data.double_val << std::endl; + break; + case GROUND_BOOL: + std::cout << "BOOL: " << (v.data.bool_val ? "true" : "false") << std::endl; + break; + case GROUND_STRING: + std::cout << "STRING: " << (v.data.string_val ? v.data.string_val : "(null)") << std::endl; + break; + case GROUND_CHAR: + std::cout << "CHAR: '" << v.data.char_val << "'" << std::endl; + break; + } +} + +#endif // GROUND_LIB_H diff --git a/extlibs/file/README.md b/extlibs/file/README.md new file mode 100644 index 0000000..d33c9cd --- /dev/null +++ b/extlibs/file/README.md @@ -0,0 +1,13 @@ +# file library + +This library allows reading from and writing to files on the system. + +## Functions + +### fun -string !readFile -string &fileName + +This function reads all content from a file and returns it. + +### fun -bool !writeFile -string &fileName -string &content + +This function overwrites a file with specified content. If successful, returns true. If not successful, returns false, and prints out a reason why it didn't work. diff --git a/extlibs/file.cpp b/extlibs/file/file.cpp similarity index 100% rename from extlibs/file.cpp rename to extlibs/file/file.cpp diff --git a/extlibs/file/ground_lib.h b/extlibs/file/ground_lib.h new file mode 100644 index 0000000..7567cf1 --- /dev/null +++ b/extlibs/file/ground_lib.h @@ -0,0 +1,193 @@ +#ifndef GROUND_LIB_H +#define GROUND_LIB_H + +#include +#include +#include +#include + +// Ground types - must match the interpreter +typedef enum { + GROUND_INT, + GROUND_DOUBLE, + GROUND_BOOL, + GROUND_STRING, + GROUND_CHAR +} GroundType; + +typedef struct { + GroundType type; + union { + int int_val; + double double_val; + int bool_val; + char* string_val; + char char_val; + } data; +} GroundValue; + +// Helper macros for creating GroundValue objects +#define GROUND_INT_VAL(x) ({ GroundValue v; v.type = GROUND_INT; v.data.int_val = (x); v; }) +#define GROUND_DOUBLE_VAL(x) ({ GroundValue v; v.type = GROUND_DOUBLE; v.data.double_val = (x); v; }) +#define GROUND_BOOL_VAL(x) ({ GroundValue v; v.type = GROUND_BOOL; v.data.bool_val = (x) ? 1 : 0; v; }) +#define GROUND_CHAR_VAL(x) ({ GroundValue v; v.type = GROUND_CHAR; v.data.char_val = (x); v; }) + +// Helper function for creating string values +inline GroundValue ground_string_val(const std::string& str) { + GroundValue v; + v.type = GROUND_STRING; + char* result_str = new char[str.length() + 1]; + std::strcpy(result_str, str.c_str()); + v.data.string_val = result_str; + return v; +} + +// Helper function for creating string values from C strings +inline GroundValue ground_cstring_val(const char* str) { + GroundValue v; + v.type = GROUND_STRING; + if (str) { + size_t len = std::strlen(str); + char* result_str = new char[len + 1]; + std::strcpy(result_str, str); + v.data.string_val = result_str; + } else { + v.data.string_val = nullptr; + } + return v; +} + +// Helper macros for type checking +#define IS_INT(v) ((v).type == GROUND_INT) +#define IS_DOUBLE(v) ((v).type == GROUND_DOUBLE) +#define IS_BOOL(v) ((v).type == GROUND_BOOL) +#define IS_STRING(v) ((v).type == GROUND_STRING) +#define IS_CHAR(v) ((v).type == GROUND_CHAR) + +// Helper macros for extracting values +#define GET_INT(v) ((v).data.int_val) +#define GET_DOUBLE(v) ((v).data.double_val) +#define GET_BOOL(v) ((v).data.bool_val != 0) +#define GET_STRING(v) ((v).data.string_val) +#define GET_CHAR(v) ((v).data.char_val) + +// Helper macros for argument validation +#define REQUIRE_ARGS(count) \ + if (arg_count < (count)) { \ + std::cerr << "Error: Expected at least " << (count) << " arguments, got " << arg_count << std::endl; \ + return GROUND_BOOL_VAL(false); \ + } + +#define REQUIRE_TYPE(arg_index, expected_type) \ + if (args[arg_index].type != expected_type) { \ + std::cerr << "Error: Argument " << (arg_index + 1) << " must be of type " << #expected_type << std::endl; \ + return GROUND_BOOL_VAL(false); \ + } + +// Convenience macro for checking both arg count and types +#define VALIDATE_ARGS_1(type1) \ + REQUIRE_ARGS(1); \ + REQUIRE_TYPE(0, type1); + +#define VALIDATE_ARGS_2(type1, type2) \ + REQUIRE_ARGS(2); \ + REQUIRE_TYPE(0, type1); \ + REQUIRE_TYPE(1, type2); + +#define VALIDATE_ARGS_3(type1, type2, type3) \ + REQUIRE_ARGS(3); \ + REQUIRE_TYPE(0, type1); \ + REQUIRE_TYPE(1, type2); \ + REQUIRE_TYPE(2, type3); + +// Function registration helpers +class GroundLibrary { +private: + std::vector function_names; + std::vector function_pointers; + +public: + void register_function(const std::string& name, void* ptr) { + function_names.push_back(name); + function_pointers.push_back(ptr); + } + + const char** get_function_names() { + static std::vector names; + names.clear(); + for (const auto& name : function_names) { + names.push_back(name.c_str()); + } + names.push_back(nullptr); // Null terminator + return names.data(); + } + + void* get_function(const char* name) { + for (size_t i = 0; i < function_names.size(); i++) { + if (function_names[i] == name) { + return function_pointers[i]; + } + } + return nullptr; + } +}; + +// Global library instance +extern GroundLibrary ground_lib_registry; + +// Macro to register functions easily +#define REGISTER_GROUND_FUNCTION(func_name) \ + ground_lib_registry.register_function(#func_name, (void*)func_name) + +// Macro to define the library interface +#define GROUND_LIBRARY_INTERFACE() \ + GroundLibrary ground_lib_registry; \ + extern "C" { \ + const char** ground_get_functions() { \ + return ground_lib_registry.get_function_names(); \ + } \ + void* ground_get_function(const char* name) { \ + return ground_lib_registry.get_function(name); \ + } \ + } + +// Optional initialization macro +#define GROUND_LIBRARY_INIT() \ + extern "C" { \ + void ground_lib_init() { + +#define GROUND_LIBRARY_INIT_END() \ + } \ + } + +// Optional cleanup macro +#define GROUND_LIBRARY_CLEANUP() \ + extern "C" { \ + void ground_lib_cleanup() { + +#define GROUND_LIBRARY_CLEANUP_END() \ + } \ + } + +// Utility function to print GroundValue for debugging +inline void debug_print_ground_value(const GroundValue& v) { + switch (v.type) { + case GROUND_INT: + std::cout << "INT: " << v.data.int_val << std::endl; + break; + case GROUND_DOUBLE: + std::cout << "DOUBLE: " << v.data.double_val << std::endl; + break; + case GROUND_BOOL: + std::cout << "BOOL: " << (v.data.bool_val ? "true" : "false") << std::endl; + break; + case GROUND_STRING: + std::cout << "STRING: " << (v.data.string_val ? v.data.string_val : "(null)") << std::endl; + break; + case GROUND_CHAR: + std::cout << "CHAR: '" << v.data.char_val << "'" << std::endl; + break; + } +} + +#endif // GROUND_LIB_H diff --git a/extlibs/math/README.md b/extlibs/math/README.md new file mode 100644 index 0000000..a9912cb --- /dev/null +++ b/extlibs/math/README.md @@ -0,0 +1,45 @@ +# math library + +This library adds extra math functions to Ground. + +## Functions + +### fun -double !sinVal -double &input + +Gets the sin of input. + +### fun -double !cosVal -double &input + +Gets the cos of input. + +### fun -double !tanVal -double &input + +Gets the tan of input. + +### fun -double !sqrtVal -double &input + +Gets the square root of input. + +### fun -int !modVal -int &a -int &b + +Gets the remainder of a divided by b. + +### fun -double !floorVal -double &input + +Gets the floor of input. + +### fun -double !ceilVal -double &input + +Gets the ceil of input. + +### fun -double !roundVal -double &input + +Rounds the input to the nearest integer. + +### fun -int !randomInt -int &min -int &max + +Gets a random integer between min and max (inclusive). + +### fun -double !randomDouble -double &min -double &max + +Gets a random double between min and max (inclusive). \ No newline at end of file diff --git a/extlibs/math/ground_lib.h b/extlibs/math/ground_lib.h new file mode 100644 index 0000000..7567cf1 --- /dev/null +++ b/extlibs/math/ground_lib.h @@ -0,0 +1,193 @@ +#ifndef GROUND_LIB_H +#define GROUND_LIB_H + +#include +#include +#include +#include + +// Ground types - must match the interpreter +typedef enum { + GROUND_INT, + GROUND_DOUBLE, + GROUND_BOOL, + GROUND_STRING, + GROUND_CHAR +} GroundType; + +typedef struct { + GroundType type; + union { + int int_val; + double double_val; + int bool_val; + char* string_val; + char char_val; + } data; +} GroundValue; + +// Helper macros for creating GroundValue objects +#define GROUND_INT_VAL(x) ({ GroundValue v; v.type = GROUND_INT; v.data.int_val = (x); v; }) +#define GROUND_DOUBLE_VAL(x) ({ GroundValue v; v.type = GROUND_DOUBLE; v.data.double_val = (x); v; }) +#define GROUND_BOOL_VAL(x) ({ GroundValue v; v.type = GROUND_BOOL; v.data.bool_val = (x) ? 1 : 0; v; }) +#define GROUND_CHAR_VAL(x) ({ GroundValue v; v.type = GROUND_CHAR; v.data.char_val = (x); v; }) + +// Helper function for creating string values +inline GroundValue ground_string_val(const std::string& str) { + GroundValue v; + v.type = GROUND_STRING; + char* result_str = new char[str.length() + 1]; + std::strcpy(result_str, str.c_str()); + v.data.string_val = result_str; + return v; +} + +// Helper function for creating string values from C strings +inline GroundValue ground_cstring_val(const char* str) { + GroundValue v; + v.type = GROUND_STRING; + if (str) { + size_t len = std::strlen(str); + char* result_str = new char[len + 1]; + std::strcpy(result_str, str); + v.data.string_val = result_str; + } else { + v.data.string_val = nullptr; + } + return v; +} + +// Helper macros for type checking +#define IS_INT(v) ((v).type == GROUND_INT) +#define IS_DOUBLE(v) ((v).type == GROUND_DOUBLE) +#define IS_BOOL(v) ((v).type == GROUND_BOOL) +#define IS_STRING(v) ((v).type == GROUND_STRING) +#define IS_CHAR(v) ((v).type == GROUND_CHAR) + +// Helper macros for extracting values +#define GET_INT(v) ((v).data.int_val) +#define GET_DOUBLE(v) ((v).data.double_val) +#define GET_BOOL(v) ((v).data.bool_val != 0) +#define GET_STRING(v) ((v).data.string_val) +#define GET_CHAR(v) ((v).data.char_val) + +// Helper macros for argument validation +#define REQUIRE_ARGS(count) \ + if (arg_count < (count)) { \ + std::cerr << "Error: Expected at least " << (count) << " arguments, got " << arg_count << std::endl; \ + return GROUND_BOOL_VAL(false); \ + } + +#define REQUIRE_TYPE(arg_index, expected_type) \ + if (args[arg_index].type != expected_type) { \ + std::cerr << "Error: Argument " << (arg_index + 1) << " must be of type " << #expected_type << std::endl; \ + return GROUND_BOOL_VAL(false); \ + } + +// Convenience macro for checking both arg count and types +#define VALIDATE_ARGS_1(type1) \ + REQUIRE_ARGS(1); \ + REQUIRE_TYPE(0, type1); + +#define VALIDATE_ARGS_2(type1, type2) \ + REQUIRE_ARGS(2); \ + REQUIRE_TYPE(0, type1); \ + REQUIRE_TYPE(1, type2); + +#define VALIDATE_ARGS_3(type1, type2, type3) \ + REQUIRE_ARGS(3); \ + REQUIRE_TYPE(0, type1); \ + REQUIRE_TYPE(1, type2); \ + REQUIRE_TYPE(2, type3); + +// Function registration helpers +class GroundLibrary { +private: + std::vector function_names; + std::vector function_pointers; + +public: + void register_function(const std::string& name, void* ptr) { + function_names.push_back(name); + function_pointers.push_back(ptr); + } + + const char** get_function_names() { + static std::vector names; + names.clear(); + for (const auto& name : function_names) { + names.push_back(name.c_str()); + } + names.push_back(nullptr); // Null terminator + return names.data(); + } + + void* get_function(const char* name) { + for (size_t i = 0; i < function_names.size(); i++) { + if (function_names[i] == name) { + return function_pointers[i]; + } + } + return nullptr; + } +}; + +// Global library instance +extern GroundLibrary ground_lib_registry; + +// Macro to register functions easily +#define REGISTER_GROUND_FUNCTION(func_name) \ + ground_lib_registry.register_function(#func_name, (void*)func_name) + +// Macro to define the library interface +#define GROUND_LIBRARY_INTERFACE() \ + GroundLibrary ground_lib_registry; \ + extern "C" { \ + const char** ground_get_functions() { \ + return ground_lib_registry.get_function_names(); \ + } \ + void* ground_get_function(const char* name) { \ + return ground_lib_registry.get_function(name); \ + } \ + } + +// Optional initialization macro +#define GROUND_LIBRARY_INIT() \ + extern "C" { \ + void ground_lib_init() { + +#define GROUND_LIBRARY_INIT_END() \ + } \ + } + +// Optional cleanup macro +#define GROUND_LIBRARY_CLEANUP() \ + extern "C" { \ + void ground_lib_cleanup() { + +#define GROUND_LIBRARY_CLEANUP_END() \ + } \ + } + +// Utility function to print GroundValue for debugging +inline void debug_print_ground_value(const GroundValue& v) { + switch (v.type) { + case GROUND_INT: + std::cout << "INT: " << v.data.int_val << std::endl; + break; + case GROUND_DOUBLE: + std::cout << "DOUBLE: " << v.data.double_val << std::endl; + break; + case GROUND_BOOL: + std::cout << "BOOL: " << (v.data.bool_val ? "true" : "false") << std::endl; + break; + case GROUND_STRING: + std::cout << "STRING: " << (v.data.string_val ? v.data.string_val : "(null)") << std::endl; + break; + case GROUND_CHAR: + std::cout << "CHAR: '" << v.data.char_val << "'" << std::endl; + break; + } +} + +#endif // GROUND_LIB_H diff --git a/extlibs/math/math.cpp b/extlibs/math/math.cpp new file mode 100644 index 0000000..df5ab68 --- /dev/null +++ b/extlibs/math/math.cpp @@ -0,0 +1,79 @@ +#include "ground_lib.h" +#include +#include + +// Math functions +GroundValue sinVal(GroundValue* args, int arg_count) { + VALIDATE_ARGS_1(GROUND_DOUBLE); + return GROUND_DOUBLE_VAL(sin(GET_DOUBLE(args[0]))); +} + +GroundValue cosVal(GroundValue* args, int arg_count) { + VALIDATE_ARGS_1(GROUND_DOUBLE); + return GROUND_DOUBLE_VAL(cos(GET_DOUBLE(args[0]))); +} + +GroundValue tanVal(GroundValue* args, int arg_count) { + VALIDATE_ARGS_1(GROUND_DOUBLE); + return GROUND_DOUBLE_VAL(tan(GET_DOUBLE(args[0]))); +} + +GroundValue sqrtVal(GroundValue* args, int arg_count) { + VALIDATE_ARGS_1(GROUND_DOUBLE); + return GROUND_DOUBLE_VAL(sqrt(GET_DOUBLE(args[0]))); +} + +GroundValue modVal(GroundValue* args, int arg_count) { + VALIDATE_ARGS_2(GROUND_INT, GROUND_INT); + return GROUND_INT_VAL(GET_INT(args[0]) % GET_INT(args[1])); +} + +GroundValue floorVal(GroundValue* args, int arg_count) { + VALIDATE_ARGS_1(GROUND_DOUBLE); + return GROUND_DOUBLE_VAL(floor(GET_DOUBLE(args[0]))); +} + +GroundValue ceilVal(GroundValue* args, int arg_count) { + VALIDATE_ARGS_1(GROUND_DOUBLE); + return GROUND_DOUBLE_VAL(ceil(GET_DOUBLE(args[0]))); +} + +GroundValue roundVal(GroundValue* args, int arg_count) { + VALIDATE_ARGS_1(GROUND_DOUBLE); + return GROUND_DOUBLE_VAL(round(GET_DOUBLE(args[0]))); +} + +GroundValue randomInt(GroundValue* args, int arg_count) { + VALIDATE_ARGS_2(GROUND_INT, GROUND_INT); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distrib(GET_INT(args[0]), GET_INT(args[1])); + return GROUND_INT_VAL(distrib(gen)); +} + +GroundValue randomDouble(GroundValue* args, int arg_count) { + VALIDATE_ARGS_2(GROUND_DOUBLE, GROUND_DOUBLE); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> distrib(GET_DOUBLE(args[0]), GET_DOUBLE(args[1])); + return GROUND_DOUBLE_VAL(distrib(gen)); +} + +// Library setup +GROUND_LIBRARY_INTERFACE() + +GROUND_LIBRARY_INIT() + REGISTER_GROUND_FUNCTION(sinVal); + REGISTER_GROUND_FUNCTION(cosVal); + REGISTER_GROUND_FUNCTION(tanVal); + REGISTER_GROUND_FUNCTION(sqrtVal); + REGISTER_GROUND_FUNCTION(modVal); + REGISTER_GROUND_FUNCTION(floorVal); + REGISTER_GROUND_FUNCTION(ceilVal); + REGISTER_GROUND_FUNCTION(roundVal); + REGISTER_GROUND_FUNCTION(randomInt); + REGISTER_GROUND_FUNCTION(randomDouble); +GROUND_LIBRARY_INIT_END() + +GROUND_LIBRARY_CLEANUP() +GROUND_LIBRARY_CLEANUP_END() diff --git a/extlibs/request/README.md b/extlibs/request/README.md new file mode 100644 index 0000000..12d185b --- /dev/null +++ b/extlibs/request/README.md @@ -0,0 +1,13 @@ +# request library + +This library allows various web requests from within Ground. + +## Functions + +### fun -string !simpleRequest -string &url + +Makes a web request to a URL, and returns the contents. If the request is not successful, returns a response beginning with "Error code", and prints it to the console. + +### fun -bool !saveContents -string &url -string &location + +Makes a web request to a URL, and saves the contents to a file. If successful, returns true. If not, returns false. diff --git a/extlibs/request/ground_lib.h b/extlibs/request/ground_lib.h new file mode 100644 index 0000000..7567cf1 --- /dev/null +++ b/extlibs/request/ground_lib.h @@ -0,0 +1,193 @@ +#ifndef GROUND_LIB_H +#define GROUND_LIB_H + +#include +#include +#include +#include + +// Ground types - must match the interpreter +typedef enum { + GROUND_INT, + GROUND_DOUBLE, + GROUND_BOOL, + GROUND_STRING, + GROUND_CHAR +} GroundType; + +typedef struct { + GroundType type; + union { + int int_val; + double double_val; + int bool_val; + char* string_val; + char char_val; + } data; +} GroundValue; + +// Helper macros for creating GroundValue objects +#define GROUND_INT_VAL(x) ({ GroundValue v; v.type = GROUND_INT; v.data.int_val = (x); v; }) +#define GROUND_DOUBLE_VAL(x) ({ GroundValue v; v.type = GROUND_DOUBLE; v.data.double_val = (x); v; }) +#define GROUND_BOOL_VAL(x) ({ GroundValue v; v.type = GROUND_BOOL; v.data.bool_val = (x) ? 1 : 0; v; }) +#define GROUND_CHAR_VAL(x) ({ GroundValue v; v.type = GROUND_CHAR; v.data.char_val = (x); v; }) + +// Helper function for creating string values +inline GroundValue ground_string_val(const std::string& str) { + GroundValue v; + v.type = GROUND_STRING; + char* result_str = new char[str.length() + 1]; + std::strcpy(result_str, str.c_str()); + v.data.string_val = result_str; + return v; +} + +// Helper function for creating string values from C strings +inline GroundValue ground_cstring_val(const char* str) { + GroundValue v; + v.type = GROUND_STRING; + if (str) { + size_t len = std::strlen(str); + char* result_str = new char[len + 1]; + std::strcpy(result_str, str); + v.data.string_val = result_str; + } else { + v.data.string_val = nullptr; + } + return v; +} + +// Helper macros for type checking +#define IS_INT(v) ((v).type == GROUND_INT) +#define IS_DOUBLE(v) ((v).type == GROUND_DOUBLE) +#define IS_BOOL(v) ((v).type == GROUND_BOOL) +#define IS_STRING(v) ((v).type == GROUND_STRING) +#define IS_CHAR(v) ((v).type == GROUND_CHAR) + +// Helper macros for extracting values +#define GET_INT(v) ((v).data.int_val) +#define GET_DOUBLE(v) ((v).data.double_val) +#define GET_BOOL(v) ((v).data.bool_val != 0) +#define GET_STRING(v) ((v).data.string_val) +#define GET_CHAR(v) ((v).data.char_val) + +// Helper macros for argument validation +#define REQUIRE_ARGS(count) \ + if (arg_count < (count)) { \ + std::cerr << "Error: Expected at least " << (count) << " arguments, got " << arg_count << std::endl; \ + return GROUND_BOOL_VAL(false); \ + } + +#define REQUIRE_TYPE(arg_index, expected_type) \ + if (args[arg_index].type != expected_type) { \ + std::cerr << "Error: Argument " << (arg_index + 1) << " must be of type " << #expected_type << std::endl; \ + return GROUND_BOOL_VAL(false); \ + } + +// Convenience macro for checking both arg count and types +#define VALIDATE_ARGS_1(type1) \ + REQUIRE_ARGS(1); \ + REQUIRE_TYPE(0, type1); + +#define VALIDATE_ARGS_2(type1, type2) \ + REQUIRE_ARGS(2); \ + REQUIRE_TYPE(0, type1); \ + REQUIRE_TYPE(1, type2); + +#define VALIDATE_ARGS_3(type1, type2, type3) \ + REQUIRE_ARGS(3); \ + REQUIRE_TYPE(0, type1); \ + REQUIRE_TYPE(1, type2); \ + REQUIRE_TYPE(2, type3); + +// Function registration helpers +class GroundLibrary { +private: + std::vector function_names; + std::vector function_pointers; + +public: + void register_function(const std::string& name, void* ptr) { + function_names.push_back(name); + function_pointers.push_back(ptr); + } + + const char** get_function_names() { + static std::vector names; + names.clear(); + for (const auto& name : function_names) { + names.push_back(name.c_str()); + } + names.push_back(nullptr); // Null terminator + return names.data(); + } + + void* get_function(const char* name) { + for (size_t i = 0; i < function_names.size(); i++) { + if (function_names[i] == name) { + return function_pointers[i]; + } + } + return nullptr; + } +}; + +// Global library instance +extern GroundLibrary ground_lib_registry; + +// Macro to register functions easily +#define REGISTER_GROUND_FUNCTION(func_name) \ + ground_lib_registry.register_function(#func_name, (void*)func_name) + +// Macro to define the library interface +#define GROUND_LIBRARY_INTERFACE() \ + GroundLibrary ground_lib_registry; \ + extern "C" { \ + const char** ground_get_functions() { \ + return ground_lib_registry.get_function_names(); \ + } \ + void* ground_get_function(const char* name) { \ + return ground_lib_registry.get_function(name); \ + } \ + } + +// Optional initialization macro +#define GROUND_LIBRARY_INIT() \ + extern "C" { \ + void ground_lib_init() { + +#define GROUND_LIBRARY_INIT_END() \ + } \ + } + +// Optional cleanup macro +#define GROUND_LIBRARY_CLEANUP() \ + extern "C" { \ + void ground_lib_cleanup() { + +#define GROUND_LIBRARY_CLEANUP_END() \ + } \ + } + +// Utility function to print GroundValue for debugging +inline void debug_print_ground_value(const GroundValue& v) { + switch (v.type) { + case GROUND_INT: + std::cout << "INT: " << v.data.int_val << std::endl; + break; + case GROUND_DOUBLE: + std::cout << "DOUBLE: " << v.data.double_val << std::endl; + break; + case GROUND_BOOL: + std::cout << "BOOL: " << (v.data.bool_val ? "true" : "false") << std::endl; + break; + case GROUND_STRING: + std::cout << "STRING: " << (v.data.string_val ? v.data.string_val : "(null)") << std::endl; + break; + case GROUND_CHAR: + std::cout << "CHAR: '" << v.data.char_val << "'" << std::endl; + break; + } +} + +#endif // GROUND_LIB_H diff --git a/extlibs/request.cpp b/extlibs/request/request.cpp similarity index 73% rename from extlibs/request.cpp rename to extlibs/request/request.cpp index 26eb206..4a1bdf4 100644 --- a/extlibs/request.cpp +++ b/extlibs/request/request.cpp @@ -4,12 +4,17 @@ #include #include +void error(std::string status) { + std::cout << "Request error: " << status << std::endl; +} + GroundValue simpleRequest(GroundValue* args, int arg_count) { VALIDATE_ARGS_1(GROUND_STRING); cpr::Response r = cpr::Get(cpr::Url(GET_STRING(args[0]))); if (!(r.status_code >= 200 && r.status_code < 300)) { + error("Non zero HTTP code " + std::to_string(r.status_code)); return ground_string_val("Error code " + std::to_string(r.status_code)); } @@ -24,10 +29,12 @@ GroundValue saveContents(GroundValue* args, int arg_count) { if (file.good()) { cpr::Response r = cpr::Download(file, cpr::Url{GET_STRING(args[0])}); - if (r.status_code >= 200 && r.status_code < 300) { + if (!(r.status_code >= 200 && r.status_code < 300)) { + error("Non zero HTTP code " + std::to_string(r.status_code)); return GROUND_BOOL_VAL(false); } } else { + error(std::string("Cannot open file ") + GET_STRING(args[1]) + " for writing"); return GROUND_BOOL_VAL(false); }