Skip to content

Commit

Permalink
Expose Scheme nodes to the repl upon _enter_tree
Browse files Browse the repository at this point in the history
  • Loading branch information
bamboo committed Nov 24, 2024
1 parent 107f36e commit 8f0b437
Show file tree
Hide file tree
Showing 15 changed files with 306 additions and 77 deletions.
28 changes: 22 additions & 6 deletions src/repl/connection.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#include "connection.hpp"
#include <godot_cpp/variant/utility_functions.hpp>
#include "debug.hpp"

using namespace godot;
using gd = UtilityFunctions;

void ReplConnection::disconnect() {
tcp_stream->disconnect_from_host();
Expand Down Expand Up @@ -69,7 +68,26 @@ bool ReplConnection::process_buffer_with(ReplRequestCompiler &compiler) {
// disconnection from repl
return false;
}
if (buffer.size() == 3 && buffer.get_string_from_utf8() == ",ls") {
buffer.clear();
send_available_nodes();
return true;
}

process_eval_request_with(compiler);
return true;
}

void ReplConnection::send_available_nodes() {
auto node_paths = node_registry->get_available_node_names();
for (auto path = node_paths.begin(); path != node_paths.end(); ++path) {
send(path->utf8());
send('\n');
}
send_prompt();
}

void ReplConnection::process_eval_request_with(ReplRequestCompiler &compiler) {
auto result = compiler.eval(buffer);
buffer.clear();

Expand All @@ -78,12 +96,10 @@ bool ReplConnection::process_buffer_with(ReplRequestCompiler &compiler) {
send(result.first);
send('\n');
}
#if DEBUG_REPL_INTERACTIONS
gd::print(result.second);
#endif

DEBUG_REPL(result.second);
send(result.second);
send('\n');

send_prompt();
return true;
}
9 changes: 7 additions & 2 deletions src/repl/connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
#define GODOT_S7_SCHEME_REPL_REQUEST_CONNECTION_HPP

#include "request_compiler.hpp"
#include "node_registry.hpp"
#include <godot_cpp/classes/stream_peer_tcp.hpp>
#include <godot_cpp/variant/string.hpp>
#include <godot_cpp/variant/packed_byte_array.hpp>
#include <memory>

