Skip to content

Commit

Permalink
Polish scheme_repl_server.cpp
Browse files Browse the repository at this point in the history
Move classes to their own files.
  • Loading branch information
bamboo committed Nov 23, 2024
1 parent c45ce29 commit f3b78ca
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 243 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ compile_commands.json
# Ignore files generated for documentation
/src/gen

# Ignore files generated for the repl
/src/repl/gen

# Binaries
*.o
*.os
Expand Down
3 changes: 2 additions & 1 deletion SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ env.Append(

sources = [
Glob("src/*.cpp"),
Glob("src/repl/*.cpp"),
Glob("s7/s7.c")
]

Expand All @@ -97,7 +98,7 @@ library = env.SharedLibrary(
copy = env.InstallAs("{}/bin/{}/{}lib{}".format(project_dir, env["platform"], file_path, file), library)

embed_scheme_repl = env.Command(
target="src/repl/generated/s7_scheme_repl_string.hpp",
target="src/repl/gen/s7_scheme_repl_string.hpp",
source="demo/addons/s7/s7_scheme_repl.scm",
action=embed_file
)
Expand Down
75 changes: 75 additions & 0 deletions src/repl/connection.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "connection.hpp"
#include <godot_cpp/variant/utility_functions.hpp>

using namespace godot;
using gd = UtilityFunctions;

void ReplClient::disconnect() {
tcp_stream->disconnect_from_host();
}

String ReplClient::get_prompt() {
// auto owner = (target_scheme != nullptr ? (Node *)target_scheme : this)->get_owner();
// auto path = owner != nullptr ? "" + owner->get_name() + "/" + get_name() : "" + get_name();
//return "\ns7@(" + path + ")> ";
return "\ns7@(:)> ";
}

void ReplClient::send_prompt() {
send(get_prompt().utf8());
}

bool ReplClient::process(ReplRequestCompiler &compiler) {
if (tcp_stream->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
return false;
}

auto available = tcp_stream->get_available_bytes();
while (available > 0) {
available--;
auto ch = tcp_stream->get_8();

#if DEBUG_REPL_INTERACTIONS
putchar(ch);
#endif

if (ch == '\n' && available == 0) {
if (!process_buffer(compiler)) {
return false;
}
} else {
buffer.push_back(ch);
}
}
return tcp_stream->poll() == Error::OK;
}

bool ReplClient::process_buffer(ReplRequestCompiler &compiler) {
if (buffer == ",q") {
// disconnection from repl
return false;
}

auto result = compiler.eval(buffer);
buffer.clear();

if (result.first) {
gd::printerr(result.first);
send(result.first);
tcp_stream->put_8('\n');
}
#if DEBUG_REPL_INTERACTIONS
gd::print(result.second);
#endif
send(result.second);
tcp_stream->put_8('\n');

send_prompt();
return true;
}

void ReplClient::send(const CharString &s) {
for (int i = 0; i < s.length(); ++i) {
tcp_stream->put_8(s[i]);
}
}
27 changes: 27 additions & 0 deletions src/repl/connection.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef GODOT_S7_SCHEME_REPL_REQUEST_CONNECTION_HPP
#define GODOT_S7_SCHEME_REPL_REQUEST_CONNECTION_HPP

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

class ReplClient {
public:
ReplClient(godot::Ref<godot::StreamPeerTCP> tcp_stream) :
tcp_stream(tcp_stream) {}

public:
void send_prompt();
bool process(ReplRequestCompiler &compiler);
void disconnect();

private:
godot::String get_prompt();
void send(const godot::CharString &s);
bool process_buffer(ReplRequestCompiler &compiler);

private:
godot::Ref<godot::StreamPeerTCP> tcp_stream;
std::string buffer;
};
#endif
23 changes: 23 additions & 0 deletions src/repl/mediator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "mediator.hpp"
#include <godot_cpp/variant/utility_functions.hpp>

using gd = godot::UtilityFunctions;

void ReplMediator::mediate() {
if (server->is_connection_available()) {
// TODO: client starts with most recent Scheme node
auto client = ReplClient(server->take_connection());
gd::print("Scheme repl client connected.");
client.send_prompt();
clients.emplace_back(std::move(client));
}

for (auto client = clients.begin(); client != clients.end();) {
if (!client->process(request_compiler)) {
client = clients.erase(client);
gd::print("Scheme repl client disconnected.");
} else {
client++;
}
}
}
22 changes: 22 additions & 0 deletions src/repl/mediator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef GODOT_S7_SCHEME_REPL_REQUEST_MEDIATOR_HPP
#define GODOT_S7_SCHEME_REPL_REQUEST_MEDIATOR_HPP

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

class ReplMediator {
public:
ReplMediator(godot::Ref<godot::TCPServer> server) :
server(server) {}

public:
void mediate();

private:
godot::Ref<godot::TCPServer> server;
std::vector<ReplClient> clients;
ReplRequestCompiler request_compiler;
};
#endif
93 changes: 93 additions & 0 deletions src/repl/request_compiler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "request_compiler.hpp"
#include "gen/s7_scheme_repl_string.hpp"
#include <functional>
#include <godot_cpp/variant/utility_functions.hpp>

using gd = godot::UtilityFunctions;

template <typename T>
std::pair<const char *, T> eval_with_error_output(s7_scheme *sc, std::function<T(s7_scheme *)> f) {
auto error_port = s7_open_output_string(sc);
auto previous_error_port = s7_set_current_error_port(sc, error_port);
auto res = f(sc);
s7_set_current_error_port(sc, previous_error_port);
auto error_output = s7_get_output_string(sc, error_port);
return std::make_pair(error_output, res);
}

inline const char *non_empty_nor_null(const char *s) {
return s != nullptr && *s != 0 ? s : nullptr;
}

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
return s7_object_to_c_string(sc,
s7_load_c_string(sc,
s7_scheme_repl_string,
strlen(s7_scheme_repl_string)));
});
auto error_output = non_empty_nor_null(result.first);
if (error_output) {
gd::printerr(error_output);
}
}

