From b3d1cd4e32130e0e67aed751cbafbff77d7d4862 Mon Sep 17 00:00:00 2001 From: Nathanne Isip Date: Thu, 21 Nov 2024 11:49:36 +0800 Subject: [PATCH] Implemented mathematics-related functions on the N8 standard library. --- std/N8Library.hpp | 38 ++ std/n8std/IO.cc | 492 +++++++++++++++++++++++ std/n8std/IO.hpp | 62 +++ std/n8std/ML.cc | 216 ++++++++++ std/n8std/ML.hpp | 41 ++ std/n8std/Math.cc | 909 ++++++++++++++++++++++++++++++++++++++++++ std/n8std/Math.hpp | 111 ++++++ std/n8std/Reflect.cc | 137 +++++++ std/n8std/Reflect.hpp | 45 +++ 9 files changed, 2051 insertions(+) create mode 100644 std/N8Library.hpp create mode 100644 std/n8std/IO.cc create mode 100644 std/n8std/IO.hpp create mode 100644 std/n8std/ML.cc create mode 100644 std/n8std/ML.hpp create mode 100644 std/n8std/Math.cc create mode 100644 std/n8std/Math.hpp create mode 100644 std/n8std/Reflect.cc create mode 100644 std/n8std/Reflect.hpp diff --git a/std/N8Library.hpp b/std/N8Library.hpp new file mode 100644 index 0000000..4aa3afb --- /dev/null +++ b/std/N8Library.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 - Nathanne Isip + * This file is part of N8. + * + * N8 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. + * + * N8 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 N8. If not, see . + */ + +#ifndef N8_STD_LIB_CC +#define N8_STD_LIB_CC + +#include +#include +#include + +#include + +#define N8_LIB_START extern "C" { +#define N8_LIB_END } + +#define N8_FUNC(funcName) \ + DynamicObject funcName( \ + std::shared_ptr address, \ + SymbolTable& symtab, \ + std::vector& args \ + ) + +#endif diff --git a/std/n8std/IO.cc b/std/n8std/IO.cc new file mode 100644 index 0000000..6287d51 --- /dev/null +++ b/std/n8std/IO.cc @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2024 - Nathanne Isip + * This file is part of N8. + * + * N8 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. + * + * N8 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 N8. If not, see . + */ + +#include "n8std/IO.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +#else +# include +# include +#endif + +N8_FUNC(io_print) { + if(args.size() == 0) + return {}; + + for(size_t i = 0; i < args.size(); i++) { + DynamicObject arg = args.at(i); + std::cout << arg.toString(); + } + + return {}; +} + +N8_FUNC(io_printLine) { + if(args.size() == 0) + return {}; + + for(size_t i = 0; i < args.size(); i++) { + DynamicObject arg = args.at(i); + std::cout << arg.toString() << std::endl; + } + + return {}; +} + +N8_FUNC(io_readString) { + std::string str; + std::getline(std::cin, str); + + return DynamicObject(str); +} + +N8_FUNC(io_readNumber) { + std::string str; + std::getline(std::cin, str); + + return DynamicObject(::atof(str.c_str())); +} + +N8_FUNC(io_readBoolean) { + std::string str; + std::getline(std::cin, str); + + bool boolVal = false; + if(str == "true") + boolVal = true; + + return DynamicObject(boolVal); +} + +N8_FUNC(io_fileRead) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject fileName = args.at(0); + std::ifstream file(fileName.toString()); + std::vector returnValues; + + if(!file) { + returnValues.emplace_back(DynamicObject()); + returnValues.emplace_back( + DynamicObject("Error: Could not open the file " + fileName.toString()) + ); + + return DynamicObject(std::make_shared>(returnValues)); + } + + std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + returnValues.emplace_back(content); + returnValues.emplace_back(DynamicObject()); + + return DynamicObject(std::make_shared>(returnValues)); +} + +N8_FUNC(io_fileWrite) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject fileName = args.at(0), + fileContent = args.at(1); + + std::ofstream file(fileName.toString()); + if(!file) + return DynamicObject(false); + + file << fileContent.toString(); + return DynamicObject(true); +} + +N8_FUNC(io_fileSize) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject fileName = args.at(0); + std::ifstream file(fileName.toString(), std::ios::binary | std::ios::ate); + std::vector returnValues; + + if(!file) { + returnValues.emplace_back(DynamicObject(0.0)); + returnValues.emplace_back(DynamicObject(false)); + + return DynamicObject(std::make_shared>(returnValues)); + } + + returnValues.emplace_back(DynamicObject( + static_cast(file.tellg()) + )); + returnValues.emplace_back(DynamicObject(true)); + return DynamicObject(std::make_shared>(returnValues)); +} + +N8_FUNC(io_filePerms) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject fileName = args.at(0); + std::vector returnValues; + + #ifdef _WIN32 + + DWORD attributes = GetFileAttributes(fileName.toString().c_str()); + if(attributes == INVALID_FILE_ATTRIBUTES) { + returnValues.emplace_back(DynamicObject(0.0)); + returnValues.emplace_back(DynamicObject("Could not get file attributes")); + + return DynamicObject(std::make_shared>(returnValues)); + } + + unsigned int permissions = 0; + if(attributes & FILE_ATTRIBUTE_READONLY) + permissions |= 0x01; + else permissions |= 0x02; + + #else + + struct stat fileStat; + if(stat(fileName.toString().c_str(), &fileStat) != 0) { + returnValues.emplace_back(DynamicObject(0.0)); + returnValues.emplace_back(DynamicObject("Could not get file status")); + + return DynamicObject(std::make_shared>(returnValues)); + } + + unsigned int permissions = 0; + permissions |= (fileStat.st_mode & S_IRUSR) ? 0x0100 : 0; + permissions |= (fileStat.st_mode & S_IWUSR) ? 0x0200 : 0; + permissions |= (fileStat.st_mode & S_IXUSR) ? 0x0400 : 0; + permissions |= (fileStat.st_mode & S_IRGRP) ? 0x0010 : 0; + permissions |= (fileStat.st_mode & S_IWGRP) ? 0x0020 : 0; + permissions |= (fileStat.st_mode & S_IXGRP) ? 0x0040 : 0; + permissions |= (fileStat.st_mode & S_IROTH) ? 0x0001 : 0; + permissions |= (fileStat.st_mode & S_IWOTH) ? 0x0002 : 0; + permissions |= (fileStat.st_mode & S_IXOTH) ? 0x0004 : 0; + + #endif + + returnValues.emplace_back(DynamicObject(static_cast(permissions))); + returnValues.emplace_back(DynamicObject()); + + return DynamicObject(std::make_shared>(returnValues)); +} + +N8_FUNC(io_fileCreationDate) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject fileName = args.at(0); + std::vector returnValues; + std::filesystem::path filePath(fileName.toString().c_str()); + + if(!std::filesystem::exists(filePath)) { + returnValues.emplace_back(DynamicObject(0.0)); + returnValues.emplace_back(DynamicObject("File does not exist")); + } + else { + auto creationTime = std::filesystem::last_write_time(filePath); + auto sctp = std::chrono::time_point_cast( + creationTime - std::filesystem::file_time_type::clock::now() + + std::chrono::system_clock::now() + ); + auto ctime = std::chrono::system_clock::to_time_t(sctp); + + returnValues.emplace_back(DynamicObject(0.0)); + returnValues.emplace_back(DynamicObject( + static_cast(ctime) + )); + } + + return DynamicObject(std::make_shared>( + returnValues + )); +} + +N8_FUNC(io_fileDelete) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject fileName = args.at(0); + std::vector returnValues; + + #ifdef _WIN32 + + if(DeleteFile(fileName.toString().c_str())) { + returnValues.emplace_back(DynamicObject(true)); + returnValues.emplace_back(DynamicObject()); + } + else { + returnValues.emplace_back(DynamicObject(false)); + returnValues.emplace_back(DynamicObject("Could not delete file")); + } + + #else + + struct stat buffer; + if(stat(fileName.toString().c_str(), &buffer) == 0) { + if(remove(fileName.toString().c_str()) == 0) { + returnValues.emplace_back(DynamicObject(true)); + returnValues.emplace_back(DynamicObject()); + } + else { + returnValues.emplace_back(DynamicObject(false)); + returnValues.emplace_back(DynamicObject("Could not delete file")); + } + } + else { + returnValues.emplace_back(DynamicObject(false)); + returnValues.emplace_back(DynamicObject("File does not exist")); + } + + #endif + + return DynamicObject(std::make_shared>(returnValues)); +} + +N8_FUNC(io_folderCreate) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject folderName = args.at(0); + std::vector returnValues; + std::filesystem::path folderPath(folderName.toString().c_str()); + + if(std::filesystem::exists(folderPath)) { + returnValues.emplace_back(DynamicObject(false)); + returnValues.emplace_back(DynamicObject("Folder already exists")); + + return DynamicObject(std::make_shared>(returnValues)); + } + + if(std::filesystem::create_directory(folderPath)) { + returnValues.emplace_back(DynamicObject(true)); + returnValues.emplace_back(DynamicObject()); + + return DynamicObject(std::make_shared>(returnValues)); + } + + returnValues.emplace_back(DynamicObject(false)); + returnValues.emplace_back(DynamicObject("Could not create folder")); + + return DynamicObject(std::make_shared>(returnValues)); +} + +N8_FUNC(io_folderSize) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject folderName = args.at(0); + std::vector returnValues; + std::filesystem::path path(folderName.toString().c_str()); + unsigned long long totalSize = 0; + + if(!std::filesystem::exists(path) || !std::filesystem::is_directory(path)) { + returnValues.emplace_back(DynamicObject(0.0)); + returnValues.emplace_back(DynamicObject( + "Provided path is not a directory or does not exist" + )); + + return DynamicObject(std::make_shared>(returnValues)); + } + + #pragma omp parallel for + for(const auto& entry : std::filesystem::recursive_directory_iterator(path)) + if(std::filesystem::is_regular_file(entry)) + totalSize += std::filesystem::file_size(entry); + + returnValues.emplace_back(DynamicObject(static_cast(totalSize))); + returnValues.emplace_back(DynamicObject()); + + return DynamicObject(std::make_shared>(returnValues)); +} + +N8_FUNC(io_folderCreationDate) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject folderName = args.at(0); + std::vector returnValues; + std::filesystem::path filePath(folderName.toString().c_str()); + + if(!std::filesystem::exists(filePath)) { + returnValues.emplace_back(DynamicObject(0.0)); + returnValues.emplace_back(DynamicObject("File does not exist")); + } + else { + auto creationTime = std::filesystem::last_write_time(filePath); + auto sctp = std::chrono::time_point_cast( + creationTime - std::filesystem::file_time_type::clock::now() + + std::chrono::system_clock::now() + ); + auto ctime = std::chrono::system_clock::to_time_t(sctp); + + returnValues.emplace_back(DynamicObject(0.0)); + returnValues.emplace_back(DynamicObject( + static_cast(ctime) + )); + } + + return DynamicObject(std::make_shared>(returnValues)); +} + +N8_FUNC(io_folderDelete) { + if(args.empty() || args.size() >= 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 or 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject folderName = args.at(0); + std::vector returnValues; + std::filesystem::path filePath(folderName.toString().c_str()); + + bool isRecursize = false; + if(args.size() == 2) { + DynamicObject recArg = args.at(1); + isRecursize = recArg.booleanEquivalent(); + } + + if(!std::filesystem::exists(filePath) || !std::filesystem::is_directory(filePath)) + return DynamicObject(false); + + if(isRecursize) + std::filesystem::remove_all(filePath); + else std::filesystem::remove(filePath); + + return DynamicObject(true); +} + +N8_FUNC(io_isFile) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject fileName = args.at(0); + std::filesystem::path filePath(fileName.toString().c_str()); + + return DynamicObject( + std::filesystem::exists(filePath) && + std::filesystem::is_regular_file(filePath) + ); +} + +N8_FUNC(io_isFolder) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject fileName = args.at(0); + std::filesystem::path filePath(fileName.toString().c_str()); + + return DynamicObject( + std::filesystem::exists(filePath) && + std::filesystem::is_directory(filePath) + ); +} + +N8_FUNC(io_listAllFiles) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject fileName = args.at(0); + std::filesystem::path dirPath(fileName.toString().c_str()); + std::vector returnValues; + + if(!std::filesystem::exists(dirPath) || !std::filesystem::is_directory(dirPath)) + return DynamicObject(); + + #pragma omp parallel for + for(const auto& entry : std::filesystem::directory_iterator(dirPath)) + returnValues.emplace_back(DynamicObject( + entry.path().filename().string() + )); + + return DynamicObject(std::make_shared>(returnValues)); +} + +N8_FUNC(io_exit) { + if(args.size() == 0) + exit(0); + + DynamicObject exitCode = args.at(0); + if(!exitCode.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Exit code is not a number." + ); + + exit(static_cast(exitCode.getNumber())); + return {}; +} diff --git a/std/n8std/IO.hpp b/std/n8std/IO.hpp new file mode 100644 index 0000000..4e8fe3b --- /dev/null +++ b/std/n8std/IO.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 - Nathanne Isip + * This file is part of N8. + * + * N8 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. + * + * N8 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 N8. If not, see . + */ + +#ifndef N8_STDLIB_IO_CC +#define N8_STDLIB_IO_CC + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#endif + +#include "N8Library.hpp" + +N8_LIB_START + +N8_FUNC(io_print); +N8_FUNC(io_printLine); + +N8_FUNC(io_readString); +N8_FUNC(io_readNumber); +N8_FUNC(io_readBoolean); + +N8_FUNC(io_fileRead); +N8_FUNC(io_fileWrite); +N8_FUNC(io_fileSize); +N8_FUNC(io_filePerms); +N8_FUNC(io_fileCreationDate); +N8_FUNC(io_fileDelete); + +N8_FUNC(io_folderCreate); +N8_FUNC(io_folderSize); +N8_FUNC(io_folderCreationDate); +N8_FUNC(io_folderDelete); + +N8_FUNC(io_isFile); +N8_FUNC(io_isFolder); +N8_FUNC(io_listAllFiles); + +N8_FUNC(io_exit); + +N8_LIB_END + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#endif diff --git a/std/n8std/ML.cc b/std/n8std/ML.cc new file mode 100644 index 0000000..17b8b76 --- /dev/null +++ b/std/n8std/ML.cc @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2024 - Nathanne Isip + * This file is part of N8. + * + * N8 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. + * + * N8 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 N8. If not, see . + */ + +#include "n8std/ML.hpp" + +#include + +#include +#include +#include + +static inline std::vector arrayToDoubleVector( + std::shared_ptr address, + std::vector array +) { + std::vector values(array.size()); + + #pragma omp parallel for + for(size_t i = 0; i < array.size(); i++) { + if(!array[i].isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Value from array is not a number" + ); + + values[i] = array[i].getNumber(); + } + + return values; +} + +static inline double calculateMean( + std::shared_ptr address, + std::vector array +) { + std::vector values = arrayToDoubleVector( + std::move(address), + array + ); + size_t arraySize = array.size(); + double sum = 0.0; + + #pragma omp parallel for reduction(+:sum) + for(size_t i = 0; i < arraySize; i++) + sum += values[i]; + + return sum / arraySize; +} + +N8_FUNC(ml_trendline_calculate) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject xObj = args.at(0), + yObj = args.at(1); + + if(!xObj.isArray() || !yObj.isArray()) + throw TerminativeThrowSignal( + std::move(address), + "Parameter x and y must be both number array" + ); + + std::vector xObjArray = *xObj.getArray(); + std::vector yObjArray = *yObj.getArray(); + + if(xObjArray.size() != yObjArray.size()) + throw TerminativeThrowSignal( + std::move(address), + "Data set size of x and y did not match" + ); + + double x = calculateMean(std::move(address), xObjArray), + y = calculateMean(std::move(address), yObjArray), + numerator = 0.0, + denominator = 0.0; + + #pragma omp parallel for + for(size_t i = 0; i < xObjArray.size(); i++) { + numerator += (xObjArray[i].getNumber() - x) * + (yObjArray[i].getNumber() - y); + + denominator += (xObjArray[i].getNumber() - x) * + (xObjArray[i].getNumber() - x); + } + + double slope = numerator / denominator; + std::vector returnValues; + + returnValues.emplace_back(DynamicObject(slope)); + returnValues.emplace_back(DynamicObject(y - slope * x)); + + return DynamicObject(std::make_shared>( + returnValues + )); +} + +N8_FUNC(ml_trendline_calculateRmse) { + if(args.size() != 3) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 3 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject xObj = args.at(0), + yObj = args.at(1), + model = args.at(2); + std::vector regModel = *model.getArray(); + + if(!model.isArray() || regModel.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Invalid linear regression model" + ); + + if(!xObj.isArray() || !yObj.isArray()) + throw TerminativeThrowSignal( + std::move(address), + "Parameter x and y must be both number array" + ); + + std::vector xObjArray = *xObj.getArray(); + std::vector yObjArray = *yObj.getArray(); + + if(xObjArray.size() != yObjArray.size()) + throw TerminativeThrowSignal( + std::move(address), + "Data set size of x and y did not match" + ); + + double sumSquaredErrs = 0.0; + size_t paramSize = xObjArray.size(); + + #pragma omp parallel for + for(size_t i = 0; i < paramSize; i++) { + std::vector model; + model.emplace_back(regModel.at(0)); + model.emplace_back(regModel.at(1)); + + std::vector params; + params.emplace_back(DynamicObject( + std::make_shared>(model) + )); + params.emplace_back(xObjArray[i]); + + double yPred = ml_trendline_predict( + std::move(address), + symtab, + params + ).getNumber(); + double error = yObjArray[i].getNumber() - yPred; + + sumSquaredErrs += error * error; + } + + return DynamicObject(std::sqrt(sumSquaredErrs / paramSize)); +} + +N8_FUNC(ml_trendline_predict) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 3 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject model = args.at(0), + value = args.at(1); + std::vector regModel = *model.getArray(); + + if(!model.isArray() || regModel.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Invalid linear regression model" + ); + + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Cannot predict linear regression value for non-numbers" + ); + + DynamicObject slope = regModel.at(0), + intercept = regModel.at(1); + + if(!slope.isNumber() || !intercept.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Linear regression model's slope and intercept must be a number" + ); + + return DynamicObject( + slope.getNumber() * + value.getNumber() + + intercept.getNumber() + ); +} diff --git a/std/n8std/ML.hpp b/std/n8std/ML.hpp new file mode 100644 index 0000000..303e120 --- /dev/null +++ b/std/n8std/ML.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 - Nathanne Isip + * This file is part of N8. + * + * N8 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. + * + * N8 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 N8. If not, see . + */ + +#ifndef N8_STDLIB_ML_CC +#define N8_STDLIB_ML_CC + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#endif + +#include "N8Library.hpp" + +N8_LIB_START + +N8_FUNC(ml_trendline_calculate); +N8_FUNC(ml_trendline_calculateRmse); +N8_FUNC(ml_trendline_predict); + +N8_LIB_END + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#endif diff --git a/std/n8std/Math.cc b/std/n8std/Math.cc new file mode 100644 index 0000000..cd27191 --- /dev/null +++ b/std/n8std/Math.cc @@ -0,0 +1,909 @@ +/* + * Copyright (c) 2024 - Nathanne Isip + * This file is part of N8. + * + * N8 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. + * + * N8 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 N8. If not, see . + */ + +#include "n8std/Math.hpp" + +#include + +#include +#include +#include + +N8_FUNC(math_cos) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(cos(value.getNumber())); +} + +N8_FUNC(math_cosh) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(cosh(value.getNumber())); +} + +N8_FUNC(math_sin) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(sin(value.getNumber())); +} + +N8_FUNC(math_sinh) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(sinh(value.getNumber())); +} + +N8_FUNC(math_tan) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(tan(value.getNumber())); +} + +N8_FUNC(math_tanh) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(tanh(value.getNumber())); +} + +N8_FUNC(math_acos) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(acos(value.getNumber())); +} + +N8_FUNC(math_acosh) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(acosh(value.getNumber())); +} + +N8_FUNC(math_asin) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(asin(value.getNumber())); +} + +N8_FUNC(math_asinh) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(asinh(value.getNumber())); +} + +N8_FUNC(math_atan) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(atan(value.getNumber())); +} + +N8_FUNC(math_atan2) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject y = args.at(0), + x = args.at(1); + if(!y.isNumber() || !x.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + return DynamicObject(atan2(y.getNumber(), x.getNumber())); +} + +N8_FUNC(math_atanh) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(atanh(value.getNumber())); +} + +N8_FUNC(math_rand) { + std::uniform_real_distribution<> dis(0.0, 1.0); + double value = 0.0f; + + if(args.size() == 1) { + DynamicObject arg = args.at(0); + if(!arg.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Expecting a number argument." + ); + + std::mt19937 gen(arg.getNumber()); + value = dis(gen); + } + else { + std::random_device rd; + std::mt19937 gen(rd()); + + value = dis(gen); + } + + return DynamicObject(value); +} + +N8_FUNC(math_pow) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject x = args.at(0), + y = args.at(1); + if(!x.isNumber() || !y.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + return DynamicObject(pow(x.getNumber(), y.getNumber())); +} + +N8_FUNC(math_pow2) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(exp2(value.getNumber())); +} + +N8_FUNC(math_log) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(log(value.getNumber())); +} + +N8_FUNC(math_log10) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(log10(value.getNumber())); +} + +N8_FUNC(math_log1p) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(log1p(value.getNumber())); +} + +N8_FUNC(math_log2) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(log2(value.getNumber())); +} + +N8_FUNC(math_exp) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(exp(value.getNumber())); +} + +N8_FUNC(math_splitExponent) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + int n; + double param = value.getNumber(), + result = frexp(param, &n); + + std::vector returnValues; + returnValues.emplace_back(DynamicObject(result)); + returnValues.emplace_back(DynamicObject(static_cast(n))); + + return DynamicObject(std::make_shared>( + returnValues + )); +} + +N8_FUNC(math_combineExponent) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject x = args.at(0), + y = args.at(1); + if(!x.isNumber() || !y.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + return DynamicObject(ldexp( + x.getNumber(), + static_cast(y.getNumber()) + )); +} + +N8_FUNC(math_extractExponent) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(logb(value.getNumber())); +} + +N8_FUNC(math_scaleByExponent) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject x = args.at(0), + y = args.at(1); + if(!x.isNumber() || !y.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + return DynamicObject(scalbn( + x.getNumber(), + static_cast(y.getNumber()) + )); +} + +N8_FUNC(math_squareRoot) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(sqrt(value.getNumber())); +} + +N8_FUNC(math_cubicRoot) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(cbrt(value.getNumber())); +} + +N8_FUNC(math_inverseSqrt) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + // Based on the Quake III Fast Inversed Square Root Algorithm + union { + float f; + uint32_t i; + } conv; + + float x2, number = value.getNumber(); + const float threehalfs = 1.5F; + + x2 = number * 0.5F; + conv.f = number; + conv.i = 0x5f3759df - (conv.i >> 1); + conv.f = conv.f * (threehalfs - (x2 * conv.f * conv.f)); + + return DynamicObject(conv.f); +} + +N8_FUNC(math_hypotenuse) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject x = args.at(0), + y = args.at(1); + if(!x.isNumber() || !y.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + return DynamicObject(hypot( + x.getNumber(), + y.getNumber() + )); +} + +N8_FUNC(math_ceil) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(ceil(value.getNumber())); +} + +N8_FUNC(math_floor) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(floor(value.getNumber())); +} + +N8_FUNC(math_round) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(round(value.getNumber())); +} + +N8_FUNC(math_dim) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject x = args.at(0), + y = args.at(1); + if(!x.isNumber() || !y.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + return DynamicObject(fdim( + x.getNumber(), + y.getNumber() + )); +} + +N8_FUNC(math_min) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject x = args.at(0), + y = args.at(1); + if(!x.isNumber() || !y.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + return DynamicObject(fmin( + x.getNumber(), + y.getNumber() + )); +} + +N8_FUNC(math_max) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject x = args.at(0), + y = args.at(1); + if(!x.isNumber() || !y.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + return DynamicObject(fmax( + x.getNumber(), + y.getNumber() + )); +} + +N8_FUNC(math_errorFunc) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(erf(value.getNumber())); +} + +N8_FUNC(math_errorFuncComp) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(erfc(value.getNumber())); +} + +N8_FUNC(math_remainder) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject x = args.at(0), + y = args.at(1); + if(!x.isNumber() || !y.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + return DynamicObject(remainder( + x.getNumber(), + y.getNumber() + )); +} + +N8_FUNC(math_remQuotient) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject x = args.at(0), + y = args.at(1); + if(!x.isNumber() || !y.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + int n; + double xn = x.getNumber(), + yn = y.getNumber(), + result = remquo(xn, yn, &n); + + std::vector returnValues; + returnValues.emplace_back(DynamicObject(result)); + returnValues.emplace_back(DynamicObject(static_cast(n))); + + return DynamicObject(std::make_shared>( + returnValues + )); +} + +N8_FUNC(math_abs) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(fabs(value.getNumber())); +} + +N8_FUNC(math_fusedMultiplyAdd) { + if(args.size() != 3) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 3 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject x = args.at(0), + y = args.at(1), + z = args.at(2); + if(!x.isNumber() || !y.isNumber() || !z.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "One of the argument type is not of number." + ); + + return DynamicObject(fma( + x.getNumber(), + y.getNumber(), + z.getNumber() + )); +} + +N8_FUNC(math_sigmoid) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + return DynamicObject(1 / (1 + exp(-value.getNumber()))); +} + +N8_FUNC(math_sigmoidDerivative) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject value = args.at(0); + if(!value.isNumber()) + throw TerminativeThrowSignal( + std::move(address), + "Argument type is not of number." + ); + + double num = value.getNumber(); + return DynamicObject(num * (1 - num)); +} + +N8_FUNC(math_step); + +N8_FUNC(math_relu); + +N8_FUNC(math_leakyRelu); + +N8_FUNC(math_elu); + +N8_FUNC(math_selu); + +N8_FUNC(math_softmax); + +N8_FUNC(math_swish); + +N8_FUNC(math_mish); + +N8_FUNC(math_hardSigmoid); + +N8_FUNC(math_hardTan); + +N8_FUNC(math_softplus); + +N8_FUNC(math_softsign); + +N8_FUNC(math_gaussian); + +N8_FUNC(math_bentIdentity); + +N8_FUNC(math_logLogistic); diff --git a/std/n8std/Math.hpp b/std/n8std/Math.hpp new file mode 100644 index 0000000..4a1a421 --- /dev/null +++ b/std/n8std/Math.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2024 - Nathanne Isip + * This file is part of N8. + * + * N8 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. + * + * N8 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 N8. If not, see . + */ + +#ifndef N8_STDLIB_MATH_CC +#define N8_STDLIB_MATH_CC + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#endif + +#include "N8Library.hpp" + +N8_LIB_START + +N8_FUNC(math_cos); +N8_FUNC(math_cosh); + +N8_FUNC(math_sin); +N8_FUNC(math_sinh); + +N8_FUNC(math_tan); +N8_FUNC(math_tanh); + +N8_FUNC(math_acos); +N8_FUNC(math_acosh); + +N8_FUNC(math_asin); +N8_FUNC(math_asinh); + +N8_FUNC(math_atan); +N8_FUNC(math_atan2); +N8_FUNC(math_atanh); + +N8_FUNC(math_rand); + +N8_FUNC(math_pow); +N8_FUNC(math_pow2); + +N8_FUNC(math_log); +N8_FUNC(math_log10); +N8_FUNC(math_log1p); +N8_FUNC(math_log2); + +N8_FUNC(math_exp); +N8_FUNC(math_splitExponent); +N8_FUNC(math_combineExponent); +N8_FUNC(math_extractExponent); +N8_FUNC(math_scaleByExponent); + +N8_FUNC(math_squareRoot); +N8_FUNC(math_cubicRoot); +N8_FUNC(math_inverseSqrt); +N8_FUNC(math_hypotenuse); + +N8_FUNC(math_ceil); +N8_FUNC(math_floor); +N8_FUNC(math_round); + +N8_FUNC(math_dim); +N8_FUNC(math_min); +N8_FUNC(math_max); + +N8_FUNC(math_errorFunc); +N8_FUNC(math_errorFuncComp); + +N8_FUNC(math_remainder); +N8_FUNC(math_remQuotient); +N8_FUNC(math_abs); +N8_FUNC(math_fusedMultiplyAdd); + +N8_FUNC(math_sigmoid); +N8_FUNC(math_sigmoidDerivative); +N8_FUNC(math_step); +N8_FUNC(math_relu); +N8_FUNC(math_leakyRelu); +N8_FUNC(math_elu); +N8_FUNC(math_selu); +N8_FUNC(math_softmax); +N8_FUNC(math_swish); +N8_FUNC(math_mish); +N8_FUNC(math_hardSigmoid); +N8_FUNC(math_hardTan); +N8_FUNC(math_softplus); +N8_FUNC(math_softsign); +N8_FUNC(math_gaussian); +N8_FUNC(math_bentIdentity); +N8_FUNC(math_logLogistic); + +N8_LIB_END + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#endif diff --git a/std/n8std/Reflect.cc b/std/n8std/Reflect.cc new file mode 100644 index 0000000..1c73f67 --- /dev/null +++ b/std/n8std/Reflect.cc @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2024 - Nathanne Isip + * This file is part of N8. + * + * N8 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. + * + * N8 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 N8. If not, see . + */ + +#include "n8std/Reflect.hpp" + +#include +#include + +N8_FUNC(reflect_get) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject name = args.at(0); + const std::string symName = name.toString(); + + return symtab.getSymbol( + std::move(address), + symName + ); +} + +N8_FUNC(reflect_has) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject name = args.at(0); + const std::string symName = name.toString(); + + return DynamicObject(symtab.hasSymbol(symName)); +} + +N8_FUNC(reflect_type) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject name = args.at(0); + const std::string symName = name.toString(); + + return symtab.getSymbol( + std::move(address), + symName + ).objectType(); +} + +N8_FUNC(reflect_declare) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject name = args.at(0), + value = args.at(1); + + std::string symName = name.toString(); + if(Tokenizer::isValidIdentifier(symName)) + throw TerminativeThrowSignal( + std::move(address), + "Invalid identifier string: " + + symName + ); + + symtab.setSymbol(symName, value); + return value; +} + +N8_FUNC(reflect_delete) { + if(args.size() != 1) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 1 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject name = args.at(0); + symtab.removeSymbol(name.toString()); + + return DynamicObject(); +} + +N8_FUNC(reflect_invoke) { + if(args.size() != 2) + throw TerminativeThrowSignal( + std::move(address), + "Expecting 2 argument, got " + + std::to_string(args.size()) + ); + + DynamicObject name = args.at(0), + params = args.at(1); + + if(!params.isArray()) + throw TerminativeThrowSignal( + std::move(address), + "Parameters must be of array type" + ); + + const std::string symName = name.toString(); + DynamicObject callable = symtab.getSymbol( + std::move(address), + symName + ); + + return callable.callFromNative( + std::move(address), + symtab, + *params.getArray() + ); +} diff --git a/std/n8std/Reflect.hpp b/std/n8std/Reflect.hpp new file mode 100644 index 0000000..1259567 --- /dev/null +++ b/std/n8std/Reflect.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 - Nathanne Isip + * This file is part of N8. + * + * N8 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. + * + * N8 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 N8. If not, see . + */ + +#ifndef N8_STDLIB_REFLECT_CC +#define N8_STDLIB_REFLECT_CC + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#endif + +#include "N8Library.hpp" + +N8_LIB_START + +N8_FUNC(reflect_get); +N8_FUNC(reflect_has); +N8_FUNC(reflect_type); + +N8_FUNC(reflect_declare); +N8_FUNC(reflect_delete); +N8_FUNC(reflect_invoke); + +N8_LIB_END + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#endif