mirror of
https://github.com/open-goal/jak-project
synced 2026-08-21 06:38:33 -04:00
decomp: add docstring support to relevant places in all-types (#1753)
* decomp: add `docstring` support to relevant places in `all-types` * decomp: output method docstring into the `defmethod` instead * goalc: handle docstrings in `define[-extern]` (gracefully ignore for now) * decomp: output docstrings for bitfield deftypes too * goalc: fix `defenum` parsing when coming from the compiler (no symbol metadata to store to) * lsp/tests: fix ups * lint: formatting * goalc: handle edge-case of defining a string constant * cleanup leftovers, fix codacy issues, rename struct
This commit is contained in:
+7
-22
@@ -84,6 +84,13 @@
|
||||
"name": "REPL",
|
||||
"args": ["--user-auto"]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "goalc.exe (bin\\goalc.exe)",
|
||||
"name": "REPL - Jak 2",
|
||||
"args": ["--user-auto", "--game", "jak2"]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
@@ -102,17 +109,6 @@
|
||||
"${workspaceRoot}/decompiler_out"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name": "Decompiler - Jak 1 - Data Only",
|
||||
"args": [
|
||||
"${workspaceRoot}/decompiler/config/jak1_ntsc_black_label.jsonc",
|
||||
"${workspaceRoot}/iso_data",
|
||||
"${workspaceRoot}/decompiler_out"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
@@ -124,17 +120,6 @@
|
||||
"${workspaceRoot}/decompiler_out"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name": "Disassembler - Jak 1",
|
||||
"args": [
|
||||
"${workspaceRoot}/decompiler/config/jak1_ntsc_black_label.jsonc",
|
||||
"${workspaceRoot}/iso_data",
|
||||
"${workspaceRoot}/decompiler_out"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
|
||||
@@ -12,10 +12,17 @@
|
||||
#include "TypeSpec.h"
|
||||
|
||||
#include "common/goal_constants.h"
|
||||
#include "common/goos/TextDB.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
class TypeSystem;
|
||||
|
||||
// Various metadata that can be associated with a symbol or form
|
||||
struct DefinitionMetadata {
|
||||
std::optional<goos::TextDb::ShortInfo> definition_info;
|
||||
std::optional<std::string> docstring;
|
||||
};
|
||||
|
||||
struct MethodInfo {
|
||||
int id = -1;
|
||||
std::string name;
|
||||
@@ -23,6 +30,7 @@ struct MethodInfo {
|
||||
std::string defined_in_type;
|
||||
bool no_virtual = false;
|
||||
bool overrides_method_type_of_parent = false;
|
||||
std::optional<std::string> docstring;
|
||||
|
||||
bool operator==(const MethodInfo& other) const;
|
||||
bool operator!=(const MethodInfo& other) const { return !((*this) == other); }
|
||||
@@ -106,6 +114,8 @@ class Type {
|
||||
|
||||
bool gen_inspect() const { return m_generate_inspect; }
|
||||
|
||||
DefinitionMetadata m_metadata;
|
||||
|
||||
protected:
|
||||
Type(std::string parent, std::string name, bool is_boxed, int heap_base);
|
||||
virtual std::string diff_impl(const Type& other) const = 0;
|
||||
@@ -123,13 +133,6 @@ class Type {
|
||||
std::string m_runtime_name;
|
||||
bool m_is_boxed = false; // does this have runtime type information?
|
||||
int m_heap_base = 0;
|
||||
|
||||
// definition information
|
||||
// TODO - LSP - .gc support
|
||||
/*std::string m_defining_file;
|
||||
int m_line_number;
|
||||
int m_line_offset;
|
||||
void update_definition_meta(const std::string& defining_file, int line_number, int line_offset);*/
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
@@ -511,11 +511,12 @@ int TypeSystem::get_load_size_allow_partial_def(const TypeSpec& ts) const {
|
||||
|
||||
MethodInfo TypeSystem::declare_method(const std::string& type_name,
|
||||
const std::string& method_name,
|
||||
const std::optional<std::string>& docstring,
|
||||
bool no_virtual,
|
||||
const TypeSpec& ts,
|
||||
bool override_type) {
|
||||
return declare_method(lookup_type(make_typespec(type_name)), method_name, no_virtual, ts,
|
||||
override_type);
|
||||
return declare_method(lookup_type(make_typespec(type_name)), method_name, docstring, no_virtual,
|
||||
ts, override_type);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -529,6 +530,7 @@ MethodInfo TypeSystem::declare_method(const std::string& type_name,
|
||||
*/
|
||||
MethodInfo TypeSystem::declare_method(Type* type,
|
||||
const std::string& method_name,
|
||||
const std::optional<std::string>& docstring,
|
||||
bool no_virtual,
|
||||
const TypeSpec& ts,
|
||||
bool override_type,
|
||||
@@ -537,7 +539,7 @@ MethodInfo TypeSystem::declare_method(Type* type,
|
||||
if (override_type) {
|
||||
throw_typesystem_error("Cannot use :replace option with a new method.");
|
||||
}
|
||||
return add_new_method(type, ts);
|
||||
return add_new_method(type, ts, docstring);
|
||||
}
|
||||
|
||||
// look up the method
|
||||
@@ -557,7 +559,7 @@ MethodInfo TypeSystem::declare_method(Type* type,
|
||||
|
||||
// use the existing ID.
|
||||
return type->add_method(
|
||||
{existing_info.id, method_name, ts, type->get_name(), no_virtual, true});
|
||||
{existing_info.id, method_name, ts, type->get_name(), no_virtual, true, docstring});
|
||||
} else {
|
||||
if (got_existing) {
|
||||
// make sure we aren't changing anything.
|
||||
@@ -587,16 +589,17 @@ MethodInfo TypeSystem::declare_method(Type* type,
|
||||
return existing_info;
|
||||
} else {
|
||||
// add a new method!
|
||||
return type->add_method(
|
||||
{get_next_method_id(type), method_name, ts, type->get_name(), no_virtual, false});
|
||||
return type->add_method({get_next_method_id(type), method_name, ts, type->get_name(),
|
||||
no_virtual, false, docstring});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MethodInfo TypeSystem::define_method(const std::string& type_name,
|
||||
const std::string& method_name,
|
||||
const TypeSpec& ts) {
|
||||
return define_method(lookup_type(make_typespec(type_name)), method_name, ts);
|
||||
const TypeSpec& ts,
|
||||
const std::optional<std::string>& docstring) {
|
||||
return define_method(lookup_type(make_typespec(type_name)), method_name, ts, docstring);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -610,9 +613,10 @@ MethodInfo TypeSystem::define_method(const std::string& type_name,
|
||||
*/
|
||||
MethodInfo TypeSystem::define_method(Type* type,
|
||||
const std::string& method_name,
|
||||
const TypeSpec& ts) {
|
||||
const TypeSpec& ts,
|
||||
const std::optional<std::string>& docstring) {
|
||||
if (method_name == "new") {
|
||||
return add_new_method(type, ts);
|
||||
return add_new_method(type, ts, docstring);
|
||||
}
|
||||
|
||||
// look up the method
|
||||
@@ -640,7 +644,9 @@ MethodInfo TypeSystem::define_method(Type* type,
|
||||
* If it turns out that other child methods can specialize arguments (seems like a bad idea), this
|
||||
* may be generalized.
|
||||
*/
|
||||
MethodInfo TypeSystem::add_new_method(Type* type, const TypeSpec& ts) {
|
||||
MethodInfo TypeSystem::add_new_method(Type* type,
|
||||
const TypeSpec& ts,
|
||||
const std::optional<std::string>& docstring) {
|
||||
MethodInfo existing;
|
||||
if (type->get_my_new_method(&existing)) {
|
||||
// it exists!
|
||||
@@ -653,7 +659,7 @@ MethodInfo TypeSystem::add_new_method(Type* type, const TypeSpec& ts) {
|
||||
|
||||
return existing;
|
||||
} else {
|
||||
return type->add_new_method({0, "new", ts, type->get_name()});
|
||||
return type->add_new_method({0, "new", ts, type->get_name(), false, false, docstring});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1038,26 +1044,27 @@ void TypeSystem::add_builtin_types(GameVersion version) {
|
||||
forward_declare_type_as("memory-usage-block", "basic");
|
||||
|
||||
// OBJECT
|
||||
declare_method(obj_type, "new", false,
|
||||
declare_method(obj_type, "new", {}, false,
|
||||
make_function_typespec({"symbol", "type", "int"}, "_type_"), false);
|
||||
declare_method(obj_type, "delete", false, make_function_typespec({"_type_"}, "none"), false);
|
||||
declare_method(obj_type, "print", false, make_function_typespec({"_type_"}, "_type_"), false);
|
||||
declare_method(obj_type, "inspect", false, make_function_typespec({"_type_"}, "_type_"), false);
|
||||
declare_method(obj_type, "length", false, make_function_typespec({"_type_"}, "int"),
|
||||
declare_method(obj_type, "delete", {}, false, make_function_typespec({"_type_"}, "none"), false);
|
||||
declare_method(obj_type, "print", {}, false, make_function_typespec({"_type_"}, "_type_"), false);
|
||||
declare_method(obj_type, "inspect", {}, false, make_function_typespec({"_type_"}, "_type_"),
|
||||
false);
|
||||
declare_method(obj_type, "length", {}, false, make_function_typespec({"_type_"}, "int"),
|
||||
false); // todo - this integer type?
|
||||
declare_method(obj_type, "asize-of", false, make_function_typespec({"_type_"}, "int"), false);
|
||||
declare_method(obj_type, "copy", false, make_function_typespec({"_type_", "symbol"}, "_type_"),
|
||||
false);
|
||||
declare_method(obj_type, "relocate", false, make_function_typespec({"_type_", "int"}, "_type_"),
|
||||
false);
|
||||
declare_method(obj_type, "mem-usage", false,
|
||||
declare_method(obj_type, "asize-of", {}, false, make_function_typespec({"_type_"}, "int"), false);
|
||||
declare_method(obj_type, "copy", {}, false,
|
||||
make_function_typespec({"_type_", "symbol"}, "_type_"), false);
|
||||
declare_method(obj_type, "relocate", {}, false,
|
||||
make_function_typespec({"_type_", "int"}, "_type_"), false);
|
||||
declare_method(obj_type, "mem-usage", {}, false,
|
||||
make_function_typespec({"_type_", "memory-usage-block", "int"}, "_type_"), false);
|
||||
|
||||
// STRUCTURE
|
||||
// structure new doesn't support dynamic sizing, which is kinda weird - it grabs the size from
|
||||
// the type. Dynamic structures use new-dynamic-structure, which is used exactly once ever.
|
||||
declare_method(structure_type, "new", false, make_function_typespec({"symbol", "type"}, "_type_"),
|
||||
false);
|
||||
declare_method(structure_type, "new", {}, false,
|
||||
make_function_typespec({"symbol", "type"}, "_type_"), false);
|
||||
// structure_type is a field-less StructureType, so we have to do this to match the runtime.
|
||||
// structure_type->override_size_in_memory(4);
|
||||
|
||||
@@ -1066,7 +1073,7 @@ void TypeSystem::add_builtin_types(GameVersion version) {
|
||||
add_field_to_type(basic_type, "type", make_typespec("type"));
|
||||
// the default new basic doesn't support dynamic sizing. anything dynamic will override this
|
||||
// and then call (method object new) to do the dynamically-sized allocation.
|
||||
declare_method(basic_type, "new", false, make_function_typespec({"symbol", "type"}, "_type_"),
|
||||
declare_method(basic_type, "new", {}, false, make_function_typespec({"symbol", "type"}, "_type_"),
|
||||
false);
|
||||
|
||||
// SYMBOL
|
||||
@@ -1075,11 +1082,11 @@ void TypeSystem::add_builtin_types(GameVersion version) {
|
||||
}
|
||||
add_field_to_type(symbol_type, "value", make_typespec("object"));
|
||||
// a new method which returns type none means new is illegal.
|
||||
declare_method(symbol_type, "new", false, make_function_typespec({}, "none"), false);
|
||||
declare_method(symbol_type, "new", {}, false, make_function_typespec({}, "none"), false);
|
||||
|
||||
// TYPE
|
||||
builtin_structure_inherit(type_type);
|
||||
declare_method(type_type, "new", false,
|
||||
declare_method(type_type, "new", {}, false,
|
||||
make_function_typespec({"symbol", "type", "int"}, "_type_"), false);
|
||||
add_field_to_type(type_type, "symbol", make_typespec("symbol"));
|
||||
add_field_to_type(type_type, "parent", make_typespec("type"));
|
||||
@@ -1096,7 +1103,7 @@ void TypeSystem::add_builtin_types(GameVersion version) {
|
||||
add_field_to_type(string_type, "data", make_typespec("uint8"), false, true); // todo integer type
|
||||
// string is never deftype'd for the decompiler, so we need to manually give the constructor
|
||||
// type here.
|
||||
declare_method(string_type, "new", false,
|
||||
declare_method(string_type, "new", {}, false,
|
||||
make_function_typespec({"symbol", "type", "int", "string"}, "_type_"), false);
|
||||
|
||||
// FUNCTION
|
||||
@@ -1125,7 +1132,7 @@ void TypeSystem::add_builtin_types(GameVersion version) {
|
||||
|
||||
// todo
|
||||
builtin_structure_inherit(array_type);
|
||||
declare_method(array_type, "new", false,
|
||||
declare_method(array_type, "new", {}, false,
|
||||
make_function_typespec({"symbol", "type", "type", "int"}, "_type_"), false);
|
||||
// array has: number, number, type
|
||||
add_field_to_type(array_type, "length", make_typespec("int32"));
|
||||
@@ -1135,7 +1142,7 @@ void TypeSystem::add_builtin_types(GameVersion version) {
|
||||
|
||||
// pair
|
||||
pair_type->override_offset(2);
|
||||
declare_method(pair_type, "new", false,
|
||||
declare_method(pair_type, "new", {}, false,
|
||||
make_function_typespec({"symbol", "type", "object", "object"}, "_type_"), false);
|
||||
add_field_to_type(pair_type, "car", make_typespec("object"));
|
||||
add_field_to_type(pair_type, "cdr", make_typespec("object"));
|
||||
@@ -1153,7 +1160,7 @@ void TypeSystem::add_builtin_types(GameVersion version) {
|
||||
add_field_to_type(file_stream_type, "mode", make_typespec("symbol"));
|
||||
add_field_to_type(file_stream_type, "name", make_typespec("string"));
|
||||
add_field_to_type(file_stream_type, "file", make_typespec("uint32"));
|
||||
declare_method(file_stream_type, "new", false,
|
||||
declare_method(file_stream_type, "new", {}, false,
|
||||
make_function_typespec({"symbol", "type", "string", "symbol"}, "_type_"), false);
|
||||
}
|
||||
|
||||
@@ -1788,7 +1795,11 @@ std::string TypeSystem::generate_deftype_footer(const Type* type) const {
|
||||
|
||||
std::string TypeSystem::generate_deftype_for_structure(const StructureType* st) const {
|
||||
std::string result;
|
||||
result += fmt::format("(deftype {} ({})\n (", st->get_name(), st->get_parent());
|
||||
result += fmt::format("(deftype {} ({})\n", st->get_name(), st->get_parent());
|
||||
if (st->m_metadata.docstring) {
|
||||
result += fmt::format(" \"{}\"\n", st->m_metadata.docstring.value());
|
||||
}
|
||||
result += " (";
|
||||
|
||||
int longest_field_name = 0;
|
||||
int longest_type_name = 0;
|
||||
@@ -1879,7 +1890,11 @@ std::string TypeSystem::generate_deftype_for_structure(const StructureType* st)
|
||||
|
||||
std::string TypeSystem::generate_deftype_for_bitfield(const BitFieldType* type) const {
|
||||
std::string result;
|
||||
result += fmt::format("(deftype {} ({})\n (", type->get_name(), type->get_parent());
|
||||
result += fmt::format("(deftype {} ({})\n", type->get_name(), type->get_parent());
|
||||
if (type->m_metadata.docstring) {
|
||||
result += fmt::format(" \"{}\"\n", type->m_metadata.docstring.value());
|
||||
}
|
||||
result += " (";
|
||||
|
||||
int longest_field_name = 0;
|
||||
int longest_type_name = 0;
|
||||
|
||||
@@ -162,20 +162,28 @@ class TypeSystem {
|
||||
|
||||
MethodInfo declare_method(const std::string& type_name,
|
||||
const std::string& method_name,
|
||||
const std::optional<std::string>& docstring,
|
||||
bool no_virtual,
|
||||
const TypeSpec& ts,
|
||||
bool override_type);
|
||||
MethodInfo declare_method(Type* type,
|
||||
const std::string& method_name,
|
||||
const std::optional<std::string>& docstring,
|
||||
bool no_virtual,
|
||||
const TypeSpec& ts,
|
||||
bool override_type,
|
||||
int id = -1);
|
||||
MethodInfo define_method(const std::string& type_name,
|
||||
const std::string& method_name,
|
||||
const TypeSpec& ts);
|
||||
MethodInfo define_method(Type* type, const std::string& method_name, const TypeSpec& ts);
|
||||
MethodInfo add_new_method(Type* type, const TypeSpec& ts);
|
||||
const TypeSpec& ts,
|
||||
const std::optional<std::string>& docstring);
|
||||
MethodInfo define_method(Type* type,
|
||||
const std::string& method_name,
|
||||
const TypeSpec& ts,
|
||||
const std::optional<std::string>& docstring);
|
||||
MethodInfo add_new_method(Type* type,
|
||||
const TypeSpec& ts,
|
||||
const std::optional<std::string>& docstring);
|
||||
MethodInfo lookup_method(const std::string& type_name, const std::string& method_name) const;
|
||||
MethodInfo lookup_method(const std::string& type_name, int method_id) const;
|
||||
bool try_lookup_method(const Type* type, const std::string& method_name, MethodInfo* info) const;
|
||||
|
||||
@@ -43,7 +43,9 @@ std::string symbol_string(const goos::Object& obj) {
|
||||
|
||||
} // namespace
|
||||
|
||||
EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts) {
|
||||
EnumType* parse_defenum(const goos::Object& defenum,
|
||||
TypeSystem* ts,
|
||||
DefinitionMetadata* symbol_metadata) {
|
||||
// default enum type will be int32.
|
||||
TypeSpec base_type = ts->make_typespec("int32");
|
||||
bool is_bitfield = false;
|
||||
@@ -53,6 +55,14 @@ EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts) {
|
||||
|
||||
auto& enum_name_obj = car(iter);
|
||||
iter = cdr(iter);
|
||||
// check for docstring
|
||||
if (iter->is_pair() && car(iter).is_string()) {
|
||||
// TODO - docstring - store and use docstring if coming from the compiler
|
||||
if (symbol_metadata) {
|
||||
symbol_metadata->docstring = car(iter).as_string()->data;
|
||||
}
|
||||
iter = cdr(iter);
|
||||
}
|
||||
|
||||
if (!enum_name_obj.is_symbol()) {
|
||||
throw std::runtime_error("defenum must be given a symbol as its name");
|
||||
|
||||
@@ -10,4 +10,6 @@
|
||||
|
||||
#include "common/goos/Object.h"
|
||||
|
||||
EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts);
|
||||
EnumType* parse_defenum(const goos::Object& defenum,
|
||||
TypeSystem* ts,
|
||||
DefinitionMetadata* symbol_metadata);
|
||||
|
||||
@@ -201,6 +201,12 @@ void declare_method(Type* type, TypeSystem* type_system, const goos::Object& def
|
||||
// (name args return-type [:no-virtual] [:replace] [:state] [id])
|
||||
auto method_name = symbol_string(car(obj));
|
||||
obj = cdr(obj);
|
||||
// check for docstring
|
||||
std::optional<std::string> docstring;
|
||||
if (obj->is_pair() && car(obj).is_string()) {
|
||||
docstring = car(obj).as_string()->data;
|
||||
obj = cdr(obj);
|
||||
}
|
||||
auto& args = car(obj);
|
||||
obj = cdr(obj);
|
||||
auto& return_type = car(obj);
|
||||
@@ -247,8 +253,8 @@ void declare_method(Type* type, TypeSystem* type_system, const goos::Object& def
|
||||
});
|
||||
function_typespec.add_arg(parse_typespec(type_system, return_type));
|
||||
|
||||
auto info = type_system->declare_method(type, method_name, no_virtual, function_typespec,
|
||||
replace_method, id);
|
||||
auto info = type_system->declare_method(type, method_name, docstring, no_virtual,
|
||||
function_typespec, replace_method, id);
|
||||
|
||||
// check the method assert
|
||||
if (id != -1) {
|
||||
@@ -566,6 +572,7 @@ TypeSpec parse_typespec(const TypeSystem* type_system, const goos::Object& src)
|
||||
DeftypeResult parse_deftype(const goos::Object& deftype,
|
||||
TypeSystem* ts,
|
||||
std::unordered_map<goos::HeapObject*, goos::Object>* constants) {
|
||||
DefinitionMetadata symbol_metadata;
|
||||
std::unordered_map<goos::HeapObject*, goos::Object> no_consts;
|
||||
auto& constants_to_use = no_consts;
|
||||
if (constants != nullptr) {
|
||||
@@ -578,6 +585,11 @@ DeftypeResult parse_deftype(const goos::Object& deftype,
|
||||
iter = cdr(iter);
|
||||
auto& parent_list_obj = car(iter);
|
||||
iter = cdr(iter);
|
||||
// check for docstring
|
||||
if (iter->is_pair() && car(iter).is_string()) {
|
||||
symbol_metadata.docstring = car(iter).as_string()->data;
|
||||
iter = cdr(iter);
|
||||
}
|
||||
auto& field_list_obj = car(iter);
|
||||
iter = cdr(iter);
|
||||
auto& options_obj = *iter;
|
||||
@@ -593,6 +605,7 @@ DeftypeResult parse_deftype(const goos::Object& deftype,
|
||||
|
||||
if (is_type("basic", parent_type, ts)) {
|
||||
auto new_type = std::make_unique<BasicType>(parent_type_name, name, false, 0);
|
||||
new_type->m_metadata = symbol_metadata;
|
||||
auto pto = dynamic_cast<BasicType*>(ts->lookup_type(parent_type));
|
||||
ASSERT(pto);
|
||||
if (pto->final()) {
|
||||
@@ -630,6 +643,7 @@ DeftypeResult parse_deftype(const goos::Object& deftype,
|
||||
ts->add_type(name, std::move(new_type));
|
||||
} else if (is_type("structure", parent_type, ts)) {
|
||||
auto new_type = std::make_unique<StructureType>(parent_type_name, name, false, false, false, 0);
|
||||
new_type->m_metadata = symbol_metadata;
|
||||
auto pto = dynamic_cast<StructureType*>(ts->lookup_type(parent_type));
|
||||
ASSERT(pto);
|
||||
new_type->inherit(pto);
|
||||
@@ -658,6 +672,7 @@ DeftypeResult parse_deftype(const goos::Object& deftype,
|
||||
ASSERT(pto);
|
||||
auto new_type = std::make_unique<BitFieldType>(
|
||||
parent_type_name, name, pto->get_size_in_memory(), pto->get_load_signed());
|
||||
new_type->m_metadata = symbol_metadata;
|
||||
auto parent_value = dynamic_cast<ValueType*>(pto);
|
||||
ASSERT(parent_value);
|
||||
new_type->inherit(parent_value);
|
||||
|
||||
+14
-11
@@ -464,22 +464,25 @@ goos::Object SetFormFormElement::to_form_internal(const Env& env) const {
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object SetFormFormElement::to_form_for_define(const Env& env) const {
|
||||
goos::Object SetFormFormElement::to_form_for_define(
|
||||
const Env& env,
|
||||
const std::optional<std::string>& docstring) const {
|
||||
std::vector<goos::Object> forms = {pretty_print::to_symbol("define"), m_dst->to_form(env)};
|
||||
if (docstring) {
|
||||
forms.push_back(pretty_print::to_symbol(fmt::format("\"{}\"", docstring.value())));
|
||||
}
|
||||
if (m_cast_for_define) {
|
||||
// for vu-function, we just put a 0. These aren't supported
|
||||
if (*m_cast_for_define == TypeSpec("vu-function")) {
|
||||
return pretty_print::build_list(
|
||||
fmt::format("define"), m_dst->to_form(env),
|
||||
pretty_print::build_list(fmt::format("the-as {}", m_cast_for_define->print()),
|
||||
pretty_print::to_symbol("0")));
|
||||
forms.push_back(pretty_print::build_list(fmt::format("the-as {}", m_cast_for_define->print()),
|
||||
pretty_print::to_symbol("0")));
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
return pretty_print::build_list(
|
||||
fmt::format("define"), m_dst->to_form(env),
|
||||
pretty_print::build_list(fmt::format("the-as {}", m_cast_for_define->print()),
|
||||
m_src->to_form(env)));
|
||||
forms.push_back(pretty_print::build_list(fmt::format("the-as {}", m_cast_for_define->print()),
|
||||
m_src->to_form(env)));
|
||||
return pretty_print::build_list(forms);
|
||||
} else {
|
||||
std::vector<goos::Object> forms = {pretty_print::to_symbol("define"), m_dst->to_form(env),
|
||||
m_src->to_form(env)};
|
||||
forms.push_back(m_src->to_form(env));
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,7 +410,8 @@ class SetFormFormElement : public FormElement {
|
||||
std::optional<TypeSpec> cast_for_set = {},
|
||||
std::optional<TypeSpec> cast_for_define = {});
|
||||
goos::Object to_form_internal(const Env& env) const override;
|
||||
goos::Object to_form_for_define(const Env& env) const;
|
||||
goos::Object to_form_for_define(const Env& env,
|
||||
const std::optional<std::string>& docstring) const;
|
||||
void apply(const std::function<void(FormElement*)>& f) override;
|
||||
void apply_form(const std::function<void(Form*)>& f) override;
|
||||
bool is_sequence_point() const override;
|
||||
|
||||
@@ -111,6 +111,15 @@ std::string final_defun_out(const Function& func,
|
||||
top.push_back(arguments);
|
||||
auto top_form = pretty_print::build_list(top);
|
||||
|
||||
// docstring if available
|
||||
if (dts.symbol_metadata_map.count(func.name()) != 0) {
|
||||
auto meta = dts.symbol_metadata_map.at(func.name());
|
||||
if (meta.docstring) {
|
||||
inline_body.insert(inline_body.begin(),
|
||||
pretty_print::to_symbol(fmt::format("\"{}\"", meta.docstring.value())));
|
||||
}
|
||||
}
|
||||
|
||||
append_body_to_function_definition(&top_form, inline_body, var_dec, func.type);
|
||||
return pretty_print::to_string(top_form);
|
||||
}
|
||||
@@ -126,6 +135,10 @@ std::string final_defun_out(const Function& func,
|
||||
top.push_back(arguments);
|
||||
auto top_form = pretty_print::build_list(top);
|
||||
|
||||
if (method_info.docstring) {
|
||||
inline_body.insert(inline_body.begin(), pretty_print::to_symbol(fmt::format(
|
||||
"\"{}\"", method_info.docstring.value())));
|
||||
}
|
||||
append_body_to_function_definition(&top_form, inline_body, var_dec, method_info.type);
|
||||
return pretty_print::to_string(top_form);
|
||||
}
|
||||
@@ -417,7 +430,13 @@ std::string write_from_top_level_form(Form* top_form,
|
||||
fmt::format(";; definition for symbol {}, type {}\n", sym_name, symbol_type.print());
|
||||
auto setset = dynamic_cast<SetFormFormElement*>(f.try_as_single_element());
|
||||
ASSERT(setset);
|
||||
result += pretty_print::to_string(setset->to_form_for_define(env));
|
||||
if (dts.symbol_metadata_map.count(sym_name) != 0) {
|
||||
result += pretty_print::to_string(
|
||||
setset->to_form_for_define(env, dts.symbol_metadata_map.at(sym_name).docstring));
|
||||
} else {
|
||||
result += pretty_print::to_string(setset->to_form_for_define(env, {}));
|
||||
}
|
||||
|
||||
result += "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,28 +54,32 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector<std::string>& file_
|
||||
for_each_in_list(data, [&](goos::Object& o) {
|
||||
try {
|
||||
if (car(o).as_symbol()->name == "define-extern") {
|
||||
auto symbol_metadata = DefinitionMetadata();
|
||||
auto* rest = &cdr(o);
|
||||
auto sym_name = car(*rest);
|
||||
rest = &cdr(*rest);
|
||||
// check for docstring
|
||||
if (rest->is_pair() && car(*rest).is_string()) {
|
||||
symbol_metadata.docstring = car(*rest).as_string()->data;
|
||||
rest = &cdr(*rest);
|
||||
}
|
||||
auto sym_type = car(*rest);
|
||||
if (!cdr(*rest).is_empty_list()) {
|
||||
throw std::runtime_error("malformed define-extern");
|
||||
}
|
||||
auto info = m_reader.db.get_short_info_for(o);
|
||||
add_symbol(sym_name.as_symbol()->name, parse_typespec(&ts, sym_type), info);
|
||||
|
||||
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") {
|
||||
auto dtr = parse_deftype(cdr(o), &ts);
|
||||
auto info = m_reader.db.get_short_info_for(o);
|
||||
dtr.type_info->m_metadata.definition_info = m_reader.db.get_short_info_for(o);
|
||||
if (dtr.create_runtime_type) {
|
||||
add_symbol(dtr.type.base_type(), "type", info);
|
||||
add_symbol(dtr.type.base_type(), "type", dtr.type_info->m_metadata);
|
||||
}
|
||||
// declare the type's states globally
|
||||
for (auto& state : dtr.type_info->get_states_declared_for_type()) {
|
||||
// TODO - get definition info for the state definitions specifically
|
||||
add_symbol(state.first, state.second, info);
|
||||
add_symbol(state.first, state.second, dtr.type_info->m_metadata);
|
||||
}
|
||||
|
||||
} else if (car(o).as_symbol()->name == "declare-type") {
|
||||
auto* rest = &cdr(o);
|
||||
auto type_name = car(*rest);
|
||||
@@ -86,7 +90,12 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector<std::string>& file_
|
||||
}
|
||||
ts.forward_declare_type_as(type_name.as_symbol()->name, type_kind.as_symbol()->name);
|
||||
} else if (car(o).as_symbol()->name == "defenum") {
|
||||
parse_defenum(cdr(o), &ts);
|
||||
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;
|
||||
// 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());
|
||||
@@ -161,16 +170,16 @@ bool DecompilerTypeSystem::lookup_flags(const std::string& type, u64* dest) cons
|
||||
return false;
|
||||
}
|
||||
|
||||
void DecompilerTypeSystem::add_symbol(
|
||||
const std::string& name,
|
||||
const TypeSpec& type_spec,
|
||||
const std::optional<goos::TextDb::ShortInfo>& definition_info) {
|
||||
void DecompilerTypeSystem::add_symbol(const std::string& name,
|
||||
const TypeSpec& type_spec,
|
||||
const DefinitionMetadata& symbol_metadata) {
|
||||
add_symbol(name);
|
||||
auto skv = symbol_types.find(name);
|
||||
if (skv == symbol_types.end() || skv->second == type_spec) {
|
||||
symbol_types[name] = type_spec;
|
||||
if (definition_info) {
|
||||
symbol_definition_info[name] = definition_info.value();
|
||||
// TODO - could get rid of this if there is a way to go from TypeSpec -> full Type
|
||||
if (symbol_metadata.definition_info) {
|
||||
symbol_metadata_map[name] = symbol_metadata;
|
||||
}
|
||||
} else {
|
||||
if (ts.tc(type_spec, skv->second)) {
|
||||
|
||||
@@ -14,10 +14,12 @@ class DecompilerTypeSystem {
|
||||
public:
|
||||
DecompilerTypeSystem(GameVersion version);
|
||||
TypeSystem ts;
|
||||
|
||||
std::unordered_map<std::string, TypeSpec> symbol_types;
|
||||
std::unordered_set<std::string> symbols;
|
||||
std::unordered_map<std::string, goos::TextDb::ShortInfo> symbol_definition_info;
|
||||
std::unordered_map<std::string, DefinitionMetadata> symbol_metadata_map;
|
||||
std::vector<std::string> symbol_add_order;
|
||||
|
||||
std::unordered_map<std::string, u64> type_flags;
|
||||
std::unordered_map<std::string, std::string> type_parents;
|
||||
std::unordered_map<std::string, int> bad_format_strings;
|
||||
@@ -34,13 +36,13 @@ class DecompilerTypeSystem {
|
||||
|
||||
void add_symbol(const std::string& name,
|
||||
const std::string& base_type,
|
||||
const std::optional<goos::TextDb::ShortInfo>& definition_info) {
|
||||
add_symbol(name, TypeSpec(base_type), definition_info);
|
||||
const DefinitionMetadata& symbol_metadata) {
|
||||
add_symbol(name, TypeSpec(base_type), symbol_metadata);
|
||||
}
|
||||
|
||||
void add_symbol(const std::string& name,
|
||||
const TypeSpec& type_spec,
|
||||
const std::optional<goos::TextDb::ShortInfo>& definition_info);
|
||||
const DefinitionMetadata& symbol_metadata);
|
||||
void parse_type_defs(const std::vector<std::string>& file_path);
|
||||
TypeSpec parse_type_spec(const std::string& str) const;
|
||||
void add_type_flags(const std::string& name, u64 flags);
|
||||
|
||||
@@ -13,6 +13,13 @@
|
||||
*/
|
||||
Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
// Grab the docstring (if it's there) and then rip it out so we can do the normal validation
|
||||
if (args.unnamed.size() == 3 && args.unnamed.at(1).is_string()) {
|
||||
// TODO - docstring - actually use it!
|
||||
// std::string docstring = args.unnamed.at(1).as_string()->data;
|
||||
args.unnamed.erase(args.unnamed.begin() + 1);
|
||||
}
|
||||
|
||||
va_check(form, args, {goos::ObjectType::SYMBOL, {}},
|
||||
{{"no-typecheck", {false, goos::ObjectType::SYMBOL}}});
|
||||
auto& sym = args.unnamed.at(0);
|
||||
@@ -73,6 +80,12 @@ Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest
|
||||
Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
(void)env;
|
||||
auto args = get_va(form, rest);
|
||||
// Grab the docstring (if it's there) and then rip it out so we can do the normal validation
|
||||
if (args.unnamed.size() == 3 && args.unnamed.at(1).is_string()) {
|
||||
// TODO - docstring - actually use it!
|
||||
// std::string docstring = args.unnamed.at(1).as_string()->data;
|
||||
args.unnamed.erase(args.unnamed.begin() + 1);
|
||||
}
|
||||
va_check(form, args, {goos::ObjectType::SYMBOL, {}}, {});
|
||||
auto& sym = args.unnamed.at(0);
|
||||
auto& typespec = args.unnamed.at(1);
|
||||
|
||||
@@ -166,6 +166,14 @@ Val* Compiler::compile_define_constant(const goos::Object& form,
|
||||
|
||||
auto sym = pair_car(*rest).as_symbol();
|
||||
rest = &pair_cdr(*rest);
|
||||
|
||||
// check for potential docstring
|
||||
// TODO - docstring - actually do something with this!
|
||||
if (rest->is_pair() && pair_car(*rest).is_string() && !pair_cdr(*rest).is_empty_list()) {
|
||||
// std::string docstring = pair_car(*rest).as_string()->data;
|
||||
rest = &pair_cdr(*rest);
|
||||
}
|
||||
|
||||
auto value = pair_car(*rest);
|
||||
|
||||
rest = &rest->as_pair()->cdr;
|
||||
|
||||
@@ -479,7 +479,7 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _
|
||||
// todo, verify argument list types (check that first arg is _type_ for methods that aren't "new")
|
||||
lambda.debug_name = fmt::format("(method {} {})", method_name.print(), type_name.print());
|
||||
|
||||
// skip docstring
|
||||
// TODO - docstring - do something with the docstring!
|
||||
if (body->as_pair()->car.is_string() && !body->as_pair()->cdr.is_empty_list()) {
|
||||
body = &pair_cdr(*body);
|
||||
}
|
||||
@@ -613,7 +613,9 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _
|
||||
|
||||
m_symbol_info.add_method(symbol_string(method_name), symbol_string(type_name), form);
|
||||
|
||||
auto info = m_ts.define_method(symbol_string(type_name), symbol_string(method_name), lambda_ts);
|
||||
// TODO!
|
||||
auto info =
|
||||
m_ts.define_method(symbol_string(type_name), symbol_string(method_name), lambda_ts, {});
|
||||
auto type_obj = compile_get_symbol_value(form, symbol_string(type_name), env)->to_gpr(form, env);
|
||||
auto id_val = compile_integer(info.id, env)->to_gpr(form, env);
|
||||
auto method_val = place->to_gpr(form, env);
|
||||
@@ -1327,7 +1329,7 @@ Val* Compiler::compile_defenum(const goos::Object& form, const goos::Object& res
|
||||
(void)form;
|
||||
(void)env;
|
||||
|
||||
parse_defenum(rest, &m_ts);
|
||||
parse_defenum(rest, &m_ts, {});
|
||||
return get_none();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,10 +40,10 @@ std::optional<goos::TextDb::ShortInfo> Workspace::get_symbol_info_from_all_types
|
||||
return {};
|
||||
}
|
||||
const auto& dts = m_tracked_all_types_files[all_types_uri].m_dts;
|
||||
if (dts.symbol_definition_info.count(symbol_name) == 0) {
|
||||
if (dts.symbol_metadata_map.count(symbol_name) == 0) {
|
||||
return {};
|
||||
}
|
||||
return dts.symbol_definition_info.at(symbol_name);
|
||||
return dts.symbol_metadata_map.at(symbol_name).definition_info;
|
||||
}
|
||||
|
||||
void Workspace::start_tracking_file(const LSPSpec::DocumentUri& file_uri,
|
||||
|
||||
+20
-16
@@ -231,28 +231,32 @@ TEST(TypeSystem, AddMethodAndLookupMethod) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types(GameVersion::Jak1);
|
||||
|
||||
auto parent_info = ts.declare_method(ts.lookup_type("structure"), "test-method-1", false,
|
||||
ts.make_function_typespec({"integer"}, "string"), false);
|
||||
auto parent_info =
|
||||
ts.declare_method(ts.lookup_type("structure"), "test-method-1", "test docstring", false,
|
||||
ts.make_function_typespec({"integer"}, "string"), false);
|
||||
|
||||
// when trying to add the same method to a child, should return the parent's method
|
||||
auto child_info_same = ts.declare_method(ts.lookup_type("basic"), "test-method-1", false,
|
||||
ts.make_function_typespec({"integer"}, "string"), false);
|
||||
auto child_info_same =
|
||||
ts.declare_method(ts.lookup_type("basic"), "test-method-1", "test docstring", false,
|
||||
ts.make_function_typespec({"integer"}, "string"), false);
|
||||
|
||||
EXPECT_EQ(parent_info.id, child_info_same.id);
|
||||
EXPECT_EQ(parent_info.id, GOAL_MEMUSAGE_METHOD + 1);
|
||||
|
||||
// any amount of fiddling with method types should cause an error
|
||||
EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", false,
|
||||
ts.make_function_typespec({"integer"}, "integer"), false));
|
||||
EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", false,
|
||||
ts.make_function_typespec({}, "string"), false));
|
||||
EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", false,
|
||||
ts.make_function_typespec({"integer", "string"}, "string"),
|
||||
EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", "test docstring",
|
||||
false, ts.make_function_typespec({"integer"}, "integer"),
|
||||
false));
|
||||
EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", "test docstring",
|
||||
false, ts.make_function_typespec({}, "string"), false));
|
||||
EXPECT_ANY_THROW(
|
||||
ts.declare_method(ts.lookup_type("basic"), "test-method-1", "test docstring", false,
|
||||
ts.make_function_typespec({"integer", "string"}, "string"), false));
|
||||
EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", "test docstring",
|
||||
false, ts.make_function_typespec({"string"}, "string"),
|
||||
false));
|
||||
EXPECT_ANY_THROW(ts.declare_method(ts.lookup_type("basic"), "test-method-1", false,
|
||||
ts.make_function_typespec({"string"}, "string"), false));
|
||||
|
||||
ts.declare_method(ts.lookup_type("basic"), "test-method-2", false,
|
||||
ts.declare_method(ts.lookup_type("basic"), "test-method-2", "test docstring", false,
|
||||
ts.make_function_typespec({"integer"}, "string"), false);
|
||||
|
||||
EXPECT_EQ(parent_info.id, ts.lookup_method("basic", "test-method-1").id);
|
||||
@@ -275,10 +279,10 @@ TEST(TypeSystem, NewMethod) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types(GameVersion::Jak1);
|
||||
ts.add_type("test-1", std::make_unique<BasicType>("basic", "test-1", false, 0));
|
||||
ts.declare_method(ts.lookup_type("test-1"), "new", false,
|
||||
ts.declare_method(ts.lookup_type("test-1"), "new", "test docstring", false,
|
||||
ts.make_function_typespec({"symbol", "string"}, "test-1"), false);
|
||||
ts.add_type("test-2", std::make_unique<BasicType>("test-1", "test-2", false, 0));
|
||||
ts.declare_method(ts.lookup_type("test-2"), "new", false,
|
||||
ts.declare_method(ts.lookup_type("test-2"), "new", "test docstring", false,
|
||||
ts.make_function_typespec({"symbol", "string", "symbol"}, "test-2"), false);
|
||||
|
||||
EXPECT_EQ(ts.lookup_method("test-1", "new").type.print(), "(function symbol string test-1)");
|
||||
@@ -297,7 +301,7 @@ TEST(TypeSystem, MethodSubstitute) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types(GameVersion::Jak1);
|
||||
ts.add_type("test-1", std::make_unique<BasicType>("basic", "test-1", false, 0));
|
||||
ts.declare_method(ts.lookup_type("test-1"), "new", false,
|
||||
ts.declare_method(ts.lookup_type("test-1"), "new", "test docstring", false,
|
||||
ts.make_function_typespec({"symbol", "string", "_type_"}, "_type_"), false);
|
||||
|
||||
auto final_type = ts.lookup_method("test-1", "new").type.substitute_for_method_call("test-1");
|
||||
|
||||
Reference in New Issue
Block a user