diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4a2c2c4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "byond-extools/src/joao/joao_repo"] + path = byond-extools/src/joao/joao_repo + url = https://github.com/Altoids1/Joao.git diff --git a/byond-extools/CMakeLists.txt b/byond-extools/CMakeLists.txt index cb42f2a..61dfa77 100644 --- a/byond-extools/CMakeLists.txt +++ b/byond-extools/CMakeLists.txt @@ -15,19 +15,20 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(SRC_DIR ${CMAKE_SOURCE_DIR}/src) if (WIN32) - file(GLOB_RECURSE SRC_FILES + file(GLOB_RECURSE SRC_FILES LIST_DIRECTORIES true "${SRC_DIR}/*.cpp" "${SRC_DIR}/*.h" "${SRC_DIR}/*.hpp" "${SRC_DIR}/*.s" ) else() - file(GLOB_RECURSE SRC_FILES + file(GLOB_RECURSE SRC_FILES LIST_DIRECTORIES true "${SRC_DIR}/*.cpp" "${SRC_DIR}/*.h" "${SRC_DIR}/*.hpp" ) endif() +list(FILTER SRC_FILES EXCLUDE REGEX ".*joao_repo.*") ## For Joćo, so we can do some manual inclusion in joao.cpp set(SRC_FILES ${SRC_FILES} "${SRC_DIR}/third_party/subhook/subhook.c") add_library(byond-extools SHARED ${SRC_FILES}) diff --git a/byond-extools/src/core/byond_structures.cpp b/byond-extools/src/core/byond_structures.cpp index 56e49ae..83c45cf 100644 --- a/byond-extools/src/core/byond_structures.cpp +++ b/byond-extools/src/core/byond_structures.cpp @@ -116,6 +116,11 @@ void Value::set(std::string name, Value newvalue) SetVariable(type, value, Core::GetStringId(name), newvalue); } +void Value::set_by_id(int id, Value newvalue) +{ + SetVariable(type, value, id, newvalue); +} + ManagedValue Value::invoke(std::string name, std::vector args, Value usr) { std::replace(name.begin(), name.end(), '_', ' '); diff --git a/byond-extools/src/core/byond_structures.h b/byond-extools/src/core/byond_structures.h index 42273c0..e4956e2 100644 --- a/byond-extools/src/core/byond_structures.h +++ b/byond-extools/src/core/byond_structures.h @@ -134,6 +134,7 @@ struct Value std::unordered_map get_all_vars(); bool has_var(std::string name); void set(std::string name, Value value); + void set_by_id(int id, Value newvalue); }; struct ManagedValue : Value diff --git a/byond-extools/src/joao/joao.cpp b/byond-extools/src/joao/joao.cpp new file mode 100644 index 0000000..5a5bd52 --- /dev/null +++ b/byond-extools/src/joao/joao.cpp @@ -0,0 +1,228 @@ +#include "joao.h" +#include "../core/core.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace // To make absolutely sure no other part of the code tries to link to all this shit +{ + namespace joao + { +#define JOAO_NO_INCLUDE_STD // A kludge necessary to avoid namespace problems associated with wrapping all of Joćo in this funky namespace block +#define JOAO_SAFE //not-as-much-of-a kludge used to enable throttling and disable some dangerous libraries such as the /file Object type +#include "./joao_repo/AST.cpp" +#include "./joao_repo/Directory.cpp" +#include "./joao_repo/Interpreter.cpp" + +#include "./joao_repo/nativefuncs/error.cpp" +#include "./joao_repo/nativefuncs/math.cpp" +#include "./joao_repo/nativefuncs/string.cpp" +#include "./joao_repo/nativefuncs/tablelib.cpp" + +#include "./joao_repo/Object.cpp" +#include "./joao_repo/Parser.cpp" +#include "./joao_repo/Program.cpp" +#include "./joao_repo/Scanner.cpp" +#include "./joao_repo/Table.cpp" +#undef JOAO_NO_INCLUDE_STD +#undef JOAO_SAFE + } +} + +//for the sake of being explicit, since there is a minor name collision going on here +using ByondValue = Value; +using JoaoValue = joao::Value; + +namespace +{ + namespace ID + { + int data; + int content; + int message; + int frequency; + int name; + } +} + + +const char* enable_joao() +{ + // get the var IDs for SANIC SPEED + ID::data = Core::GetStringId("data", true); + ID::content = Core::GetStringId("content", true); + ID::message = Core::GetStringId("message", true); + ID::frequency = Core::GetStringId("frequency", true); + ID::name = Core::GetStringId("name", true); + + //Set up hooks + Core::get_proc("/proc/run_script").hook(run_script); + return "ok"; +} + +trvh run_script(unsigned int argn, ByondValue* args, ByondValue src) +{ + using namespace joao; + if (argn < 2) // If we weren't given a script or signal + return ByondValue::False(); + + ByondValue& script = args[0]; + if (script.type != DataType::STRING) // If what we were given can't be a script + { + return ByondValue::False(); + } + + ByondValue& packet = args[1]; + if (packet.type != DataType::DATUM) + return ByondValue::False(); + if (!(packet.has_var("signal"))) // Lazy type-checking of $packet + { + return ByondValue::False(); + } + + ByondValue& signal = args[1].get("signal"); + if (signal.type != DataType::DATUM) // If what we were given isn't even a datum (signals are, this comment upholding, /datum/signal/subspace/vocal) + return ByondValue::False(); + + + std::istringstream stream = std::istringstream(std::string(script)); + + + Scanner scanner; + try + { + scanner.scan(stream); + } + catch (joao::error::scanner s_err) + { + ByondValue& error = packet.get("err"); + error.set("what", s_err.what()); + error.set("code", -3); + return ByondValue::False(); + } + Parser pears(scanner); + + + //Tries to follow the structure of the script_signal var from NTSL + //The default values are not based on the actual incoming signal, since they are used whenever *any* /signal Object is instantiated within Joao. + ObjectType signal_type("signal"); + JoaoValue empty_string = JoaoValue(std::string("")); + signal_type.set_typeproperty_raw("content", empty_string); + signal_type.set_typeproperty_raw("freq", JoaoValue(0)); + signal_type.set_typeproperty_raw("source", empty_string); + + pears.IncludeAlienType(&signal_type); + Program parsed; + try + { + parsed = pears.parse(); + } + catch (joao::error::parser p_err) + { + ByondValue& error = packet.get("err"); + error.set("what", p_err.what()); + error.set("code", -3); + return ByondValue::False(); + } + + Interpreter interpreter(parsed, false); + + //Constructing the Joćo Object to be passed as the argument + Object* signal_obj = signal_type.makeObject(interpreter, {}); + + signal_obj->set_property_raw("content", JoaoValue(Core::GetStringFromId(signal.get_by_id(ID::data).get_by_id(ID::message).value))); + signal_obj->set_property_raw("freq", JoaoValue(JoaoValue::JoaoInt(signal.get_by_id(ID::frequency).valuef))); // I think? + signal_obj->set_property_raw("source", JoaoValue(Core::GetStringFromId(signal.get_by_id(ID::data).get_by_id(ID::name).value))); + + JoaoValue jargs = interpreter.makeBaseTable({ JoaoValue(signal_obj) }, {}, nullptr); + + //Execute! + JoaoValue ret; + try + { + ret = interpreter.execute(parsed, jargs); + } + catch (const JoaoValue& err) + { + ByondValue& error = packet.get("err"); + error.set("what", ByondValue(*(err.t_value.as_object_ptr->get_property_raw("what").t_value.as_string_ptr))); + error.set("code", ByondValue(err.t_value.as_object_ptr->get_property_raw("code").t_value.as_int)); + return ByondValue::False(); + } + catch (std::exception exp) + { + ByondValue& error = packet.get("err"); + error.set("what", ByondValue(std::string(exp.what()))); + error.set("code", -1); + return ByondValue::False(); + } + catch(...) // A real fucking scary thing to put here but I dunno how else to resolve this + { + ByondValue& error = packet.get("err"); + error.set("what", "An unknown error occurred!"); + error.set("code", -2); + return ByondValue::False(); + } + if (ret.t_vType != JoaoValue::vType::Object || ret.t_value.as_object_ptr->object_type != "signal") // If the program returned something that isn't a signal object + { + ByondValue& error = packet.get("err"); + error.set("what", "Returned value was not a /signal!"); + error.set("code", -3); + return ByondValue::False(); + } + Object*& retptr = ret.t_value.as_object_ptr; // $#(@$*@!!!! + + //preparing for the agony of setting the elements of a /list + auto datas = signal.get_by_id(ID::data); // "datas" is the plural of "data," which is in turn the plural of "datum." + + //Content typecheck & set + JoaoValue& ret_content = retptr->get_property_raw("content"); + if (ret_content.t_vType != JoaoValue::vType::String) + { + ByondValue& error = packet.get("err"); + error.set("what", "/signal.content was not of type String!"); + error.set("code", -4); + return ByondValue::False(); + } + + ManagedValue msg = ManagedValue(*ret_content.t_value.as_string_ptr); + SetAssocElement(datas.type, datas.value, DataType::STRING, ID::message, msg.type, msg.value); // Have to use this raw shit since extools lacks a good API for it + + //Frequency typecheck & set + JoaoValue& ret_freq = retptr->get_property_raw("freq"); + if (ret_freq.t_vType != JoaoValue::vType::Integer) + { + ByondValue& error = packet.get("err"); + error.set("what", "/signal.freq was not of type Integer!"); + error.set("code", -4); + return ByondValue::False(); + } + //signal.get_by_id(ID::data).set_by_id(ID::frequency, ret_freq.t_value.as_int); + + ManagedValue freq = ManagedValue(ret_freq.t_value.as_int); + SetAssocElement(datas.type, datas.value, DataType::STRING, ID::frequency, freq.type, freq.value); // Have to use this raw shit since extools lacks a good API for it + + //Source typecheck & set + JoaoValue& ret_source = retptr->get_property_raw("source"); + if (ret_source.t_vType != JoaoValue::vType::String) + { + ByondValue& error = packet.get("err"); + error.set("what", "/signal.source was not of type String!"); + error.set("code", -4); + return ByondValue::False(); + } + //signal.get_by_id(ID::data).set_by_id(ID::name, *ret_source.t_value.as_string_ptr); + + ManagedValue source = ManagedValue(*ret_source.t_value.as_string_ptr); + SetAssocElement(datas.type, datas.value, DataType::STRING, ID::name, source.type, source.value); // Have to use this raw shit since extools lacks a good API for it + + return ByondValue::True(); +} + + diff --git a/byond-extools/src/joao/joao.h b/byond-extools/src/joao/joao.h new file mode 100644 index 0000000..eb745bc --- /dev/null +++ b/byond-extools/src/joao/joao.h @@ -0,0 +1,5 @@ +#pragma once +#include "../core/byond_structures.h" +trvh run_script(unsigned int, Value*, Value); // Joćo also has a Value class, so... + +const char* enable_joao(); \ No newline at end of file diff --git a/byond-extools/src/joao/joao_exports.cpp b/byond-extools/src/joao/joao_exports.cpp new file mode 100644 index 0000000..1534a89 --- /dev/null +++ b/byond-extools/src/joao/joao_exports.cpp @@ -0,0 +1,11 @@ +#include "../core/core.h" +#include "joao.h" + +extern "C" EXPORT const char* init_joao(int n_args, const char** args) +{ + if (!Core::initialize()) + { + return "Extools Init Failed"; + } + return enable_joao(); +} \ No newline at end of file diff --git a/byond-extools/src/joao/joao_repo b/byond-extools/src/joao/joao_repo new file mode 160000 index 0000000..4c2267c --- /dev/null +++ b/byond-extools/src/joao/joao_repo @@ -0,0 +1 @@ +Subproject commit 4c2267c9e1da3d33b6adda0cc55681b57e7fbf19