diff --git a/extlibs/file.cpp b/extlibs/file.cpp new file mode 100644 index 0000000..285fa6c --- /dev/null +++ b/extlibs/file.cpp @@ -0,0 +1,43 @@ +#include "ground_lib.h" + +#include +#include + +GroundValue readFile(GroundValue* args, int arg_count) { + VALIDATE_ARGS_1(GROUND_STRING); + + std::ifstream ffile(GET_STRING(args[0])); + + std::string tmp; + std::string out; + + while (std::getline(ffile, tmp)) { + out += tmp + "\n"; + } + + return ground_string_val(out); +} + +GroundValue writeFile(GroundValue* args, int arg_count) { + VALIDATE_ARGS_2(GROUND_STRING, GROUND_STRING); + + std::ofstream file(GET_STRING(args[0])); + if (file.good()) { + file << GET_STRING(args[1]); + } else { + std::cout << "File isn't good for writing in" << std::endl; + return GROUND_BOOL_VAL(false); + } + + return GROUND_BOOL_VAL(true); +} + +GROUND_LIBRARY_INTERFACE() + +GROUND_LIBRARY_INIT() + REGISTER_GROUND_FUNCTION(readFile); + REGISTER_GROUND_FUNCTION(writeFile); +GROUND_LIBRARY_INIT_END() + +GROUND_LIBRARY_CLEANUP() +GROUND_LIBRARY_CLEANUP_END() diff --git a/extlibs/ground_lib.h b/extlibs/ground_lib.h new file mode 100644 index 0000000..7567cf1 --- /dev/null +++ b/extlibs/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.cpp new file mode 100644 index 0000000..26eb206 --- /dev/null +++ b/extlibs/request.cpp @@ -0,0 +1,45 @@ +#include "ground_lib.h" + +#include +#include +#include + +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)) { + return ground_string_val("Error code " + std::to_string(r.status_code)); + } + + return ground_string_val(r.text); +} + +GroundValue saveContents(GroundValue* args, int arg_count) { + VALIDATE_ARGS_2(GROUND_STRING, GROUND_STRING); + + std::ofstream file(GET_STRING(args[1]), std::ios::binary); + + if (file.good()) { + cpr::Response r = cpr::Download(file, cpr::Url{GET_STRING(args[0])}); + + if (r.status_code >= 200 && r.status_code < 300) { + return GROUND_BOOL_VAL(false); + } + } else { + return GROUND_BOOL_VAL(false); + } + + return GROUND_BOOL_VAL(true); +} + +GROUND_LIBRARY_INTERFACE() + +GROUND_LIBRARY_INIT() + REGISTER_GROUND_FUNCTION(simpleRequest); + REGISTER_GROUND_FUNCTION(saveContents); +GROUND_LIBRARY_INIT_END() + +GROUND_LIBRARY_CLEANUP() +GROUND_LIBRARY_CLEANUP_END()