class ReplConnection {
public:
enum Status { IDLE, TRANSMITTING, DISCONNECTED };
ReplConnection(godot::Ref<godot::StreamPeerTCP> tcp_stream) :
tcp_stream(tcp_stream) {}
ReplConnection(godot::Ref<godot::StreamPeerTCP> tcp_stream, std::shared_ptr<ReplNodeRegistry> node_registry) :
tcp_stream(tcp_stream), node_registry(node_registry) {}

public:
void send_prompt();
Expand All @@ -23,9 +25,12 @@ class ReplConnection {
void send(const char *p, size_t count);
void send(char c);
bool process_buffer_with(ReplRequestCompiler &compiler);
void process_eval_request_with(ReplRequestCompiler& compiler);
void send_available_nodes();

private:
godot::Ref<godot::StreamPeerTCP> tcp_stream;
godot::PackedByteArray buffer;
std::shared_ptr<ReplNodeRegistry> node_registry;
};
#endif
16 changes: 16 additions & 0 deletions src/repl/debug.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef GODOT_S7_SCHEME_REPL_DEBUG_HPP
#define GODOT_S7_SCHEME_REPL_DEBUG_HPP

#include <godot_cpp/variant/utility_functions.hpp>

using gd = godot::UtilityFunctions;

#define DEBUG_REPL_INTERACTIONS 0

#if DEBUG_REPL_INTERACTIONS
#define DEBUG_REPL(...) gd::print(__VA_ARGS__)
#else
#define DEBUG_REPL(...) 0
#endif

#endif
29 changes: 26 additions & 3 deletions src/repl/mediator.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,36 @@
#include "mediator.hpp"
#include "debug.hpp"
#include <godot_cpp/variant/utility_functions.hpp>

using gd = godot::UtilityFunctions;
ReplMediator::ReplMediator(godot::Ref<godot::TCPServer> server) :
server(server) {
node_registry = std::make_shared<ReplNodeRegistry>();
}

struct ReplMessageHandler {
ReplMediator* mediator;

bool ReplMediator::mediate() {
void operator()(const ReplMessage::PublishNode &m) {
mediator->node_registry->register_node(m.node_id, std::move(m.node_name));
}
void operator()(const ReplMessage::UnpublishNode &m) {
mediator->node_registry->unregister_node(m.node_id);
}
void operator()(const ReplMessage::EvalResponse &m) {
}
};

bool ReplMediator::mediate(MessageQueue &queue) {
int interactions = 0;
auto message = std::move(queue.pop());
if (message) {
std::visit(ReplMessageHandler{this}, message->payload);
interactions++;
}

if (server->is_connection_available()) {
// TODO: client starts with most recent Scheme node
auto connection = ReplConnection(server->take_connection());
auto connection = ReplConnection(server->take_connection(), node_registry);
gd::print("Scheme repl client connected.");
connection.send_prompt();
connections.emplace_back(std::move(connection));
Expand Down
16 changes: 12 additions & 4 deletions src/repl/mediator.hpp
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
#ifndef GODOT_S7_SCHEME_REPL_REQUEST_MEDIATOR_HPP
#define GODOT_S7_SCHEME_REPL_REQUEST_MEDIATOR_HPP

#include "request_compiler.hpp"
#include "connection.hpp"
#include "message.hpp"
#include "node_registry.hpp"
#include "request_compiler.hpp"
#include "thread_safe_queue.hpp"
#include <godot_cpp/classes/tcp_server.hpp>
#include <vector>

class ReplMediator {
public:
ReplMediator(godot::Ref<godot::TCPServer> server) :
server(server) {}
using MessageQueue = ThreadSafeQueue<ReplMessage>;

ReplMediator(godot::Ref<godot::TCPServer> server);

public:
/**
* Process all pending interactions.
* @return true when at least one interaction was processed, false otherwise.
*/
bool mediate();
bool mediate(MessageQueue& queue);

private:
friend struct ReplMessageHandler;

private:
godot::Ref<godot::TCPServer> server;
std::shared_ptr<ReplNodeRegistry> node_registry;
std::vector<ReplConnection> connections;
ReplRequestCompiler request_compiler;
};
Expand Down
40 changes: 40 additions & 0 deletions src/repl/message.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef GODOT_S7_SCHEME_REPL_MESSAGE_HPP
#define GODOT_S7_SCHEME_REPL_MESSAGE_HPP

#include <godot_cpp/variant/string_name.hpp>
#include <godot_cpp/variant/variant.hpp>
#include <variant>

struct ReplMessage {

static ReplMessage publish_node(godot::StringName node_name, uint64_t node_id) {
return ReplMessage{ PublishNode{ node_name, node_id } };
}

static ReplMessage unpublish_node(uint64_t node_id) {
return ReplMessage{ UnpublishNode{ node_id } };
}

static ReplMessage eval_response(uint64_t request_id, godot::Variant result) {
return ReplMessage{ EvalResponse{ request_id, result } };
}

struct PublishNode {
godot::StringName node_name;
uint64_t node_id;
};

struct UnpublishNode {
uint64_t node_id;
};

struct EvalResponse {
uint64_t request_id;
godot::Variant result;
};

using Payload = std::variant<PublishNode, UnpublishNode, EvalResponse>;
Payload payload;
};

#endif
28 changes: 28 additions & 0 deletions src/repl/node_registry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "node_registry.hpp"
#include "debug.hpp"
#include <godot_cpp/variant/utility_functions.hpp>

using namespace godot;

std::vector<String> ReplNodeRegistry::get_available_node_names() {
auto result = std::vector<String>();
for (auto node = nodes.begin(); node != nodes.end(); ++node) {
result.push_back(node->node_name);
}
return result;
}

void ReplNodeRegistry::register_node(uint64_t node_id, String node_name) {
DEBUG_REPL("Scheme node ", node_name, " is available for repl interaction.");
nodes.emplace_back(std::move(NodeRecord{ node_id, std::move(node_name) }));
}

void ReplNodeRegistry::unregister_node(uint64_t node_id) {
for (auto node = nodes.begin(); node != nodes.end(); ++node) {
if (node->node_id == node_id) {
DEBUG_REPL("Scheme node ", node->node_name, " is no longer available for repl interaction.");
nodes.erase(node);
break;
}
}
}
21 changes: 21 additions & 0 deletions src/repl/node_registry.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef GODOT_S7_SCHEME_REPL_NODE_REGISTRY_HPP
#define GODOT_S7_SCHEME_REPL_NODE_REGISTRY_HPP

#include <godot_cpp/variant/string.hpp>
#include <vector>

class ReplNodeRegistry {
public:
std::vector<godot::String> get_available_node_names();
void register_node(uint64_t node_id, godot::String node_name);
void unregister_node(uint64_t node_id);

private:
struct NodeRecord {
uint64_t node_id;
godot::String node_name;
};

std::vector<NodeRecord> nodes;
};
#endif
15 changes: 4 additions & 11 deletions src/repl/request_compiler.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#include "request_compiler.hpp"
#include "debug.hpp"
#include "gen/s7_scheme_repl_string.hpp"
#include <functional>
#include <godot_cpp/variant/utility_functions.hpp>

using namespace godot;
using gd = UtilityFunctions;

template <typename T>
std::pair<const char *, T> eval_with_error_output(s7_scheme *sc, std::function<T(s7_scheme *)> f) {
Expand All @@ -23,9 +23,7 @@ inline const char *non_empty_nor_null(const char *s) {
ReplRequestCompiler::ReplRequestCompiler() {
compile_geiser_request = scheme.make_symbol("compile-geiser-request");
auto result = eval_with_error_output<const char *>(scheme.get(), [](auto sc) {
#if DEBUG_REPL_INTERACTIONS
std::cout << s7_scheme_repl_string << std::endl;
#endif
DEBUG_REPL(s7_scheme_repl_string);
return s7_object_to_c_string(sc,
s7_load_c_string(sc,
s7_scheme_repl_string,
Expand Down Expand Up @@ -56,7 +54,7 @@ error_output_and_response ReplRequestCompiler::eval(const PackedByteArray &reque
auto args = s7_cons(sc,
s7_make_string_wrapper_with_length(
sc,
reinterpret_cast<const char*>(request.ptr()),
reinterpret_cast<const char *>(request.ptr()),
static_cast<s7_int>(request.size())),
s7_nil(sc));
return s7_call_with_location(
Expand All @@ -77,12 +75,7 @@ error_output_and_response ReplRequestCompiler::eval(const PackedByteArray &reque
}

auto code_string = s7_string(compiled_request);

#if DEBUG_REPL_INTERACTIONS
std::cout << "```" << std::endl
<< code_string << std::endl
<< "```" << std::endl;
#endif
DEBUG_REPL("```\n", code_string, "\n```");

auto eval_sc = /*target_scheme != nullptr ? target_scheme->get_s7().get() :*/ sc;
auto res = eval_with_error_output<const char *>(eval_sc,
Expand Down
2 changes: 0 additions & 2 deletions src/repl/request_compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
#include "../s7.hpp"
#include <godot_cpp/variant/packed_byte_array.hpp>

#define DEBUG_REPL_INTERACTIONS 0

typedef std::pair<const char *, const char *> error_output_and_response;

class ReplRequestCompiler {
Expand Down
30 changes: 30 additions & 0 deletions src/repl/thread_safe_queue.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef GODOT_S7_SCHEME_THREAD_SAFE_QUEUE_HPP
#define GODOT_S7_SCHEME_THREAD_SAFE_QUEUE_HPP

#include <mutex>
#include <optional>
#include <queue>

template <typename T>
class ThreadSafeQueue {
private:
std::queue<T> queue;
std::mutex mutex;

public:
void push(T item) {
std::lock_guard<std::mutex> lock(mutex);
queue.push(std::move(item));
}

std::optional<T> pop() {
std::unique_lock<std::mutex> lock(mutex);
if (queue.empty()) {
return std::nullopt;
}
T item = std::move(queue.front());
queue.pop();
return std::move(item);
}
};
#endif
Loading

0 comments on commit 8f0b437

Please sign in to comment.