mirror of
https://github.com/open-goal/jak-project
synced 2026-08-18 21:57:54 -04:00
goalc: cleanup goalc's main method and add nrepl listener socket
This commit is contained in:
+1
-1
@@ -75,7 +75,7 @@
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "goalc.exe (bin\\goalc.exe)",
|
||||
"name" : "Run - REPL - Auto Listen",
|
||||
"args" : [ "-auto-lt" ]
|
||||
"args" : [ "--auto-lt" ]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
|
||||
@@ -139,6 +139,9 @@ add_subdirectory(third-party/imgui)
|
||||
# build the game code in C++
|
||||
add_subdirectory(game)
|
||||
|
||||
# build asio library
|
||||
include_directories(third-party/asio/asio/include)
|
||||
|
||||
# build the compiler
|
||||
add_subdirectory(goalc)
|
||||
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ tasks:
|
||||
env:
|
||||
OPENGOAL_DECOMP_DIR: "jak1/"
|
||||
cmds:
|
||||
- "{{.GOALC_BIN_RELEASE_DIR}}/goalc -auto-lt"
|
||||
- "{{.GOALC_BIN_RELEASE_DIR}}/goalc --auto-lt"
|
||||
# DECOMPILING
|
||||
decomp:
|
||||
cmds:
|
||||
|
||||
@@ -335,7 +335,6 @@ int main(int argc, char** argv) {
|
||||
app.add_flag("-c,--compile", flag_compile, "Compile the game");
|
||||
app.add_flag("-p,--play", flag_play, "Play the game");
|
||||
app.validate_positionals();
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
fmt::print("Working Directory - {}\n", std::filesystem::current_path().string());
|
||||
|
||||
@@ -26,6 +26,7 @@ add_library(compiler
|
||||
compiler/compilation/Type.cpp
|
||||
compiler/compilation/State.cpp
|
||||
compiler/compilation/Static.cpp
|
||||
compiler/nrepl/ReplServer.cpp
|
||||
compiler/Util.cpp
|
||||
data_compiler/game_text.cpp
|
||||
data_compiler/dir_tpages.cpp
|
||||
|
||||
+73
-54
@@ -1,3 +1,5 @@
|
||||
#include "nrepl/ReplServer.h" // this import has to come first because WinSock sucks
|
||||
|
||||
#include "Compiler.h"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
@@ -12,7 +14,9 @@
|
||||
|
||||
using namespace goos;
|
||||
|
||||
Compiler::Compiler(const std::string& user_profile, std::unique_ptr<ReplWrapper> repl)
|
||||
Compiler::Compiler(const int nrepl_port,
|
||||
const std::string& user_profile,
|
||||
std::unique_ptr<ReplWrapper> repl)
|
||||
: m_goos(user_profile), m_debugger(&m_listener, &m_goos.reader), m_repl(std::move(repl)) {
|
||||
m_listener.add_debugger(&m_debugger);
|
||||
m_ts.add_builtin_types();
|
||||
@@ -48,6 +52,18 @@ Compiler::Compiler(const std::string& user_profile, std::unique_ptr<ReplWrapper>
|
||||
|
||||
// add GOOS forms that get info from the compiler
|
||||
setup_goos_forms();
|
||||
|
||||
m_nrepl_port = nrepl_port;
|
||||
fmt::print("[nREPL]: Server Will Listen for a Connection on Port {}!\n\r", m_nrepl_port);
|
||||
nrepl_thread = std::thread([&]() {
|
||||
asio::io_context io_context;
|
||||
ReplServer s(io_context, this);
|
||||
try {
|
||||
io_context.run();
|
||||
} catch (std::exception& e) {
|
||||
print_compiler_warning("Could not setup nREPL {}\n", e.what());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ReplStatus Compiler::execute_repl(bool auto_listen, bool auto_debug) {
|
||||
@@ -64,65 +80,15 @@ ReplStatus Compiler::execute_repl(bool auto_listen, bool auto_debug) {
|
||||
m_repl->get_repl().set_highlighter_callback(
|
||||
std::bind(&Compiler::repl_coloring, this, _1, _2, std::cref(regex_colors)));
|
||||
|
||||
std::string auto_input;
|
||||
if (auto_debug || auto_listen) {
|
||||
auto_input.append("(lt)");
|
||||
read_eval_print("(lt)");
|
||||
}
|
||||
if (auto_debug) {
|
||||
auto_input.append("(dbg) (:cont)");
|
||||
read_eval_print("(dbg) (:cont)");
|
||||
}
|
||||
|
||||
while (!m_want_exit && !m_want_reload) {
|
||||
try {
|
||||
std::optional<goos::Object> code;
|
||||
|
||||
if (auto_input.empty()) {
|
||||
// 1). get a line from the user (READ)
|
||||
std::string prompt = fmt::format(fmt::emphasis::bold | fg(fmt::color::cyan), "g > ");
|
||||
if (m_listener.is_connected()) {
|
||||
prompt = fmt::format(fmt::emphasis::bold | fg(fmt::color::lime_green), "gc> ");
|
||||
}
|
||||
if (m_debugger.is_halted()) {
|
||||
prompt = fmt::format(fmt::emphasis::bold | fg(fmt::color::magenta), "gs> ");
|
||||
} else if (m_debugger.is_attached()) {
|
||||
prompt = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), "gr> ");
|
||||
}
|
||||
|
||||
code = m_goos.reader.read_from_stdin(prompt, *m_repl);
|
||||
} else {
|
||||
code = m_goos.reader.read_from_string(auto_input);
|
||||
auto_input.clear();
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2). compile
|
||||
auto obj_file = compile_object_file("repl", *code, m_listener.is_connected());
|
||||
if (m_settings.debug_print_ir) {
|
||||
obj_file->debug_print_tl();
|
||||
}
|
||||
|
||||
if (!obj_file->is_empty()) {
|
||||
// 3). color
|
||||
color_object_file(obj_file);
|
||||
|
||||
// 4). codegen
|
||||
auto data = codegen_object_file(obj_file);
|
||||
|
||||
// 4). send!
|
||||
if (m_listener.is_connected()) {
|
||||
m_listener.send_code(data);
|
||||
if (!m_listener.most_recent_send_was_acked()) {
|
||||
print_compiler_warning("Runtime is not responding. Did it crash?\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (std::exception& e) {
|
||||
print_compiler_warning("REPL Error: {}\n", e.what());
|
||||
}
|
||||
read_eval_print();
|
||||
}
|
||||
|
||||
if (m_listener.is_connected()) {
|
||||
@@ -141,6 +107,59 @@ ReplStatus Compiler::execute_repl(bool auto_listen, bool auto_debug) {
|
||||
return ReplStatus::OK;
|
||||
}
|
||||
|
||||
void Compiler::read_eval_print(std::string input) {
|
||||
try {
|
||||
std::optional<goos::Object> code;
|
||||
|
||||
// Explicitly specified input
|
||||
if (!input.empty()) {
|
||||
code = m_goos.reader.read_from_string(input);
|
||||
} else {
|
||||
// if this is pulled out into a function....illegal instruction on checking the debugger? strange
|
||||
std::string prompt = fmt::format(fmt::emphasis::bold | fg(fmt::color::cyan), "g > ");
|
||||
if (m_listener.is_connected()) {
|
||||
prompt = fmt::format(fmt::emphasis::bold | fg(fmt::color::lime_green), "gc> ");
|
||||
}
|
||||
if (m_debugger.is_halted()) {
|
||||
prompt = fmt::format(fmt::emphasis::bold | fg(fmt::color::magenta), "gs> ");
|
||||
} else if (m_debugger.is_attached()) {
|
||||
prompt = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), "gr> ");
|
||||
}
|
||||
// 1). get a line from the user (READ)
|
||||
code = m_goos.reader.read_from_stdin(prompt, *m_repl);
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2). compile
|
||||
auto obj_file = compile_object_file("repl", *code, m_listener.is_connected());
|
||||
if (m_settings.debug_print_ir) {
|
||||
obj_file->debug_print_tl();
|
||||
}
|
||||
|
||||
if (!obj_file->is_empty()) {
|
||||
// 3). color
|
||||
color_object_file(obj_file);
|
||||
|
||||
// 4). codegen
|
||||
auto data = codegen_object_file(obj_file);
|
||||
|
||||
// 4). send!
|
||||
if (m_listener.is_connected()) {
|
||||
m_listener.send_code(data);
|
||||
if (!m_listener.most_recent_send_was_acked()) {
|
||||
print_compiler_warning("Runtime is not responding. Did it crash?\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (std::exception& e) {
|
||||
print_compiler_warning("REPL Error: {}\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
FileEnv* Compiler::compile_object_file(const std::string& name,
|
||||
goos::Object code,
|
||||
bool allow_emit) {
|
||||
|
||||
@@ -27,7 +27,10 @@ enum class ReplStatus { OK, WANT_EXIT, WANT_RELOAD };
|
||||
|
||||
class Compiler {
|
||||
public:
|
||||
Compiler(const std::string& user_profile = "#f", std::unique_ptr<ReplWrapper> repl = nullptr);
|
||||
Compiler(const int nrepl_port = 8181,
|
||||
const std::string& user_profile = "#f",
|
||||
std::unique_ptr<ReplWrapper> repl = nullptr);
|
||||
void read_eval_print(std::string input = "");
|
||||
ReplStatus execute_repl(bool auto_listen = false, bool auto_debug = false);
|
||||
goos::Interpreter& get_goos() { return m_goos; }
|
||||
FileEnv* compile_object_file(const std::string& name, goos::Object code, bool allow_emit);
|
||||
@@ -70,6 +73,8 @@ class Compiler {
|
||||
bool knows_object_file(const std::string& name);
|
||||
MakeSystem& make_system() { return m_make; }
|
||||
|
||||
int m_nrepl_port;
|
||||
|
||||
private:
|
||||
TypeSystem m_ts;
|
||||
std::unique_ptr<GlobalEnv> m_global_env = nullptr;
|
||||
@@ -89,6 +94,8 @@ class Compiler {
|
||||
std::unique_ptr<ReplWrapper> m_repl;
|
||||
MakeSystem m_make;
|
||||
|
||||
std::thread nrepl_thread;
|
||||
|
||||
struct DebugStats {
|
||||
int num_spills = 0;
|
||||
int num_spills_v1 = 0;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "ReplServer.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
// TODO - basically REPL to listen and inject commands into a running REPL
|
||||
// - we will need a C++ side client as well which will let us communicate with the repl via for
|
||||
// example, ImgUI
|
||||
//
|
||||
// TODO - The server also needs to eventually return the result of the evaluation
|
||||
|
||||
ReplSession::ReplSession(tcp::socket socket, Compiler* repl) : socket_(std::move(socket)) {
|
||||
m_repl = repl;
|
||||
}
|
||||
|
||||
void ReplSession::start() {
|
||||
fmt::print("[nREPL]: Client Connected!\n\r");
|
||||
do_read();
|
||||
}
|
||||
|
||||
void ReplSession::do_read() {
|
||||
auto self(shared_from_this());
|
||||
socket_.async_read_some(asio::buffer(data_, max_length),
|
||||
[this, self](std::error_code ec, std::size_t length) {
|
||||
if (!ec) {
|
||||
auto input = std::string(data_, length);
|
||||
if (!input.empty()) {
|
||||
m_repl->read_eval_print(input);
|
||||
}
|
||||
// TODO - i think this is kinda a hack, but its to keep the server
|
||||
// cycling
|
||||
do_write(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ReplSession::do_write(std::size_t length) {
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_, asio::buffer(data_, length),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/) {
|
||||
if (!ec) {
|
||||
do_read();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ReplServer::ReplServer(asio::io_context& io_context, Compiler* repl)
|
||||
: acceptor_(io_context, tcp::endpoint(tcp::v4(), repl->m_nrepl_port)), socket_(io_context) {
|
||||
m_repl = repl;
|
||||
m_port = repl->m_nrepl_port;
|
||||
do_accept();
|
||||
}
|
||||
|
||||
void ReplServer::do_accept() {
|
||||
acceptor_.async_accept(socket_, [this](std::error_code ec) {
|
||||
if (!ec) {
|
||||
std::make_shared<ReplSession>(std::move(socket_), this->m_repl)->start();
|
||||
}
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <asio/ts/buffer.hpp>
|
||||
#include <asio/ts/internet.hpp>
|
||||
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class ReplSession : public std::enable_shared_from_this<ReplSession> {
|
||||
public:
|
||||
ReplSession(tcp::socket socket, Compiler* repl);
|
||||
|
||||
void start();
|
||||
|
||||
private:
|
||||
Compiler* m_repl;
|
||||
tcp::socket socket_;
|
||||
enum { max_length = 1024 * 20 };
|
||||
char data_[max_length];
|
||||
|
||||
void do_read();
|
||||
void do_write(std::size_t length);
|
||||
};
|
||||
|
||||
class ReplServer {
|
||||
public:
|
||||
ReplServer(asio::io_context& io_context, Compiler* repl);
|
||||
|
||||
int m_port;
|
||||
|
||||
private:
|
||||
Compiler* m_repl;
|
||||
tcp::acceptor acceptor_;
|
||||
tcp::socket socket_;
|
||||
|
||||
void do_accept();
|
||||
};
|
||||
+52
-44
@@ -4,10 +4,12 @@
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/log/log.h"
|
||||
|
||||
#include "third-party/CLI11.hpp"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/fmt/color.h"
|
||||
|
||||
#include "common/goos/ReplUtils.h"
|
||||
#include <regex>
|
||||
|
||||
void setup_logging(bool verbose) {
|
||||
lg::set_file(file_util::get_file_path({"log/compiler.txt"}));
|
||||
@@ -24,52 +26,58 @@ void setup_logging(bool verbose) {
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
if (!file_util::setup_project_path(std::nullopt)) {
|
||||
return 1;
|
||||
}
|
||||
std::string argument;
|
||||
std::string username = "#f";
|
||||
bool verbose = false;
|
||||
bool auto_listen = false;
|
||||
bool auto_debug = false;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (std::string("-v") == argv[i]) {
|
||||
verbose = true;
|
||||
} else if (std::string("-cmd") == argv[i] && i + 1 < argc) {
|
||||
argument = argv[++i];
|
||||
} else if (std::string("-auto-lt") == argv[i]) {
|
||||
auto_listen = true;
|
||||
} else if (std::string("-auto-dbg") == argv[i]) {
|
||||
auto_debug = true;
|
||||
} else if (std::string("-user") == argv[i] && i + 1 < argc) {
|
||||
username = argv[++i];
|
||||
} else if (std::string("-user-auto") == argv[i]) {
|
||||
try {
|
||||
auto text = std::make_shared<goos::FileText>(
|
||||
file_util::get_file_path({"goal_src", "user", "user.txt"}), "goal_src/user/user.txt");
|
||||
goos::TextStream ts(text);
|
||||
ts.seek_past_whitespace_and_comments();
|
||||
username.clear();
|
||||
while (ts.text_remains()) {
|
||||
char c = ts.read();
|
||||
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
c == '-' || c == '.' || c == '!' || c == '?' || c == '<' || c == '>') {
|
||||
username.push_back(c);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
bool auto_find_user = false;
|
||||
std::string cmd = "";
|
||||
std::string username = "#f";
|
||||
int nrepl_port = 8181;
|
||||
|
||||
CLI::App app{"OpenGOAL Compiler / REPL"};
|
||||
app.add_option("-c,--cmd", cmd, "Specify a command to run");
|
||||
app.add_option("-u,--user", username,
|
||||
"Specify the username to use for your user profile in 'goal_src/user/'");
|
||||
app.add_option("-p,--port", nrepl_port, "Specify the nREPL port. Defaults to 8181");
|
||||
app.add_flag("-v,--verbose", verbose, "Enable verbose output");
|
||||
app.add_flag("--auto-lt", auto_listen,
|
||||
"Attempt to automatically connect to the listener on startup");
|
||||
app.add_flag("--auto-dbg", auto_debug,
|
||||
"Attempt to automatically connect to the debugger on startup");
|
||||
app.add_flag("--user-auto", auto_find_user,
|
||||
"Attempt to automatically deduce the user, overrides '-user'");
|
||||
app.validate_positionals();
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
if (!file_util::setup_project_path(std::nullopt)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (auto_find_user) {
|
||||
username = "#f";
|
||||
std::regex allowed_chars("[0-9a-zA-Z\\-\\.\\!\\?<>]");
|
||||
try {
|
||||
auto text = std::make_shared<goos::FileText>(
|
||||
file_util::get_file_path({"goal_src", "user", "user.txt"}), "goal_src/user/user.txt");
|
||||
goos::TextStream ts(text);
|
||||
ts.seek_past_whitespace_and_comments();
|
||||
std::string found_username;
|
||||
while (ts.text_remains()) {
|
||||
auto character = std::string(1, ts.read());
|
||||
if (std::regex_match(character, allowed_chars)) {
|
||||
found_username.push_back(ts.read());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if (username.empty()) {
|
||||
username = "#f";
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
printf("error opening user desc file: %s\n", e.what());
|
||||
username = "#f";
|
||||
}
|
||||
if (!found_username.empty()) {
|
||||
username = found_username;
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
printf("error opening user desc file: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
setup_logging(verbose);
|
||||
|
||||
lg::info("OpenGOAL Compiler {}.{}", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
|
||||
@@ -78,18 +86,18 @@ int main(int argc, char** argv) {
|
||||
// the compiler may throw an exception if it fails to load its standard library.
|
||||
try {
|
||||
std::unique_ptr<Compiler> compiler;
|
||||
if (argument.empty()) {
|
||||
if (!cmd.empty()) {
|
||||
compiler = std::make_unique<Compiler>();
|
||||
compiler->run_front_end_on_string(cmd);
|
||||
} else {
|
||||
ReplStatus status = ReplStatus::WANT_RELOAD;
|
||||
while (status == ReplStatus::WANT_RELOAD) {
|
||||
compiler = std::make_unique<Compiler>(username, std::make_unique<ReplWrapper>());
|
||||
compiler = std::make_unique<Compiler>(nrepl_port, username, std::make_unique<ReplWrapper>());
|
||||
status = compiler->execute_repl(auto_listen, auto_debug);
|
||||
if (status == ReplStatus::WANT_RELOAD) {
|
||||
fmt::print("Reloading compiler...\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
compiler = std::make_unique<Compiler>();
|
||||
compiler->run_front_end_on_string(argument);
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
fmt::print("Compiler Fatal Error: {}\n", e.what());
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@echo off
|
||||
cd ..\..
|
||||
out\build\Release\bin\goalc -v -auto-dbg -user-auto
|
||||
out\build\Release\bin\goalc -v --auto-dbg --user-auto
|
||||
pause
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@echo off
|
||||
cd ..\..
|
||||
out\build\Release\bin\goalc -v -user-auto
|
||||
out\build\Release\bin\goalc -v --user-auto
|
||||
pause
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@echo off
|
||||
cd ..\..
|
||||
out\build\Release\bin\goalc -v -auto-lt -user-auto
|
||||
out\build\Release\bin\goalc -v --auto-lt --user-auto
|
||||
pause
|
||||
|
||||
@@ -48,7 +48,7 @@ echo " ================ Decompiling..."
|
||||
../scripts/shell/decomp.sh
|
||||
|
||||
echo " ================ Building project..."
|
||||
../scripts/shell/gc.sh -cmd \(make-group\ \"iso\"\)
|
||||
../scripts/shell/gc.sh --cmd \(make-group\ \"iso\"\)
|
||||
|
||||
echo " ================ Checking assets..."
|
||||
../scripts/shell/check.sh
|
||||
|
||||
Reference in New Issue
Block a user