From 395c98db19f307e21d92c4900e42ce2dcf88b06c Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sat, 7 Oct 2023 07:48:17 -0700 Subject: [PATCH] [goalc] Cleaned up speedups (#3066) Started at 349,880,038 allocations and 42s - Switched to making `Symbol` in GOOS be a "fixed type", just a wrapper around a `const char*` pointing to the string in the symbol table. This is a step toward making a lot of things better, but by itself not a huge improvement. Some things may be worse due to more temp `std::string` allocations, but one day all these can be removed. On linux it saved allocations (347,685,429), and saved a second or two (41 s). - cache `#t` and `#f` in interpreter, better lookup for special forms/builtins (hashtable of pointers instead of strings, vector for the small special form list). Dropped time to 38s. - special-case in quasiquote when splicing is the last thing in a list. Allocation dropped to 340,603,082 - custom hash table for environment lookups (lexical vars). Dropped to 36s and 314,637,194 - less allocation in `read_list` 311,613,616. Time about the same. - `let` and `let*` in Interpreter.cpp 191,988,083, time down to 28s. --- common/goos/Interpreter.cpp | 500 +++++++++++++----- common/goos/Interpreter.h | 56 +- common/goos/Object.cpp | 94 +++- common/goos/Object.h | 177 ++++--- common/goos/ParseHelpers.cpp | 6 +- common/goos/PrettyPrinter.cpp | 2 +- common/goos/Printer.cpp | 2 +- common/goos/Reader.cpp | 73 ++- common/serialization/subtitles/subtitles.cpp | 8 +- common/serialization/text/text_ser.cpp | 15 +- common/type_system/defenum.cpp | 4 +- common/type_system/deftype.cpp | 54 +- common/type_system/deftype.h | 3 +- common/util/crc32.cpp | 40 -- common/util/crc32.h | 42 +- decompiler/IR2/FormExpressionAnalysis.cpp | 2 +- decompiler/analysis/insert_lets.cpp | 2 +- decompiler/util/DecompilerTypeSystem.cpp | 16 +- decompiler/util/data_decompile.cpp | 2 +- goal_src/goos-lib.gs | 30 +- goalc/compiler/Compiler.cpp | 12 +- goalc/compiler/Compiler.h | 13 +- goalc/compiler/CompilerSettings.cpp | 2 +- goalc/compiler/Env.cpp | 4 +- goalc/compiler/Env.h | 12 +- goalc/compiler/Lambda.h | 2 +- goalc/compiler/Util.cpp | 12 +- goalc/compiler/compilation/Asm.cpp | 14 +- goalc/compiler/compilation/Atoms.cpp | 8 +- .../compiler/compilation/CompilerControl.cpp | 21 +- .../compilation/ConstantPropagation.cpp | 10 +- goalc/compiler/compilation/ControlFlow.cpp | 6 +- goalc/compiler/compilation/Define.cpp | 10 +- goalc/compiler/compilation/Function.cpp | 37 +- goalc/compiler/compilation/Macro.cpp | 12 +- goalc/compiler/compilation/State.cpp | 38 +- goalc/compiler/compilation/Static.cpp | 43 +- goalc/compiler/compilation/Type.cpp | 35 +- goalc/make/MakeSystem.cpp | 4 +- lsp/state/workspace.cpp | 24 +- lsp/state/workspace.h | 13 +- test/test_goos.cpp | 39 +- test/test_reader.cpp | 2 +- 43 files changed, 950 insertions(+), 551 deletions(-) diff --git a/common/goos/Interpreter.cpp b/common/goos/Interpreter.cpp index cf4f6b3e642..b7d50df3092 100644 --- a/common/goos/Interpreter.cpp +++ b/common/goos/Interpreter.cpp @@ -12,6 +12,7 @@ #include "common/goos/Printer.h" #include "common/log/log.h" #include "common/util/FileUtil.h" +#include "common/util/crc32.h" #include "common/util/string_util.h" #include "common/util/unicode_util.h" @@ -19,6 +20,10 @@ namespace goos { Interpreter::Interpreter(const std::string& username) { + m_true_object = intern("#t"); + m_false_object = intern("#f"); + m_true_sym = m_true_object.as_symbol().name_ptr; + m_false_sym = m_false_object.as_symbol().name_ptr; // Interpreter startup: // create the GOOS global environment global_environment = EnvironmentObject::make_new("global"); @@ -33,14 +38,16 @@ Interpreter::Interpreter(const std::string& username) { define_var_in_env(goal_env, global_environment, "*global-env*"); // set user profile name - auto user = SymbolObject::make_new(reader.symbolTable, username); + auto user = Object::make_symbol(&reader.symbolTable, username.c_str()); define_var_in_env(global_environment, user, "*user*"); // setup maps - special_forms = { + init_special_forms({ {"define", &Interpreter::eval_define}, {"quote", &Interpreter::eval_quote}, {"set!", &Interpreter::eval_set}, + {"let", &Interpreter::eval_let}, + {"let*", &Interpreter::eval_let_star}, {"lambda", &Interpreter::eval_lambda}, {"cond", &Interpreter::eval_cond}, {"or", &Interpreter::eval_or}, @@ -48,54 +55,54 @@ Interpreter::Interpreter(const std::string& username) { {"macro", &Interpreter::eval_macro}, {"quasiquote", &Interpreter::eval_quasiquote}, {"while", &Interpreter::eval_while}, - }; - - builtin_forms = {{"top-level", &Interpreter::eval_begin}, - {"begin", &Interpreter::eval_begin}, - {"exit", &Interpreter::eval_exit}, - {"read", &Interpreter::eval_read}, - {"read-data-file", &Interpreter::eval_read_data_file}, - {"read-file", &Interpreter::eval_read_file}, - {"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}, - {"cons", &Interpreter::eval_cons}, - {"car", &Interpreter::eval_car}, - {"cdr", &Interpreter::eval_cdr}, - {"set-car!", &Interpreter::eval_set_car}, - {"set-cdr!", &Interpreter::eval_set_cdr}, - {"+", &Interpreter::eval_plus}, - {"-", &Interpreter::eval_minus}, - {"*", &Interpreter::eval_times}, - {"/", &Interpreter::eval_divide}, - {"=", &Interpreter::eval_numequals}, - {"<", &Interpreter::eval_lt}, - {">", &Interpreter::eval_gt}, - {"<=", &Interpreter::eval_leq}, - {">=", &Interpreter::eval_geq}, - {"null?", &Interpreter::eval_null}, - {"type?", &Interpreter::eval_type}, - {"fmt", &Interpreter::eval_format}, - {"error", &Interpreter::eval_error}, - {"string-ref", &Interpreter::eval_string_ref}, - {"string-length", &Interpreter::eval_string_length}, - {"string-append", &Interpreter::eval_string_append}, - {"string-starts-with?", &Interpreter::eval_string_starts_with}, - {"string-ends-with?", &Interpreter::eval_string_ends_with}, - {"string-split", &Interpreter::eval_string_split}, - {"string-substr", &Interpreter::eval_string_substr}, - {"ash", &Interpreter::eval_ash}, - {"symbol->string", &Interpreter::eval_symbol_to_string}, - {"string->symbol", &Interpreter::eval_string_to_symbol}, - {"int->string", &Interpreter::eval_int_to_string}, - {"get-environment-variable", &Interpreter::eval_get_env}, - {"make-string-hash-table", &Interpreter::eval_make_string_hash_table}, - {"hash-table-set!", &Interpreter::eval_hash_table_set}, - {"hash-table-try-ref", &Interpreter::eval_hash_table_try_ref}}; + }); + + init_builtin_forms({{"top-level", &Interpreter::eval_begin}, + {"begin", &Interpreter::eval_begin}, + {"exit", &Interpreter::eval_exit}, + {"read", &Interpreter::eval_read}, + {"read-data-file", &Interpreter::eval_read_data_file}, + {"read-file", &Interpreter::eval_read_file}, + {"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}, + {"cons", &Interpreter::eval_cons}, + {"car", &Interpreter::eval_car}, + {"cdr", &Interpreter::eval_cdr}, + {"set-car!", &Interpreter::eval_set_car}, + {"set-cdr!", &Interpreter::eval_set_cdr}, + {"+", &Interpreter::eval_plus}, + {"-", &Interpreter::eval_minus}, + {"*", &Interpreter::eval_times}, + {"/", &Interpreter::eval_divide}, + {"=", &Interpreter::eval_numequals}, + {"<", &Interpreter::eval_lt}, + {">", &Interpreter::eval_gt}, + {"<=", &Interpreter::eval_leq}, + {">=", &Interpreter::eval_geq}, + {"null?", &Interpreter::eval_null}, + {"type?", &Interpreter::eval_type}, + {"fmt", &Interpreter::eval_format}, + {"error", &Interpreter::eval_error}, + {"string-ref", &Interpreter::eval_string_ref}, + {"string-length", &Interpreter::eval_string_length}, + {"int->string", &Interpreter::eval_int_to_string}, + {"string-append", &Interpreter::eval_string_append}, + {"string-starts-with?", &Interpreter::eval_string_starts_with}, + {"string-ends-with?", &Interpreter::eval_string_ends_with}, + {"string-split", &Interpreter::eval_string_split}, + {"string-substr", &Interpreter::eval_string_substr}, + {"ash", &Interpreter::eval_ash}, + {"symbol->string", &Interpreter::eval_symbol_to_string}, + {"string->symbol", &Interpreter::eval_string_to_symbol}, + {"get-environment-variable", &Interpreter::eval_get_env}, + {"make-string-hash-table", &Interpreter::eval_make_string_hash_table}, + {"hash-table-set!", &Interpreter::eval_hash_table_set}, + {"hash-table-try-ref", &Interpreter::eval_hash_table_try_ref}}); string_to_type = {{"empty-list", ObjectType::EMPTY_LIST}, {"integer", ObjectType::INTEGER}, @@ -113,6 +120,28 @@ Interpreter::Interpreter(const std::string& username) { load_goos_library(); } +void Interpreter::init_builtin_forms( + const std::unordered_map&)>& + forms) { + for (const auto& [name, fn] : forms) { + builtin_forms[(void*)intern_ptr(name).name_ptr] = fn; + } +} + +void Interpreter::init_special_forms( + const std::unordered_map&)>& + forms) { + for (const auto& [name, fn] : forms) { + special_forms.push_back(std::make_pair((void*)intern_ptr(name).name_ptr, fn)); + } +} + /*! * Add a user defined special form. The given function will be called with unevaluated arguments. * Lookup from these forms occurs after special/builtin, but before any env lookups. @@ -121,7 +150,7 @@ void Interpreter::register_form( const std::string& name, const std::function< Object(const Object&, Arguments&, const std::shared_ptr&)>& form) { - m_custom_forms[name] = form; + m_custom_forms.push_back(std::make_pair((void*)intern_ptr(name).name_ptr, form)); } Interpreter::~Interpreter() { @@ -149,19 +178,19 @@ void Interpreter::load_goos_library() { /*! * In env, set the variable named "name" to the value var. */ -void Interpreter::define_var_in_env(Object& env, Object& var, const std::string& name) { - env.as_env()->vars[intern_ptr(name)] = var; +void Interpreter::define_var_in_env(Object& env, const Object& var, const std::string& name) { + env.as_env()->vars.set(InternedSymbolPtr{intern_ptr(name)}, var); } /*! * Get a symbol with the given name, creating one if none exist. */ Object Interpreter::intern(const std::string& name) { - return SymbolObject::make_new(reader.symbolTable, name); + return Object::make_symbol(&reader.symbolTable, name.c_str()); } -HeapObject* Interpreter::intern_ptr(const std::string& name) { - return reader.symbolTable.intern_ptr(name); +InternedSymbolPtr Interpreter::intern_ptr(const std::string& name) { + return reader.symbolTable.intern(name.c_str()); } /*! @@ -218,10 +247,9 @@ Object Interpreter::eval_with_rewind(const Object& obj, * Returns if the variable was found. */ bool Interpreter::get_global_variable_by_name(const std::string& name, Object* dest) { - auto kv = global_environment.as_env()->vars.find( - SymbolObject::make_new(reader.symbolTable, name).as_symbol()); - if (kv != global_environment.as_env()->vars.end()) { - *dest = kv->second; + auto* obj = global_environment.as_env()->find(name.c_str(), &reader.symbolTable); + if (obj) { + *dest = *obj; return true; } return false; @@ -231,12 +259,11 @@ bool Interpreter::get_global_variable_by_name(const std::string& name, Object* d * Sets the variable to the value. Overwrites an existing value, or creates a new global. */ void Interpreter::set_global_variable_by_name(const std::string& name, const Object& value) { - auto sym = SymbolObject::make_new(reader.symbolTable, name).as_symbol(); - global_environment.as_env()->vars[sym] = value; + define_var_in_env(global_environment, value, name); } void Interpreter::set_global_variable_to_symbol(const std::string& name, const std::string& value) { - auto sym = SymbolObject::make_new(reader.symbolTable, value); + auto sym = Object::make_symbol(&reader.symbolTable, value.c_str()); set_global_variable_by_name(name, sym); } @@ -269,18 +296,18 @@ Arguments Interpreter::get_args(const Object& form, const Object& rest, const Ar const auto& arg = current->as_pair()->car; // did we get a ":keyword" - if (arg.is_symbol() && arg.as_symbol()->name.at(0) == ':') { - auto key_name = arg.as_symbol()->name.substr(1); + if (arg.is_symbol() && arg.as_symbol().name_ptr[0] == ':') { + auto key_name = arg.as_symbol().name_ptr + 1; const auto& kv = spec.named.find(key_name); // check for unknown key name if (!spec.varargs && kv == spec.named.end()) { - throw_eval_error(form, "Key argument " + key_name + " wasn't expected"); + throw_eval_error(form, fmt::format("Key argument {} wasn't expected", key_name)); } // check for multiple definition of key if (args.named.find(key_name) != args.named.end()) { - throw_eval_error(form, "Key argument " + key_name + " multiply defined"); + throw_eval_error(form, fmt::format("Key argument {} multiply defined", key_name)); } // check for well-formed :key value expression @@ -409,7 +436,7 @@ ArgumentSpec Interpreter::parse_arg_spec(const Object& form, Object& rest) { throw_eval_error(form, "args must be symbols"); } - if (arg.as_symbol()->name == "&rest") { + if (arg.as_symbol() == "&rest") { // special case for &rest current = current.as_pair()->cdr; if (!current.is_pair()) { @@ -420,20 +447,20 @@ ArgumentSpec Interpreter::parse_arg_spec(const Object& form, Object& rest) { throw_eval_error(form, "rest name must be a symbol"); } - spec.rest = rest_name.as_symbol()->name; + spec.rest = rest_name.as_symbol().name_ptr; if (!current.as_pair()->cdr.is_empty_list()) { throw_eval_error(form, "rest must be the last argument"); } - } else if (arg.as_symbol()->name == "&key") { + } else if (arg.as_symbol() == "&key") { // special case for &key current = current.as_pair()->cdr; auto key_arg = current.as_pair()->car; if (key_arg.is_symbol()) { // form is &key name - auto key_arg_name = key_arg.as_symbol()->name; + auto key_arg_name = key_arg.as_symbol().name_ptr; if (spec.named.find(key_arg_name) != spec.named.end()) { - throw_eval_error(form, "key argument " + key_arg_name + " multiply defined"); + throw_eval_error(form, fmt::format("key argument {} multiply defined", key_arg_name)); } spec.named[key_arg_name] = NamedArg(); } else if (key_arg.is_pair()) { @@ -444,9 +471,9 @@ ArgumentSpec Interpreter::parse_arg_spec(const Object& form, Object& rest) { if (!kn.is_symbol()) { throw_eval_error(form, "key argument must have a symbol as a name"); } - auto key_arg_name = kn.as_symbol()->name; + auto key_arg_name = kn.as_symbol().name_ptr; if (spec.named.find(key_arg_name) != spec.named.end()) { - throw_eval_error(form, "key argument " + key_arg_name + " multiply defined"); + throw_eval_error(form, fmt::format("key argument {} multiply defined", key_arg_name)); } NamedArg na; @@ -466,7 +493,7 @@ ArgumentSpec Interpreter::parse_arg_spec(const Object& form, Object& rest) { throw_eval_error(form, "invalid key argument"); } } else { - spec.unnamed.push_back(arg.as_symbol()->name); + spec.unnamed.push_back(arg.as_symbol().name_ptr); } current = current.as_pair()->cdr; @@ -497,17 +524,19 @@ void Interpreter::vararg_check( Object Interpreter::eval_list_return_last(const Object& form, Object rest, const std::shared_ptr& env) { - Object o = std::move(rest); - Object rv = Object::make_empty_list(); - for (;;) { - if (o.is_pair()) { - auto op = o.as_pair(); - rv = eval_with_rewind(op->car, env); - o = op->cdr; - } else if (o.is_empty_list()) { - return rv; + if (rest.is_empty_list()) { + return rest; + } + + const Object* iter = &rest; + while (true) { + const Object* next = &iter->as_pair()->cdr; + const Object* item = &iter->as_pair()->car; + if (next->is_empty_list()) { + return eval_with_rewind(*item, env); } else { - throw_eval_error(form, "malformed body to evaluate"); + eval(*item, env); + iter = next; } } } @@ -542,6 +571,104 @@ Object Interpreter::eval(Object obj, const std::shared_ptr& e } } +EnvironmentMap::EnvironmentMap() { + clear(); +} + +void EnvironmentMap::clear() { + m_entries.clear(); + m_power_of_two_size = 3; // 2 ^ 3 = 8 + m_entries.resize(8); + m_used_entries = 0; + m_next_resize = (m_entries.size() * kMaxUsed); + m_mask = 0b111; +} + +Object* EnvironmentMap::lookup(InternedSymbolPtr str) { + if (m_entries.size() < 10) { + for (auto& e : m_entries) { + if (e.key == str.name_ptr) { + return &e.value; + } + } + return nullptr; + } + u32 hash = crc32((const u8*)&str.name_ptr, sizeof(const char*)); + + // probe + for (u32 i = 0; i < m_entries.size(); i++) { + u32 slot_addr = (hash + i) & m_mask; + auto& slot = m_entries[slot_addr]; + if (!slot.key) { + return nullptr; + } else { + if (slot.key != str.name_ptr) { + continue; // bad hash + } + return &slot.value; + } + } + + // should be impossible to reach. + ASSERT_NOT_REACHED(); +} + +void EnvironmentMap::set(InternedSymbolPtr ptr, const Object& obj) { + u32 hash = crc32((const u8*)&ptr.name_ptr, sizeof(const char*)); + + // probe + for (u32 i = 0; i < m_entries.size(); i++) { + u32 slot_addr = (hash + i) & m_mask; + auto& slot = m_entries[slot_addr]; + if (!slot.key) { + // not found, insert! + slot.key = ptr.name_ptr; + slot.value = obj; + m_used_entries++; + + if (m_used_entries >= m_next_resize) { + resize(); + } + return; + } else { + if (slot.key == ptr.name_ptr) { + slot.value = obj; + return; + } + } + } + + // should be impossible to reach. + ASSERT_NOT_REACHED(); +} + +void EnvironmentMap::resize() { + m_power_of_two_size++; + m_mask = (1U << m_power_of_two_size) - 1; + + std::vector new_entries(m_entries.size() * 2); + for (const auto& old_entry : m_entries) { + if (old_entry.key) { + bool done = false; + u32 hash = crc32((const u8*)&old_entry.key, sizeof(const char*)); + for (u32 i = 0; i < new_entries.size(); i++) { + u32 slot_addr = (hash + i) & m_mask; + auto& slot = new_entries[slot_addr]; + if (!slot.key) { + slot.key = old_entry.key; + slot.value = std::move(old_entry.value); + done = true; + break; + } + } + ASSERT(done); + } + } + + m_entries = std::move(new_entries); + m_next_resize = kMaxUsed * m_entries.size(); +} + namespace { /*! @@ -552,7 +679,7 @@ bool try_symbol_lookup(const Object& sym, const std::shared_ptr& env, Object* dest) { // booleans are hard-coded here - if (sym.as_symbol()->name == "#t" || sym.as_symbol()->name == "#f") { + if (sym.as_symbol() == "#t" || sym.as_symbol() == "#f") { *dest = sym; return true; } @@ -560,9 +687,9 @@ bool try_symbol_lookup(const Object& sym, // loop up envs until we find it. EnvironmentObject* search_env = env.get(); for (;;) { - const auto& kv = search_env->vars.find(sym.as_symbol()); - if (kv != search_env->vars.end()) { - *dest = kv->second; + auto* obj = search_env->vars.lookup(sym.as_symbol()); + if (obj) { + *dest = *obj; return true; } @@ -593,6 +720,59 @@ bool Interpreter::eval_symbol(const Object& sym, return try_symbol_lookup(sym, env, result); } +Object Interpreter::eval_let_star(const goos::Object& form, + const goos::Object& rest, + const std::shared_ptr& env) { + return eval_let_common(form, rest, env, true); +} + +Object Interpreter::eval_let(const goos::Object& form, + const goos::Object& rest, + const std::shared_ptr& env) { + return eval_let_common(form, rest, env, false); +} + +Object Interpreter::eval_let_common(const goos::Object& form, + const goos::Object& rest, + const std::shared_ptr& env, + bool is_star) { + if (!rest.is_pair()) { + throw_eval_error(form, "first argument to let must be bindings"); + } + + const auto* bindings_iter = &rest.as_pair()->car; + const auto* body_iter = &rest.as_pair()->cdr; + + if (!bindings_iter->is_pair()) { + throw_eval_error(form, "let cannot have empty bindings"); + } + + std::shared_ptr new_env = std::make_shared(); + new_env->parent_env = env; + + while (!bindings_iter->is_empty_list()) { + const auto* binding = &bindings_iter->as_pair()->car; + if (!binding->is_pair()) { + throw_eval_error(form, "let binding invalid"); + } + const auto& name = binding->as_pair()->car; + if (!name.is_symbol()) { + throw_eval_error(form, "let binding invalid"); + } + + binding = &binding->as_pair()->cdr; + if (!binding->is_pair() || !binding->as_pair()->cdr.is_empty_list()) { + throw_eval_error(form, "let binding invalid"); + } + + new_env->vars.set(name.as_symbol(), eval(binding->as_pair()->car, is_star ? new_env : env)); + + bindings_iter = &bindings_iter->as_pair()->cdr; + } + + return eval_list_return_last(*body_iter, *body_iter, new_env); +} + /*! * Evaluate a pair, either as special form, builtin form, macro application, or lambda application. */ @@ -606,13 +786,14 @@ Object Interpreter::eval_pair(const Object& obj, const std::shared_ptrname); - if (kv_sf != special_forms.end()) { - return ((*this).*(kv_sf->second))(obj, rest, env); + for (const auto& sf : special_forms) { + if (sf.first == head_sym.name_ptr) { + return ((*this).*(sf.second))(obj, rest, env); + } } // try builtins next - const auto& kv_b = builtin_forms.find(head_sym->name); + const auto& kv_b = builtin_forms.find((void*)head_sym.name_ptr); if (kv_b != builtin_forms.end()) { Arguments args = get_args(obj, rest, make_varargs()); // all "built-in" forms expect arguments to be evaluated (that's why they aren't special) @@ -621,10 +802,11 @@ Object Interpreter::eval_pair(const Object& obj, const std::shared_ptrname); - if (kv_u != m_custom_forms.end()) { - Arguments args = get_args(obj, rest, make_varargs()); - return (kv_u->second)(obj, args, env); + for (const auto& cf : m_custom_forms) { + if (cf.first == head_sym.name_ptr) { + Arguments args = get_args(obj, rest, make_varargs()); + return (cf.second)(obj, args, env); + } } // try macros next @@ -678,18 +860,18 @@ void Interpreter::set_args_in_env(const Object& form, // unnamed args for (size_t i = 0; i < arg_spec.unnamed.size(); i++) { - env->vars[intern(arg_spec.unnamed.at(i)).as_symbol()] = args.unnamed.at(i); + env->vars.set(intern_ptr(arg_spec.unnamed.at(i).c_str()), args.unnamed.at(i)); } // named args for (const auto& kv : arg_spec.named) { - env->vars[intern(kv.first).as_symbol()] = args.named.at(kv.first); + env->vars.set(intern_ptr(kv.first), args.named.at(kv.first)); } // rest args if (!arg_spec.rest.empty()) { // will correctly handle the '() case - env->vars[intern(arg_spec.rest).as_symbol()] = build_list(args.rest); + env->vars.set(intern_ptr(arg_spec.rest), build_list(args.rest)); } else { if (!args.rest.empty()) { throw_eval_error(form, "got too many arguments"); @@ -714,7 +896,7 @@ Object Interpreter::eval_define(const Object& form, } Object value = eval_with_rewind(args.unnamed[1], env); - define_env->vars[args.unnamed[0].as_symbol()] = value; + define_env->vars.set(args.unnamed[0].as_symbol(), value); return value; } @@ -732,10 +914,10 @@ Object Interpreter::eval_set(const Object& form, std::shared_ptr search_env = env; for (;;) { - auto kv = search_env->vars.find(to_define.as_symbol()); - if (kv != search_env->vars.end()) { - kv->second = to_set; - return kv->second; + auto kv = search_env->vars.lookup(to_define.as_symbol()); + if (kv) { + search_env->vars.set(to_define.as_symbol(), to_set); + return to_set; } auto pe = search_env->parent_env; @@ -819,6 +1001,32 @@ Object Interpreter::eval_quote(const Object& form, return args.unnamed.front(); } +Object build_list_with_spliced_tail(std::vector&& objects, const Object& tail) { + if (objects.empty()) { + return tail; + } + + std::shared_ptr head = std::make_shared(objects.back(), tail); + + s64 idx = ((s64)objects.size()) - 2; + while (idx >= 0) { + Object next; + next.type = ObjectType::PAIR; + next.heap_obj = std::move(head); + + head = std::make_shared(); + head->car = std::move(objects[idx]); + head->cdr = std::move(next); + + idx--; + } + + Object result; + result.type = ObjectType::PAIR; + result.heap_obj = std::move(head); + return result; +} + /*! * Recursive quasi-quote evaluation */ @@ -831,7 +1039,7 @@ Object Interpreter::quasiquote_helper(const Object& form, const Object& item = lst_iter->as_pair()->car; if (item.type == ObjectType::PAIR) { if (item.as_pair()->car.type == ObjectType::SYMBOL && - item.as_pair()->car.as_symbol()->name == "unquote") { + item.as_pair()->car.as_symbol() == "unquote") { const Object& unquote_arg = item.as_pair()->cdr; if (unquote_arg.type != ObjectType::PAIR || unquote_arg.as_pair()->cdr.type != ObjectType::EMPTY_LIST) { @@ -841,7 +1049,7 @@ Object Interpreter::quasiquote_helper(const Object& form, lst_iter = &lst_iter->as_pair()->cdr; continue; } else if (item.as_pair()->car.type == ObjectType::SYMBOL && - item.as_pair()->car.as_symbol()->name == "unquote-splicing") { + item.as_pair()->car.as_symbol() == "unquote-splicing") { const Object& unquote_arg = item.as_pair()->cdr; if (unquote_arg.type != ObjectType::PAIR || unquote_arg.as_pair()->cdr.type != ObjectType::EMPTY_LIST) { @@ -851,6 +1059,11 @@ Object Interpreter::quasiquote_helper(const Object& form, // bypass normal addition: lst_iter = &lst_iter->as_pair()->cdr; Object splice_result = eval_with_rewind(unquote_arg.as_pair()->car, env); + if (lst_iter->type == ObjectType::EMPTY_LIST) { + // optimization! + return build_list_with_spliced_tail(std::move(result), splice_result); + } + const Object* to_add = &splice_result; for (;;) { if (to_add->type == ObjectType::PAIR) { @@ -864,8 +1077,13 @@ Object Interpreter::quasiquote_helper(const Object& form, } continue; } else { - result.push_back(quasiquote_helper(item, env)); lst_iter = &lst_iter->as_pair()->cdr; + + if (item.is_pair()) { + result.push_back(quasiquote_helper(item, env)); + } else { + result.push_back(item); + } continue; } } @@ -891,7 +1109,7 @@ Object Interpreter::eval_quasiquote(const Object& form, } bool Interpreter::truthy(const Object& o) { - return !(o.is_symbol() && o.as_symbol()->name == "#f"); + return !(o.is_symbol() && o.as_symbol().name_ptr == m_false_sym); } /*! @@ -924,7 +1142,7 @@ Object Interpreter::eval_cond(const Object& form, lst = lst.as_pair()->cdr; } } else if (lst.type == ObjectType::EMPTY_LIST) { - return SymbolObject::make_new(reader.symbolTable, "#f"); + return m_false_object; } else { throw_eval_error(form, "malformed cond"); } @@ -950,7 +1168,7 @@ Object Interpreter::eval_or(const Object& form, } lst = lst.as_pair()->cdr; } else if (lst.type == ObjectType::EMPTY_LIST) { - return SymbolObject::make_new(reader.symbolTable, "#f"); + return m_false_object; } else { throw_eval_error(form, "invalid or form"); } @@ -973,7 +1191,7 @@ Object Interpreter::eval_and(const Object& form, if (lst.type == ObjectType::PAIR) { current = eval_with_rewind(lst.as_pair()->car, env); if (!truthy(current)) { - return SymbolObject::make_new(reader.symbolTable, "#f"); + return m_false_object; } lst = lst.as_pair()->cdr; } else if (lst.type == ObjectType::EMPTY_LIST) { @@ -1000,7 +1218,7 @@ Object Interpreter::eval_while(const Object& form, throw_eval_error(form, "while must have condition and body"); } - Object rv = SymbolObject::make_new(reader.symbolTable, "#f"); + Object rv = m_false_object; while (truthy(eval_with_rewind(condition, env))) { rv = eval_list_return_last(form, body, env); } @@ -1126,7 +1344,7 @@ Object Interpreter::eval_try_load_file(const Object& form, auto path = {args.unnamed.at(0).as_string()->data}; if (!fs::exists(file_util::get_file_path(path))) { - return SymbolObject::make_new(reader.symbolTable, "#f"); + return m_false_object; } Object o; @@ -1141,7 +1359,7 @@ Object Interpreter::eval_try_load_file(const Object& form, } 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"); + return m_true_object; } /*! @@ -1177,6 +1395,14 @@ Object Interpreter::eval_inspect(const Object& form, return Object::make_empty_list(); } +const Object& Interpreter::true_or_false(bool val) { + if (val) { + return m_true_object; + } else { + return m_false_object; + } +} + /*! * Fancy equality check (using Object::operator==) */ @@ -1185,8 +1411,7 @@ Object Interpreter::eval_equals(const Object& form, const std::shared_ptr& env) { (void)env; vararg_check(form, args, {{}, {}}, {}); - return SymbolObject::make_new(reader.symbolTable, - args.unnamed[0] == args.unnamed[1] ? "#t" : "#f"); + return true_or_false(args.unnamed[0] == args.unnamed[1]); } /*! @@ -1430,7 +1655,7 @@ Object Interpreter::eval_numequals(const Object& form, return Object::make_empty_list(); } - return SymbolObject::make_new(reader.symbolTable, result ? "#t" : "#f"); + return true_or_false(result); } template @@ -1441,7 +1666,7 @@ Object Interpreter::num_lt(const Object& form, (void)env; T a = number(args.unnamed[0]); T b = number(args.unnamed[1]); - return SymbolObject::make_new(reader.symbolTable, (a < b) ? "#t" : "#f"); + return true_or_false(a < b); } Object Interpreter::eval_lt(const Object& form, @@ -1469,7 +1694,7 @@ Object Interpreter::num_gt(const Object& form, (void)env; T a = number(args.unnamed[0]); T b = number(args.unnamed[1]); - return SymbolObject::make_new(reader.symbolTable, (a > b) ? "#t" : "#f"); + return true_or_false(a > b); } Object Interpreter::eval_gt(const Object& form, @@ -1497,7 +1722,7 @@ Object Interpreter::num_leq(const Object& form, (void)env; T a = number(args.unnamed[0]); T b = number(args.unnamed[1]); - return SymbolObject::make_new(reader.symbolTable, (a <= b) ? "#t" : "#f"); + return true_or_false(a <= b); } Object Interpreter::eval_leq(const Object& form, @@ -1525,7 +1750,7 @@ Object Interpreter::num_geq(const Object& form, (void)env; T a = number(args.unnamed[0]); T b = number(args.unnamed[1]); - return SymbolObject::make_new(reader.symbolTable, (a >= b) ? "#t" : "#f"); + return true_or_false(a >= b); } Object Interpreter::eval_geq(const Object& form, @@ -1591,7 +1816,7 @@ Object Interpreter::eval_gensym(const Object& form, const std::shared_ptr& env) { (void)env; vararg_check(form, args, {}, {}); - return SymbolObject::make_new(reader.symbolTable, "gensym" + std::to_string(gensym_id++)); + return Object::make_symbol(&reader.symbolTable, ("gensym" + std::to_string(gensym_id++)).c_str()); } Object Interpreter::eval_cons(const Object& form, @@ -1607,7 +1832,7 @@ Object Interpreter::eval_null(const Object& form, const std::shared_ptr& env) { (void)env; vararg_check(form, args, {{}}, {}); - return SymbolObject::make_new(reader.symbolTable, args.unnamed[0].is_empty_list() ? "#t" : "#f"); + return true_or_false(args.unnamed[0].is_empty_list()); } Object Interpreter::eval_type(const Object& form, @@ -1616,15 +1841,15 @@ Object Interpreter::eval_type(const Object& form, (void)env; vararg_check(form, args, {{ObjectType::SYMBOL}, {}}, {}); - auto kv = string_to_type.find(args.unnamed[0].as_symbol()->name); + auto kv = string_to_type.find(args.unnamed[0].as_symbol().name_ptr); if (kv == string_to_type.end()) { throw_eval_error(form, "invalid type given to type?"); } if (args.unnamed[1].type == kv->second) { - return SymbolObject::make_new(reader.symbolTable, "#t"); + return m_true_object; } else { - return SymbolObject::make_new(reader.symbolTable, "#f"); + return m_false_object; } } @@ -1729,9 +1954,9 @@ Object Interpreter::eval_string_starts_with(const Object& form, auto& suffix = args.unnamed.at(1).as_string()->data; if (str_util::starts_with(str, suffix)) { - return SymbolObject::make_new(reader.symbolTable, "#t"); + return m_true_object; } - return SymbolObject::make_new(reader.symbolTable, "#f"); + return m_false_object; } Object Interpreter::eval_string_ends_with(const Object& form, @@ -1743,9 +1968,9 @@ Object Interpreter::eval_string_ends_with(const Object& form, auto& suffix = args.unnamed.at(1).as_string()->data; if (str_util::ends_with(str, suffix)) { - return SymbolObject::make_new(reader.symbolTable, "#t"); + return m_true_object; } - return SymbolObject::make_new(reader.symbolTable, "#f"); + return m_false_object; } Object Interpreter::eval_string_split(const Object& form, @@ -1792,14 +2017,14 @@ Object Interpreter::eval_symbol_to_string(const Object& form, Arguments& args, const std::shared_ptr&) { vararg_check(form, args, {ObjectType::SYMBOL}, {}); - return StringObject::make_new(args.unnamed.at(0).as_symbol()->name); + return StringObject::make_new(args.unnamed.at(0).as_symbol().name_ptr); } Object Interpreter::eval_string_to_symbol(const Object& form, Arguments& args, const std::shared_ptr&) { vararg_check(form, args, {ObjectType::STRING}, {}); - return SymbolObject::make_new(reader.symbolTable, args.unnamed.at(0).as_string()->data); + return Object::make_symbol(&reader.symbolTable, args.unnamed.at(0).as_string()->data.c_str()); } Object Interpreter::eval_int_to_string(const Object& form, @@ -1846,7 +2071,7 @@ Object Interpreter::eval_hash_table_set(const Object& form, vararg_check(form, args, {ObjectType::STRING_HASH_TABLE, {}, {}}, {}); const char* str = nullptr; if (args.unnamed.at(1).is_symbol()) { - str = args.unnamed.at(1).as_symbol()->name.c_str(); + str = args.unnamed.at(1).as_symbol().name_ptr; } else if (args.unnamed.at(1).is_string()) { str = args.unnamed.at(1).as_string()->data.c_str(); } else { @@ -1868,7 +2093,7 @@ Object Interpreter::eval_hash_table_try_ref(const Object& form, const char* str = nullptr; if (args.unnamed.at(1).is_symbol()) { - str = args.unnamed.at(1).as_symbol()->name.c_str(); + str = args.unnamed.at(1).as_symbol().name_ptr; } else if (args.unnamed.at(1).is_string()) { str = args.unnamed.at(1).as_string()->data.c_str(); } else { @@ -1877,10 +2102,9 @@ Object Interpreter::eval_hash_table_try_ref(const Object& form, const auto& it = table->data.find(str); if (it == table->data.end()) { // not in table - return PairObject::make_new(SymbolObject::make_new(reader.symbolTable, "#f"), - Object::make_empty_list()); + return PairObject::make_new(m_false_object, Object::make_empty_list()); } else { - return PairObject::make_new(SymbolObject::make_new(reader.symbolTable, "#t"), it->second); + return PairObject::make_new(m_true_object, it->second); } } } // namespace goos diff --git a/common/goos/Interpreter.h b/common/goos/Interpreter.h index 661c467ee4c..9fe2694455b 100644 --- a/common/goos/Interpreter.h +++ b/common/goos/Interpreter.h @@ -25,7 +25,7 @@ class Interpreter { void set_global_variable_to_int(const std::string& name, int value); Object eval(Object obj, const std::shared_ptr& env); Object intern(const std::string& name); - HeapObject* intern_ptr(const std::string& name); + InternedSymbolPtr intern_ptr(const std::string& name); void disable_printfs(); Object eval_symbol(const Object& sym, const std::shared_ptr& env); bool eval_symbol(const Object& sym, @@ -55,7 +55,7 @@ class Interpreter { private: friend class Goal; void load_goos_library(); - void define_var_in_env(Object& env, Object& var, const std::string& name); + void define_var_in_env(Object& env, const Object& var, const std::string& name); void expect_env(const Object& form, const Object& o); void vararg_check( const Object& form, @@ -264,6 +264,16 @@ class Interpreter { Object eval_while(const Object& form, const Object& rest, const std::shared_ptr& env); + Object eval_let(const Object& form, + const Object& rest, + const std::shared_ptr& env); + Object eval_let_star(const Object& form, + const Object& rest, + const std::shared_ptr& env); + Object eval_let_common(const Object& form, + const Object& rest, + const std::shared_ptr& env, + bool star); Object eval_make_string_hash_table(const Object& form, Arguments& args, @@ -275,26 +285,46 @@ class Interpreter { Arguments& args, const std::shared_ptr& env); + const Object& true_or_false(bool val); + + void init_special_forms( + const std::unordered_map&)>& + forms); + + void init_builtin_forms( + const std::unordered_map&)>& + forms); + bool want_exit = false; bool disable_printing = false; - std::unordered_map& env)> + std::unordered_map< + void*, + Object (Interpreter::*)(const Object&, Arguments&, const std::shared_ptr&)> builtin_forms; - std::unordered_map< - std::string, - std::function&)>> + std::vector&)>>> m_custom_forms; - std::unordered_map& env)> + + std::vector& env)>> special_forms; int64_t gensym_id = 0; std::unordered_map string_to_type; + + const char* m_false_sym = nullptr; + const char* m_true_sym = nullptr; + Object m_false_object, m_true_object; }; } // namespace goos diff --git a/common/goos/Object.cpp b/common/goos/Object.cpp index c2c4bcf9e29..9bb5b3a2aca 100644 --- a/common/goos/Object.cpp +++ b/common/goos/Object.cpp @@ -44,12 +44,90 @@ #include #include "common/util/FileUtil.h" +#include "common/util/crc32.h" #include "common/util/print_float.h" #include "third-party/fmt/core.h" namespace goos { +SymbolTable::SymbolTable() { + m_power_of_two_size = 1; // 2 ^ 1 = 2 + m_entries.resize(2); + m_used_entries = 0; + m_next_resize = (m_entries.size() * kMaxUsed); + m_mask = 0b1; +} + +SymbolTable::~SymbolTable() { + for (auto& e : m_entries) { + delete[] e.name; + } +} + +InternedSymbolPtr SymbolTable::intern(const char* str) { + InternedSymbolPtr result; + size_t string_len = strlen(str); + u32 hash = crc32((const u8*)str, string_len); + + // probe + for (u32 i = 0; i < m_entries.size(); i++) { + u32 slot_addr = (hash + i) & m_mask; + auto& slot = m_entries[slot_addr]; + if (!slot.name) { + // not found, insert! + slot.hash = hash; + auto* name = new char[string_len + 1]; + memcpy(name, str, string_len + 1); + slot.name = name; + m_used_entries++; + + if (m_used_entries >= m_next_resize) { + resize(); + return intern(str); + } + return {name}; + } else { + if (slot.hash != hash) { + continue; // bad hash + } + if (strcmp(slot.name, str) != 0) { + continue; // bad name + } + return {slot.name}; + } + } + + // should be impossible to reach. + ASSERT_NOT_REACHED(); +} + +void SymbolTable::resize() { + m_power_of_two_size++; + m_mask = (1U << m_power_of_two_size) - 1; + + std::vector new_entries(m_entries.size() * 2); + for (const auto& old_entry : m_entries) { + if (old_entry.name) { + bool done = false; + for (u32 i = 0; i < new_entries.size(); i++) { + u32 slot_addr = (old_entry.hash + i) & m_mask; + auto& slot = new_entries[slot_addr]; + if (!slot.name) { + slot.name = old_entry.name; + slot.hash = old_entry.hash; + done = true; + break; + } + } + ASSERT(done); + } + } + + m_entries = std::move(new_entries); + m_next_resize = kMaxUsed * m_entries.size(); +} + /*! * Convert type to string (name in brackets) */ @@ -137,14 +215,9 @@ std::string fixed_to_string(char x) { return {buff}; } -/*! - * Create a new symbol object by interning - */ -Object SymbolObject::make_new(SymbolTable& st, const std::string& name) { - Object obj; - obj.type = ObjectType::SYMBOL; - obj.heap_obj = st.intern(name); - return obj; +template <> +std::string fixed_to_string(InternedSymbolPtr x) { + return x.name_ptr; } /*! @@ -238,8 +311,9 @@ bool Object::operator==(const Object& other) const { return float_obj == other.float_obj; case ObjectType::CHAR: return char_obj == other.char_obj; - case ObjectType::SYMBOL: + return symbol_obj == other.symbol_obj; + case ObjectType::ENVIRONMENT: case ObjectType::LAMBDA: case ObjectType::MACRO: @@ -271,7 +345,7 @@ bool Object::operator==(const Object& other) const { } bool Object::is_symbol(const std::string& name) const { - return is_symbol() && as_symbol()->name == name; + return is_symbol() && name == as_symbol().name_ptr; } bool Object::is_string(const std::string& val) const { diff --git a/common/goos/Object.h b/common/goos/Object.h index 8466502687d..b01efc036cb 100644 --- a/common/goos/Object.h +++ b/common/goos/Object.h @@ -43,6 +43,7 @@ * */ +#include #include #include #include @@ -57,9 +58,6 @@ namespace goos { -using FloatType = double; -using IntType = s64; - /*! * All objects have one of these kinds */ @@ -71,9 +69,9 @@ enum class ObjectType : u8 { INTEGER, FLOAT, CHAR, + SYMBOL, // allocated - SYMBOL, STRING, PAIR, ARRAY, @@ -87,6 +85,29 @@ enum class ObjectType : u8 { std::string object_type_to_string(ObjectType kind); // Some objects are "fixed", meaning they are stored inline in the Object, and not heap allocated. +// These use value semantics and are copied on object assignment. + +// For int/float, these are simply double/int64_t +using FloatType = double; +using IntType = s64; + +// For symbols, we simply store a pointer to the symbol's name string. +// this is wrapped in a type just to avoid confusion with normal const char*. +struct InternedSymbolPtr { + const char* name_ptr; + + struct hash { + auto operator()(const InternedSymbolPtr& x) const { + return std::hash()((const void*)x.name_ptr); + } + }; + bool operator==(const char* msg) const { return strcmp(msg, name_ptr) == 0; } + bool operator!=(const char* msg) const { return strcmp(msg, name_ptr) != 0; } + bool operator==(const std::string& str) const { return str == name_ptr; } + bool operator!=(const std::string& str) const { return str != name_ptr; } + bool operator==(const InternedSymbolPtr& other) const { return other.name_ptr == name_ptr; } + bool operator!=(const InternedSymbolPtr& other) const { return other.name_ptr != name_ptr; } +}; /*! * By default, convert a fixed object data to string with std::to_string. @@ -115,6 +136,31 @@ std::string fixed_to_string(char); template <> std::string fixed_to_string(IntType); +template <> +std::string fixed_to_string(InternedSymbolPtr); + +class SymbolTable { + public: + SymbolTable(const SymbolTable&) = delete; + SymbolTable& operator=(const SymbolTable&) = delete; + SymbolTable(); + ~SymbolTable(); + InternedSymbolPtr intern(const char* str); + + private: + void resize(); + int m_power_of_two_size = 0; + struct Entry { + u32 hash = 0; + const char* name = nullptr; + }; + std::vector m_entries; + int m_used_entries = 0; + int m_next_resize = 0; + u32 m_mask = 0; + static constexpr float kMaxUsed = 0.7; +}; + /*! * Common implementation for a fixed object */ @@ -139,6 +185,8 @@ class FixedObject { return object_type_to_string(ObjectType::INTEGER); if (std::is_same()) return object_type_to_string(ObjectType::CHAR); + if (std::is_same()) + return object_type_to_string(ObjectType::SYMBOL); throw std::runtime_error("Unsupported FixedObject type"); } }; @@ -149,6 +197,7 @@ class FixedObject { using IntegerObject = FixedObject; using FloatObject = FixedObject; using CharObject = FixedObject; +using SymbolObject = FixedObject; // Other objects are separate allocated on the heap. These objects should be HeapObjects. @@ -162,7 +211,6 @@ class HeapObject { // forward declare all HeapObjects class PairObject; class EnvironmentObject; -class SymbolObject; class StringObject; class LambdaObject; class MacroObject; @@ -180,6 +228,7 @@ class Object { IntegerObject integer_obj; FloatObject float_obj; CharObject char_obj; + SymbolObject symbol_obj; }; ObjectType type = ObjectType::INVALID; @@ -192,6 +241,8 @@ class Object { return float_obj.print(); case ObjectType::CHAR: return char_obj.print(); + case ObjectType::SYMBOL: + return symbol_obj.print(); case ObjectType::EMPTY_LIST: return "()"; default: @@ -207,6 +258,8 @@ class Object { return float_obj.inspect(); case ObjectType::CHAR: return char_obj.inspect(); + case ObjectType::SYMBOL: + return symbol_obj.inspect(); case ObjectType::EMPTY_LIST: return "[empty list] ()\n"; default: @@ -244,16 +297,30 @@ class Object { return o; } + static Object make_symbol(SymbolTable* table, const char* name) { + Object o; + o.type = ObjectType::SYMBOL; + o.symbol_obj.value = table->intern(name); + return o; + } + PairObject* as_pair() const; EnvironmentObject* as_env() const; std::shared_ptr as_env_ptr() const; - SymbolObject* as_symbol() const; StringObject* as_string() const; LambdaObject* as_lambda() const; MacroObject* as_macro() const; ArrayObject* as_array() const; StringHashTableObject* as_string_hash_table() const; + const InternedSymbolPtr& as_symbol() const { + if (type != ObjectType::SYMBOL) { + throw std::runtime_error("as_symbol called on a " + object_type_to_string(type) + " " + + print()); + } + return symbol_obj.value; + } + IntType& as_int() { if (type != ObjectType::INTEGER) { throw std::runtime_error("as_int called on a " + object_type_to_string(type) + " " + print()); @@ -315,56 +382,6 @@ class Object { bool operator!=(const Object& other) const { return !((*this) == other); } }; -class SymbolTable; - -/*! - * A Symbol Object, which is just a wrapper around a string. - * The make_new function will correctly - */ -class SymbolObject : public HeapObject { - public: - std::string name; - explicit SymbolObject(std::string _name) : name(std::move(_name)) {} - static Object make_new(SymbolTable& st, const std::string& name); - - std::string print() const override { return name; } - - std::string inspect() const override { return "[symbol] " + name + "\n"; } - - ~SymbolObject() = default; -}; - -/*! - * A Symbol Table, which holds all symbols. - */ -class SymbolTable { - public: - std::shared_ptr intern(const std::string& name) { - const auto& kv = table.find(name); - if (kv == table.end()) { - auto iter = table.insert({name, std::make_shared(name)}); - return (*iter.first).second; - } else { - return kv->second; - } - } - - HeapObject* intern_ptr(const std::string& name) { - const auto& kv = table.find(name); - if (kv == table.end()) { - auto iter = table.insert({name, std::make_shared(name)}); - return (*iter.first).second.get(); - } else { - return kv->second.get(); - } - } - - ~SymbolTable() = default; - - private: - std::unordered_map> table; -}; - class StringObject : public HeapObject { public: std::string data; @@ -439,14 +456,41 @@ class PairObject : public HeapObject { ~PairObject() = default; }; +class EnvironmentMap { + public: + EnvironmentMap(const EnvironmentMap&) = delete; + EnvironmentMap& operator=(const EnvironmentMap&) = delete; + EnvironmentMap(); + Object* lookup(InternedSymbolPtr ptr); + void set(InternedSymbolPtr ptr, const Object& obj); + void clear(); + + private: + struct Entry { + const char* key = nullptr; + Object value; + }; + std::vector m_entries; + + void resize(); + int m_power_of_two_size = 0; + int m_used_entries = 0; + int m_next_resize = 0; + u32 m_mask = 0; + static constexpr float kMaxUsed = 0.7; +}; + class EnvironmentObject : public HeapObject { public: std::string name; std::shared_ptr parent_env; - // the symbols will be stored in the symbol table and never removed, so we don't need shared - // pointers here. - std::unordered_map vars; + // note: this is keyed on the address in the symbol table. + EnvironmentMap vars; + + // this find work by any name string + Object* find(const char* n, SymbolTable* st) { return vars.lookup(st->intern(n)); } + Object* find(InternedSymbolPtr ptr) { return vars.lookup(ptr); } EnvironmentObject() = default; @@ -478,11 +522,8 @@ class EnvironmentObject : public HeapObject { std::string inspect() const override { std::string result = "[environment]\n name: " + name + - "\n parent: " + (parent_env ? parent_env->print() : "NONE") + - "\n vars:\n"; - for (const auto& kv : vars) { - result += " " + kv.first->print() + ": " + kv.second.print() + "\n"; - } + "\n parent: " + (parent_env ? parent_env->print() : "NONE") + "\n"; + return result; } }; @@ -662,14 +703,6 @@ inline std::shared_ptr Object::as_env_ptr() const { return std::dynamic_pointer_cast(heap_obj); } -inline SymbolObject* Object::as_symbol() const { - if (type != ObjectType::SYMBOL) { - throw std::runtime_error("as_symbol called on a " + object_type_to_string(type) + " " + - print()); - } - return static_cast(heap_obj.get()); -} - inline StringObject* Object::as_string() const { if (type != ObjectType::STRING) { throw std::runtime_error("as_string called on a " + object_type_to_string(type) + " " + diff --git a/common/goos/ParseHelpers.cpp b/common/goos/ParseHelpers.cpp index c75f5e9d1c0..e70a60ca596 100644 --- a/common/goos/ParseHelpers.cpp +++ b/common/goos/ParseHelpers.cpp @@ -12,12 +12,12 @@ bool get_va(const goos::Object& rest, std::string* err_string, goos::Arguments* auto arg = current.as_pair()->car; // did we get a ":keyword" - if (arg.is_symbol() && arg.as_symbol()->name.at(0) == ':') { - auto key_name = arg.as_symbol()->name.substr(1); + if (arg.is_symbol() && arg.as_symbol().name_ptr[0] == ':') { + auto key_name = arg.as_symbol().name_ptr + 1; // check for multiple definition of key if (args.named.find(key_name) != args.named.end()) { - *err_string = "Key argument " + key_name + " multiply defined"; + *err_string = fmt::format("Key argument {} multiply defined", key_name); return false; } diff --git a/common/goos/PrettyPrinter.cpp b/common/goos/PrettyPrinter.cpp index 0e25ec91151..5edd7089743 100644 --- a/common/goos/PrettyPrinter.cpp +++ b/common/goos/PrettyPrinter.cpp @@ -98,7 +98,7 @@ void add_to_token_list(const goos::Object& obj, std::vector* tokens) // look for a (quote x) to print as 'x auto& first = obj.as_pair()->car; - if (first.is_symbol() && first.as_symbol()->name == "quote") { + if (first.is_symbol() && first.as_symbol() == "quote") { auto& second = obj.as_pair()->cdr; if (second.is_pair() && second.as_pair()->cdr.is_empty_list()) { tokens->emplace_back(FormToken::TokenKind::QUOTE); diff --git a/common/goos/Printer.cpp b/common/goos/Printer.cpp index 80b47c79c88..86be08d6745 100644 --- a/common/goos/Printer.cpp +++ b/common/goos/Printer.cpp @@ -49,7 +49,7 @@ goos::Reader& get_pretty_printer_reader() { goos::Object to_symbol(const std::string& str) { std::lock_guard guard(pretty_printer_reader_mutex); - return goos::SymbolObject::make_new(get_pretty_printer_reader().symbolTable, str); + return goos::Object::make_symbol(&get_pretty_printer_reader().symbolTable, str.c_str()); } goos::Object new_string(const std::string& str) { diff --git a/common/goos/Reader.cpp b/common/goos/Reader.cpp index 7c77d4dae7d..30a7cfd01cc 100644 --- a/common/goos/Reader.cpp +++ b/common/goos/Reader.cpp @@ -286,7 +286,7 @@ Object Reader::internal_read(std::shared_ptr text, try { auto objs = read_list(ts, false); if (add_top_level) { - return PairObject::make_new(SymbolObject::make_new(symbolTable, "top-level"), objs); + return PairObject::make_new(Object::make_symbol(&symbolTable, "top-level"), objs); } else { return objs; } @@ -459,12 +459,51 @@ bool Reader::read_array(TextStream& stream, Object& o) { return true; } +struct ListBuilder { + Object head; + std::shared_ptr prev_tail; + std::shared_ptr tail; + int size = 0; + + ListBuilder() { head = Object::make_empty_list(); } + + void push_back(Object&& o) { + size++; + if (!tail) { + tail = std::make_shared(o, Object{}); + head.type = ObjectType::PAIR; + head.heap_obj = tail; + } else { + auto next = std::make_shared(o, Object{}); + tail->cdr.type = ObjectType::PAIR; + tail->cdr.heap_obj = next; + prev_tail = std::move(tail); + tail = std::move(next); + } + } + + Object pop_back() { + auto obj = tail->car; + tail = std::move(prev_tail); + return obj; + } + + void finalize() { + if (tail) { + tail->cdr = Object::make_empty_list(); + } else { + head = Object::make_empty_list(); + } + } +}; + /*! * Call this on the character after the open paren. */ Object Reader::read_list(TextStream& ts, bool expect_close_paren) { ts.seek_past_whitespace_and_comments(); - std::vector objects; + // std::vector objects; + ListBuilder list_builder; bool got_close_paren = false; // does this list end? bool got_dot = false; // did we get a . ? @@ -506,22 +545,23 @@ Object Reader::read_list(TextStream& ts, bool expect_close_paren) { } // inserter function, used to properly insert a next object - auto insert_object = [&](const Object& o) { + auto insert_object = [&](Object&& o) { if (got_thing_after_dot) { throw_reader_error(ts, "A list cannot have multiple entries after the dot", -1); } if (reader_macro_string_stack.empty()) { - objects.push_back(o); + list_builder.push_back(std::move(o)); + // objects.push_back(o); } else { - Object to_push_back = o; + Object to_push_back = std::move(o); while (!reader_macro_string_stack.empty()) { - to_push_back = - build_list({SymbolObject::make_new(symbolTable, reader_macro_string_stack.back()), - to_push_back}); + to_push_back = build_list( + {Object::make_symbol(&symbolTable, reader_macro_string_stack.back().c_str()), + to_push_back}); reader_macro_string_stack.pop_back(); } - objects.push_back(to_push_back); + list_builder.push_back(std::move(to_push_back)); } // remember if we got an object after the dot @@ -551,7 +591,7 @@ Object Reader::read_list(TextStream& ts, bool expect_close_paren) { if (read_object(tok, ts, obj)) { ts.seek_past_whitespace_and_comments(); - insert_object(obj); + insert_object(std::move(obj)); } else { throw_reader_error(ts, "invalid token encountered in reader: " + tok.text, -int(tok.text.size())); @@ -574,12 +614,12 @@ Object Reader::read_list(TextStream& ts, bool expect_close_paren) { // build up list or improper list, link it, and return! if (got_thing_after_dot) { - if (objects.size() < 2) { + if (list_builder.size < 2) { throw_reader_error(ts, "A list with a dot must have at least one thing before the dot", -1); } - auto back = objects.back(); - objects.pop_back(); - auto rv = build_list(objects); + auto back = list_builder.pop_back(); + list_builder.finalize(); + auto rv = list_builder.head; auto lst = rv; while (true) { @@ -593,7 +633,8 @@ Object Reader::read_list(TextStream& ts, bool expect_close_paren) { db.link(rv, ts.text, start_offset); return rv; } else { - auto rv = build_list(std::move(objects)); + list_builder.finalize(); + auto rv = list_builder.head; db.link(rv, ts.text, start_offset); return rv; } @@ -607,7 +648,7 @@ bool Reader::try_token_as_symbol(const Token& tok, Object& obj) { ASSERT(!tok.text.empty()); char start = tok.text[0]; if (m_valid_symbols_chars[(int)start]) { - obj = SymbolObject::make_new(symbolTable, tok.text); + obj = Object::make_symbol(&symbolTable, tok.text.c_str()); return true; } else { return false; diff --git a/common/serialization/subtitles/subtitles.cpp b/common/serialization/subtitles/subtitles.cpp index 5c80fb2f9c4..21b69ee7b5d 100644 --- a/common/serialization/subtitles/subtitles.cpp +++ b/common/serialization/subtitles/subtitles.cpp @@ -13,20 +13,20 @@ void open_subtitle_project(const std::string& project_kind, goos::Reader reader; auto& proj = reader.read_from_file({file_path}).as_pair()->cdr.as_pair()->car; if (!proj.is_pair() || !proj.as_pair()->car.is_symbol() || - proj.as_pair()->car.as_symbol()->name != project_kind) { + proj.as_pair()->car.as_symbol() != project_kind) { throw std::runtime_error(fmt::format("invalid project '{}'", project_kind)); } goos::for_each_in_list(proj.as_pair()->cdr, [&](const goos::Object& o) { if (o.is_pair() && o.as_pair()->cdr.is_pair()) { auto args = o.as_pair(); - auto& action = args->car.as_symbol()->name; + auto& action = args->car.as_symbol(); args = args->cdr.as_pair(); if (action == "file-json") { auto new_file = GameSubtitleDefinitionFile(); while (true) { - const auto& kwarg = args->car.as_symbol()->name; + const auto& kwarg = args->car.as_symbol(); args = args->cdr.as_pair(); if (kwarg == ":language-id") { new_file.language_id = args->car.as_int(); @@ -49,7 +49,7 @@ void open_subtitle_project(const std::string& project_kind, subtitle_files.push_back(new_file); } else { throw std::runtime_error( - fmt::format("unknown action {} in {} project", action, project_kind)); + fmt::format("unknown action {} in {} project", action.name_ptr, project_kind)); } } else { throw std::runtime_error(fmt::format("invalid entry in {} project", project_kind)); diff --git a/common/serialization/text/text_ser.cpp b/common/serialization/text/text_ser.cpp index 91f9c1673b4..6e579401b35 100644 --- a/common/serialization/text/text_ser.cpp +++ b/common/serialization/text/text_ser.cpp @@ -147,7 +147,7 @@ void parse_text_goal(const goos::Object& data, throw std::runtime_error("invalid text version entry"); } - font = get_font_bank(ver_name.as_symbol()->name); + font = get_font_bank(ver_name.as_symbol().name_ptr); } else if (head.is_int()) { @@ -278,7 +278,7 @@ GameTextVersion parse_text_only_version(const goos::Object& data) { throw std::runtime_error("invalid text version entry"); } - font = get_font_bank(ver_name.as_symbol()->name); + font = get_font_bank(ver_name.as_symbol().name_ptr); } } }); @@ -294,14 +294,14 @@ void open_text_project(const std::string& kind, goos::Reader reader; auto& proj = reader.read_from_file({filename}).as_pair()->cdr.as_pair()->car; if (!proj.is_pair() || !proj.as_pair()->car.is_symbol() || - proj.as_pair()->car.as_symbol()->name != kind) { + proj.as_pair()->car.as_symbol() != kind) { throw std::runtime_error(fmt::format("invalid {} project", kind)); } goos::for_each_in_list(proj.as_pair()->cdr, [&](const goos::Object& o) { if (o.is_pair() && o.as_pair()->cdr.is_pair()) { auto args = o.as_pair(); - auto& action = args->car.as_symbol()->name; + auto& action = args->car.as_symbol(); args = args->cdr.as_pair(); if (action == "file") { @@ -313,17 +313,18 @@ void open_text_project(const std::string& kind, } else if (action == "file-json") { auto& language_id = args->car.as_int(); args = args->cdr.as_pair(); - auto& text_version = args->car.as_symbol()->name; + auto& text_version = args->car.as_symbol(); args = args->cdr.as_pair(); std::optional group_name = std::nullopt; group_name = args->car.as_string()->data; args = args->cdr.as_pair()->car.as_pair(); goos::for_each_in_list(args->cdr.as_pair()->car, [&](const goos::Object& o) { text_files.push_back({GameTextDefinitionFile::Format::JSON, o.as_string()->data, - (int)language_id, text_version, group_name}); + (int)language_id, text_version.name_ptr, group_name}); }); } else { - throw std::runtime_error(fmt::format("unknown action {} in {} project", action, kind)); + throw std::runtime_error( + fmt::format("unknown action {} in {} project", action.name_ptr, kind)); } } else { throw std::runtime_error(fmt::format("invalid entry in {} project", kind)); diff --git a/common/type_system/defenum.cpp b/common/type_system/defenum.cpp index a18206452a4..9a5f1e5a89b 100644 --- a/common/type_system/defenum.cpp +++ b/common/type_system/defenum.cpp @@ -38,7 +38,7 @@ bool is_type(const std::string& expected, const TypeSpec& actual, const TypeSyst std::string symbol_string(const goos::Object& obj) { if (obj.is_symbol()) { - return obj.as_symbol()->name; + return obj.as_symbol().name_ptr; } throw std::runtime_error(obj.print() + " was supposed to be a symbol, but isn't"); } @@ -69,7 +69,7 @@ EnumType* parse_defenum(const goos::Object& defenum, if (!enum_name_obj.is_symbol()) { throw std::runtime_error("defenum must be given a symbol as its name"); } - std::string name = enum_name_obj.as_symbol()->name; + std::string name = enum_name_obj.as_symbol().name_ptr; auto current = car(iter); while (current.is_symbol() && symbol_string(current).at(0) == ':') { diff --git a/common/type_system/deftype.cpp b/common/type_system/deftype.cpp index 2d3387d99f9..fb230097c9f 100644 --- a/common/type_system/deftype.cpp +++ b/common/type_system/deftype.cpp @@ -54,7 +54,7 @@ std::string deftype_parent_list(const goos::Object& list) { throw std::runtime_error("invalid parent in deftype parent list"); } - return parent.as_symbol()->name; + return parent.as_symbol().name_ptr; } bool is_type(const std::string& expected, const TypeSpec& actual, const TypeSystem* ts) { @@ -63,7 +63,7 @@ bool is_type(const std::string& expected, const TypeSpec& actual, const TypeSyst std::string symbol_string(const goos::Object& obj) { if (obj.is_symbol()) { - return obj.as_symbol()->name; + return obj.as_symbol().name_ptr; } throw std::runtime_error(obj.print() + " was supposed to be a symbol, but isn't"); } @@ -84,10 +84,12 @@ double get_float(const goos::Object& obj) { throw std::runtime_error(obj.print() + " was supposed to be an number, but isn't"); } -void add_field(StructureType* structure, - TypeSystem* ts, - const goos::Object& def, - std::unordered_map& constants) { +void add_field( + StructureType* structure, + TypeSystem* ts, + const goos::Object& def, + std::unordered_map& + constants) { auto rest = &def; auto name = symbol_string(car(rest)); @@ -317,7 +319,7 @@ void declare_method(Type* type, // // Doing it like this makes the ordering not critical while (!obj->is_empty_list() && car(obj).is_symbol()) { - const auto& keyword = car(obj).as_symbol()->name; + const auto& keyword = car(obj).as_symbol(); if (keyword == ":no-virtual") { no_virtual = true; } else if (keyword == ":replace") { @@ -334,8 +336,8 @@ void declare_method(Type* type, auto docstring_list = &car(obj); auto elem = docstring_list; while (!elem->is_empty_list() && car(elem).is_symbol()) { - const auto& handler = car(elem).as_symbol()->name; - const auto handler_kind = handler_keyword_to_kind(handler); + const auto& handler = car(elem).as_symbol(); + const auto handler_kind = handler_keyword_to_kind(handler.name_ptr); // Get the docstring elem = cdr(elem); @@ -419,8 +421,8 @@ void declare_state(Type* type, auto docstring_list = &car(obj); auto elem = docstring_list; while (!elem->is_empty_list() && car(elem).is_symbol()) { - const auto& handler = car(elem).as_symbol()->name; - const auto handler_kind = handler_keyword_to_kind(handler); + const auto& handler = car(elem).as_symbol(); + const auto handler_kind = handler_keyword_to_kind(handler.name_ptr); // Get the docstring elem = cdr(elem); @@ -463,7 +465,8 @@ StructureDefResult parse_structure_def( TypeSystem* ts, const goos::Object& fields, const goos::Object& options, - std::unordered_map& constants) { + std::unordered_map& + constants) { StructureDefResult result; for_each_in_list(fields, [&](const goos::Object& o) { add_field(type, ts, o, constants); }); TypeFlags flags; @@ -686,8 +689,8 @@ TypeSpec parse_typespec(const TypeSystem* type_system, const goos::Object& src) while (rest->is_pair()) { auto& it = rest->as_pair()->car; - if (it.is_symbol() && it.as_symbol()->name.at(0) == ':') { - auto tag_name = it.as_symbol()->name.substr(1); + if (it.is_symbol() && it.as_symbol().name_ptr[0] == ':') { + auto tag_name = it.as_symbol().name_ptr + 1; rest = &rest->as_pair()->cdr; if (!rest->is_pair()) { @@ -696,13 +699,13 @@ TypeSpec parse_typespec(const TypeSystem* type_system, const goos::Object& src) auto& tag_val = rest->as_pair()->car; - if (tag_name == "behavior") { - if (!type_system->fully_defined_type_exists(tag_val.as_symbol()->name) && - !type_system->partially_defined_type_exists(tag_val.as_symbol()->name)) { + if (std::string_view(tag_name) == "behavior") { + if (!type_system->fully_defined_type_exists(tag_val.as_symbol().name_ptr) && + !type_system->partially_defined_type_exists(tag_val.as_symbol().name_ptr)) { throw std::runtime_error( - fmt::format("Behavior tag uses an unknown type {}", tag_val.as_symbol()->name)); + fmt::format("Behavior tag uses an unknown type {}", tag_val.as_symbol().name_ptr)); } - ts.add_new_tag(tag_name, tag_val.as_symbol()->name); + ts.add_new_tag(tag_name, tag_val.as_symbol().name_ptr); } else { throw std::runtime_error(fmt::format("Type tag {} is unknown", tag_name)); } @@ -723,11 +726,14 @@ TypeSpec parse_typespec(const TypeSystem* type_system, const goos::Object& src) return {}; } -DeftypeResult parse_deftype(const goos::Object& deftype, - TypeSystem* ts, - std::unordered_map* constants) { +DeftypeResult parse_deftype( + const goos::Object& deftype, + TypeSystem* ts, + std::unordered_map* + constants) { DefinitionMetadata symbol_metadata; - std::unordered_map no_consts; + std::unordered_map + no_consts; auto& constants_to_use = no_consts; if (constants != nullptr) { constants_to_use = *constants; @@ -752,7 +758,7 @@ DeftypeResult parse_deftype(const goos::Object& deftype, throw std::runtime_error("deftype must be given a symbol as the type name"); } - auto& name = type_name_obj.as_symbol()->name; + auto& name = type_name_obj.as_symbol().name_ptr; auto parent_type_name = deftype_parent_list(parent_list_obj); auto parent_type = ts->make_typespec(parent_type_name); DeftypeResult result; diff --git a/common/type_system/deftype.h b/common/type_system/deftype.h index d6aa240afa6..a8270216e2f 100644 --- a/common/type_system/deftype.h +++ b/common/type_system/deftype.h @@ -20,5 +20,6 @@ struct DeftypeResult { DeftypeResult parse_deftype( const goos::Object& deftype, TypeSystem* ts, - std::unordered_map* constants = nullptr); + std::unordered_map* + constants = nullptr); TypeSpec parse_typespec(const TypeSystem* type_system, const goos::Object& src); diff --git a/common/util/crc32.cpp b/common/util/crc32.cpp index e295ef64e15..76226695e00 100644 --- a/common/util/crc32.cpp +++ b/common/util/crc32.cpp @@ -1,41 +1 @@ #include "crc32.h" - -#include - -#ifdef __aarch64__ -#include -u32 crc32(const u8* data, size_t size) { - u32 result = 0xffffffff; - while (size >= 4) { - u32 x; - memcpy(&x, data, 4); - data += 4; - size -= 4; - result = __crc32w(result, x); - } - while (size) { - result = __crc32b(result, *data); - data++; - size--; - } - return ~result; -} -#else -#include -u32 crc32(const u8* data, size_t size) { - u32 result = 0xffffffff; - while (size >= 4) { - u32 x; - memcpy(&x, data, 4); - data += 4; - size -= 4; - result = _mm_crc32_u32(result, x); - } - while (size) { - result = _mm_crc32_u8(result, *data); - data++; - size--; - } - return ~result; -} -#endif diff --git a/common/util/crc32.h b/common/util/crc32.h index 2cd5fad94db..a874fe18889 100644 --- a/common/util/crc32.h +++ b/common/util/crc32.h @@ -2,4 +2,44 @@ #include "common/common_types.h" -u32 crc32(const u8* data, size_t size); \ No newline at end of file +u32 crc32(const u8* data, size_t size); + +#include + +#ifdef __aarch64__ +#include +inline u32 crc32(const u8* data, size_t size) { + u32 result = 0xffffffff; + while (size >= 4) { + u32 x; + memcpy(&x, data, 4); + data += 4; + size -= 4; + result = __crc32w(result, x); + } + while (size) { + result = __crc32b(result, *data); + data++; + size--; + } + return ~result; +} +#else +#include +inline u32 crc32(const u8* data, size_t size) { + u32 result = 0xffffffff; + while (size >= 4) { + u32 x; + memcpy(&x, data, 4); + data += 4; + size -= 4; + result = _mm_crc32_u32(result, x); + } + while (size) { + result = _mm_crc32_u8(result, *data); + data++; + size--; + } + return ~result; +} +#endif \ No newline at end of file diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index fa3bb87d28b..0497d601887 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -3537,7 +3537,7 @@ void FunctionCallElement::update_from_stack(const Env& env, Form* so_group_f = nullptr; if (!elt_group->elts().at(0)->to_form(env).is_symbol("sfx")) { so_group_f = pool.form( - elt_group->elts().at(0)->to_form(env).as_symbol()->name); + elt_group->elts().at(0)->to_form(env).as_symbol().name_ptr); } auto so_positional_f = arg_forms.at(6); if (so_positional_f->to_form(env).is_symbol("#t")) { diff --git a/decompiler/analysis/insert_lets.cpp b/decompiler/analysis/insert_lets.cpp index e023aa91b7e..f92cb4d65b5 100644 --- a/decompiler/analysis/insert_lets.cpp +++ b/decompiler/analysis/insert_lets.cpp @@ -1281,7 +1281,7 @@ FormElement* rewrite_joint_macro(LetElement* in, const Env& env, FormPool& pool) } auto group_lisp = strip_cast("art-joint-anim", arg_group)->to_form(env); - if (group_lisp.is_symbol() && group_lisp.as_symbol()->name == "#f") { + if (group_lisp.is_symbol() && group_lisp.as_symbol() == "#f") { arg_group = nullptr; } diff --git a/decompiler/util/DecompilerTypeSystem.cpp b/decompiler/util/DecompilerTypeSystem.cpp index f9d4d4e401c..d6e44e49296 100644 --- a/decompiler/util/DecompilerTypeSystem.cpp +++ b/decompiler/util/DecompilerTypeSystem.cpp @@ -54,7 +54,7 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector& file_ for_each_in_list(data, [&](goos::Object& o) { try { - if (car(o).as_symbol()->name == "define-extern") { + if (car(o).as_symbol() == "define-extern") { auto symbol_metadata = DefinitionMetadata(); auto* rest = &cdr(o); auto sym_name = car(*rest); @@ -69,8 +69,8 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector& file_ throw std::runtime_error("malformed define-extern"); } symbol_metadata.definition_info = m_reader.db.get_short_info_for(o); - add_symbol(sym_name.as_symbol()->name, parse_typespec(&ts, sym_type), symbol_metadata); - } else if (car(o).as_symbol()->name == "deftype") { + add_symbol(sym_name.as_symbol().name_ptr, parse_typespec(&ts, sym_type), symbol_metadata); + } else if (car(o).as_symbol() == "deftype") { auto dtr = parse_deftype(cdr(o), &ts); dtr.type_info->m_metadata.definition_info = m_reader.db.get_short_info_for(o); if (dtr.create_runtime_type) { @@ -87,7 +87,7 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector& file_ for (const auto& [state_name, meta] : dtr.type_info->m_state_definition_meta) { state_metadata.emplace(state_name, meta); } - } else if (car(o).as_symbol()->name == "declare-type") { + } else if (car(o).as_symbol() == "declare-type") { auto* rest = &cdr(o); auto type_name = car(*rest); rest = &cdr(*rest); @@ -95,14 +95,14 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector& file_ if (!cdr(*rest).is_empty_list()) { throw std::runtime_error("malformed declare-type"); } - ts.forward_declare_type_as(type_name.as_symbol()->name, type_kind.as_symbol()->name); - } else if (car(o).as_symbol()->name == "defenum") { + ts.forward_declare_type_as(type_name.as_symbol().name_ptr, type_kind.as_symbol().name_ptr); + } else if (car(o).as_symbol() == "defenum") { auto symbol_metadata = DefinitionMetadata(); parse_defenum(cdr(o), &ts, &symbol_metadata); symbol_metadata.definition_info = m_reader.db.get_short_info_for(o); auto* rest = &cdr(o); - const auto& enum_name = car(*rest).as_symbol()->name; - symbol_metadata_map[enum_name] = symbol_metadata; + const auto& enum_name = car(*rest).as_symbol(); + symbol_metadata_map[enum_name.name_ptr] = symbol_metadata; // so far, enums are never runtime types so there's no symbol for them. } else { throw std::runtime_error("Decompiler cannot parse " + car(o).print()); diff --git a/decompiler/util/data_decompile.cpp b/decompiler/util/data_decompile.cpp index d8a0dba4af7..2898582bbe3 100644 --- a/decompiler/util/data_decompile.cpp +++ b/decompiler/util/data_decompile.cpp @@ -343,7 +343,7 @@ s32 word_as_s32(const LinkedWord& w) { std::string print_def(const goos::Object& obj) { if (obj.is_pair() && obj.as_pair()->car.is_symbol() && - obj.as_pair()->car.as_symbol()->name == "quote") { + obj.as_pair()->car.as_symbol() == "quote") { auto& rest = obj.as_pair()->cdr; if (rest.is_pair() && rest.as_pair()->cdr.is_empty_list()) { return fmt::format("'{}", rest.as_pair()->car.print()); diff --git a/goal_src/goos-lib.gs b/goal_src/goos-lib.gs index aa76bfab82b..b799c464a55 100644 --- a/goal_src/goos-lib.gs +++ b/goal_src/goos-lib.gs @@ -225,20 +225,22 @@ ) ) -(defsmacro let (bindings &rest body) - `((lambda ,(apply first bindings) ,@body) - ,@(apply second bindings))) - -(defsmacro let* (bindings &rest body) - (if (null? bindings) - `(begin ,@body) - `((lambda (,(caar bindings)) - (let* ,(cdr bindings) ,@body)) - ;;(begin ,@(cdar bindings)) - ,(car (cdar bindings)) - ) - ) - ) +;; These are now implemented in Interpreter.cpp for speed. +;; But I leave these here because it was cool to implement them in GOOS itself. +; (defsmacro let (bindings &rest body) +; `((lambda ,(apply first bindings) ,@body) +; ,@(apply second bindings))) + +; (defsmacro let* (bindings &rest body) +; (if (null? bindings) +; `(begin ,@body) +; `((lambda (,(caar bindings)) +; (let* ,(cdr bindings) ,@body)) +; ;;(begin ,@(cdar bindings)) +; ,(car (cdar bindings)) +; ) +; ) +; ) (defsmacro dotimes (var &rest body) `(let (( ,(first var) 0)) diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index bb64b2512e0..4fb01e083bf 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -166,10 +166,10 @@ std::unique_ptr Compiler::compile_top_level_function(const std::str // the compiler doesn't waste much time. // the actual source code is (top-level ...) right now though so we need some tricks. code.as_pair()->cdr = PairObject::make_new( - PairObject::make_new(SymbolObject::make_new(m_goos.reader.symbolTable, "when"), - PairObject::make_new(SymbolObject::make_new(m_goos.reader.symbolTable, - "*debug-segment*"), - code.as_pair()->cdr)), + PairObject::make_new( + Object::make_symbol(&m_goos.reader.symbolTable, "when"), + PairObject::make_new(Object::make_symbol(&m_goos.reader.symbolTable, "*debug-segment*"), + code.as_pair()->cdr)), Object::make_empty_list()); result = compile_error_guard(code, fe.get()); } @@ -252,7 +252,7 @@ void Compiler::color_object_file(FileEnv* env) { input.is_asm_function = f->is_asm_func; for (auto& i : f->code()) { input.instructions.push_back(i->to_rai()); - input.debug_instruction_names.push_back(i->print()); + // input.debug_instruction_names.push_back(i->print()); } for (auto& reg_val : f->reg_vals()) { @@ -385,7 +385,7 @@ void Compiler::setup_goos_forms() { va_check(form, args, {goos::ObjectType::SYMBOL}, {}); std::vector enum_vals; - const auto& enum_name = args.unnamed.at(0).as_symbol()->name; + const auto& enum_name = args.unnamed.at(0).as_symbol().name_ptr; auto enum_type = m_ts.try_enum_lookup(enum_name); if (!enum_type) { throw_compiler_error(form, "Unknown enum {} in get-enum-vals", enum_name); diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 07634759950..c3ccb329211 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -97,7 +97,7 @@ class Compiler { MakeSystem& make_system() { return m_make; } std::set lookup_symbol_infos_starting_with(const std::string& prefix) const; std::vector* lookup_exact_name_info(const std::string& name) const; - std::optional lookup_typespec(const std::string& symbol_name) const; + std::optional lookup_typespec(const std::string& symbol_name); private: GameVersion m_version; @@ -110,9 +110,12 @@ class Compiler { goos::Interpreter m_goos; Debugger m_debugger; std::unordered_map m_macro_specs; - std::unordered_map m_symbol_types; - std::unordered_map m_global_constants; - std::unordered_map m_inlineable_functions; + std::unordered_map + m_symbol_types; + std::unordered_map + m_global_constants; + std::unordered_map + m_inlineable_functions; CompilerSettings m_settings; bool m_throw_on_define_extern_redefinition = false; std::unordered_set m_allow_inconsistent_definition_symbols; @@ -220,7 +223,7 @@ class Compiler { const std::unordered_map>>& named); const std::string& as_string(const goos::Object& o); - const std::string& symbol_string(const goos::Object& o); + std::string symbol_string(const goos::Object& o); std::string quoted_sym_as_string(const goos::Object& o); goos::Object unquote(const goos::Object& o); bool is_quoted_sym(const goos::Object& o); diff --git a/goalc/compiler/CompilerSettings.cpp b/goalc/compiler/CompilerSettings.cpp index 8ccedb85686..360151a8809 100644 --- a/goalc/compiler/CompilerSettings.cpp +++ b/goalc/compiler/CompilerSettings.cpp @@ -19,7 +19,7 @@ void CompilerSettings::set(const std::string& name, const goos::Object& value) { kv->second.value = value; if (kv->second.boolp) { - *kv->second.boolp = !(value.is_symbol() && value.as_symbol()->name == "#f"); + *kv->second.boolp = !(value.is_symbol() && value.as_symbol() == "#f"); } } diff --git a/goalc/compiler/Env.cpp b/goalc/compiler/Env.cpp index 09c3a10ba21..5893f51a595 100644 --- a/goalc/compiler/Env.cpp +++ b/goalc/compiler/Env.cpp @@ -278,7 +278,7 @@ RegVal* FunctionEnv::lexical_lookup(goos::Object sym) { throw std::runtime_error("invalid symbol in lexical_lookup"); } - auto kv = params.find(sym.as_symbol()->name); + auto kv = params.find(sym.as_symbol()); if (kv == params.end()) { return parent()->lexical_lookup(sym); } @@ -350,7 +350,7 @@ RegVal* LexicalEnv::lexical_lookup(goos::Object sym) { throw std::runtime_error("invalid symbol in lexical_lookup"); } - auto kv = vars.find(sym.as_symbol()->name); + auto kv = vars.find(sym.as_symbol()); if (kv == vars.end()) { return parent()->lexical_lookup(sym); } diff --git a/goalc/compiler/Env.h b/goalc/compiler/Env.h index 74014ebaae7..a5bb7e7d8cc 100644 --- a/goalc/compiler/Env.h +++ b/goalc/compiler/Env.h @@ -247,7 +247,7 @@ class FunctionEnv : public DeclareEnv { TypeSpec asm_func_return_type; std::vector unresolved_gotos; std::vector unresolved_cond_gotos; - std::unordered_map params; + std::unordered_map params; protected: void resolve_gotos(); @@ -288,7 +288,7 @@ class LexicalEnv : public DeclareEnv { explicit LexicalEnv(Env* parent) : DeclareEnv(EnvKind::OTHER_ENV, parent) {} RegVal* lexical_lookup(goos::Object sym) override; std::string print() override; - std::unordered_map vars; + std::unordered_map vars; }; class LabelEnv : public Env { @@ -306,14 +306,14 @@ class SymbolMacroEnv : public Env { public: explicit SymbolMacroEnv(Env* parent) : Env(EnvKind::SYMBOL_MACRO_ENV, parent) {} // key is goos symbols. - std::unordered_map macros; + std::unordered_map macros; std::string print() override { return "symbol-macro-env"; } }; class MacroExpandEnv : public Env { public: MacroExpandEnv(Env* parent, - const goos::HeapObject* macro_name, + const goos::InternedSymbolPtr macro_name, const goos::Object& macro_body, const goos::Object& macro_use) : Env(EnvKind::MACRO_EXPAND_ENV, parent), @@ -333,13 +333,13 @@ class MacroExpandEnv : public Env { std::string print() override { return "macro-env"; } - const goos::HeapObject* name() const { return m_macro_name; } + const goos::InternedSymbolPtr name() const { return m_macro_name; } const goos::Object& macro_body() const { return m_macro_body; } const goos::Object& macro_use_location() const { return m_macro_use_location; } const goos::Object& root_form() const { return m_root_form; } private: - const goos::HeapObject* m_macro_name = nullptr; + const goos::InternedSymbolPtr m_macro_name = {nullptr}; goos::Object m_macro_body; goos::Object m_macro_use_location; goos::Object m_root_form; diff --git a/goalc/compiler/Lambda.h b/goalc/compiler/Lambda.h index bcfb987bca1..b13a39661c6 100644 --- a/goalc/compiler/Lambda.h +++ b/goalc/compiler/Lambda.h @@ -6,7 +6,7 @@ // note - we cannot easily reuse the GOOS argument system because GOAL's is slightly different. // there's no rest or keyword support. struct GoalArg { - std::string name; + std::string name; // todo intern TypeSpec type; }; diff --git a/goalc/compiler/Util.cpp b/goalc/compiler/Util.cpp index 81b1b41c0d9..2b80e3aefda 100644 --- a/goalc/compiler/Util.cpp +++ b/goalc/compiler/Util.cpp @@ -219,10 +219,10 @@ const std::string& Compiler::as_string(const goos::Object& o) { } /*! - * Convert a goos::Object that's a symbol to a std::string. Must be a string. + * Convert a goos::Object that's a symbol to a std::string. Must be a symbol. */ -const std::string& Compiler::symbol_string(const goos::Object& o) { - return o.as_symbol()->name; +std::string Compiler::symbol_string(const goos::Object& o) { + return o.as_symbol().name_ptr; } /*! @@ -257,7 +257,7 @@ bool Compiler::is_quoted_sym(const goos::Object& o) { if (o.is_pair()) { auto car = pair_car(o); auto cdr = pair_cdr(o); - if (car.is_symbol() && car.as_symbol()->name == "quote") { + if (car.is_symbol() && car.as_symbol() == "quote") { if (cdr.is_pair()) { auto thing = pair_car(cdr); if (thing.is_symbol()) { @@ -348,10 +348,10 @@ bool Compiler::is_symbol(const TypeSpec& ts) { bool Compiler::get_true_or_false(const goos::Object& form, const goos::Object& boolean) { // todo try other things. if (boolean.is_symbol()) { - if (boolean.as_symbol()->name == "#t") { + if (boolean.as_symbol() == "#t") { return true; } - if (boolean.as_symbol()->name == "#f") { + if (boolean.as_symbol() == "#f") { return false; } } diff --git a/goalc/compiler/compilation/Asm.cpp b/goalc/compiler/compilation/Asm.cpp index 17dbf35f749..95f88227428 100644 --- a/goalc/compiler/compilation/Asm.cpp +++ b/goalc/compiler/compilation/Asm.cpp @@ -16,7 +16,7 @@ emitter::Register Compiler::parse_register(const goos::Object& code) { auto nas = code.as_symbol(); for (int i = 0; i < 32; i++) { - if (nas->name == reg_names[i]) { + if (std::string_view(nas.name_ptr) == reg_names[i]) { return emitter::Register(i); } } @@ -60,7 +60,7 @@ Val* Compiler::compile_rlet(const goos::Object& form, const goos::Object& rest, // figure out the class RegClass register_class = RegClass::GPR_64; if (def_args.has_named("class")) { - auto& class_name = def_args.named.at("class").as_symbol()->name; + auto& class_name = def_args.named.at("class").as_symbol(); if (class_name == "gpr") { register_class = RegClass::GPR_64; } else if (class_name == "fpr") { @@ -70,7 +70,7 @@ Val* Compiler::compile_rlet(const goos::Object& form, const goos::Object& rest, } else if (class_name == "i128") { register_class = RegClass::INT_128; } else { - throw_compiler_error(o, "Register class {} is unknown.", class_name); + throw_compiler_error(o, "Register class {} is unknown.", class_name.name_ptr); } } @@ -119,7 +119,7 @@ Val* Compiler::compile_rlet(const goos::Object& form, const goos::Object& rest, } } - lenv->vars[new_place_name.as_symbol()->name] = new_place_reg; + lenv->vars[new_place_name.as_symbol()] = new_place_reg; }); if (!reset_regs.empty()) { @@ -218,10 +218,10 @@ Val* Compiler::compile_asm_load_sym(const goos::Object& form, const goos::Object va_check( form, args, {{}, {goos::ObjectType::SYMBOL}}, {{"sext", {false, goos::ObjectType::SYMBOL}}, {"color", {false, goos::ObjectType::SYMBOL}}}); - auto& sym_name = args.unnamed.at(1).as_symbol()->name; + auto& sym_name = args.unnamed.at(1).as_symbol(); auto sym_kv = m_symbol_types.find(sym_name); if (sym_kv == m_symbol_types.end()) { - throw_compiler_error(form, "Cannot find a symbol named {}.", sym_name); + throw_compiler_error(form, "Cannot find a symbol named {}.", sym_name.name_ptr); } auto ts = sym_kv->second; bool sext = m_ts.lookup_type(ts)->get_load_signed(); @@ -239,7 +239,7 @@ Val* Compiler::compile_asm_load_sym(const goos::Object& form, const goos::Object throw_compiler_error(form, "Cannot .load-sym this. Got a {}.", dest->print()); } - env->emit_ir(form, color, dest, sym_name, sext); + env->emit_ir(form, color, dest, sym_name.name_ptr, sext); return get_none(); } diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index aa9e47c6f78..bdbcc8a4d68 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -323,14 +323,14 @@ Val* Compiler::compile_pair(const goos::Object& code, Env* env) { } // next try as a goal compiler form - auto kv_gfs = g_goal_forms.find(head_sym->name); + auto kv_gfs = g_goal_forms.find(head_sym.name_ptr); if (kv_gfs != g_goal_forms.end()) { auto& [docstring, func] = kv_gfs->second; return ((*this).*(func))(code, rest, env); } // next try as an enum - auto enum_type = m_ts.try_enum_lookup(head_sym->name); + auto enum_type = m_ts.try_enum_lookup(head_sym.name_ptr); if (enum_type) { return compile_enum_lookup(code, enum_type, rest, env); } @@ -388,7 +388,7 @@ SymbolVal* Compiler::compile_get_sym_obj(const std::string& name, Env* env) { Val* Compiler::compile_get_symbol_value(const goos::Object& form, const std::string& name, Env* env) { - auto existing_symbol = m_symbol_types.find(name); + auto existing_symbol = m_symbol_types.find(m_goos.intern_ptr(name)); if (existing_symbol == m_symbol_types.end()) { throw_compiler_error( form, "The symbol {} was looked up as a global variable, but it does not exist.", name); @@ -432,7 +432,7 @@ Val* Compiler::compile_symbol(const goos::Object& form, Env* env) { } auto global_constant = m_global_constants.find(form.as_symbol()); - auto existing_symbol = m_symbol_types.find(form.as_symbol()->name); + auto existing_symbol = m_symbol_types.find(form.as_symbol()); // see if it's a constant if (global_constant != m_global_constants.end()) { diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index 2941acf6e89..5fe136b0805 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -360,7 +360,7 @@ std::string Compiler::make_symbol_info_description(const SymbolInfo& info) { switch (info.kind()) { case SymbolInfo::Kind::GLOBAL_VAR: return fmt::format("[Global Variable] Type: {} Defined: {}", - m_symbol_types.at(info.name()).print(), + m_symbol_types.at(m_goos.intern_ptr(info.name())).print(), m_goos.reader.db.get_info_for(info.src_form())); case SymbolInfo::Kind::LANGUAGE_BUILTIN: return fmt::format("[Built-in Form] {}\n", info.name()); @@ -377,7 +377,7 @@ std::string Compiler::make_symbol_info_description(const SymbolInfo& info) { case SymbolInfo::Kind::CONSTANT: return fmt::format( "[Constant] Name: {} Value: {} Defined: {}", info.name(), - m_global_constants.at(m_goos.reader.symbolTable.intern_ptr(info.name())).print(), + m_global_constants.at(m_goos.reader.symbolTable.intern(info.name().c_str())).print(), m_goos.reader.db.get_info_for(info.src_form())); case SymbolInfo::Kind::FUNCTION: return fmt::format("[Function] Name: {} Defined: {}", info.name(), @@ -396,7 +396,7 @@ Val* Compiler::compile_get_info(const goos::Object& form, const goos::Object& re auto args = get_va(form, rest); va_check(form, args, {goos::ObjectType::SYMBOL}, {}); - auto result = m_symbol_info.lookup_exact_name(args.unnamed.at(0).as_symbol()->name); + auto result = m_symbol_info.lookup_exact_name(args.unnamed.at(0).as_symbol().name_ptr); if (!result) { lg::print("No results found.\n"); } else { @@ -548,7 +548,7 @@ Val* Compiler::compile_autocomplete(const goos::Object& form, const goos::Object va_check(form, args, {goos::ObjectType::SYMBOL}, {}); Timer timer; - auto result = m_symbol_info.lookup_symbols_starting_with(args.unnamed.at(0).as_symbol()->name); + auto result = m_symbol_info.lookup_symbols_starting_with(args.unnamed.at(0).as_symbol().name_ptr); auto time = timer.getMs(); for (auto& x : result) { @@ -575,7 +575,7 @@ Val* Compiler::compile_update_macro_metadata(const goos::Object& form, throw_compiler_error(form, "Invalid arguments provided to `update-macro-metadata"); } - auto& name = args.unnamed.at(0).as_symbol()->name; + auto& name = args.unnamed.at(0).as_symbol().name_ptr; auto arg_spec = m_goos.parse_arg_spec(form, args.unnamed.at(2)); m_macro_specs[name] = arg_spec; @@ -601,9 +601,10 @@ std::vector* Compiler::lookup_exact_name_info(const std::string& nam } } -std::optional Compiler::lookup_typespec(const std::string& symbol_name) const { - if (m_symbol_types.find(symbol_name) != m_symbol_types.end()) { - return m_symbol_types.at(symbol_name); +std::optional Compiler::lookup_typespec(const std::string& symbol_name) { + const auto& it = m_symbol_types.find(m_goos.intern_ptr(symbol_name)); + if (it != m_symbol_types.end()) { + return it->second; } return {}; } @@ -716,7 +717,7 @@ Val* Compiler::compile_gen_docs(const goos::Object& form, const goos::Object& re if (sym_info.kind() == SymbolInfo::Kind::CONSTANT) { var.type = "unknown"; // Unfortunately, constants are not properly typed } else { - var.type = m_symbol_types.at(var.name).base_type(); + var.type = m_symbol_types.at(m_goos.intern_ptr(var.name)).base_type(); } var.def_location = def_loc; if (sym_info.kind() == SymbolInfo::Kind::GLOBAL_VAR) { @@ -731,7 +732,7 @@ Val* Compiler::compile_gen_docs(const goos::Object& form, const goos::Object& re func.def_location = def_loc; func.args = Docs::get_args_from_docstring(sym_info.args(), func.description); // The last arg in the typespec is the return type - const auto& func_type = m_symbol_types.at(func.name); + const auto& func_type = m_symbol_types.at(m_goos.intern_ptr(func.name)); func.return_type = func_type.last_arg().base_type(); file_doc.functions.push_back(func); } else if (sym_info.kind() == SymbolInfo::Kind::TYPE) { diff --git a/goalc/compiler/compilation/ConstantPropagation.cpp b/goalc/compiler/compilation/ConstantPropagation.cpp index 59736be3e07..a63c096876c 100644 --- a/goalc/compiler/compilation/ConstantPropagation.cpp +++ b/goalc/compiler/compilation/ConstantPropagation.cpp @@ -188,7 +188,7 @@ Compiler::ConstPropResult Compiler::constant_propagation_dispatch(const goos::Ob // it can either be a global or symbol const auto& global_constant = m_global_constants.find(expanded.as_symbol()); - const auto& existing_symbol = m_symbol_types.find(expanded.as_symbol()->name); + const auto& existing_symbol = m_symbol_types.find(expanded.as_symbol()); // see if it's a constant if (global_constant != m_global_constants.end()) { @@ -225,12 +225,12 @@ Compiler::ConstPropResult Compiler::constant_propagation_dispatch(const goos::Ob // all are expanded, so we don't need to do it again. // first try as a goal compiler form - auto kv_gfs = g_const_prop_forms.find(head_sym->name); + auto kv_gfs = g_const_prop_forms.find(head_sym.name_ptr); if (kv_gfs != g_const_prop_forms.end()) { return ((*this).*(kv_gfs->second))(expanded, rest, env); } - const auto& kv_goal = g_goal_forms.find(head_sym->name); + const auto& kv_goal = g_goal_forms.find(head_sym.name_ptr); if (kv_goal != g_goal_forms.end()) { // it's a compiler form that we can't constant propagate. return {expanded, true}; @@ -250,7 +250,7 @@ s64 Compiler::get_constant_integer_or_error(const goos::Object& in, Env* env) { auto head = prop.value.as_pair()->car; if (head.is_symbol()) { auto head_sym = head.as_symbol(); - auto enum_type = m_ts.try_enum_lookup(head_sym->name); + auto enum_type = m_ts.try_enum_lookup(head_sym.name_ptr); if (enum_type) { bool success; u64 as_enum = @@ -281,7 +281,7 @@ ValOrConstInt Compiler::get_constant_integer_or_variable(const goos::Object& in, auto head = prop.value.as_pair()->car; if (head.is_symbol()) { auto head_sym = head.as_symbol(); - auto enum_type = m_ts.try_enum_lookup(head_sym->name); + auto enum_type = m_ts.try_enum_lookup(head_sym.name_ptr); if (enum_type) { bool success; u64 as_enum = diff --git a/goalc/compiler/compilation/ControlFlow.cpp b/goalc/compiler/compilation/ControlFlow.cpp index c750662bbee..ffb52e2d368 100644 --- a/goalc/compiler/compilation/ControlFlow.cpp +++ b/goalc/compiler/compilation/ControlFlow.cpp @@ -54,7 +54,7 @@ Condition Compiler::compile_condition(const goos::Object& condition, Env* env, b auto fas = first.as_symbol(); // if there's a not, we can just try again to get an optimization with the invert flipped. - if (fas->name == "not") { + if (fas == "not") { if (!pair_cdr(rest).is_empty_list()) { throw_compiler_error(condition, "A condition with \"not\" can have only one argument"); } @@ -62,7 +62,7 @@ Condition Compiler::compile_condition(const goos::Object& condition, Env* env, b } auto& conditions = invert ? conditions_inverted : conditions_normal; - auto nc_kv = conditions.find(fas->name); + auto nc_kv = conditions.find(fas.name_ptr); if (nc_kv != conditions.end()) { // it is an optimizable condition! @@ -290,7 +290,7 @@ Val* Compiler::compile_cond(const goos::Object& form, const goos::Object& rest, } Val* Compiler::compile_and_or(const goos::Object& form, const goos::Object& rest, Env* env) { - std::string op_name = form.as_pair()->car.as_symbol()->name; + std::string op_name = form.as_pair()->car.as_symbol().name_ptr; bool is_and = false; if (op_name == "and") { is_and = true; diff --git a/goalc/compiler/compilation/Define.cpp b/goalc/compiler/compilation/Define.cpp index ddfd21b5710..ff8e6df4e42 100644 --- a/goalc/compiler/compilation/Define.cpp +++ b/goalc/compiler/compilation/Define.cpp @@ -63,9 +63,9 @@ Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest } auto in_gpr = compiled_val->to_gpr(form, fe); - auto existing_type = m_symbol_types.find(sym.as_symbol()->name); + auto existing_type = m_symbol_types.find(sym.as_symbol()); if (existing_type == m_symbol_types.end()) { - m_symbol_types[sym.as_symbol()->name] = in_gpr->type(); + m_symbol_types[sym.as_symbol()] = in_gpr->type(); } else { bool do_typecheck = true; if (args.has_named("no-typecheck")) { @@ -73,7 +73,7 @@ Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest } if (do_typecheck) { typecheck(form, existing_type->second, in_gpr->type(), - fmt::format("define on existing symbol {}", sym.as_symbol()->name)); + fmt::format("define on existing symbol {}", sym.as_symbol().name_ptr)); } } @@ -104,7 +104,7 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec auto new_type = parse_typespec(typespec, env); - auto existing_type = m_symbol_types.find(symbol_string(sym)); + auto existing_type = m_symbol_types.find(sym.as_symbol()); // symbol already declared, and doesn't match existing definition. do more checks... if (existing_type != m_symbol_types.end() && existing_type->second != new_type) { if (m_allow_inconsistent_definition_symbols.find(symbol_string(sym)) == @@ -124,7 +124,7 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec } } - m_symbol_types[symbol_string(sym)] = new_type; + m_symbol_types[sym.as_symbol()] = new_type; m_symbol_info.add_fwd_dec(symbol_string(sym), form); return get_none(); } diff --git a/goalc/compiler/compilation/Function.cpp b/goalc/compiler/compilation/Function.cpp index 4458c53c2b8..1fb519a7fb5 100644 --- a/goalc/compiler/compilation/Function.cpp +++ b/goalc/compiler/compilation/Function.cpp @@ -17,7 +17,7 @@ const goos::Object& get_lambda_body(const goos::Object& def) { auto* iter = &def; while (true) { auto car = iter->as_pair()->car; - if (car.is_symbol() && car.as_symbol()->name.at(0) == ':') { + if (car.is_symbol() && car.as_symbol().name_ptr[0] == ':') { iter = &iter->as_pair()->cdr; iter = &iter->as_pair()->cdr; } else { @@ -56,9 +56,10 @@ Val* Compiler::compile_local_vars(const goos::Object& form, const goos::Object& for_each_in_list(rest, [&](const goos::Object& o) { if (o.is_symbol()) { // if it has no type, assume object. - auto name = symbol_string(o); + auto name = o.as_symbol(); if (fe->params.find(name) != fe->params.end()) { - throw_compiler_error(form, "Cannot declare a local named {}, this already exists.", name); + throw_compiler_error(form, "Cannot declare a local named {}, this already exists.", + name.name_ptr); } auto ireg = fe->make_ireg(m_ts.make_typespec("object"), RegClass::GPR_64); ireg->mark_as_settable(); @@ -66,11 +67,12 @@ Val* Compiler::compile_local_vars(const goos::Object& form, const goos::Object& } else { auto param_args = get_va(o, o); va_check(o, param_args, {goos::ObjectType::SYMBOL, {}}, {}); - auto name = symbol_string(param_args.unnamed.at(0)); + auto name = param_args.unnamed.at(0).as_symbol(); auto type = parse_typespec(param_args.unnamed.at(1), env); if (fe->params.find(name) != fe->params.end()) { - throw_compiler_error(form, "Cannot declare a local named {}, this already exists.", name); + throw_compiler_error(form, "Cannot declare a local named {}, this already exists.", + name.name_ptr); } if (m_ts.tc(TypeSpec("float"), type)) { @@ -207,10 +209,10 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest self_var->set_rlet_constraint(constr.desired_register); new_func_env->constrain(constr); - if (new_func_env->params.find("self") != new_func_env->params.end()) { + if (new_func_env->params.find(m_goos.intern_ptr("self")) != new_func_env->params.end()) { throw_compiler_error(form, "Cannot have an argument named self in a behavior"); } - new_func_env->params["self"] = self_var; + new_func_env->params[m_goos.intern_ptr("self")] = self_var; reset_args_for_coloring.push_back(self_var); lambda_ts.add_new_tag("behavior", behavior_type); } @@ -228,7 +230,8 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest auto ireg = new_func_env->make_ireg( lambda.params.at(i).type, arg_regs.at(i).is_gpr() ? RegClass::GPR_64 : RegClass::INT_128); ireg->mark_as_settable(); - if (!new_func_env->params.insert({lambda.params.at(i).name, ireg}).second) { + if (!new_func_env->params.insert({m_goos.intern_ptr(lambda.params.at(i).name), ireg}) + .second) { throw_compiler_error(form, "lambda has multiple arguments named {}", lambda.params.at(i).name); } @@ -340,11 +343,11 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en if (!auto_inline) { // if auto-inlining failed, we must get the thing to call in a different way. if (uneval_head.is_symbol()) { - if (uneval_head.as_symbol()->name == "inspect" || uneval_head.as_symbol()->name == "print") { + if (uneval_head.as_symbol() == "inspect" || uneval_head.as_symbol() == "print") { is_method_call = true; } else { if (is_local_symbol(uneval_head, env) || - m_symbol_types.find(symbol_string(uneval_head)) != m_symbol_types.end()) { + m_symbol_types.find(uneval_head.as_symbol()) != m_symbol_types.end()) { // the local environment (mlets, lexicals, constants, globals) defines this symbol. // this will "win" over a method name lookup, so we should compile as normal head = compile_error_guard(args.unnamed.front(), env); @@ -457,7 +460,7 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en env->make_ireg(type, m_ts.lookup_type_allow_partial_def(type)->get_preferred_reg_class()); env->emit_ir(form, copy, eval_args.at(i)); copy->mark_as_settable(); - lexical_env->vars[head_as_lambda->lambda.params.at(i).name] = copy; + lexical_env->vars[m_goos.intern_ptr(head_as_lambda->lambda.params.at(i).name)] = copy; } // setup env @@ -668,21 +671,21 @@ Val* Compiler::compile_declare(const goos::Object& form, const goos::Object& res first.print()); } - if (first.as_symbol()->name == "inline") { + if (first.as_symbol() == "inline") { if (!rrest->is_empty_list()) { throw_compiler_error(first, "Invalid inline declare, no options were expected."); } settings.allow_inline = true; settings.inline_by_default = true; settings.save_code = true; - } else if (first.as_symbol()->name == "allow-inline") { + } else if (first.as_symbol() == "allow-inline") { if (!rrest->is_empty_list()) { throw_compiler_error(first, "Invalid allow-inline declare"); } settings.allow_inline = true; settings.inline_by_default = false; settings.save_code = true; - } else if (first.as_symbol()->name == "asm-func") { + } else if (first.as_symbol() == "asm-func") { auto fe = env->function_env(); fe->is_asm_func = true; if (!rrest->is_pair()) { @@ -693,13 +696,13 @@ Val* Compiler::compile_declare(const goos::Object& form, const goos::Object& res if (!rrest->as_pair()->cdr.is_empty_list()) { throw_compiler_error(first, "Invalid asm-func declare"); } - } else if (first.as_symbol()->name == "print-asm") { + } else if (first.as_symbol() == "print-asm") { if (!rrest->is_empty_list()) { throw_compiler_error(first, "Invalid print-asm declare"); } settings.print_asm = true; - } else if (first.as_symbol()->name == "allow-saved-regs") { + } else if (first.as_symbol() == "allow-saved-regs") { if (!rrest->is_empty_list()) { throw_compiler_error(first, "Invalid allow-saved-regs declare"); } @@ -730,7 +733,7 @@ Val* Compiler::compile_declare_file(const goos::Object& /*form*/, first.print()); } - if (first.as_symbol()->name == "debug") { + if (first.as_symbol() == "debug") { if (!rrest->is_empty_list()) { throw_compiler_error(first, "Invalid debug declare"); } diff --git a/goalc/compiler/compilation/Macro.cpp b/goalc/compiler/compilation/Macro.cpp index 309983ffa94..61bf473e0e0 100644 --- a/goalc/compiler/compilation/Macro.cpp +++ b/goalc/compiler/compilation/Macro.cpp @@ -151,7 +151,7 @@ Val* Compiler::compile_quote(const goos::Object& form, const goos::Object& rest, auto& thing = args.unnamed.front(); switch (thing.type) { case goos::ObjectType::SYMBOL: - return compile_get_sym_obj(thing.as_symbol()->name, env); + return compile_get_sym_obj(thing.as_symbol().name_ptr, env); case goos::ObjectType::EMPTY_LIST: { auto empty_pair = compile_get_sym_obj("_empty_", env); empty_pair->set_type(m_ts.make_typespec("pair")); @@ -196,16 +196,16 @@ Val* Compiler::compile_define_constant(const goos::Object& form, // GOAL constant if (goal) { - if (m_symbol_types.find(sym->name) != m_symbol_types.end()) { + if (m_symbol_types.find(sym) != m_symbol_types.end()) { throw_compiler_error(form, "Cannot define {} as a constant because " "it is already the name of a symbol of type {}", - sym->name, m_symbol_types.at(sym->name).print()); + sym.name_ptr, m_symbol_types.at(sym).print()); } auto existing = m_global_constants.find(sym); if (existing != m_global_constants.end() && existing->second != value) { - print_compiler_warning("Constant {} has been redefined {} -> {}", sym->print(), + print_compiler_warning("Constant {} has been redefined {} -> {}", sym.name_ptr, existing->second.print(), value.print()); } m_global_constants[sym] = value; @@ -213,12 +213,12 @@ Val* Compiler::compile_define_constant(const goos::Object& form, // GOOS constant if (goos) { - m_goos.global_environment.as_env()->vars[sym] = value; + m_goos.global_environment.as_env()->vars.set(sym, value); } // TODO - eventually, it'd be nice if global constants were properly typed // and this information was propagated - m_symbol_info.add_constant(sym->name, form, sym_meta); + m_symbol_info.add_constant(sym.name_ptr, form, sym_meta); return get_none(); } diff --git a/goalc/compiler/compilation/State.cpp b/goalc/compiler/compilation/State.cpp index 6ce4d367290..e47c3465619 100644 --- a/goalc/compiler/compilation/State.cpp +++ b/goalc/compiler/compilation/State.cpp @@ -17,7 +17,7 @@ void Compiler::compile_state_handler_set(StructureType* state_type_info, // we don't use #f here because you might want to actually set the state to #f // (see crate-buzzer wait) auto& arg = args.named.at(name); - if (!(arg.is_symbol() && arg.as_symbol()->name == "*no-state*")) { + if (!(arg.is_symbol() && arg.as_symbol() == "*no-state*")) { auto field = get_field_of_structure(state_type_info, state_object, name, env); auto value = compile_error_guard(arg, env); // we need to save these for typechecking later @@ -60,8 +60,8 @@ Val* Compiler::compile_define_state_hook(const goos::Object& form, Val* enter_value = NULL; // check parent - auto& state_parent = args.unnamed.at(1).as_symbol()->name; - auto state_parent_type = m_ts.make_typespec(state_parent); + auto& state_parent = args.unnamed.at(1).as_symbol(); + auto state_parent_type = m_ts.make_typespec(state_parent.name_ptr); if (!m_ts.tc(TypeSpec("process"), state_parent_type)) { throw_compiler_error(form, "define-state got a type {} which is not a child of process", state_parent_type.print()); @@ -97,7 +97,7 @@ Val* Compiler::compile_define_state_hook(const goos::Object& form, state_parent_type); } - auto& state_name = args.unnamed.at(0).as_symbol()->name; + auto& state_name = args.unnamed.at(0).as_symbol(); auto existing_var = m_symbol_types.find(state_name); TypeSpec type_to_use; @@ -107,7 +107,7 @@ Val* Compiler::compile_define_state_hook(const goos::Object& form, throw_compiler_error(form, "define-state doesn't have enough information to determine the type of " "state {}, and it was not forward declared.", - state_name); + state_name.name_ptr); } type_to_use = *state_type; m_symbol_types[state_name] = *state_type; @@ -115,11 +115,11 @@ Val* Compiler::compile_define_state_hook(const goos::Object& form, type_to_use = existing_var->second; if (state_type) { typecheck(form, existing_var->second, *state_type, - fmt::format("type of state {}", state_name)); + fmt::format("type of state {}", state_name.name_ptr)); } } - auto sym_val = env->function_env()->alloc_val(state_name, type_to_use); + auto sym_val = env->function_env()->alloc_val(state_name.name_ptr, type_to_use); env->emit_ir(form, sym_val, state_object); @@ -158,8 +158,8 @@ Val* Compiler::compile_define_virtual_state_hook(const goos::Object& form, Val* enter_value = NULL; // check parent - auto& state_parent = args.unnamed.at(1).as_symbol()->name; - auto state_parent_type = m_ts.make_typespec(state_parent); + auto& state_parent = args.unnamed.at(1).as_symbol(); + auto state_parent_type = m_ts.make_typespec(state_parent.name_ptr); if (!m_ts.tc(TypeSpec("process"), state_parent_type)) { throw_compiler_error(form, "define-state got a type {} which is not a child of process", state_parent_type.print()); @@ -172,24 +172,24 @@ Val* Compiler::compile_define_virtual_state_hook(const goos::Object& form, state_object->type().print()); } - auto& state_name = args.unnamed.at(0).as_symbol()->name; + auto& state_name = args.unnamed.at(0).as_symbol(); MethodInfo child_method_info; - if (!m_ts.try_lookup_method(state_parent, state_name, &child_method_info)) { + if (!m_ts.try_lookup_method(state_parent.name_ptr, state_name.name_ptr, &child_method_info)) { throw_compiler_error( form, "Tried to define a virtual state {} for type {}, but the state was not declared.", - state_name, state_parent); + state_name.name_ptr, state_parent.name_ptr); } MethodInfo parent_method_info; - auto parent_of_parent_type = m_ts.lookup_type(state_parent)->get_parent(); - if (m_ts.try_lookup_method(parent_of_parent_type, state_name, &parent_method_info) && - args.unnamed.at(3).as_symbol()->name == "#f") { + auto parent_of_parent_type = m_ts.lookup_type(state_parent.name_ptr)->get_parent(); + if (m_ts.try_lookup_method(parent_of_parent_type, state_name.name_ptr, &parent_method_info) && + args.unnamed.at(3).as_symbol() == "#f") { // need to call inherit state TODO auto inherit_state_func = compile_get_symbol_value(form, "inherit-state", env)->to_gpr(form, env); auto parents_state = - compile_get_method_of_type(form, TypeSpec(parent_of_parent_type), state_name, env) + compile_get_method_of_type(form, TypeSpec(parent_of_parent_type), state_name.name_ptr, env) ->to_gpr(form, env); compile_real_function_call(form, inherit_state_func, {state_object, parents_state}, env); } @@ -197,7 +197,7 @@ Val* Compiler::compile_define_virtual_state_hook(const goos::Object& form, // call method set. // (method-set! sunken-elevator 22 (the-as function gp-0)) auto method_set_func = compile_get_symbol_value(form, "method-set!", env)->to_gpr(form, env); - auto type_obj = compile_get_symbol_value(form, state_parent, env)->to_gpr(form, env); + auto type_obj = compile_get_symbol_value(form, state_parent.name_ptr, env)->to_gpr(form, env); auto method_id = compile_integer(child_method_info.id, env)->to_gpr(form, env); compile_real_function_call(form, method_set_func, {type_obj, method_id, state_object}, env); @@ -229,8 +229,8 @@ Val* Compiler::compile_define_virtual_state_hook(const goos::Object& form, child_method_info.type.substitute_for_method_call(state_parent_type.base_type())) { throw_compiler_error( form, "Virtual state {} of {} was declared as {}, but got {}. First declared in type {}.", - state_name, state_parent, child_method_info.type.print(), state_type->print(), - child_method_info.defined_in_type); + state_name.name_ptr, state_parent.name_ptr, child_method_info.type.print(), + state_type->print(), child_method_info.defined_in_type); } } diff --git a/goalc/compiler/compilation/Static.cpp b/goalc/compiler/compilation/Static.cpp index 6d9235058f8..86560bf73b0 100644 --- a/goalc/compiler/compilation/Static.cpp +++ b/goalc/compiler/compilation/Static.cpp @@ -64,13 +64,13 @@ void Compiler::compile_static_structure_inline(const goos::Object& form, "Array field must be defined with (new 'static ['array, 'inline-array] type-name ...)"); } - if (!new_form.at(0).is_symbol() || new_form.at(0).as_symbol()->name != "new") { + if (!new_form.at(0).is_symbol() || new_form.at(0).as_symbol() != "new") { throw_compiler_error( field_value, "Array field must be defined with (new 'static ['array, 'inline-array] type-name ...)"); } - if (!is_quoted_sym(new_form.at(1)) || unquote(new_form.at(1)).as_symbol()->name != "static") { + if (!is_quoted_sym(new_form.at(1)) || unquote(new_form.at(1)).as_symbol() != "static") { throw_compiler_error( field_value, "Array field must be defined with (new 'static ['array, 'inline-array] type-name ...)"); @@ -169,13 +169,12 @@ void Compiler::compile_static_structure_inline(const goos::Object& form, "Inline field must be defined with (new 'static 'type-name ...)"); } - if (!new_form.at(0).is_symbol() || new_form.at(0).as_symbol()->name != "new") { + if (!new_form.at(0).is_symbol() || new_form.at(0).as_symbol() != "new") { throw_compiler_error(field_value, "Inline field must be defined with (new 'static 'type-name ...)"); } - if (!is_quoted_sym(new_form.at(1)) || - unquote(new_form.at(1)).as_symbol()->name != "static") { + if (!is_quoted_sym(new_form.at(1)) || unquote(new_form.at(1)).as_symbol() != "static") { throw_compiler_error(field_value, "Inline field must be defined with (new 'static 'type-name ...)"); } @@ -615,7 +614,7 @@ StaticResult Compiler::compile_static_no_eval_for_pairs(const goos::Object& form (can_macro && form.as_pair()->car.is_symbol("lambda"))) { return compile_static(form, env); } - if (form.as_pair()->car.is_symbol() && form.as_pair()->car.as_symbol()->name == "unquote") { + if (form.as_pair()->car.is_symbol() && form.as_pair()->car.as_symbol() == "unquote") { // ,(macro-name args...) is actually (unquote (macro-name args...)) // decompile the arg as macro if possible. // ,(lambda ...) is also a special case. @@ -658,7 +657,7 @@ StaticResult Compiler::compile_static_no_eval_for_pairs(const goos::Object& form } return StaticResult::make_constant_data(bint_val, TypeSpec("int32")); } else if (form.is_symbol()) { - return StaticResult::make_symbol(form.as_symbol()->name); + return StaticResult::make_symbol(form.as_symbol().name_ptr); } else if (form.is_empty_list()) { return StaticResult::make_symbol("_empty_"); } else if (form.is_string()) { @@ -695,9 +694,9 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env if (form.is_symbol()) { // constant, #t, or #f - auto& name = form.as_symbol()->name; + auto& name = form.as_symbol(); if (name == "#t" || name == "#f") { - return StaticResult::make_symbol(name); + return StaticResult::make_symbol(name.name_ptr); } // as a constant @@ -715,7 +714,7 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env } else if (form.is_int()) { return StaticResult::make_constant_data(form.as_int(), TypeSpec("integer")); } else if (is_quoted_sym(form)) { - return StaticResult::make_symbol(unquote(form).as_symbol()->name); + return StaticResult::make_symbol(unquote(form).as_symbol().name_ptr); } else if (form.is_string()) { // todo string pool auto obj = std::make_unique(form.as_string()->data, segment); @@ -725,7 +724,7 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env } else if (form.is_pair()) { auto first = form.as_pair()->car; auto rest = form.as_pair()->cdr; - if (first.is_symbol() && first.as_symbol()->name == "quote") { + if (first.is_symbol() && first.as_symbol() == "quote") { if (rest.is_pair()) { auto second = rest.as_pair()->car; if (!rest.as_pair()->cdr.is_empty_list()) { @@ -739,14 +738,14 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env } } throw_compiler_error(form, "The quoted form {} has no argument.", form.print()); - } else if (first.is_symbol() && first.as_symbol()->name == "new") { + } else if (first.is_symbol() && first.as_symbol() == "new") { goos::Object constructor_args; auto args = get_list_as_vector(rest, &constructor_args, 2); if (args.size() < 2) { throw_compiler_error(form, "New form evaluated at compile must specify (new 'static ...)"); } - if (!is_quoted_sym(args.at(0)) || unquote(args.at(0)).as_symbol()->name != "static") { + if (!is_quoted_sym(args.at(0)) || unquote(args.at(0)).as_symbol() != "static") { throw_compiler_error(form, "New form evaluated at compile time must use 'static. Got {}.", args.at(0).print()); } @@ -757,11 +756,11 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env } auto unquoted_type = unquote(args.at(1)); - if (unquoted_type.as_symbol()->name == "boxed-array") { + if (unquoted_type.as_symbol() == "boxed-array") { return fill_static_boxed_array(form, rest, env, segment, "array"); - } else if (unquoted_type.as_symbol()->name == "array") { + } else if (unquoted_type.as_symbol() == "array") { return fill_static_array(form, rest, env, segment); - } else if (unquoted_type.as_symbol()->name == "inline-array") { + } else if (unquoted_type.as_symbol() == "inline-array") { return fill_static_inline_array(form, rest, env, segment); } else { auto ts = parse_typespec(unquoted_type, env); @@ -792,7 +791,7 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env throw_compiler_error(form, "Cannot construct a static {}.", ts.print()); } } - } else if (first.is_symbol() && first.as_symbol()->name == "the-as") { + } else if (first.is_symbol() && first.as_symbol() == "the-as") { auto args = get_va(form, rest); va_check(form, args, {{}, {}}, {}); auto type = parse_typespec(args.unnamed.at(0), env); @@ -802,7 +801,7 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env return StaticResult::make_constant_data(value, TypeSpec("float")); } } - } else if (first.is_symbol() && first.as_symbol()->name == "the") { + } else if (first.is_symbol() && first.as_symbol() == "the") { auto args = get_va(form, rest); va_check(form, args, {{}, {}}, {}); auto type = parse_typespec(args.unnamed.at(0), env); @@ -817,7 +816,7 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env va_check(form, args, {goos::ObjectType::SYMBOL}, {{{"method-count", {false, goos::ObjectType::INTEGER}}}}); - auto type_name = args.unnamed.at(0).as_symbol()->name; + std::string type_name = args.unnamed.at(0).as_symbol().name_ptr; std::optional expected_method_count = m_ts.try_get_type_method_count(type_name); int method_count = -1; @@ -878,7 +877,7 @@ void Compiler::fill_static_array_inline(const goos::Object& form, // Special case for symbols that refer to types StaticResult sr; if (content_type == TypeSpec("type") && arg.is_symbol()) { - const auto& type_name = arg.as_symbol()->name; + std::string type_name = arg.as_symbol().name_ptr; std::optional expected_method_count = m_ts.try_get_type_method_count(type_name); if (!expected_method_count) { throw_compiler_error(form, "Undeclared type used in inline-array - {}", type_name); @@ -1076,12 +1075,12 @@ void Compiler::fill_static_inline_array_inline(const goos::Object& form, elt_def, "Inline array element must be defined with (new 'static 'type-name ...)"); } - if (!new_form.at(0).is_symbol() || new_form.at(0).as_symbol()->name != "new") { + if (!new_form.at(0).is_symbol() || new_form.at(0).as_symbol() != "new") { throw_compiler_error( elt_def, "Inline array element must be defined with (new 'static 'type-name ...)"); } - if (!is_quoted_sym(new_form.at(1)) || unquote(new_form.at(1)).as_symbol()->name != "static") { + if (!is_quoted_sym(new_form.at(1)) || unquote(new_form.at(1)).as_symbol() != "static") { throw_compiler_error( elt_def, "Inline array element must be defined with (new 'static 'type-name ...)"); } diff --git a/goalc/compiler/compilation/Type.cpp b/goalc/compiler/compilation/Type.cpp index 1fbea4517f9..5d6da83c932 100644 --- a/goalc/compiler/compilation/Type.cpp +++ b/goalc/compiler/compilation/Type.cpp @@ -369,18 +369,19 @@ Val* Compiler::compile_deftype(const goos::Object& form, const goos::Object& res auto result = parse_deftype(rest, &m_ts, &m_global_constants); // look up the type name - auto kv = m_symbol_types.find(result.type.base_type()); + auto kv = m_symbol_types.find(m_goos.intern_ptr(result.type.base_type())); if (kv != m_symbol_types.end() && kv->second.base_type() != "type") { // we already have something that's not a type with the same name, this is bad. lg::print("[Warning] deftype will redefine {} from {} to a type.\n", result.type.base_type(), kv->second.print()); } // remember that this is a type - m_symbol_types[result.type.base_type()] = m_ts.make_typespec("type"); + m_symbol_types[m_goos.intern_ptr(result.type.base_type())] = m_ts.make_typespec("type"); // add declared states for (auto& state : result.type_info->get_states_declared_for_type()) { - auto existing_type = m_symbol_types.find(state.first); + auto interned_state_first = m_goos.intern_ptr(state.first); + auto existing_type = m_symbol_types.find(interned_state_first); if (existing_type != m_symbol_types.end() && existing_type->second != state.second) { if (m_throw_on_define_extern_redefinition) { throw_compiler_error(form, "deftype would redefine the type of state {} from {} to {}.", @@ -394,7 +395,7 @@ Val* Compiler::compile_deftype(const goos::Object& form, const goos::Object& res } } - m_symbol_types[state.first] = state.second; + m_symbol_types[interned_state_first] = state.second; m_symbol_info.add_fwd_dec(state.first, form); } @@ -530,10 +531,10 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _ self_var->set_rlet_constraint(constr.desired_register); new_func_env->constrain(constr); - if (new_func_env->params.find("self") != new_func_env->params.end()) { + if (new_func_env->params.find(m_goos.intern_ptr("self")) != new_func_env->params.end()) { throw_compiler_error(form, "Cannot have an argument named self in a behavior"); } - new_func_env->params["self"] = self_var; + new_func_env->params[m_goos.intern_ptr("self")] = self_var; reset_args_for_coloring.push_back(self_var); lambda_ts.add_new_tag("behavior", *behavior); } @@ -551,7 +552,7 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _ auto ireg = new_func_env->make_ireg( lambda.params.at(i).type, arg_regs.at(i).is_gpr() ? RegClass::GPR_64 : RegClass::INT_128); ireg->mark_as_settable(); - if (!new_func_env->params.insert({lambda.params.at(i).name, ireg}).second) { + if (!new_func_env->params.insert({m_goos.intern_ptr(lambda.params.at(i).name), ireg}).second) { throw_compiler_error(form, "defmethod has multiple arguments named {}", lambda.params.at(i).name); } @@ -975,7 +976,7 @@ Val* Compiler::compile_heap_new(const goos::Object& form, const goos::Object& type, const goos::Object* rest, Env* env) { - bool making_boxed_array = unquote(type).as_symbol()->name == "boxed-array"; + bool making_boxed_array = unquote(type).as_symbol() == "boxed-array"; TypeSpec main_type; if (!making_boxed_array) { main_type = parse_typespec(unquote(type), env); @@ -1042,7 +1043,7 @@ Val* Compiler::compile_heap_new(const goos::Object& form, if (making_boxed_array && !got_content_type) { got_content_type = true; if (o.is_symbol()) { - content_type = o.as_symbol()->name; + content_type = o.as_symbol().name_ptr; args.push_back(compile_get_symbol_value(form, content_type, env)->to_reg(form, env)); } else { throw_compiler_error(form, "Invalid boxed-array type {}", o.print()); @@ -1070,7 +1071,7 @@ Val* Compiler::compile_static_new(const goos::Object& form, const goos::Object* rest, Env* env) { auto unquoted_type = unquote(type); - const auto& sym_name = unquoted_type.as_symbol()->name; + const auto& sym_name = unquoted_type.as_symbol(); // Check if the type is an array or a subtype of 'array' bool is_array = sym_name == "boxed-array" || sym_name == "array" || sym_name == "inline-array"; if (!is_array) { @@ -1369,14 +1370,14 @@ Val* Compiler::compile_declare_type(const goos::Object& form, const goos::Object va_check(form, args, {goos::ObjectType::SYMBOL, goos::ObjectType::SYMBOL}, {}); auto kind = symbol_string(args.unnamed.at(1)); - auto type_name = symbol_string(args.unnamed.at(0)); + auto type_name = args.unnamed.at(0).as_symbol(); - m_ts.forward_declare_type_as(type_name, kind); + m_ts.forward_declare_type_as(type_name.name_ptr, kind); auto existing_type = m_symbol_types.find(type_name); if (existing_type != m_symbol_types.end() && existing_type->second != TypeSpec("type")) { - throw_compiler_error(form, "Cannot forward declare {} as a type: it is already a {}", type_name, - existing_type->second.print()); + throw_compiler_error(form, "Cannot forward declare {} as a type: it is already a {}", + type_name.name_ptr, existing_type->second.print()); } m_symbol_types[type_name] = TypeSpec("type"); @@ -1473,15 +1474,15 @@ int Compiler::get_size_for_size_of(const goos::Object& form, const goos::Object& auto args = get_va(form, rest); va_check(form, args, {goos::ObjectType::SYMBOL}, {}); - auto type_to_look_for = args.unnamed.at(0).as_symbol()->name; + auto type_to_look_for = args.unnamed.at(0).as_symbol(); - if (!m_ts.fully_defined_type_exists(type_to_look_for)) { + if (!m_ts.fully_defined_type_exists(type_to_look_for.name_ptr)) { throw_compiler_error( form, "The type or enum {} given to size-of could not be found, or was not fully defined", args.unnamed.at(0).print()); } - auto type = m_ts.lookup_type(type_to_look_for); + auto type = m_ts.lookup_type(type_to_look_for.name_ptr); auto as_value = dynamic_cast(type); auto as_structure = dynamic_cast(type); diff --git a/goalc/make/MakeSystem.cpp b/goalc/make/MakeSystem.cpp index 98d92f282a4..4f8429a039f 100644 --- a/goalc/make/MakeSystem.cpp +++ b/goalc/make/MakeSystem.cpp @@ -136,7 +136,7 @@ goos::Object MakeSystem::handle_defstep(const goos::Object& form, step->outputs.push_back(m_path_map.apply_remaps(obj.as_string()->data)); }); - step->tool = args.get_named("tool").as_symbol()->name; + step->tool = args.get_named("tool").as_symbol().name_ptr; if (m_tools.find(step->tool) == m_tools.end()) { throw std::runtime_error(fmt::format("The tool {} is unknown.", step->tool)); @@ -232,7 +232,7 @@ goos::Object MakeSystem::handle_get_gsrc_path(const goos::Object& form, if (m_gsrc_files.count(file_name) != 0) { return goos::StringObject::make_new(m_gsrc_files.at(file_name)); } else { - return goos::SymbolObject::make_new(m_goos.reader.symbolTable, "#f"); + return goos::Object::make_symbol(&m_goos.reader.symbolTable, "#f"); } } diff --git a/lsp/state/workspace.cpp b/lsp/state/workspace.cpp index 187752a907e..83f09153e6b 100644 --- a/lsp/state/workspace.cpp +++ b/lsp/state/workspace.cpp @@ -131,11 +131,11 @@ std::optional Workspace::get_definition_info_from_all_types( if (m_tracked_all_types_files.count(all_types_uri) == 0) { return {}; } - const auto& dts = m_tracked_all_types_files[all_types_uri].m_dts; - if (dts.symbol_metadata_map.count(symbol_name) == 0) { + const auto& dts = m_tracked_all_types_files[all_types_uri]->m_dts; + if (dts->symbol_metadata_map.count(symbol_name) == 0) { return {}; } - return dts.symbol_metadata_map.at(symbol_name); + return dts->symbol_metadata_map.at(symbol_name); } // TODO - a gross hack that should go away when the language isn't so tightly coupled to the jak @@ -219,9 +219,9 @@ void Workspace::start_tracking_file(const LSPSpec::DocumentUri& file_uri, if (!file.m_all_types_uri.empty()) { if (m_tracked_all_types_files.count(file.m_all_types_uri) == 0) { lg::debug("new all-types file - {}", file.m_all_types_uri); - m_tracked_all_types_files[file.m_all_types_uri] = WorkspaceAllTypesFile( + m_tracked_all_types_files[file.m_all_types_uri] = std::make_unique( file.m_all_types_uri, file.m_game_version, file.m_all_types_file_path); - m_tracked_all_types_files[file.m_all_types_uri].parse_type_system(); + m_tracked_all_types_files[file.m_all_types_uri]->parse_type_system(); } } } else if (language_id == "opengoal") { @@ -269,10 +269,10 @@ void Workspace::update_tracked_file(const LSPSpec::DocumentUri& file_uri, if (!file.m_all_types_uri.empty() && m_tracked_all_types_files.count(file.m_all_types_uri) == 0) { auto& all_types_file = m_tracked_all_types_files[file.m_all_types_uri]; - all_types_file.m_file_path = file.m_all_types_file_path; - all_types_file.m_uri = file.m_all_types_uri; - all_types_file.m_game_version = file.m_game_version; - all_types_file.update_type_system(); + all_types_file->m_file_path = file.m_all_types_file_path; + all_types_file->m_uri = file.m_all_types_uri; + all_types_file->m_game_version = file.m_game_version; + all_types_file->update_type_system(); } } @@ -280,7 +280,7 @@ void Workspace::update_tracked_file(const LSPSpec::DocumentUri& file_uri, lg::debug("updating tracked all types file - {}", file_uri); // If the all-types file has changed, re-parse it // NOTE - this assumes its still for the same game version! - m_tracked_all_types_files[file_uri].update_type_system(); + m_tracked_all_types_files[file_uri]->update_type_system(); } }; @@ -504,11 +504,11 @@ std::optional WorkspaceIRFile::get_symbol_at_position( void WorkspaceAllTypesFile::parse_type_system() { lg::debug("DTS Loading - '{}'", m_file_path.string()); - m_dts.parse_type_defs({m_file_path.string()}); + m_dts->parse_type_defs({m_file_path.string()}); lg::debug("DTS Loaded At - '{}'", m_file_path.string()); } void WorkspaceAllTypesFile::update_type_system() { - m_dts = decompiler::DecompilerTypeSystem(m_game_version); + m_dts = std::make_unique(m_game_version); parse_type_system(); } diff --git a/lsp/state/workspace.h b/lsp/state/workspace.h index 28c7315d5d9..54825c9d973 100644 --- a/lsp/state/workspace.h +++ b/lsp/state/workspace.h @@ -58,15 +58,19 @@ class WorkspaceIRFile { class WorkspaceAllTypesFile { public: - WorkspaceAllTypesFile() : m_dts(GameVersion::Jak1){}; + WorkspaceAllTypesFile() + : m_dts(std::make_unique(GameVersion::Jak1)){}; WorkspaceAllTypesFile(const LSPSpec::DocumentUri& uri, const GameVersion version, const fs::path file_path) - : m_game_version(version), m_uri(uri), m_dts(m_game_version), m_file_path(file_path){}; + : m_game_version(version), + m_uri(uri), + m_dts(std::make_unique(m_game_version)), + m_file_path(file_path){}; GameVersion m_game_version; LSPSpec::DocumentUri m_uri; - decompiler::DecompilerTypeSystem m_dts; + std::unique_ptr m_dts; fs::path m_file_path; void parse_type_system(); @@ -110,7 +114,8 @@ class Workspace { bool m_initialized = false; std::unordered_map m_tracked_og_files = {}; std::unordered_map m_tracked_ir_files = {}; - std::unordered_map m_tracked_all_types_files = {}; + std::unordered_map> + m_tracked_all_types_files = {}; // TODO: // OpenGOAL is still incredibly tightly coupled to the jak projects as a language diff --git a/test/test_goos.cpp b/test/test_goos.cpp index e488f97fb7e..ab109933943 100644 --- a/test/test_goos.cpp +++ b/test/test_goos.cpp @@ -441,31 +441,6 @@ TEST(GoosBuiltins, Type) { EXPECT_EQ(e(i, "(type? 'environment '())"), "#f"); } -/*! - * Confirm that the global and goos env variables appear - */ -TEST(GoosEval, GlobalAndGoalEnv) { - Interpreter i; - Object goal_env, goos_env; - EXPECT_TRUE(i.get_global_variable_by_name("*global-env*", &goos_env)); - EXPECT_TRUE(i.get_global_variable_by_name("*goal-env*", &goal_env)); - - auto& goal_vars = goal_env.as_env()->vars; - auto& goos_vars = goos_env.as_env()->vars; - - EXPECT_TRUE(goal_vars.find(i.intern("*global-env*").as_symbol()) != goal_vars.end()); - EXPECT_TRUE(goal_vars.find(i.intern("*goal-env*").as_symbol()) != goal_vars.end()); - EXPECT_TRUE(goos_vars.find(i.intern("*global-env*").as_symbol()) != goos_vars.end()); - EXPECT_TRUE(goos_vars.find(i.intern("*goal-env*").as_symbol()) != goos_vars.end()); - - EXPECT_TRUE(goos_vars.find(i.intern("*goal-env*").as_symbol())->second == - goal_vars.find(i.intern("*goal-env*").as_symbol())->second); - EXPECT_TRUE(goos_vars.find(i.intern("*global-env*").as_symbol())->second == - goal_vars.find(i.intern("*global-env*").as_symbol())->second); - EXPECT_TRUE(goos_vars.find(i.intern("*global-env*").as_symbol())->second != - goos_vars.find(i.intern("*goal-env*").as_symbol())->second); -} - /*! * Confirm that the GOOS Library is loaded automatically on interpreter start. */ @@ -1047,10 +1022,10 @@ TEST(GoosObject, Char) { */ TEST(GoosObject, Symbol) { SymbolTable st, st2; - Object obj = SymbolObject::make_new(st, "test1"); - Object obj2 = SymbolObject::make_new(st, "test2"); - Object obj3 = SymbolObject::make_new(st, "test1"); - Object obj4 = SymbolObject::make_new(st2, "test1"); + Object obj = Object::make_symbol(&st, "test1"); + Object obj2 = Object::make_symbol(&st, "test2"); + Object obj3 = Object::make_symbol(&st, "test1"); + Object obj4 = Object::make_symbol(&st2, "test1"); // check type EXPECT_TRUE(obj.is_symbol()); @@ -1173,9 +1148,9 @@ TEST(GoosSpecialForms, Define) { Object goal_env; EXPECT_TRUE(i.get_global_variable_by_name("*goal-env*", &goal_env)); - auto x_in_goal_env = goal_env.as_env()->vars.find(i.intern("x").as_symbol()); - EXPECT_TRUE(x_in_goal_env != goal_env.as_env()->vars.end()); - EXPECT_EQ(x_in_goal_env->second.print(), "20"); + auto x_in_goal_env = goal_env.as_env()->vars.lookup(i.intern("x").as_symbol()); + EXPECT_TRUE(x_in_goal_env); + EXPECT_EQ(x_in_goal_env->print(), "20"); // test automatic environment of define e(i, "(begin (desfun test-define () (define x 500)) (test-define))"); diff --git a/test/test_reader.cpp b/test/test_reader.cpp index aeba24a666a..20502b5c0dc 100644 --- a/test/test_reader.cpp +++ b/test/test_reader.cpp @@ -26,7 +26,7 @@ bool check_first_float(Object o, double x) { } bool check_first_symbol(Object o, const std::string& sym) { - return o.as_pair()->cdr.as_pair()->car.as_symbol()->name == sym; + return o.as_pair()->cdr.as_pair()->car.as_symbol() == sym; } bool check_first_string(Object o, const std::string& str) {