2020-08-22 22:30:12 -04:00
|
|
|
#include <cstdio>
|
2022-06-22 23:37:46 -04:00
|
|
|
#include <regex>
|
|
|
|
|
2021-01-06 12:16:39 -05:00
|
|
|
#include "common/log/log.h"
|
2023-01-07 11:24:02 -05:00
|
|
|
#include "common/repl/nrepl/ReplServer.h"
|
2024-06-03 00:14:52 -04:00
|
|
|
#include "common/repl/repl_wrapper.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "common/util/FileUtil.h"
|
repl: Add a few new quality of life improvements (#2030)
- You can define a `startup.gc` in your user folder, each line will be
executed on startup (deprecates the usefulness of some cli flags)
- You can define a `repl-config.json` file to override REPL settings.
Long-term this is a better approach than a bunch of CLI flags as well
- Via this, you can override the amount of time the repl will attempt to
listen for the target
- At the same time, I think i may have found why on Windows it can
sometimes take forever to timeout when the game dies, will dig into this
later
- Added some keybinds for common operations, shown here
https://user-images.githubusercontent.com/13153231/202890278-1ff2bb06-dddf-4bde-9178-aa0883799167.mp4
> builds the game, connects to it, attaches a debugger and continues,
launches it, gets the backtrace, stops the target -- all with only
keybinds.
If you want these keybinds to work inside VSCode's integrated terminal,
you need to add the following to your settings file
```json
"terminal.integrated.commandsToSkipShell": [
"-workbench.action.quickOpen",
"-workbench.action.quickOpenView"
]
```
2022-11-20 14:28:41 -05:00
|
|
|
#include "common/util/diff.h"
|
2023-01-07 11:24:02 -05:00
|
|
|
#include "common/util/string_util.h"
|
2023-08-08 12:59:37 -04:00
|
|
|
#include "common/util/term_util.h"
|
2023-03-09 23:13:01 -05:00
|
|
|
#include "common/util/unicode_util.h"
|
2023-04-22 14:13:57 -04:00
|
|
|
#include "common/versions/versions.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
|
|
|
|
#include "goalc/compiler/Compiler.h"
|
2020-10-29 19:03:44 -04:00
|
|
|
|
2024-03-05 22:11:52 -05:00
|
|
|
#include "fmt/color.h"
|
|
|
|
#include "fmt/core.h"
|
2022-05-06 18:19:37 -04:00
|
|
|
#include "third-party/CLI11.hpp"
|
2021-03-03 00:05:13 -05:00
|
|
|
|
2023-08-08 12:59:37 -04:00
|
|
|
void setup_logging(const bool disable_ansi_colors) {
|
2022-06-21 21:26:11 -04:00
|
|
|
lg::set_file_level(lg::level::info);
|
|
|
|
lg::set_stdout_level(lg::level::info);
|
|
|
|
lg::set_flush_level(lg::level::info);
|
2023-08-08 12:59:37 -04:00
|
|
|
if (disable_ansi_colors) {
|
|
|
|
lg::disable_ansi_colors();
|
|
|
|
}
|
2024-06-03 00:14:52 -04:00
|
|
|
lg::set_file("compiler");
|
2021-01-06 12:16:39 -05:00
|
|
|
lg::initialize();
|
2020-10-29 19:03:44 -04:00
|
|
|
}
|
2020-08-22 22:30:12 -04:00
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
2023-03-09 23:13:01 -05:00
|
|
|
ArgumentGuard u8_guard(argc, argv);
|
|
|
|
|
2022-05-06 18:19:37 -04:00
|
|
|
bool auto_find_user = false;
|
|
|
|
std::string cmd = "";
|
|
|
|
std::string username = "#f";
|
2022-07-06 21:18:08 -04:00
|
|
|
std::string game = "jak1";
|
2023-06-07 20:04:16 -04:00
|
|
|
int nrepl_port = -1;
|
2022-09-24 12:06:26 -04:00
|
|
|
fs::path project_path_override;
|
2022-05-06 18:19:37 -04:00
|
|
|
|
repl: Add a few new quality of life improvements (#2030)
- You can define a `startup.gc` in your user folder, each line will be
executed on startup (deprecates the usefulness of some cli flags)
- You can define a `repl-config.json` file to override REPL settings.
Long-term this is a better approach than a bunch of CLI flags as well
- Via this, you can override the amount of time the repl will attempt to
listen for the target
- At the same time, I think i may have found why on Windows it can
sometimes take forever to timeout when the game dies, will dig into this
later
- Added some keybinds for common operations, shown here
https://user-images.githubusercontent.com/13153231/202890278-1ff2bb06-dddf-4bde-9178-aa0883799167.mp4
> builds the game, connects to it, attaches a debugger and continues,
launches it, gets the backtrace, stops the target -- all with only
keybinds.
If you want these keybinds to work inside VSCode's integrated terminal,
you need to add the following to your settings file
```json
"terminal.integrated.commandsToSkipShell": [
"-workbench.action.quickOpen",
"-workbench.action.quickOpenView"
]
```
2022-11-20 14:28:41 -05:00
|
|
|
// TODO - a lot of these flags could be deprecated and moved into `repl-config.json`
|
2022-05-06 18:19:37 -04:00
|
|
|
CLI::App app{"OpenGOAL Compiler / REPL"};
|
2023-01-07 11:24:02 -05:00
|
|
|
app.add_option("-c,--cmd", cmd, "Specify a command to run, no REPL is launched in this mode");
|
2022-05-06 18:19:37 -04:00
|
|
|
app.add_option("-u,--user", username,
|
|
|
|
"Specify the username to use for your user profile in 'goal_src/user/'");
|
2024-06-03 00:14:52 -04:00
|
|
|
app.add_option("-p,--port", nrepl_port, "Specify the nREPL port. Defaults to 8181");
|
2022-05-06 18:19:37 -04:00
|
|
|
app.add_flag("--user-auto", auto_find_user,
|
2023-01-07 11:24:02 -05:00
|
|
|
"Attempt to automatically deduce the user, overrides '--user'");
|
2022-07-06 21:18:08 -04:00
|
|
|
app.add_option("-g,--game", game, "The game name: 'jak1' or 'jak2'");
|
2022-09-24 12:06:26 -04:00
|
|
|
app.add_option("--proj-path", project_path_override,
|
|
|
|
"Specify the location of the 'data/' folder");
|
2023-08-08 12:59:37 -04:00
|
|
|
define_common_cli_arguments(app);
|
2022-05-06 18:19:37 -04:00
|
|
|
app.validate_positionals();
|
|
|
|
CLI11_PARSE(app, argc, argv);
|
|
|
|
|
2022-09-24 12:06:26 -04:00
|
|
|
GameVersion game_version = game_name_to_version(game);
|
|
|
|
|
|
|
|
if (!project_path_override.empty()) {
|
|
|
|
if (!fs::exists(project_path_override)) {
|
|
|
|
lg::error("Error: project path override '{}' does not exist", project_path_override.string());
|
|
|
|
return 1;
|
|
|
|
}
|
2024-06-03 00:14:52 -04:00
|
|
|
if (!file_util::setup_project_path(project_path_override, true)) {
|
2022-09-24 12:06:26 -04:00
|
|
|
lg::error("Could not setup project path!");
|
|
|
|
return 1;
|
|
|
|
}
|
2024-06-03 00:14:52 -04:00
|
|
|
} else if (!file_util::setup_project_path(std::nullopt, true)) {
|
2022-05-06 18:19:37 -04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-03-01 17:52:33 -05:00
|
|
|
try {
|
2023-08-08 12:59:37 -04:00
|
|
|
setup_logging(_cli_flag_disable_ansi);
|
2023-03-01 17:52:33 -05:00
|
|
|
} catch (const std::exception& e) {
|
|
|
|
lg::error("Failed to setup logging: {}", e.what());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
// Figure out the username
|
2022-05-06 18:19:37 -04:00
|
|
|
if (auto_find_user) {
|
2023-01-07 11:24:02 -05:00
|
|
|
username = REPL::find_repl_username();
|
2020-10-29 19:03:44 -04:00
|
|
|
}
|
2023-01-07 11:24:02 -05:00
|
|
|
// Load the user's startup file
|
2023-01-30 20:45:03 -05:00
|
|
|
auto startup_file = REPL::load_user_startup_file(username, game_version);
|
2023-01-07 11:24:02 -05:00
|
|
|
// Load the user's REPL config
|
2024-06-03 00:14:52 -04:00
|
|
|
auto repl_config = REPL::load_repl_config(username, game_version, nrepl_port);
|
2022-05-06 18:19:37 -04:00
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
// Init Compiler
|
|
|
|
std::unique_ptr<Compiler> compiler;
|
|
|
|
std::mutex compiler_mutex;
|
|
|
|
// if a command is provided on the command line, no REPL just run the compiler on it
|
|
|
|
try {
|
|
|
|
if (!cmd.empty()) {
|
|
|
|
compiler = std::make_unique<Compiler>(game_version);
|
|
|
|
compiler->run_front_end_on_string(cmd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} catch (std::exception& e) {
|
|
|
|
lg::error("Compiler Fatal Error: {}", e.what());
|
2023-02-20 19:49:37 -05:00
|
|
|
return 1;
|
2023-01-07 11:24:02 -05:00
|
|
|
}
|
2020-08-22 22:30:12 -04:00
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
// Otherwise, start the REPL normally
|
2022-11-30 22:36:09 -05:00
|
|
|
ReplStatus status = ReplStatus::OK;
|
2023-01-07 11:24:02 -05:00
|
|
|
std::function<void()> repl_startup_func = [&]() {
|
|
|
|
// Run automatic forms if applicable
|
|
|
|
std::lock_guard<std::mutex> lock(compiler_mutex);
|
|
|
|
for (const auto& cmd : startup_file.run_before_listen) {
|
|
|
|
status = compiler->handle_repl_string(cmd);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Initialize nREPL server socket
|
2022-05-06 18:19:37 -04:00
|
|
|
std::function<bool()> shutdown_callback = [&]() { return status == ReplStatus::WANT_EXIT; };
|
2024-06-03 00:14:52 -04:00
|
|
|
ReplServer repl_server(shutdown_callback, repl_config.get_nrepl_port());
|
|
|
|
bool nrepl_server_ok = repl_server.init_server(true);
|
2022-05-06 18:19:37 -04:00
|
|
|
std::thread nrepl_thread;
|
2021-05-24 19:52:19 -04:00
|
|
|
// the compiler may throw an exception if it fails to load its standard library.
|
|
|
|
try {
|
2023-01-07 11:24:02 -05:00
|
|
|
compiler = std::make_unique<Compiler>(
|
2023-04-11 17:57:20 -04:00
|
|
|
game_version, std::make_optional(repl_config), username,
|
2024-06-03 00:14:52 -04:00
|
|
|
std::make_unique<REPL::Wrapper>(username, repl_config, startup_file, nrepl_server_ok));
|
2023-01-07 11:24:02 -05:00
|
|
|
// Start nREPL Server if it spun up successfully
|
2024-06-03 00:14:52 -04:00
|
|
|
if (nrepl_server_ok) {
|
2022-05-06 18:19:37 -04:00
|
|
|
nrepl_thread = std::thread([&]() {
|
|
|
|
while (!shutdown_callback()) {
|
|
|
|
auto resp = repl_server.get_msg();
|
|
|
|
if (resp) {
|
|
|
|
std::lock_guard<std::mutex> lock(compiler_mutex);
|
|
|
|
status = compiler->handle_repl_string(resp.value());
|
|
|
|
// Print out the prompt, just for better UX
|
|
|
|
compiler->print_to_repl(compiler->get_prompt());
|
|
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(50000));
|
2021-05-24 19:52:19 -04:00
|
|
|
}
|
2022-05-06 18:19:37 -04:00
|
|
|
});
|
|
|
|
}
|
2023-01-07 11:24:02 -05:00
|
|
|
repl_startup_func();
|
repl: Add a few new quality of life improvements (#2030)
- You can define a `startup.gc` in your user folder, each line will be
executed on startup (deprecates the usefulness of some cli flags)
- You can define a `repl-config.json` file to override REPL settings.
Long-term this is a better approach than a bunch of CLI flags as well
- Via this, you can override the amount of time the repl will attempt to
listen for the target
- At the same time, I think i may have found why on Windows it can
sometimes take forever to timeout when the game dies, will dig into this
later
- Added some keybinds for common operations, shown here
https://user-images.githubusercontent.com/13153231/202890278-1ff2bb06-dddf-4bde-9178-aa0883799167.mp4
> builds the game, connects to it, attaches a debugger and continues,
launches it, gets the backtrace, stops the target -- all with only
keybinds.
If you want these keybinds to work inside VSCode's integrated terminal,
you need to add the following to your settings file
```json
"terminal.integrated.commandsToSkipShell": [
"-workbench.action.quickOpen",
"-workbench.action.quickOpenView"
]
```
2022-11-20 14:28:41 -05:00
|
|
|
|
2022-05-06 18:19:37 -04:00
|
|
|
// Poll Terminal
|
|
|
|
while (status != ReplStatus::WANT_EXIT) {
|
|
|
|
if (status == ReplStatus::WANT_RELOAD) {
|
2022-10-01 11:58:36 -04:00
|
|
|
lg::info("Reloading compiler...");
|
2022-05-06 18:19:37 -04:00
|
|
|
std::lock_guard<std::mutex> lock(compiler_mutex);
|
|
|
|
if (compiler) {
|
|
|
|
compiler->save_repl_history();
|
|
|
|
}
|
2023-01-07 11:24:02 -05:00
|
|
|
compiler = std::make_unique<Compiler>(
|
2023-04-11 17:57:20 -04:00
|
|
|
game_version, std::make_optional(repl_config), username,
|
2024-06-03 00:14:52 -04:00
|
|
|
std::make_unique<REPL::Wrapper>(username, repl_config, startup_file, nrepl_server_ok));
|
2022-05-06 18:19:37 -04:00
|
|
|
status = ReplStatus::OK;
|
|
|
|
}
|
2023-01-07 11:24:02 -05:00
|
|
|
// process user input
|
2022-05-06 18:19:37 -04:00
|
|
|
std::string input_from_stdin = compiler->get_repl_input();
|
|
|
|
if (!input_from_stdin.empty()) {
|
|
|
|
// lock, while we compile
|
|
|
|
std::lock_guard<std::mutex> lock(compiler_mutex);
|
|
|
|
status = compiler->handle_repl_string(input_from_stdin);
|
2021-03-03 20:52:25 -05:00
|
|
|
}
|
|
|
|
}
|
2021-05-24 19:52:19 -04:00
|
|
|
} catch (std::exception& e) {
|
2022-10-01 11:58:36 -04:00
|
|
|
lg::error("Compiler Fatal Error: {}", e.what());
|
2022-05-19 17:08:01 -04:00
|
|
|
status = ReplStatus::WANT_EXIT;
|
2020-11-24 20:48:38 -05:00
|
|
|
}
|
2020-08-22 22:30:12 -04:00
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
// TODO - investigate why there is such a delay when exitting
|
|
|
|
|
2022-05-06 18:19:37 -04:00
|
|
|
// Cleanup
|
2024-06-03 00:14:52 -04:00
|
|
|
if (nrepl_server_ok) {
|
2022-05-06 18:19:37 -04:00
|
|
|
repl_server.shutdown_server();
|
|
|
|
nrepl_thread.join();
|
|
|
|
}
|
2020-08-22 22:30:12 -04:00
|
|
|
return 0;
|
|
|
|
}
|