Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Behaviors #678

Merged
merged 8 commits into from
Jul 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/goos/PrettyPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ void insertSpecialBreaks(NodePool& pool, PrettyPrinterNode* node) {
}

if (name == "defun" || name == "defmethod" || name == "defun-debug" || name == "let" ||
name == "let*" || name == "rlet") {
name == "let*" || name == "rlet" || name == "defbehavior") {
auto* first_list = getNextListOrEmptyListOnLine(node);
if (first_list) {
if (first_list->tok->kind == FormToken::TokenKind::EMPTY_PAIR) {
Expand Down
101 changes: 82 additions & 19 deletions common/type_system/TypeSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,30 @@
* A GOAL TypeSpec is a reference to a type or compound type.
*/

#include <stdexcept>
#include "TypeSpec.h"
#include "Type.h"
#include "third-party/fmt/core.h"

TypeSpec::TypeSpec(std::string type) : m_type(std::move(type)) {}

TypeSpec::TypeSpec(std::string type, std::vector<TypeSpec> arguments)
: m_type(std::move(type)), m_arguments(std::move(arguments)) {}
bool TypeTag::operator==(const TypeTag& other) const {
return name == other.name && value == other.value;
}

std::string TypeSpec::print() const {
if (m_arguments.empty()) {
if ((!m_arguments || m_arguments->empty()) && m_tags.empty()) {
return m_type;
} else {
std::string result = "(" + m_type;
for (auto& x : m_arguments) {
result += " " + x.print();

if (m_arguments) {
for (auto& x : *m_arguments) {
result += " " + x.print();
}
}

for (const auto& tag : m_tags) {
result += fmt::format(" :{} {}", tag.name, tag.value);
}

return result + ")";
}
}
Expand All @@ -28,41 +36,96 @@ bool TypeSpec::operator!=(const TypeSpec& other) const {
}

bool TypeSpec::operator==(const TypeSpec& other) const {
if (other.m_type != m_type || other.m_arguments.size() != m_arguments.size()) {
if (m_type != other.m_type) {
return false;
}

for (size_t i = 0; i < m_arguments.size(); i++) {
if (other.m_arguments[i] != m_arguments[i]) {
return false;
}
if (m_tags != other.m_tags) {
return false;
}

return true;
if (m_arguments && other.m_arguments) {
return *m_arguments == *other.m_arguments;
}

return empty() && other.empty();
}

TypeSpec TypeSpec::substitute_for_method_call(const std::string& method_type) const {
TypeSpec result;
result.m_type = (m_type == "_type_") ? method_type : m_type;
for (const auto& x : m_arguments) {
result.m_arguments.push_back(x.substitute_for_method_call(method_type));
if (m_arguments) {
result.m_arguments = new std::vector<TypeSpec>();
for (const auto& x : *m_arguments) {
result.m_arguments->push_back(x.substitute_for_method_call(method_type));
}
}

return result;
}

bool TypeSpec::is_compatible_child_method(const TypeSpec& implementation,
const std::string& child_type) const {
bool ok = implementation.m_type == m_type ||
(m_type == "_type_" && implementation.m_type == child_type);
if (!ok || implementation.m_arguments.size() != m_arguments.size()) {
if (!ok || implementation.arg_count() != arg_count()) {
return false;
}

for (size_t i = 0; i < m_arguments.size(); i++) {
if (!m_arguments[i].is_compatible_child_method(implementation.m_arguments[i], child_type)) {
for (size_t i = 0; i < arg_count(); i++) {
if (!get_arg(i).is_compatible_child_method(implementation.get_arg(i), child_type)) {
return false;
}
}

return true;
}

void TypeSpec::add_new_tag(const std::string& tag_name, const std::string& tag_value) {
for (auto& v : m_tags) {
if (v.name == tag_name) {
throw std::runtime_error(
fmt::format("Attempted to add a duplicate tag {} to typespec.", tag_name));
}
}

m_tags.push_back({tag_name, tag_value});
}

std::optional<std::string> TypeSpec::try_get_tag(const std::string& tag_name) const {
for (auto& tag : m_tags) {
if (tag.name == tag_name) {
return tag.value;
}
}
return {};
}

const std::string& TypeSpec::get_tag(const std::string& tag_name) const {
for (auto& tag : m_tags) {
if (tag.name == tag_name) {
return tag.value;
}
}
throw std::runtime_error(fmt::format("TypeSpec didn't have tag {}", tag_name));
}

void TypeSpec::modify_tag(const std::string& tag_name, const std::string& tag_value) {
for (auto& tag : m_tags) {
if (tag.name == tag_name) {
tag.value = tag_value;
return;
}
}
throw std::runtime_error(fmt::format("TypeSpec didn't have tag {}", tag_name));
}

void TypeSpec::add_or_modify_tag(const std::string& tag_name, const std::string& tag_value) {
for (auto& tag : m_tags) {
if (tag.name == tag_name) {
tag.value = tag_value;
return;
}
}
m_tags.push_back({tag_name, tag_value});
}
123 changes: 104 additions & 19 deletions common/type_system/TypeSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,22 @@
* A GOAL TypeSpec is a reference to a type or compound type.
*/

#ifndef JAK_TYPESPEC_H
#define JAK_TYPESPEC_H

#include <vector>
#include <string>
#include <optional>
#include "common/util/assert.h"
#include "common/util/SmallVector.h"

/*!
* A :name value modifier to apply to a type.
*/
struct TypeTag {
std::string name;
std::string value;

class Type;
bool operator==(const TypeTag& other) const;
bool operator!=(const TypeTag& other) const { return !((*this) == other); }
};

/*!
* A TypeSpec is a reference to a Type, or possible a compound type. This is the best way to
Expand All @@ -24,43 +32,120 @@ class Type;
*/
class TypeSpec {
public:
// create a typespec for a single type
TypeSpec() = default;
TypeSpec(std::string type);
TypeSpec(std::string type, std::vector<TypeSpec> arguments);
TypeSpec(const std::string& type) : m_type(type) {}

TypeSpec(const std::string& type, const std::vector<TypeSpec>& arguments)
: m_type(type), m_arguments(new std::vector<TypeSpec>(arguments)) {}

TypeSpec(const TypeSpec& other) {
m_type = other.m_type;
m_tags = other.m_tags;
if (other.m_arguments) {
m_arguments = new std::vector<TypeSpec>(*other.m_arguments);
}
}

TypeSpec& operator=(const TypeSpec& other) {
if (this == &other) {
return *this;
}

if (m_arguments) {
delete m_arguments;
m_arguments = nullptr;
}

m_type = other.m_type;
m_tags = other.m_tags;
if (other.m_arguments) {
m_arguments = new std::vector<TypeSpec>(*other.m_arguments);
}

return *this;
}

~TypeSpec() { delete m_arguments; }

// TypeSpec(const std::string& type, const std::vector<TypeTag>& tags)
// : m_type(type), m_tags(tags) {}
//
// TypeSpec(const std::string type,
// const std::vector<TypeSpec>& arguments,
// const std::vector<TypeTag>& tags)
// : m_type(type), m_arguments(arguments), m_tags(tags) {}

bool operator!=(const TypeSpec& other) const;
bool operator==(const TypeSpec& other) const;
bool is_compatible_child_method(const TypeSpec& implementation,
const std::string& child_type) const;
std::string print() const;

void add_arg(const TypeSpec& ts) { m_arguments.push_back(ts); }
void add_arg(const TypeSpec& ts) {
if (!m_arguments) {
m_arguments = new std::vector<TypeSpec>();
}
m_arguments->push_back(ts);
}
void add_new_tag(const std::string& tag_name, const std::string& tag_value);
std::optional<std::string> try_get_tag(const std::string& tag_name) const;
const std::string& get_tag(const std::string& tag_name) const;
void modify_tag(const std::string& tag_name, const std::string& tag_value);
void add_or_modify_tag(const std::string& tag_name, const std::string& tag_value);

const std::string base_type() const { return m_type; }

bool has_single_arg() const { return m_arguments.size() == 1; }
bool has_single_arg() const {
if (m_arguments) {
return m_arguments->size() == 1;
}
return 0;
}

const TypeSpec& get_single_arg() const {
assert(m_arguments.size() == 1);
return m_arguments.front();
assert(m_arguments);
assert(m_arguments->size() == 1);
return m_arguments->front();
}

TypeSpec substitute_for_method_call(const std::string& method_type) const;

size_t arg_count() const { return m_arguments.size(); }
size_t arg_count() const {
if (!m_arguments) {
return 0;
}
return m_arguments->size();
}

const TypeSpec& get_arg(int idx) const { return m_arguments.at(idx); }
TypeSpec& get_arg(int idx) { return m_arguments.at(idx); }
const TypeSpec& get_arg(int idx) const {
assert(m_arguments);
return m_arguments->at(idx);
}
TypeSpec& get_arg(int idx) {
assert(m_arguments);
return m_arguments->at(idx);
}
const TypeSpec& last_arg() const {
assert(!m_arguments.empty());
return m_arguments.back();
assert(m_arguments);
assert(!m_arguments->empty());
return m_arguments->back();
}

bool empty() const {
if (!m_arguments) {
return true;
} else {
return m_arguments->empty();
}
}

const cu::SmallVector<TypeTag, 1>& tags() const { return m_tags; }

private:
friend class TypeSystem;
std::string m_type;
std::vector<TypeSpec> m_arguments;
// hiding this behind a pointer makes things faster in the case where we have no
// arguments (most of the time) and makes the type analysis pass in the decompiler 2x faster.
std::vector<TypeSpec>* m_arguments = nullptr;
cu::SmallVector<TypeTag, 1> m_tags;
};

#endif // JAK_TYPESPEC_H
Loading