[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:
ManDude 2021-11-24 05:44:04 +00:00 committed by GitHub
parent 8544e95b4b
commit 59d071eccb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 130 additions and 19 deletions

View file

@ -6,10 +6,11 @@
#include <utility>
#include "Interpreter.h"
#include "ParseHelpers.h"
#include "common/util/FileUtil.h"
#include <third-party/fmt/core.h>
namespace goos {
Interpreter::Interpreter() {
Interpreter::Interpreter(const std::string& username) {
// Interpreter startup:
goal_to_goos.reset();
@ -21,9 +22,14 @@ Interpreter::Interpreter() {
// make both environments available in both.
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, 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
special_forms = {
@ -47,6 +53,7 @@ Interpreter::Interpreter() {
{"print", &Interpreter::eval_print},
{"inspect", &Interpreter::eval_inspect},
{"load-file", &Interpreter::eval_load_file},
{"try-load-file", &Interpreter::eval_try_load_file},
{"eq?", &Interpreter::eval_equals},
{"gensym", &Interpreter::eval_gensym},
{"eval", &Interpreter::eval_eval},
@ -1030,6 +1037,35 @@ Object Interpreter::eval_load_file(const Object& form,
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.
* Returns ()

View file

@ -13,7 +13,7 @@
namespace goos {
class Interpreter {
public:
Interpreter();
Interpreter(const std::string& user_profile = "#f");
~Interpreter();
void execute_repl(ReplWrapper& repl);
void throw_eval_error(const Object& o, const std::string& err);
@ -128,6 +128,9 @@ class Interpreter {
Object eval_load_file(const Object& form,
Arguments& args,
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,
Arguments& args,
const std::shared_ptr<EnvironmentObject>& env);

View file

@ -64,6 +64,7 @@ void TextStream::seek_past_whitespace_and_comments() {
case ' ':
case '\t':
case '\n':
case '\r':
// just a whitespace, eat it!
read();
break;

View file

@ -1455,7 +1455,7 @@
(let ((startup-level (case *kernel-boot-message*
(('play)
(if *debug-segment*
'village1
(#if (user? dass) 'finalboss 'village1)
'title
)
)

View file

@ -109,6 +109,10 @@
`(#cond ((not ,clause) ,@body))
)
(defmacro #if (clause true false)
`(#cond (,clause ,true) (#t ,false))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TARGET CONTROL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -288,7 +288,6 @@
`(seval (desfun ,name ,args ,@body))
)
;;;;;;;;;;;;;;;;;;;
;; enum stuff
;;;;;;;;;;;;;;;;;;;
@ -341,3 +340,26 @@
;; this is checked in a test to see if this file is loaded.
(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
View file

@ -0,0 +1,3 @@
*
!.gitignore
!readme.md

14
goal_src/user/readme.md Normal file
View 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.

View file

@ -11,8 +11,8 @@
using namespace goos;
Compiler::Compiler(std::unique_ptr<ReplWrapper> repl)
: m_debugger(&m_listener, &m_goos.reader), m_repl(std::move(repl)) {
Compiler::Compiler(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();
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"});
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
for (auto& builtin : g_goal_forms) {
m_symbol_info.add_builtin(builtin.first);

View file

@ -25,7 +25,7 @@ enum class ReplStatus { OK, WANT_EXIT, WANT_RELOAD };
class Compiler {
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);
goos::Interpreter& get_goos() { return m_goos; }
FileEnv* compile_object_file(const std::string& name, goos::Object code, bool allow_emit);

View file

@ -28,21 +28,44 @@ int main(int argc, char** argv) {
(void)argv;
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;
}
if (std::string("-cmd") == argv[i] && i < argc - 1) {
} else if (std::string("-cmd") == argv[i] && i + 1 < argc) {
argument = argv[++i];
}
if (std::string("-auto-lt") == argv[i]) {
} else if (std::string("-auto-lt") == argv[i]) {
auto_listen = true;
}
if (std::string("-auto-dbg") == argv[i]) {
} 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;
}
}
if (username.empty()) {
username = "#f";
}
} catch (std::exception& e) {
printf("error opening user desc file: %s\n", e.what());
username = "#f";
}
}
}
setup_logging(verbose);
@ -56,7 +79,7 @@ int main(int argc, char** argv) {
if (argument.empty()) {
ReplStatus 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);
if (status == ReplStatus::WANT_RELOAD) {
fmt::print("Reloading compiler...\n");

View file

@ -1,4 +1,4 @@
@echo off
cd ..\..
out\build\Release\bin\goalc -v
out\build\Release\bin\goalc -v -user-auto
pause

View file

@ -1,4 +1,4 @@
@echo off
cd ..\..
out\build\Release\bin\goalc -v -auto-dbg
out\build\Release\bin\goalc -v -auto-dbg -user-auto
pause

View file

@ -1,2 +1,2 @@
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

View file

@ -1,2 +1,2 @@
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