Skip to content

Commit

Permalink
Support Behaviors (#678)
Browse files Browse the repository at this point in the history
* temp

* working, but type pass got really slow

* clean up

* changelog and flip order

* clean up and add tests

* fix zero size array

* handle lambdas correctly

* another windows fix
  • Loading branch information
water111 authored Jul 4, 2021
1 parent 285c10f commit e251f8b
Show file tree
Hide file tree
Showing 28 changed files with 1,514 additions and 325 deletions.
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

0 comments on commit e251f8b

Please sign in to comment.