ReplRequestCompiler::~ReplRequestCompiler() {
compile_geiser_request = nullptr;
}

error_output_and_response ReplRequestCompiler::eval(const std::string &request) {
auto compile_geiser_request = scheme.value_of(this->compile_geiser_request);
if (!s7_is_procedure(compile_geiser_request)) {
return std::make_pair(
"repl script is missing a compile-geiser-request function!",
"'done");
}

auto sc = scheme.get();
auto compile_result =
eval_with_error_output<s7_pointer>(sc,
[compile_geiser_request, &request](auto sc) {
auto args = s7_cons(sc,
s7_make_string_wrapper_with_length(
sc,
request.c_str(),
static_cast<s7_int>(request.length())),
s7_nil(sc));
return s7_call_with_location(
sc,
compile_geiser_request,
args,
__func__,
__FILE__,
__LINE__);
});

auto compile_error_output = compile_result.first;
auto compiled_request = compile_result.second;
if (!s7_is_string(compiled_request)) {
return std::make_pair(
non_empty_nor_null(compile_error_output),
s7_object_to_c_string(sc, compiled_request));
}

auto code_string = s7_string(compiled_request);

#if DEBUG_REPL_INTERACTIONS
std::cout << "```" << std::endl
<< code_string << std::endl
<< "```" << std::endl;
#endif

auto eval_sc = /*target_scheme != nullptr ? target_scheme->get_s7().get() :*/ sc;
auto res = eval_with_error_output<const char *>(eval_sc,
[code_string](auto sc) {
auto res = s7_eval_c_string(sc, code_string);
return s7_is_string(res) ? s7_string(res) : s7_object_to_c_string(sc, res);
});
return std::make_pair(non_empty_nor_null(res.first), res.second);
}
23 changes: 23 additions & 0 deletions src/repl/request_compiler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef GODOT_S7_SCHEME_REPL_REQUEST_COMPILER_HPP
#define GODOT_S7_SCHEME_REPL_REQUEST_COMPILER_HPP

#include "../s7.hpp"
#include <string>

#define DEBUG_REPL_INTERACTIONS 0

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

class ReplRequestCompiler {
public:
ReplRequestCompiler();
~ReplRequestCompiler();

public:
error_output_and_response eval(const std::string &request);

private:
s7_protected_ptr compile_geiser_request;
s7 scheme;
};
#endif
6 changes: 3 additions & 3 deletions src/s7.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
#include <godot_cpp/variant/utility_functions.hpp>
#include <vector>

class godot::s7_scheme_context {
using namespace godot;

class s7_scheme_context {
public:
void print_error(uint8_t char_code) {
if (char_code == '\n') {
Expand All @@ -23,8 +25,6 @@ class godot::s7_scheme_context {
std::vector<uint8_t> error_buffer;
};

using namespace godot;

void add_scheme_mapping(s7_scheme *sc, s7_scheme_context *scheme) {
s7_define_constant(sc, "*ctx*", s7_make_c_pointer(sc, scheme));
}
Expand Down
6 changes: 2 additions & 4 deletions src/s7.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

typedef void (*s7_output_port_function_t)(s7_scheme *sc, uint8_t c, s7_pointer port);

namespace godot {
typedef std::shared_ptr<s7_cell> s7_protected_ptr;

inline s7_protected_ptr s7_gc_protected(s7_scheme *sc, s7_pointer p) {
Expand All @@ -30,8 +29,8 @@ class s7 {
const char *name,
s7_pointer value,
const char *documentation) const;
[[nodiscard]] s7_pointer eval(const String &code) const;
void load_string(const String &code) const;
[[nodiscard]] s7_pointer eval(const godot::String &code) const;
void load_string(const godot::String &code) const;
void set_current_error_port_function(s7_output_port_function_t f) const;

s7_protected_ptr make_symbol(const char *name) const {
Expand Down Expand Up @@ -84,5 +83,4 @@ class s7 {
std::shared_ptr<s7_scheme> scheme;
std::shared_ptr<s7_scheme_context> scheme_context;
};
} //namespace godot
#endif //GODOT_S7_SCHEME_S7_HPP
Loading

0 comments on commit f3b78ca

Please sign in to comment.