mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
[goos/goal] user profiles (#977)
* implement user profiles * example usage! * typo * use a cond * fix errors * fixes * fix potential commandline args disaster
This commit is contained in:
parent
8544e95b4b
commit
59d071eccb
|
@ -6,10 +6,11 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "Interpreter.h"
|
#include "Interpreter.h"
|
||||||
#include "ParseHelpers.h"
|
#include "ParseHelpers.h"
|
||||||
|
#include "common/util/FileUtil.h"
|
||||||
#include <third-party/fmt/core.h>
|
#include <third-party/fmt/core.h>
|
||||||
|
|
||||||
namespace goos {
|
namespace goos {
|
||||||
Interpreter::Interpreter() {
|
Interpreter::Interpreter(const std::string& username) {
|
||||||
// Interpreter startup:
|
// Interpreter startup:
|
||||||
goal_to_goos.reset();
|
goal_to_goos.reset();
|
||||||
|
|
||||||
|
@ -21,9 +22,14 @@ Interpreter::Interpreter() {
|
||||||
|
|
||||||
// make both environments available in both.
|
// make both environments available in both.
|
||||||
define_var_in_env(global_environment, global_environment, "*global-env*");
|
define_var_in_env(global_environment, global_environment, "*global-env*");
|
||||||
|
define_var_in_env(global_environment, goal_env, "*goal-env*");
|
||||||
define_var_in_env(goal_env, goal_env, "*goal-env*");
|
define_var_in_env(goal_env, goal_env, "*goal-env*");
|
||||||
define_var_in_env(goal_env, global_environment, "*global-env*");
|
define_var_in_env(goal_env, global_environment, "*global-env*");
|
||||||
define_var_in_env(global_environment, goal_env, "*goal-env*");
|
|
||||||
|
// set user profile name
|
||||||
|
auto user = SymbolObject::make_new(reader.symbolTable, username);
|
||||||
|
define_var_in_env(global_environment, user, "*user*");
|
||||||
|
define_var_in_env(goal_env, user, "*user*");
|
||||||
|
|
||||||
// setup maps
|
// setup maps
|
||||||
special_forms = {
|
special_forms = {
|
||||||
|
@ -47,6 +53,7 @@ Interpreter::Interpreter() {
|
||||||
{"print", &Interpreter::eval_print},
|
{"print", &Interpreter::eval_print},
|
||||||
{"inspect", &Interpreter::eval_inspect},
|
{"inspect", &Interpreter::eval_inspect},
|
||||||
{"load-file", &Interpreter::eval_load_file},
|
{"load-file", &Interpreter::eval_load_file},
|
||||||
|
{"try-load-file", &Interpreter::eval_try_load_file},
|
||||||
{"eq?", &Interpreter::eval_equals},
|
{"eq?", &Interpreter::eval_equals},
|
||||||
{"gensym", &Interpreter::eval_gensym},
|
{"gensym", &Interpreter::eval_gensym},
|
||||||
{"eval", &Interpreter::eval_eval},
|
{"eval", &Interpreter::eval_eval},
|
||||||
|
@ -1030,6 +1037,35 @@ Object Interpreter::eval_load_file(const Object& form,
|
||||||
return Object::make_empty_list();
|
return Object::make_empty_list();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Combines read-file and eval to load in a file. Return #f if it doesn't exist.
|
||||||
|
*/
|
||||||
|
Object Interpreter::eval_try_load_file(const Object& form,
|
||||||
|
Arguments& args,
|
||||||
|
const std::shared_ptr<EnvironmentObject>& env) {
|
||||||
|
(void)env;
|
||||||
|
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||||
|
|
||||||
|
auto path = {args.unnamed.at(0).as_string()->data};
|
||||||
|
if (!std::filesystem::exists(file_util::get_file_path(path))) {
|
||||||
|
return SymbolObject::make_new(reader.symbolTable, "#f");
|
||||||
|
}
|
||||||
|
|
||||||
|
Object o;
|
||||||
|
try {
|
||||||
|
o = reader.read_from_file(path);
|
||||||
|
} catch (std::runtime_error& e) {
|
||||||
|
throw_eval_error(form, std::string("reader error inside of try-load-file:\n") + e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return eval_with_rewind(o, global_environment.as_env_ptr());
|
||||||
|
} catch (std::runtime_error& e) {
|
||||||
|
throw_eval_error(form, std::string("eval error inside of try-load-file:\n") + e.what());
|
||||||
|
}
|
||||||
|
return SymbolObject::make_new(reader.symbolTable, "#t");
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Print the form to stdout, including a newline.
|
* Print the form to stdout, including a newline.
|
||||||
* Returns ()
|
* Returns ()
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
namespace goos {
|
namespace goos {
|
||||||
class Interpreter {
|
class Interpreter {
|
||||||
public:
|
public:
|
||||||
Interpreter();
|
Interpreter(const std::string& user_profile = "#f");
|
||||||
~Interpreter();
|
~Interpreter();
|
||||||
void execute_repl(ReplWrapper& repl);
|
void execute_repl(ReplWrapper& repl);
|
||||||
void throw_eval_error(const Object& o, const std::string& err);
|
void throw_eval_error(const Object& o, const std::string& err);
|
||||||
|
@ -128,6 +128,9 @@ class Interpreter {
|
||||||
Object eval_load_file(const Object& form,
|
Object eval_load_file(const Object& form,
|
||||||
Arguments& args,
|
Arguments& args,
|
||||||
const std::shared_ptr<EnvironmentObject>& env);
|
const std::shared_ptr<EnvironmentObject>& env);
|
||||||
|
Object eval_try_load_file(const Object& form,
|
||||||
|
Arguments& args,
|
||||||
|
const std::shared_ptr<EnvironmentObject>& env);
|
||||||
Object eval_print(const Object& form,
|
Object eval_print(const Object& form,
|
||||||
Arguments& args,
|
Arguments& args,
|
||||||
const std::shared_ptr<EnvironmentObject>& env);
|
const std::shared_ptr<EnvironmentObject>& env);
|
||||||
|
|
|
@ -64,6 +64,7 @@ void TextStream::seek_past_whitespace_and_comments() {
|
||||||
case ' ':
|
case ' ':
|
||||||
case '\t':
|
case '\t':
|
||||||
case '\n':
|
case '\n':
|
||||||
|
case '\r':
|
||||||
// just a whitespace, eat it!
|
// just a whitespace, eat it!
|
||||||
read();
|
read();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1455,7 +1455,7 @@
|
||||||
(let ((startup-level (case *kernel-boot-message*
|
(let ((startup-level (case *kernel-boot-message*
|
||||||
(('play)
|
(('play)
|
||||||
(if *debug-segment*
|
(if *debug-segment*
|
||||||
'village1
|
(#if (user? dass) 'finalboss 'village1)
|
||||||
'title
|
'title
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -109,6 +109,10 @@
|
||||||
`(#cond ((not ,clause) ,@body))
|
`(#cond ((not ,clause) ,@body))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(defmacro #if (clause true false)
|
||||||
|
`(#cond (,clause ,true) (#t ,false))
|
||||||
|
)
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; TARGET CONTROL
|
;; TARGET CONTROL
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
|
@ -288,7 +288,6 @@
|
||||||
`(seval (desfun ,name ,args ,@body))
|
`(seval (desfun ,name ,args ,@body))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;
|
||||||
;; enum stuff
|
;; enum stuff
|
||||||
;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -341,3 +340,26 @@
|
||||||
|
|
||||||
;; this is checked in a test to see if this file is loaded.
|
;; this is checked in a test to see if this file is loaded.
|
||||||
(define __goos-lib-loaded__ #t)
|
(define __goos-lib-loaded__ #t)
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; USER PROFILES ;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;; *user* is defined when goos starts!
|
||||||
|
(when *user*
|
||||||
|
(fmt #t "Loading user scripts for user: {}...\n" *user*)
|
||||||
|
;; i'm not sure what naming scheme to use here. user/<name>/user.gs?
|
||||||
|
;; the GOAL one is loaded in Compiler.cpp
|
||||||
|
(load-file (fmt #f "goal_src/user/{}/user.gs" *user*))
|
||||||
|
)
|
||||||
|
|
||||||
|
(defsmacro user? (&rest users)
|
||||||
|
(cond
|
||||||
|
((null? users) #f)
|
||||||
|
((eq? *user* (car users)) #t)
|
||||||
|
(#t `(user? ,@(cdr users)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
3
goal_src/user/.gitignore
vendored
Normal file
3
goal_src/user/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!readme.md
|
14
goal_src/user/readme.md
Normal file
14
goal_src/user/readme.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
This directory holds the user profiles.
|
||||||
|
|
||||||
|
To make your own profile, create a new directory here with your username.
|
||||||
|
e.g. for username `mark` make a directory called `mark`
|
||||||
|
Inside that directory, create `user.gs` and `user.gc` files.
|
||||||
|
These are your own user scripts, loaded after the GOOS library and GOAL library respectively.
|
||||||
|
|
||||||
|
The rest of the directory can be used however you please!
|
||||||
|
|
||||||
|
To automatically log in as a specific user, create a `user.txt` file in this directory
|
||||||
|
which contains just the username you want to log in as. That way you don't have to
|
||||||
|
modify multiple scripts when you want to change users.
|
||||||
|
|
||||||
|
If you want to make your profile public, edit the .gitignore in this directory.
|
|
@ -11,8 +11,8 @@
|
||||||
|
|
||||||
using namespace goos;
|
using namespace goos;
|
||||||
|
|
||||||
Compiler::Compiler(std::unique_ptr<ReplWrapper> repl)
|
Compiler::Compiler(const std::string& user_profile, std::unique_ptr<ReplWrapper> repl)
|
||||||
: m_debugger(&m_listener, &m_goos.reader), m_repl(std::move(repl)) {
|
: m_goos(user_profile), m_debugger(&m_listener, &m_goos.reader), m_repl(std::move(repl)) {
|
||||||
m_listener.add_debugger(&m_debugger);
|
m_listener.add_debugger(&m_debugger);
|
||||||
m_ts.add_builtin_types();
|
m_ts.add_builtin_types();
|
||||||
m_global_env = std::make_unique<GlobalEnv>();
|
m_global_env = std::make_unique<GlobalEnv>();
|
||||||
|
@ -25,6 +25,11 @@ Compiler::Compiler(std::unique_ptr<ReplWrapper> repl)
|
||||||
Object library_code = m_goos.reader.read_from_file({"goal_src", "goal-lib.gc"});
|
Object library_code = m_goos.reader.read_from_file({"goal_src", "goal-lib.gc"});
|
||||||
compile_object_file("goal-lib", library_code, false);
|
compile_object_file("goal-lib", library_code, false);
|
||||||
|
|
||||||
|
if (user_profile != "#f") {
|
||||||
|
Object user_code = m_goos.reader.read_from_file({"goal_src", "user", user_profile, "user.gc"});
|
||||||
|
compile_object_file(user_profile, user_code, false);
|
||||||
|
}
|
||||||
|
|
||||||
// add built-in forms to symbol info
|
// add built-in forms to symbol info
|
||||||
for (auto& builtin : g_goal_forms) {
|
for (auto& builtin : g_goal_forms) {
|
||||||
m_symbol_info.add_builtin(builtin.first);
|
m_symbol_info.add_builtin(builtin.first);
|
||||||
|
|
|
@ -25,7 +25,7 @@ enum class ReplStatus { OK, WANT_EXIT, WANT_RELOAD };
|
||||||
|
|
||||||
class Compiler {
|
class Compiler {
|
||||||
public:
|
public:
|
||||||
Compiler(std::unique_ptr<ReplWrapper> repl = nullptr);
|
Compiler(const std::string& user_profile = "#f", std::unique_ptr<ReplWrapper> repl = nullptr);
|
||||||
ReplStatus execute_repl(bool auto_listen = false, bool auto_debug = false);
|
ReplStatus execute_repl(bool auto_listen = false, bool auto_debug = false);
|
||||||
goos::Interpreter& get_goos() { return m_goos; }
|
goos::Interpreter& get_goos() { return m_goos; }
|
||||||
FileEnv* compile_object_file(const std::string& name, goos::Object code, bool allow_emit);
|
FileEnv* compile_object_file(const std::string& name, goos::Object code, bool allow_emit);
|
||||||
|
|
|
@ -28,21 +28,44 @@ int main(int argc, char** argv) {
|
||||||
(void)argv;
|
(void)argv;
|
||||||
|
|
||||||
std::string argument;
|
std::string argument;
|
||||||
|
std::string username = "#f";
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
bool auto_listen = false;
|
bool auto_listen = false;
|
||||||
bool auto_debug = false;
|
bool auto_debug = false;
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (std::string("-v") == argv[i]) {
|
if (std::string("-v") == argv[i]) {
|
||||||
verbose = true;
|
verbose = true;
|
||||||
}
|
} else if (std::string("-cmd") == argv[i] && i + 1 < argc) {
|
||||||
if (std::string("-cmd") == argv[i] && i < argc - 1) {
|
|
||||||
argument = argv[++i];
|
argument = argv[++i];
|
||||||
}
|
} else if (std::string("-auto-lt") == argv[i]) {
|
||||||
if (std::string("-auto-lt") == argv[i]) {
|
|
||||||
auto_listen = true;
|
auto_listen = true;
|
||||||
}
|
} else if (std::string("-auto-dbg") == argv[i]) {
|
||||||
if (std::string("-auto-dbg") == argv[i]) {
|
|
||||||
auto_debug = true;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (username.empty()) {
|
||||||
|
username = "#f";
|
||||||
|
}
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
printf("error opening user desc file: %s\n", e.what());
|
||||||
|
username = "#f";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setup_logging(verbose);
|
setup_logging(verbose);
|
||||||
|
@ -56,7 +79,7 @@ int main(int argc, char** argv) {
|
||||||
if (argument.empty()) {
|
if (argument.empty()) {
|
||||||
ReplStatus status = ReplStatus::WANT_RELOAD;
|
ReplStatus status = ReplStatus::WANT_RELOAD;
|
||||||
while (status == ReplStatus::WANT_RELOAD) {
|
while (status == ReplStatus::WANT_RELOAD) {
|
||||||
compiler = std::make_unique<Compiler>(std::make_unique<ReplWrapper>());
|
compiler = std::make_unique<Compiler>(username, std::make_unique<ReplWrapper>());
|
||||||
status = compiler->execute_repl(auto_listen, auto_debug);
|
status = compiler->execute_repl(auto_listen, auto_debug);
|
||||||
if (status == ReplStatus::WANT_RELOAD) {
|
if (status == ReplStatus::WANT_RELOAD) {
|
||||||
fmt::print("Reloading compiler...\n");
|
fmt::print("Reloading compiler...\n");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@echo off
|
@echo off
|
||||||
cd ..\..
|
cd ..\..
|
||||||
out\build\Release\bin\goalc -v
|
out\build\Release\bin\goalc -v -user-auto
|
||||||
pause
|
pause
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@echo off
|
@echo off
|
||||||
cd ..\..
|
cd ..\..
|
||||||
out\build\Release\bin\goalc -v -auto-dbg
|
out\build\Release\bin\goalc -v -auto-dbg -user-auto
|
||||||
pause
|
pause
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
cd ..\..
|
cd ..\..
|
||||||
git update-index --assume-unchanged decompiler\config\jak1_ntsc_black_label.jsonc decompiler\config\jak1_ntsc_black_label\inputs.jsonc
|
git update-index --assume-unchanged decompiler\config\jak1_ntsc_black_label.jsonc decompiler\config\jak1_ntsc_black_label\inputs.jsonc goal_src\user\user.txt
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
cd ..\..
|
cd ..\..
|
||||||
git update-index --no-assume-unchanged decompiler\config\jak1_ntsc_black_label.jsonc decompiler\config\jak1_ntsc_black_label\inputs.jsonc
|
git update-index --no-assume-unchanged decompiler\config\jak1_ntsc_black_label.jsonc decompiler\config\jak1_ntsc_black_label\inputs.jsonc goal_src\user\user.txt
|
||||||
|
|
Loading…
Reference in a new issue