mirror of
https://github.com/open-goal/jak-project
synced 2026-09-02 02:02:51 -04:00
Support Behaviors (#678)
* 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
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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 + ")";
|
||||
}
|
||||
}
|
||||
@@ -28,25 +36,31 @@ 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;
|
||||
}
|
||||
|
||||
@@ -54,15 +68,64 @@ 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});
|
||||
}
|
||||
+106
-21
@@ -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"
|
||||
|
||||
class Type;
|
||||
/*!
|
||||
* A :name value modifier to apply to a type.
|
||||
*/
|
||||
struct TypeTag {
|
||||
std::string name;
|
||||
std::string value;
|
||||
|
||||
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
|
||||
@@ -24,10 +32,48 @@ 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;
|
||||
@@ -35,32 +81,71 @@ class TypeSpec {
|
||||
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(); }
|
||||
|
||||
const TypeSpec& get_arg(int idx) const { return m_arguments.at(idx); }
|
||||
TypeSpec& get_arg(int idx) { return m_arguments.at(idx); }
|
||||
const TypeSpec& last_arg() const {
|
||||
assert(!m_arguments.empty());
|
||||
return m_arguments.back();
|
||||
size_t arg_count() const {
|
||||
if (!m_arguments) {
|
||||
return 0;
|
||||
}
|
||||
return m_arguments->size();
|
||||
}
|
||||
|
||||
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);
|
||||
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
|
||||
|
||||
@@ -1295,18 +1295,18 @@ bool TypeSystem::typecheck_and_throw(const TypeSpec& expected,
|
||||
}
|
||||
|
||||
// next argument checks:
|
||||
if (expected.m_arguments.size() == actual.m_arguments.size()) {
|
||||
for (size_t i = 0; i < expected.m_arguments.size(); i++) {
|
||||
if (expected.arg_count() == actual.arg_count()) {
|
||||
for (size_t i = 0; i < expected.arg_count(); i++) {
|
||||
// don't print/throw because the error would be confusing. Better to fail only the
|
||||
// outer most check and print a single error message.
|
||||
if (!tc(expected.m_arguments[i], actual.m_arguments[i])) {
|
||||
if (!tc(expected.get_arg(i), actual.get_arg(i))) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// different sizes of arguments.
|
||||
if (expected.m_arguments.empty()) {
|
||||
if (expected.arg_count() == 0) {
|
||||
// we expect zero arguments, but got some. The actual type is more specific, so this is fine.
|
||||
} else {
|
||||
// different sizes, and we expected arguments. No good!
|
||||
@@ -1314,6 +1314,22 @@ bool TypeSystem::typecheck_and_throw(const TypeSpec& expected,
|
||||
}
|
||||
}
|
||||
|
||||
// next, tag checks. It's fine to throw away tags, but the child must match all parent tags
|
||||
for (auto& tag : expected.tags()) {
|
||||
if (tag.name == "behavior") {
|
||||
auto got = actual.try_get_tag(tag.name);
|
||||
if (!got) {
|
||||
success = false;
|
||||
} else {
|
||||
if (*got != tag.value) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw_typesystem_error("Unknown tag {}", tag.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
if (print_on_error) {
|
||||
if (error_source_name.empty()) {
|
||||
@@ -1431,16 +1447,14 @@ std::string TypeSystem::lca_base(const std::string& a, const std::string& b) con
|
||||
*/
|
||||
TypeSpec TypeSystem::lowest_common_ancestor(const TypeSpec& a, const TypeSpec& b) const {
|
||||
auto result = make_typespec(lca_base(a.base_type(), b.base_type()));
|
||||
if (result == TypeSpec("function") && a.m_arguments.size() == 2 && b.m_arguments.size() == 2 &&
|
||||
(a.m_arguments.at(0) == TypeSpec("_varargs_") ||
|
||||
b.m_arguments.at(0) == TypeSpec("_varargs_"))) {
|
||||
if (result == TypeSpec("function") && a.arg_count() == 2 && b.arg_count() == 2 &&
|
||||
(a.get_arg(0) == TypeSpec("_varargs_") || b.get_arg(0) == TypeSpec("_varargs_"))) {
|
||||
return TypeSpec("function");
|
||||
}
|
||||
if (!a.m_arguments.empty() && !b.m_arguments.empty() &&
|
||||
a.m_arguments.size() == b.m_arguments.size()) {
|
||||
if (!a.empty() && !b.empty() && a.arg_count() == b.arg_count()) {
|
||||
// recursively add arguments
|
||||
for (size_t i = 0; i < a.m_arguments.size(); i++) {
|
||||
result.add_arg(lowest_common_ancestor(a.m_arguments.at(i), b.m_arguments.at(i)));
|
||||
for (size_t i = 0; i < a.arg_count(); i++) {
|
||||
result.add_arg(lowest_common_ancestor(a.get_arg(i), b.get_arg(i)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -1594,8 +1608,15 @@ std::string TypeSystem::generate_deftype_footer(const Type* type) const {
|
||||
methods_string.push_back(' ');
|
||||
}
|
||||
}
|
||||
methods_string.append(fmt::format(
|
||||
") {} {})\n ", new_info->type.get_arg(new_info->type.arg_count() - 1).print(), 0));
|
||||
methods_string.append(
|
||||
fmt::format(") {} ", new_info->type.get_arg(new_info->type.arg_count() - 1).print(), 0));
|
||||
|
||||
auto behavior = new_info->type.try_get_tag("behavior");
|
||||
if (behavior) {
|
||||
methods_string.append(fmt::format(":behavior {} ", *behavior));
|
||||
}
|
||||
|
||||
methods_string.append("0)\n ");
|
||||
}
|
||||
|
||||
for (auto& info : type->get_methods_defined_for_type()) {
|
||||
@@ -1617,6 +1638,11 @@ std::string TypeSystem::generate_deftype_footer(const Type* type) const {
|
||||
methods_string.append(":replace ");
|
||||
}
|
||||
|
||||
auto behavior = info.type.try_get_tag("behavior");
|
||||
if (behavior) {
|
||||
methods_string.append(fmt::format(":behavior {} ", *behavior));
|
||||
}
|
||||
|
||||
methods_string.append(fmt::format("{})\n ", info.id));
|
||||
}
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ void declare_method(Type* type, TypeSystem* type_system, const goos::Object& def
|
||||
|
||||
bool no_virtual = false;
|
||||
bool replace_method = false;
|
||||
TypeSpec function_typespec("function");
|
||||
|
||||
if (!obj->is_empty_list() && car(obj).is_symbol(":no-virtual")) {
|
||||
obj = cdr(obj);
|
||||
@@ -200,6 +201,12 @@ void declare_method(Type* type, TypeSystem* type_system, const goos::Object& def
|
||||
replace_method = true;
|
||||
}
|
||||
|
||||
if (!obj->is_empty_list() && car(obj).is_symbol(":behavior")) {
|
||||
obj = cdr(obj);
|
||||
function_typespec.add_new_tag("behavior", symbol_string(obj->as_pair()->car));
|
||||
obj = cdr(obj);
|
||||
}
|
||||
|
||||
int id = -1;
|
||||
if (!obj->is_empty_list() && car(obj).is_int()) {
|
||||
auto& id_obj = car(obj);
|
||||
@@ -211,8 +218,6 @@ void declare_method(Type* type, TypeSystem* type_system, const goos::Object& def
|
||||
throw std::runtime_error("too many things in method def: " + def.print());
|
||||
}
|
||||
|
||||
TypeSpec function_typespec("function");
|
||||
|
||||
for_each_in_list(args, [&](const goos::Object& o) {
|
||||
function_typespec.add_arg(parse_typespec(type_system, o));
|
||||
});
|
||||
@@ -441,10 +446,39 @@ TypeSpec parse_typespec(const TypeSystem* type_system, const goos::Object& src)
|
||||
return type_system->make_typespec(symbol_string(src));
|
||||
} else if (src.is_pair()) {
|
||||
TypeSpec ts = type_system->make_typespec(symbol_string(car(&src)));
|
||||
const auto& rest = *cdr(&src);
|
||||
const auto* rest = cdr(&src);
|
||||
|
||||
for_each_in_list(rest,
|
||||
[&](const goos::Object& o) { ts.add_arg(parse_typespec(type_system, o)); });
|
||||
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);
|
||||
rest = &rest->as_pair()->cdr;
|
||||
|
||||
if (!rest->is_pair()) {
|
||||
throw std::runtime_error("TypeSpec missing tag value");
|
||||
}
|
||||
|
||||
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)) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Behavior tag uses an unknown type {}", tag_val.as_symbol()->name));
|
||||
}
|
||||
ts.add_new_tag(tag_name, tag_val.as_symbol()->name);
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format("Type tag {} is unknown", tag_name));
|
||||
}
|
||||
|
||||
} else {
|
||||
// normal argument.
|
||||
ts.add_arg(parse_typespec(type_system, it));
|
||||
}
|
||||
|
||||
rest = &rest->as_pair()->cdr;
|
||||
}
|
||||
|
||||
return ts;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,637 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace cu {
|
||||
// This might seem stupid, but compiling an empty file with #include <algorithm> takes 0.5 seconds.
|
||||
// Avoiding the include for <algorithm> is a huge win for compile times.
|
||||
template <typename T>
|
||||
constexpr const T& max(const T& a, const T& b) {
|
||||
return (a < b) ? b : a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr const T& min(const T& a, const T& b) {
|
||||
return (b < a) ? b : a;
|
||||
}
|
||||
/*!
|
||||
* A SmallVector is a replacement for std::vector. It is optimized for the case where there
|
||||
* are only a few elements by having a small inline array of objects. This uses more memory, but
|
||||
* handles the very common case of having only a few elements much more efficiently.
|
||||
* It will work correctly even with many elements by falling back to allocating on the heap.
|
||||
* Even when it falls back to being a normal heap allocation, it is the same speed as a
|
||||
* std::vector for most operations. Resizing operations may be a tiny bit slower, but this
|
||||
* doesn't seem to cause issues in practice.
|
||||
*
|
||||
* You can specify the number of inline elements. If you don't exceed this number, it won't
|
||||
* allocate. By default, it picks the largest nonzero number of elements that keeps the size
|
||||
* under 128-bytes, which is somewhat arbitrary.
|
||||
*
|
||||
* It does not implement all features of a std::vector.
|
||||
* Missing:
|
||||
* - the "at" operator asserts instead of throwing an exception if the index is invalid.
|
||||
* - no custom Allocators
|
||||
* - no reverse iterators
|
||||
* - max_size is not implemented
|
||||
* - Can't have a SmallVector<X> as a member of X.
|
||||
*
|
||||
*/
|
||||
template <typename T, std::size_t inline_elt_count = max(std::size_t(1), 128 / sizeof(T))>
|
||||
class SmallVector {
|
||||
private:
|
||||
// how much to increase the storage amount when we run out.
|
||||
static constexpr double GROW_AMOUNT = 1.5;
|
||||
|
||||
// by default, store stuff in here. C++ always uses sizeof(T) as the stride of an array, so this
|
||||
// is just like an uninitialized array of T's, which starts at the proper alignment.
|
||||
// We can't use an array of T's because that would default construct them, which we don't want.
|
||||
typename std::aligned_storage<sizeof(T), alignof(T)>::type m_inline[inline_elt_count];
|
||||
|
||||
// get a T* at the beginning of our inline storage.
|
||||
constexpr const T* inline_begin() const {
|
||||
return std::launder(reinterpret_cast<const T*>(m_inline));
|
||||
}
|
||||
constexpr T* inline_begin() { return std::launder(reinterpret_cast<T*>(m_inline)); }
|
||||
|
||||
// regardless of our storage mode, these hold the beginning and end of the storage.
|
||||
// by default, they are initialized to the inline storage.
|
||||
T* m_storage_begin;
|
||||
T* m_storage_end;
|
||||
|
||||
// the number of in-use elements
|
||||
std::size_t m_size;
|
||||
|
||||
//// STORAGE HELPERS
|
||||
|
||||
/*!
|
||||
* Allocate new heap storage and set it as the current.
|
||||
* The objects in storage are uninitialized.
|
||||
*/
|
||||
void allocate_and_set_heap_storage(std::size_t elt_count) {
|
||||
m_storage_begin = std::launder(reinterpret_cast<T*>(new uint8_t[elt_count * sizeof(T)]));
|
||||
m_storage_end = m_storage_begin + elt_count;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Free heap storage, without calling destructors of objects.
|
||||
*/
|
||||
void free_heap_storage(T* ptr) { delete[] std::launder(reinterpret_cast<uint8_t*>(ptr)); }
|
||||
|
||||
/*!
|
||||
* Set the current storage to the inline memory.
|
||||
* We rely on m_storage_begin == inline_begin() to determine if we are using the inline memory.
|
||||
* This ends up being faster than storing a bool for the mode.
|
||||
*/
|
||||
void set_inline_storage() {
|
||||
m_storage_begin = inline_begin();
|
||||
m_storage_end = inline_begin() + inline_elt_count;
|
||||
}
|
||||
|
||||
bool using_heap_storage() const { return m_storage_begin != inline_begin(); }
|
||||
|
||||
/*!
|
||||
* Allocate (if needed) storage to hold size objects.
|
||||
* Sets the container size.
|
||||
* Assumes the current storage is stack (the default)
|
||||
*/
|
||||
void initialize_storage_and_size(std::size_t size) {
|
||||
if (size > inline_elt_count) {
|
||||
allocate_and_set_heap_storage(size);
|
||||
} else {
|
||||
set_inline_storage();
|
||||
}
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
void free_current_storage() {
|
||||
if (using_heap_storage()) {
|
||||
free_heap_storage(m_storage_begin);
|
||||
}
|
||||
}
|
||||
|
||||
//// CONSTRUCTION HELPERS
|
||||
|
||||
/*!
|
||||
* Construct objects using the copy constructor, copying val for each.
|
||||
*/
|
||||
void construct_objects_by_filling(T* ptr, std::size_t count, const T& val) {
|
||||
for (std::size_t i = 0; i < count; i++) {
|
||||
new (ptr + i) T(val);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Construct objects using the default constructor.
|
||||
*/
|
||||
void construct_objects_by_default_ctor(T* ptr, std::size_t count) {
|
||||
for (std::size_t i = 0; i < count; i++) {
|
||||
new (ptr + i) T();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Call destructor on an array of objects.
|
||||
*/
|
||||
void destroy_objects(T* ptr, std::size_t count) {
|
||||
for (std::size_t i = 0; i < count; i++) {
|
||||
ptr[i].~T();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move objects from src to dst, deleting them in src.
|
||||
* Doing this in a single pass is more cache friendly for huge vectors.
|
||||
* Compilers seem to be smart enough to turn this into a memcpy for trivial types still.
|
||||
*/
|
||||
void move_and_destroy(T* dst, T* src, std::size_t count) {
|
||||
for (std::size_t i = 0; i < count; i++) {
|
||||
new (dst + i) T(std::move(src[i]));
|
||||
src[i].~T();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move objects from src to dst, starting at the highest memory address. This is useful
|
||||
* if src and dst overlap.
|
||||
*/
|
||||
void move_and_destroy_reverse(T* dst, T* src, std::size_t count) {
|
||||
for (std::size_t i = count; i-- > 0;) {
|
||||
new (dst + i) T(std::move(src[i]));
|
||||
src[i].~T();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Copy objects from src to dst, using the copy constructor.
|
||||
*/
|
||||
void copy_construct_objects(T* dst, const T* src, std::size_t count) {
|
||||
for (std::size_t i = 0; i < count; i++) {
|
||||
new (dst + i) T(src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputIt>
|
||||
void copy_objects_from_range(T* dst, InputIt first, InputIt last) {
|
||||
auto iter = first;
|
||||
while (iter != last) {
|
||||
new (dst) T(*iter);
|
||||
iter++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
using iterator = T*;
|
||||
using const_iterator = const T*;
|
||||
using reverse_iterator = std::reverse_iterator<T*>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const T*>;
|
||||
//// CONSTRUCTORS
|
||||
/*!
|
||||
* Constructor a SmallVector. By default the size is 0 and no elements are constructed.
|
||||
*/
|
||||
SmallVector() { initialize_storage_and_size(0); }
|
||||
|
||||
/*!
|
||||
* Initialize vector with count elements that are default initialized
|
||||
*/
|
||||
explicit SmallVector(std::size_t count) {
|
||||
initialize_storage_and_size(count);
|
||||
construct_objects_by_default_ctor(m_storage_begin, count);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Initialize vector with count values that are copied from the given value.
|
||||
*/
|
||||
SmallVector(std::size_t count, const T& value) {
|
||||
// set up storage:
|
||||
initialize_storage_and_size(count);
|
||||
construct_objects_by_filling(m_storage_begin, count, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Initialize vector from iterators.
|
||||
* In the case where your input iterator doesn't support random access, this ends up
|
||||
* making two passes through your data. The first to check the size, then the second to copy.
|
||||
*/
|
||||
template <typename InputIt>
|
||||
SmallVector(InputIt first, InputIt last) {
|
||||
// this does a subtraction for random access iterators, or increments one-by-one for others.
|
||||
initialize_storage_and_size(std::distance(first, last));
|
||||
copy_objects_from_range(m_storage_begin, first, last);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Initialize vector using values from another vector.
|
||||
*/
|
||||
SmallVector(const SmallVector<T, inline_elt_count>& other) {
|
||||
initialize_storage_and_size(other.size());
|
||||
copy_construct_objects(m_storage_begin, other.m_storage_begin, other.size());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Initialize vector by moving from existing vector. Will leave other vector with size = 0.
|
||||
*/
|
||||
SmallVector(SmallVector<T, inline_elt_count>&& other) {
|
||||
if (other.using_heap_storage()) {
|
||||
// steal the storage from the existing vector
|
||||
m_storage_begin = other.m_storage_begin;
|
||||
m_storage_end = other.m_storage_end;
|
||||
// don't let the other vector reuse the storage by setting it back to inline.
|
||||
other.set_inline_storage();
|
||||
} else {
|
||||
set_inline_storage();
|
||||
// move from inline to inline
|
||||
// we're going to set the other's size to 0, so we have to destroy its objects.
|
||||
move_and_destroy(m_storage_begin, other.m_storage_begin, other.size());
|
||||
}
|
||||
|
||||
m_size = other.m_size;
|
||||
other.m_size = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Initialize vector from an initializer list.
|
||||
*/
|
||||
SmallVector(std::initializer_list<T> lst) {
|
||||
initialize_storage_and_size(lst.size());
|
||||
copy_objects_from_range(m_storage_begin, lst.begin(), lst.end());
|
||||
}
|
||||
|
||||
//// ASSIGNMENT
|
||||
/*!
|
||||
* Copy all data from another vector.
|
||||
* Uses copy ctor in all cases, never copy assignment.
|
||||
*/
|
||||
SmallVector& operator=(const SmallVector<T, inline_elt_count>& other) {
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// destroy all existing objects
|
||||
destroy_objects(m_storage_begin, size());
|
||||
|
||||
// if we're bothering to copy, let's resize the storage to exactly what we need.
|
||||
if (capacity() != other.size()) {
|
||||
free_current_storage();
|
||||
initialize_storage_and_size(other.size());
|
||||
}
|
||||
m_size = other.size();
|
||||
|
||||
copy_construct_objects(m_storage_begin, other.m_storage_begin, other.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move all data from another vector. Leaves the other vector with nothing.
|
||||
*/
|
||||
SmallVector& operator=(SmallVector<T, inline_elt_count>&& other) {
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// destroy existing objects
|
||||
destroy_objects(m_storage_begin, size());
|
||||
|
||||
// kill old storage. We will either steal it from other or use stack.
|
||||
free_current_storage();
|
||||
|
||||
if (other.size() > inline_elt_count) {
|
||||
// steal the heap array
|
||||
m_storage_begin = other.m_storage_begin;
|
||||
m_storage_end = other.m_storage_end;
|
||||
other.set_inline_storage();
|
||||
} else {
|
||||
// move from inline storage
|
||||
set_inline_storage();
|
||||
move_and_destroy(m_storage_begin, other.m_storage_begin, other.size());
|
||||
}
|
||||
m_size = other.size();
|
||||
other.m_size = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the contents to count copies of a given value.
|
||||
* Note - this is not super efficient and could avoid reallocating the heap memory in some cases.
|
||||
*/
|
||||
void assign(std::size_t count, const T& value) {
|
||||
*this = SmallVector<T, inline_elt_count>(count, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
* See note on other assign.
|
||||
*/
|
||||
template <typename InputIt>
|
||||
void assign(InputIt first, InputIt last) {
|
||||
*this = SmallVector<T, inline_elt_count>(first, last);
|
||||
}
|
||||
|
||||
/*!
|
||||
* See note on other assign.
|
||||
*/
|
||||
void assign(std::initializer_list<T> lst) { *this = SmallVector<T, inline_elt_count>(lst); }
|
||||
|
||||
/*!
|
||||
* Get the element at the index. Assert the index is valid.
|
||||
*/
|
||||
T& at(std::size_t idx) {
|
||||
assert(idx < m_size);
|
||||
return m_storage_begin[idx];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the element at the index. Assert the index is valid.
|
||||
*/
|
||||
const T& at(std::size_t idx) const {
|
||||
assert(idx < m_size);
|
||||
return m_storage_begin[idx];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the element at the index. No range checking.
|
||||
*/
|
||||
T& operator[](std::size_t idx) { return m_storage_begin[idx]; }
|
||||
|
||||
/*!
|
||||
* Get the element at the index. No range checking.
|
||||
*/
|
||||
const T& operator[](std::size_t idx) const { return m_storage_begin[idx]; }
|
||||
|
||||
/*!
|
||||
* Get the first element. Not valid if size == 0
|
||||
*/
|
||||
T& front() { return m_storage_begin[0]; }
|
||||
|
||||
/*!
|
||||
* Get the first element. Not valid if size == 0
|
||||
*/
|
||||
const T& front() const { return m_storage_begin[0]; }
|
||||
|
||||
/*!
|
||||
* Get the last element. Not valid if size == 0
|
||||
*/
|
||||
T& back() { return m_storage_begin[m_size - 1]; }
|
||||
|
||||
/*!
|
||||
* Get the last element. Not valid if size == 0
|
||||
*/
|
||||
const T& back() const { return m_storage_begin[m_size - 1]; }
|
||||
|
||||
/*!
|
||||
* Get a pointer to data. This may become invalid after any resize, just like a std::vector.
|
||||
*/
|
||||
T* data() { return m_storage_begin; }
|
||||
|
||||
/*!
|
||||
* Get a pointer to data. This may become invalid after any resize, just like a std::vector.
|
||||
*/
|
||||
const T* data() const { return m_storage_begin; }
|
||||
|
||||
iterator begin() { return m_storage_begin; }
|
||||
const_iterator begin() const { return m_storage_begin; }
|
||||
const_iterator cbegin() const { return m_storage_begin; }
|
||||
iterator end() { return m_storage_begin + m_size; }
|
||||
const_iterator end() const { return m_storage_begin + m_size; }
|
||||
const_iterator cend() const { return m_storage_begin + m_size; }
|
||||
|
||||
reverse_iterator rbegin() { return m_storage_begin + m_size; }
|
||||
const_reverse_iterator rbegin() const { return m_storage_begin + m_size; }
|
||||
const_reverse_iterator crbegin() const { return m_storage_begin + m_size; }
|
||||
reverse_iterator rend() { return m_storage_begin; }
|
||||
const_reverse_iterator rend() const { return m_storage_begin; }
|
||||
const_reverse_iterator crend() const { return m_storage_begin; }
|
||||
|
||||
bool empty() const { return m_size == 0; }
|
||||
std::size_t size() const { return m_size; }
|
||||
|
||||
/*!
|
||||
* Make sure we have enough storage to hold desired_count without allocating.
|
||||
*/
|
||||
void reserve(std::size_t desired_count) {
|
||||
auto old_count = std::size_t(m_storage_end - m_storage_begin);
|
||||
if (desired_count > old_count) {
|
||||
// we need more storage.
|
||||
auto old_storage = m_storage_begin;
|
||||
auto old_uses_heap = using_heap_storage();
|
||||
|
||||
// replace current storage
|
||||
allocate_and_set_heap_storage(desired_count);
|
||||
// move objects into the new location and destroy the old ones.
|
||||
move_and_destroy(m_storage_begin, old_storage, size());
|
||||
// free old, if needed.
|
||||
if (old_uses_heap) {
|
||||
free_heap_storage(old_storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* The number of elements we can hold without allocating more memory.
|
||||
*/
|
||||
std::size_t capacity() const { return m_storage_end - m_storage_begin; }
|
||||
|
||||
/*!
|
||||
* Shrink our heap allocation to the smallest possible size that can possibly hold the
|
||||
* current elements. This can shrink it to zero and move us back to inline storage.
|
||||
*/
|
||||
void shrink_to_fit() {
|
||||
if (using_heap_storage() && capacity() > size()) {
|
||||
auto old_storage = m_storage_begin;
|
||||
if (size() > inline_elt_count) {
|
||||
// go to smaller heap
|
||||
allocate_and_set_heap_storage(size());
|
||||
} else {
|
||||
// go back to inline
|
||||
set_inline_storage();
|
||||
}
|
||||
move_and_destroy(m_storage_begin, old_storage, size());
|
||||
free_heap_storage(old_storage); // was heap.
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Destroy all objects and free all of our memory. Sets our size to 0.
|
||||
*/
|
||||
void clear() {
|
||||
destroy_objects(m_storage_begin, m_size);
|
||||
if (using_heap_storage()) {
|
||||
free_heap_storage(m_storage_begin);
|
||||
}
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Insert value _before_ pos.
|
||||
*/
|
||||
iterator insert(iterator pos, const T& value) {
|
||||
auto objects_before_insert = pos - begin();
|
||||
auto objects_after_insert = end() - pos;
|
||||
iterator result;
|
||||
if (size() + 1 > capacity()) {
|
||||
// not enough room, need to reallocate
|
||||
auto old_storage = m_storage_begin;
|
||||
auto old_used_heap = using_heap_storage();
|
||||
allocate_and_set_heap_storage(GROW_AMOUNT * (size() + 1)); // todo grow policy here.
|
||||
|
||||
// copy the objects before the insert
|
||||
move_and_destroy(m_storage_begin, old_storage, objects_before_insert);
|
||||
// copy the object to insert
|
||||
result = new (m_storage_begin + objects_before_insert) T(value);
|
||||
// copy the objects after the insert
|
||||
move_and_destroy(m_storage_begin + objects_before_insert + 1,
|
||||
old_storage + objects_before_insert, objects_after_insert);
|
||||
|
||||
if (old_used_heap) {
|
||||
free_heap_storage(old_storage);
|
||||
}
|
||||
} else {
|
||||
// we have enough room, just move.
|
||||
move_and_destroy_reverse(pos + 1, pos, objects_after_insert);
|
||||
// and insert!
|
||||
result = new (pos) T(value);
|
||||
}
|
||||
|
||||
m_size++;
|
||||
return result;
|
||||
}
|
||||
|
||||
// insert iterator T&& value
|
||||
// insert iterator count value
|
||||
// insert iterator (it, it)
|
||||
|
||||
// emplace
|
||||
// erase
|
||||
|
||||
// push_back
|
||||
void push_back(const T& value) {
|
||||
if (size() + 1 > capacity()) {
|
||||
// not enough room!
|
||||
reserve(GROW_AMOUNT * (size() + 1));
|
||||
}
|
||||
// copy
|
||||
new (m_storage_begin + size()) T(value);
|
||||
m_size++;
|
||||
}
|
||||
|
||||
void push_back(const T&& value) {
|
||||
if (size() + 1 > capacity()) {
|
||||
// not enough room!
|
||||
reserve(GROW_AMOUNT * (size() + 1));
|
||||
}
|
||||
// move:
|
||||
new (m_storage_begin + size()) T(std::move(value));
|
||||
m_size++;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args&&... args) {
|
||||
if (size() + 1 > capacity()) {
|
||||
// not enough room!
|
||||
reserve(GROW_AMOUNT * (size() + 1));
|
||||
}
|
||||
new (m_storage_begin + size()) T(std::forward<Args>(args)...);
|
||||
m_size++;
|
||||
}
|
||||
|
||||
void pop_back() { m_size--; }
|
||||
|
||||
void relocate_some_and_destroy_rest(T* dst,
|
||||
T* src,
|
||||
std::size_t dst_count,
|
||||
std::size_t src_count) {
|
||||
std::size_t i = 0;
|
||||
while (i < dst_count) {
|
||||
new (dst + i) T(std::move(src[i]));
|
||||
src[i].~T();
|
||||
i++;
|
||||
}
|
||||
|
||||
while (i < src_count) {
|
||||
src[i].~T();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void resize(std::size_t new_size) {
|
||||
if (size() == new_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto copy_count = min(size(), new_size);
|
||||
bool old_used_heap = using_heap_storage();
|
||||
auto old_storage = m_storage_begin;
|
||||
|
||||
if (new_size < inline_elt_count) {
|
||||
// heap to inline
|
||||
set_inline_storage();
|
||||
} else {
|
||||
allocate_and_set_heap_storage(new_size);
|
||||
}
|
||||
|
||||
relocate_some_and_destroy_rest(m_storage_begin, old_storage, copy_count, size());
|
||||
if (old_used_heap) {
|
||||
free_heap_storage(old_storage);
|
||||
}
|
||||
construct_objects_by_default_ctor(m_storage_begin + copy_count, new_size - copy_count);
|
||||
m_size = new_size;
|
||||
}
|
||||
|
||||
void resize(std::size_t new_size, const T& value) {
|
||||
if (size() == new_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto copy_count = min(size(), new_size);
|
||||
bool old_used_heap = using_heap_storage();
|
||||
auto old_storage = m_storage_begin;
|
||||
|
||||
if (new_size < inline_elt_count) {
|
||||
// heap to inline
|
||||
set_inline_storage();
|
||||
} else {
|
||||
allocate_and_set_heap_storage(new_size);
|
||||
}
|
||||
|
||||
relocate_some_and_destroy_rest(m_storage_begin, old_storage, copy_count, size());
|
||||
if (old_used_heap) {
|
||||
free_heap_storage(old_storage);
|
||||
}
|
||||
construct_objects_by_filling(m_storage_begin + copy_count, new_size - copy_count, value);
|
||||
m_size = new_size;
|
||||
}
|
||||
|
||||
// swap
|
||||
|
||||
bool operator==(const SmallVector<T, inline_elt_count>& other) const {
|
||||
if (other.size() != size()) {
|
||||
return false;
|
||||
}
|
||||
for (std::size_t i = 0; i < size(); i++) {
|
||||
if (operator[](i) != other[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const SmallVector<T, inline_elt_count>& other) const {
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
// std::swap
|
||||
|
||||
~SmallVector() {
|
||||
// destroy allocated objects
|
||||
destroy_objects(m_storage_begin, m_size);
|
||||
// free heap storage.
|
||||
if (using_heap_storage()) {
|
||||
free_heap_storage(m_storage_begin);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace cu
|
||||
+24
-6
@@ -9,7 +9,8 @@
|
||||
#include "common/util/math_util.h"
|
||||
|
||||
namespace decompiler {
|
||||
void Env::set_remap_for_function(int nargs) {
|
||||
void Env::set_remap_for_function(const TypeSpec& ts) {
|
||||
int nargs = ts.arg_count() - 1;
|
||||
for (int i = 0; i < nargs; i++) {
|
||||
std::string var_name;
|
||||
var_name.push_back(i >= 4 ? 't' : 'a');
|
||||
@@ -18,10 +19,16 @@ void Env::set_remap_for_function(int nargs) {
|
||||
var_name.push_back('0');
|
||||
m_var_remap[var_name] = ("arg" + std::to_string(i));
|
||||
}
|
||||
m_var_remap["s6-0"] = "pp";
|
||||
if (ts.try_get_tag("behavior")) {
|
||||
m_var_remap["s6-0"] = "self";
|
||||
m_pp_mapped_by_behavior = true;
|
||||
} else {
|
||||
m_var_remap["s6-0"] = "pp";
|
||||
}
|
||||
}
|
||||
|
||||
void Env::set_remap_for_new_method(int nargs) {
|
||||
void Env::set_remap_for_new_method(const TypeSpec& ts) {
|
||||
int nargs = ts.arg_count() - 1;
|
||||
m_var_remap["a0-0"] = "allocation";
|
||||
m_var_remap["a1-0"] = "type-to-make";
|
||||
for (int i = 2; i < nargs; i++) {
|
||||
@@ -32,10 +39,16 @@ void Env::set_remap_for_new_method(int nargs) {
|
||||
var_name.push_back('0');
|
||||
m_var_remap[var_name] = ("arg" + std::to_string(i - 2));
|
||||
}
|
||||
m_var_remap["s6-0"] = "pp";
|
||||
if (ts.try_get_tag("behavior")) {
|
||||
m_var_remap["s6-0"] = "self";
|
||||
m_pp_mapped_by_behavior = true;
|
||||
} else {
|
||||
m_var_remap["s6-0"] = "pp";
|
||||
}
|
||||
}
|
||||
|
||||
void Env::set_remap_for_method(int nargs) {
|
||||
void Env::set_remap_for_method(const TypeSpec& ts) {
|
||||
int nargs = ts.arg_count() - 1;
|
||||
m_var_remap["a0-0"] = "obj";
|
||||
for (int i = 1; i < nargs; i++) {
|
||||
std::string var_name;
|
||||
@@ -45,7 +58,12 @@ void Env::set_remap_for_method(int nargs) {
|
||||
var_name.push_back('0');
|
||||
m_var_remap[var_name] = ("arg" + std::to_string(i - 1));
|
||||
}
|
||||
m_var_remap["s6-0"] = "pp";
|
||||
if (ts.try_get_tag("behavior")) {
|
||||
m_var_remap["s6-0"] = "self";
|
||||
m_pp_mapped_by_behavior = true;
|
||||
} else {
|
||||
m_var_remap["s6-0"] = "pp";
|
||||
}
|
||||
}
|
||||
|
||||
void Env::map_args_from_config(const std::vector<std::string>& args_names,
|
||||
|
||||
@@ -149,9 +149,9 @@ class Env {
|
||||
|
||||
const std::unordered_map<int, StackTypeCast>& stack_casts() const { return m_stack_typecasts; }
|
||||
|
||||
void set_remap_for_function(int nargs);
|
||||
void set_remap_for_method(int nargs);
|
||||
void set_remap_for_new_method(int nargs);
|
||||
void set_remap_for_function(const TypeSpec& ts);
|
||||
void set_remap_for_method(const TypeSpec& ts);
|
||||
void set_remap_for_new_method(const TypeSpec& ts);
|
||||
void map_args_from_config(const std::vector<std::string>& args_names,
|
||||
const std::unordered_map<std::string, std::string>& var_names);
|
||||
void map_args_from_config(const std::vector<std::string>& args_names,
|
||||
@@ -208,6 +208,8 @@ class Env {
|
||||
// hacks:
|
||||
bool aggressively_reject_cond_to_value_rewrite = false;
|
||||
|
||||
bool pp_mapped_by_behavior() const { return m_pp_mapped_by_behavior; }
|
||||
|
||||
private:
|
||||
RegisterAccess m_end_var;
|
||||
|
||||
@@ -218,6 +220,7 @@ class Env {
|
||||
VariableNames m_var_names;
|
||||
|
||||
bool m_has_types = false;
|
||||
bool m_pp_mapped_by_behavior = false;
|
||||
std::vector<TypeState> m_block_init_types;
|
||||
std::vector<TypeState> m_op_end_types;
|
||||
std::vector<TypeState*> m_op_init_types;
|
||||
|
||||
@@ -23,12 +23,14 @@ bool convert_to_expressions(
|
||||
// give us anything more specific.
|
||||
if (f.guessed_name.kind == FunctionName::FunctionKind::GLOBAL ||
|
||||
f.guessed_name.kind == FunctionName::FunctionKind::UNIDENTIFIED) {
|
||||
f.ir2.env.set_remap_for_function(f.type.arg_count() - 1);
|
||||
f.ir2.env.set_remap_for_function(f.type);
|
||||
} else if (f.guessed_name.kind == FunctionName::FunctionKind::METHOD) {
|
||||
auto method_type =
|
||||
dts.ts.lookup_method(f.guessed_name.type_name, f.guessed_name.method_id).type;
|
||||
if (f.guessed_name.method_id == GOAL_NEW_METHOD) {
|
||||
f.ir2.env.set_remap_for_new_method(f.type.arg_count() - 1);
|
||||
f.ir2.env.set_remap_for_new_method(method_type);
|
||||
} else {
|
||||
f.ir2.env.set_remap_for_method(f.type.arg_count() - 1);
|
||||
f.ir2.env.set_remap_for_method(method_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,13 @@ goos::Object get_arg_list_for_function(const Function& func, const Env& env) {
|
||||
namespace {
|
||||
void append_body_to_function_definition(goos::Object* top_form,
|
||||
const std::vector<goos::Object>& inline_body,
|
||||
const FunctionVariableDefinitions& var_dec) {
|
||||
const FunctionVariableDefinitions& var_dec,
|
||||
const TypeSpec& ts) {
|
||||
if (var_dec.local_vars) {
|
||||
pretty_print::append(*top_form, pretty_print::build_list(*var_dec.local_vars));
|
||||
}
|
||||
|
||||
if (var_dec.had_pp) {
|
||||
if (var_dec.had_pp && !ts.try_get_tag("behavior")) {
|
||||
std::vector<goos::Object> body_with_pp;
|
||||
body_with_pp.push_back(pretty_print::to_symbol("with-pp"));
|
||||
body_with_pp.insert(body_with_pp.end(), inline_body.begin(), inline_body.end());
|
||||
@@ -47,9 +48,18 @@ goos::Object final_output_lambda(const Function& func) {
|
||||
std::vector<goos::Object> inline_body;
|
||||
func.ir2.top_form->inline_forms(inline_body, func.ir2.env);
|
||||
auto var_dec = func.ir2.env.local_var_type_list(func.ir2.top_form, func.type.arg_count() - 1);
|
||||
auto result = pretty_print::build_list("lambda", get_arg_list_for_function(func, func.ir2.env));
|
||||
append_body_to_function_definition(&result, inline_body, var_dec);
|
||||
return result;
|
||||
|
||||
auto behavior = func.type.try_get_tag("behavior");
|
||||
if (behavior) {
|
||||
auto result = pretty_print::build_list(fmt::format("lambda :behavior {}", *behavior),
|
||||
get_arg_list_for_function(func, func.ir2.env));
|
||||
append_body_to_function_definition(&result, inline_body, var_dec, func.type);
|
||||
return result;
|
||||
} else {
|
||||
auto result = pretty_print::build_list("lambda", get_arg_list_for_function(func, func.ir2.env));
|
||||
append_body_to_function_definition(&result, inline_body, var_dec, func.type);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::string final_defun_out(const Function& func,
|
||||
@@ -75,13 +85,24 @@ std::string final_defun_out(const Function& func,
|
||||
} else {
|
||||
assert(special_mode == FunctionDefSpecials::NONE);
|
||||
}
|
||||
|
||||
auto behavior = func.type.try_get_tag("behavior");
|
||||
if (behavior) {
|
||||
def_name = "defbehavior";
|
||||
}
|
||||
|
||||
std::vector<goos::Object> top;
|
||||
top.push_back(pretty_print::to_symbol(def_name));
|
||||
top.push_back(pretty_print::to_symbol(func.guessed_name.to_string()));
|
||||
|
||||
if (behavior) {
|
||||
top.push_back(pretty_print::to_symbol(func.guessed_name.to_string() + " " + *behavior));
|
||||
} else {
|
||||
top.push_back(pretty_print::to_symbol(func.guessed_name.to_string()));
|
||||
}
|
||||
top.push_back(arguments);
|
||||
auto top_form = pretty_print::build_list(top);
|
||||
|
||||
append_body_to_function_definition(&top_form, inline_body, var_dec);
|
||||
append_body_to_function_definition(&top_form, inline_body, var_dec, func.type);
|
||||
return pretty_print::to_string(top_form);
|
||||
}
|
||||
|
||||
@@ -96,7 +117,7 @@ std::string final_defun_out(const Function& func,
|
||||
top.push_back(arguments);
|
||||
auto top_form = pretty_print::build_list(top);
|
||||
|
||||
append_body_to_function_definition(&top_form, inline_body, var_dec);
|
||||
append_body_to_function_definition(&top_form, inline_body, var_dec, method_info.type);
|
||||
return pretty_print::to_string(top_form);
|
||||
}
|
||||
|
||||
@@ -107,7 +128,7 @@ std::string final_defun_out(const Function& func,
|
||||
top.push_back(arguments);
|
||||
auto top_form = pretty_print::build_list(top);
|
||||
|
||||
append_body_to_function_definition(&top_form, inline_body, var_dec);
|
||||
append_body_to_function_definition(&top_form, inline_body, var_dec, func.type);
|
||||
return pretty_print::to_string(top_form);
|
||||
}
|
||||
|
||||
@@ -120,7 +141,7 @@ std::string final_defun_out(const Function& func,
|
||||
top.push_back(arguments);
|
||||
auto top_form = pretty_print::build_list(top);
|
||||
|
||||
append_body_to_function_definition(&top_form, inline_body, var_dec);
|
||||
append_body_to_function_definition(&top_form, inline_body, var_dec, func.type);
|
||||
return pretty_print::to_string(top_form);
|
||||
}
|
||||
return "nyi";
|
||||
|
||||
@@ -1244,8 +1244,8 @@
|
||||
(define-extern load-package (function string kheap pair))
|
||||
(define-extern unload-package (function string pair))
|
||||
(define-extern malloc (function symbol int pointer)) ;; from kernel-defs.gc
|
||||
(define-extern remove-exit (function stack-frame))
|
||||
(define-extern stream<-process-mask (function object int object))
|
||||
(define-extern remove-exit (function :behavior process stack-frame))
|
||||
(define-extern stream<-process-mask (function object process-mask process-mask))
|
||||
(define-extern return-from-thread (function none))
|
||||
(define-extern return-from-thread-dead (function none))
|
||||
(define-extern process-count (function process-tree int))
|
||||
@@ -9666,7 +9666,7 @@
|
||||
:size-assert #x134
|
||||
:flag-assert #xe00000134
|
||||
(:methods
|
||||
(new (symbol type process) _type_ 0)
|
||||
(new (symbol type process) _type_ :behavior process-drawable 0)
|
||||
(dummy-9 () none 9)
|
||||
(dummy-10 () none 10)
|
||||
(dummy-11 () none 11)
|
||||
@@ -17698,7 +17698,7 @@
|
||||
|
||||
(define-extern matrix-world->local (function matrix))
|
||||
(define-extern camera-angle (function float))
|
||||
(define-extern camera-teleport-to-entity (function entity-actor none))
|
||||
(define-extern camera-teleport-to-entity (function entity-actor none :behavior process))
|
||||
|
||||
;; - Symbols
|
||||
|
||||
|
||||
@@ -172,4 +172,7 @@
|
||||
- You can now set a field which has a forward declared structure or basic type
|
||||
- `cdr` now returns an object of type `pair`.
|
||||
- `lambda`s can now be used inside of a static object definition.
|
||||
- Methods can now be `:replace`d to override their type from their parent. Use this with extreme care.
|
||||
- Methods can now be `:replace`d to override their type from their parent. Use this with extreme care.
|
||||
- TypeSpecs now support "tags". This can specify a `:behavior` tag for a function.
|
||||
- Lambdas and methods now support `:behavior` to specify the current process type.
|
||||
- `defbehavior` has been added to define a global behavior.
|
||||
@@ -21,7 +21,7 @@
|
||||
:size-assert #x134
|
||||
:flag-assert #xe00000134
|
||||
(:methods
|
||||
(new (symbol type process) _type_ 0)
|
||||
(new (symbol type process) _type_ :behavior process-drawable 0)
|
||||
(dummy-9 () none 9)
|
||||
(dummy-10 () none 10)
|
||||
(dummy-11 () none 11)
|
||||
|
||||
@@ -63,22 +63,21 @@
|
||||
;; definition for function camera-teleport-to-entity
|
||||
;; INFO: Return type mismatch int vs none.
|
||||
;; Used lq/sq
|
||||
(defun camera-teleport-to-entity ((arg0 entity-actor))
|
||||
(with-pp
|
||||
(let ((gp-0 (new 'stack 'transformq)))
|
||||
(let ((v1-1 (-> gp-0 trans)))
|
||||
(set! (-> v1-1 quad) (-> (the-as transform (-> arg0 extra)) scale quad))
|
||||
)
|
||||
(quaternion-copy! (-> gp-0 quat) (-> arg0 quat))
|
||||
(vector-identity! (-> gp-0 scale))
|
||||
(let ((a1-2 (new 'stack-no-clear 'event-message-block)))
|
||||
(set! (-> a1-2 from) pp)
|
||||
(set! (-> a1-2 num-params) 1)
|
||||
(set! (-> a1-2 message) 'teleport-to-transformq)
|
||||
(set! (-> a1-2 param 0) (the-as uint gp-0))
|
||||
(send-event-function *camera* a1-2)
|
||||
)
|
||||
(defbehavior camera-teleport-to-entity process ((arg0 entity-actor))
|
||||
(let ((gp-0 (new 'stack 'transformq)))
|
||||
(set! (-> gp-0 trans quad)
|
||||
(-> (the-as transform (-> arg0 extra)) scale quad)
|
||||
)
|
||||
(quaternion-copy! (-> gp-0 quat) (-> arg0 quat))
|
||||
(vector-identity! (-> gp-0 scale))
|
||||
(let ((a1-2 (new 'stack-no-clear 'event-message-block)))
|
||||
(set! (-> a1-2 from) self)
|
||||
(set! (-> a1-2 num-params) 1)
|
||||
(set! (-> a1-2 message) 'teleport-to-transformq)
|
||||
(set! (-> a1-2 param 0) (the-as uint gp-0))
|
||||
(send-event-function *camera* a1-2)
|
||||
)
|
||||
(none)
|
||||
)
|
||||
0
|
||||
(none)
|
||||
)
|
||||
|
||||
@@ -37,4 +37,7 @@
|
||||
|
||||
;; NOTE - I'm guessing there was a define-extern earlier in the build process
|
||||
;; This is actually set in process-drawable.gc, but it's used by files earlier in the process
|
||||
;; assuming (state process-drawable)
|
||||
(define-extern process-drawable-art-error state)
|
||||
|
||||
(declare-type process-drawable process)
|
||||
@@ -558,3 +558,15 @@
|
||||
`(rlet ((pp :reg r13 :reset-here #t :type process))
|
||||
,@body)
|
||||
)
|
||||
|
||||
(defmacro defbehavior (name process-type bindings &rest body)
|
||||
(if (and
|
||||
(> (length body) 1) ;; more than one thing in function
|
||||
(string? (first body)) ;; first thing is a string
|
||||
)
|
||||
;; then it's a docstring and we ignore it.
|
||||
`(define ,name (lambda :name ,name :behavior ,process-type ,bindings ,@(cdr body)))
|
||||
;; otherwise don't ignore it.
|
||||
`(define ,name (lambda :name ,name :behavior ,process-type ,bindings ,@body))
|
||||
)
|
||||
)
|
||||
|
||||
+84
-84
@@ -249,13 +249,10 @@
|
||||
;; Remove Exit
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun remove-exit ()
|
||||
"This is likely a defbehavior for process.
|
||||
Pops a single stack frame, if there is one."
|
||||
(rlet ((self :reg r13 :type process))
|
||||
(when (-> self stack-frame-top)
|
||||
(set! (-> self stack-frame-top) (-> self stack-frame-top next))
|
||||
)
|
||||
(defbehavior remove-exit process ()
|
||||
"Pops a single stack frame, if there is one."
|
||||
(when (-> self stack-frame-top)
|
||||
(set! (-> self stack-frame-top) (-> self stack-frame-top next))
|
||||
)
|
||||
)
|
||||
|
||||
@@ -268,83 +265,86 @@
|
||||
;; The base class of process is process-tree.
|
||||
;; Each process-tree element has a process-mask which indicates what type of node it is.
|
||||
|
||||
(defun-debug stream<-process-mask (stream (mask int))
|
||||
"Print out a process mask. This function may have been auto-generated?"
|
||||
; 24
|
||||
(if (not (eq? 0 (logand mask (process-mask death))))
|
||||
(format stream "death "))
|
||||
; 23
|
||||
(if (not (eq? 0 (logand mask (process-mask attackable))))
|
||||
(format stream "attackable "))
|
||||
; 22
|
||||
(if (not (eq? 0 (logand mask (process-mask projectile))))
|
||||
(format stream "projectile "))
|
||||
; 21
|
||||
(if (not (eq? 0 (logand mask (process-mask entity))))
|
||||
(format stream "entity "))
|
||||
; 20
|
||||
(if (not (eq? 0 (logand mask (process-mask ambient))))
|
||||
(format stream "ambient "))
|
||||
; 19
|
||||
(if (not (eq? 0 (logand mask (process-mask platform))))
|
||||
(format stream "platform "))
|
||||
; 18
|
||||
(if (not (eq? 0 (logand mask (process-mask camera))))
|
||||
(format stream "camera "))
|
||||
; 17
|
||||
(if (not (eq? 0 (logand mask (process-mask enemy))))
|
||||
(format stream "enemy "))
|
||||
; 16
|
||||
(if (not (eq? 0 (logand mask (process-mask collectable))))
|
||||
(format stream "collectable "))
|
||||
; 15
|
||||
(if (not (eq? 0 (logand mask (process-mask crate))))
|
||||
(format stream "crate "))
|
||||
; 14
|
||||
(if (not (eq? 0 (logand mask (process-mask sidekick))))
|
||||
(format stream "sidekick "))
|
||||
; 13
|
||||
(if (not (eq? 0 (logand mask (process-mask target))))
|
||||
(format stream "target "))
|
||||
; 12
|
||||
(if (not (eq? 0 (logand mask (process-mask movie-subject))))
|
||||
(format stream "movie-subject "))
|
||||
; 11
|
||||
(if (not (eq? 0 (logand mask (process-mask movie))))
|
||||
(format stream "movie "))
|
||||
; 10
|
||||
(if (not (eq? 0 (logand mask (process-mask going))))
|
||||
(format stream "going "))
|
||||
; 9
|
||||
(if (not (eq? 0 (logand mask (process-mask heap-shrunk))))
|
||||
(format stream "heap-shrunk "))
|
||||
; 8
|
||||
(if (not (eq? 0 (logand mask (process-mask process-tree))))
|
||||
(format stream "process-tree "))
|
||||
; 7
|
||||
(if (not (eq? 0 (logand mask (process-mask sleep-code))))
|
||||
(format stream "sleep-code "))
|
||||
; 6
|
||||
(if (not (eq? 0 (logand mask (process-mask sleep))))
|
||||
(format stream "sleep "))
|
||||
; 5
|
||||
(if (not (eq? 0 (logand mask (process-mask actor-pause))))
|
||||
(format stream "actor-pause "))
|
||||
; 4
|
||||
(if (not (eq? 0 (logand mask (process-mask progress))))
|
||||
(format stream "progress "))
|
||||
; 3
|
||||
(if (not (eq? 0 (logand mask (process-mask menu))))
|
||||
(format stream "menu "))
|
||||
; 2
|
||||
(if (not (eq? 0 (logand mask (process-mask pause))))
|
||||
(format stream "pause "))
|
||||
; 1
|
||||
(if (not (eq? 0 (logand mask (process-mask draw))))
|
||||
(format stream "draw "))
|
||||
; 0
|
||||
(if (not (eq? 0 (logand mask (process-mask execute))))
|
||||
(format stream "execute "))
|
||||
(defun-debug stream<-process-mask ((arg0 object) (arg1 process-mask))
|
||||
(let ((s4-0 arg1))
|
||||
(if (= (logand #x1000000 (the-as int s4-0)) (process-mask death))
|
||||
(format arg0 "death ")
|
||||
)
|
||||
(if (= (logand #x800000 (the-as int s4-0)) (process-mask attackable))
|
||||
(format arg0 "attackable ")
|
||||
)
|
||||
(if (= (logand #x400000 (the-as int s4-0)) (process-mask projectile))
|
||||
(format arg0 "projectile ")
|
||||
)
|
||||
(if (= (logand #x200000 (the-as int s4-0)) (process-mask entity))
|
||||
(format arg0 "entity ")
|
||||
)
|
||||
(if (= (logand #x100000 (the-as int s4-0)) (process-mask ambient))
|
||||
(format arg0 "ambient ")
|
||||
)
|
||||
(if (= (logand #x80000 (the-as int s4-0)) (process-mask platform))
|
||||
(format arg0 "platform ")
|
||||
)
|
||||
(if (= (logand #x40000 (the-as int s4-0)) (process-mask camera))
|
||||
(format arg0 "camera ")
|
||||
)
|
||||
(if (= (logand #x20000 (the-as int s4-0)) (process-mask enemy))
|
||||
(format arg0 "enemy ")
|
||||
)
|
||||
(if (= (logand #x10000 (the-as int s4-0)) (process-mask collectable))
|
||||
(format arg0 "collectable ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask crate)) (process-mask crate))
|
||||
(format arg0 "crate ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask sidekick)) (process-mask sidekick))
|
||||
(format arg0 "sidekick ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask target)) (process-mask target))
|
||||
(format arg0 "target ")
|
||||
)
|
||||
(if
|
||||
(= (logand s4-0 (process-mask movie-subject)) (process-mask movie-subject))
|
||||
(format arg0 "movie-subject ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask movie)) (process-mask movie))
|
||||
(format arg0 "movie ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask going)) (process-mask going))
|
||||
(format arg0 "going ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask heap-shrunk)) (process-mask heap-shrunk))
|
||||
(format arg0 "heap-shrunk ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask process-tree)) (process-mask process-tree))
|
||||
(format arg0 "process-tree ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask sleep-code)) (process-mask sleep-code))
|
||||
(format arg0 "sleep-code ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask sleep)) (process-mask sleep))
|
||||
(format arg0 "sleep ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask actor-pause)) (process-mask actor-pause))
|
||||
(format arg0 "actor-pause ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask progress)) (process-mask progress))
|
||||
(format arg0 "progress ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask menu)) (process-mask menu))
|
||||
(format arg0 "menu ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask pause)) (process-mask pause))
|
||||
(format arg0 "pause ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask draw)) (process-mask draw))
|
||||
(format arg0 "draw ")
|
||||
)
|
||||
(if (= (logand s4-0 (process-mask execute)) (process-mask execute))
|
||||
(format arg0 "execute ")
|
||||
)
|
||||
)
|
||||
arg1
|
||||
)
|
||||
|
||||
;; game state
|
||||
|
||||
+2
-14
@@ -1,6 +1,7 @@
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
#include "goalc/compiler/IR.h"
|
||||
#include "common/goos/ParseHelpers.h"
|
||||
#include "common/type_system/deftype.h"
|
||||
|
||||
/*!
|
||||
* Parse arguments into a goos::Arguments format.
|
||||
@@ -124,20 +125,7 @@ void Compiler::expect_empty_list(const goos::Object& o) {
|
||||
}
|
||||
|
||||
TypeSpec Compiler::parse_typespec(const goos::Object& src) {
|
||||
if (src.is_symbol()) {
|
||||
return m_ts.make_typespec(symbol_string(src));
|
||||
} else if (src.is_pair()) {
|
||||
TypeSpec ts = m_ts.make_typespec(symbol_string(pair_car(src)));
|
||||
const auto& rest = pair_cdr(src);
|
||||
|
||||
for_each_in_list(rest, [&](const goos::Object& o) { ts.add_arg(parse_typespec(o)); });
|
||||
|
||||
return ts;
|
||||
} else {
|
||||
throw_compiler_error(src, "Invalid typespec.");
|
||||
}
|
||||
assert(false);
|
||||
return {};
|
||||
return ::parse_typespec(&m_ts, src);
|
||||
}
|
||||
|
||||
bool Compiler::is_local_symbol(const goos::Object& obj, Env* env) {
|
||||
|
||||
@@ -116,7 +116,7 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
|
||||
auto obj_env = get_parent_env_of_type<FileEnv>(env);
|
||||
auto args = get_va(form, rest);
|
||||
if (args.unnamed.empty() || !args.unnamed.front().is_list() ||
|
||||
!args.only_contains_named({"name", "inline-only", "segment"})) {
|
||||
!args.only_contains_named({"name", "inline-only", "segment", "behavior"})) {
|
||||
throw_compiler_error(form, "Invalid lambda form");
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
|
||||
}
|
||||
|
||||
// set up argument register constraints.
|
||||
std::vector<RegVal*> args_for_coloring;
|
||||
std::vector<RegVal*> reset_args_for_coloring;
|
||||
std::vector<TypeSpec> arg_types;
|
||||
for (auto& parm : lambda.params) {
|
||||
arg_types.push_back(parm.type);
|
||||
@@ -213,7 +213,25 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
|
||||
constr.desired_register = arg_regs.at(i);
|
||||
new_func_env->params[lambda.params.at(i).name] = ireg;
|
||||
new_func_env->constrain(constr);
|
||||
args_for_coloring.push_back(ireg);
|
||||
reset_args_for_coloring.push_back(ireg);
|
||||
}
|
||||
|
||||
if (args.has_named("behavior")) {
|
||||
const std::string behavior_type = symbol_string(args.get_named("behavior"));
|
||||
auto self_var = new_func_env->make_gpr(m_ts.make_typespec(behavior_type));
|
||||
IRegConstraint constr;
|
||||
constr.contrain_everywhere = true;
|
||||
constr.desired_register = emitter::gRegInfo.get_process_reg();
|
||||
constr.ireg = self_var->ireg();
|
||||
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()) {
|
||||
throw_compiler_error(form, "Cannot have an argument named self in a behavior");
|
||||
}
|
||||
new_func_env->params["self"] = self_var;
|
||||
reset_args_for_coloring.push_back(self_var);
|
||||
lambda_ts.add_new_tag("behavior", behavior_type);
|
||||
}
|
||||
|
||||
place->func = new_func_env.get();
|
||||
@@ -224,7 +242,7 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
|
||||
auto func_block_env = new_func_env->alloc_env<BlockEnv>(new_func_env.get(), "#f");
|
||||
func_block_env->return_value = return_reg;
|
||||
func_block_env->end_label = Label(new_func_env.get());
|
||||
func_block_env->emit(std::make_unique<IR_ValueReset>(args_for_coloring));
|
||||
func_block_env->emit(std::make_unique<IR_ValueReset>(reset_args_for_coloring));
|
||||
|
||||
// compile the function, iterating through the body.
|
||||
Val* result = nullptr;
|
||||
@@ -287,6 +305,10 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
|
||||
if (new_func_env->settings.save_code) {
|
||||
obj_env->add_function(std::move(new_func_env));
|
||||
}
|
||||
} else {
|
||||
if (args.has_named("behavior")) {
|
||||
throw_compiler_error(form, "Inline behaviors are not yet implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
place->set_type(lambda_ts);
|
||||
|
||||
@@ -424,7 +424,7 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _
|
||||
if (lambda.params.size() > 8) {
|
||||
throw_compiler_error(form, "Methods cannot have more than 8 arguments");
|
||||
}
|
||||
std::vector<RegVal*> args_for_coloring;
|
||||
std::vector<RegVal*> reset_args_for_coloring;
|
||||
std::vector<TypeSpec> arg_types;
|
||||
for (auto& parm : lambda.params) {
|
||||
arg_types.push_back(parm.type);
|
||||
@@ -441,7 +441,26 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _
|
||||
constr.desired_register = arg_regs.at(i);
|
||||
new_func_env->params[lambda.params.at(i).name] = ireg;
|
||||
new_func_env->constrain(constr);
|
||||
args_for_coloring.push_back(ireg);
|
||||
reset_args_for_coloring.push_back(ireg);
|
||||
}
|
||||
|
||||
auto method_info = m_ts.lookup_method(symbol_string(type_name), symbol_string(method_name));
|
||||
auto behavior = method_info.type.try_get_tag("behavior");
|
||||
if (behavior) {
|
||||
auto self_var = new_func_env->make_gpr(m_ts.make_typespec(*behavior));
|
||||
IRegConstraint constr;
|
||||
constr.contrain_everywhere = true;
|
||||
constr.desired_register = emitter::gRegInfo.get_process_reg();
|
||||
constr.ireg = self_var->ireg();
|
||||
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()) {
|
||||
throw_compiler_error(form, "Cannot have an argument named self in a behavior");
|
||||
}
|
||||
new_func_env->params["self"] = self_var;
|
||||
reset_args_for_coloring.push_back(self_var);
|
||||
lambda_ts.add_new_tag("behavior", *behavior);
|
||||
}
|
||||
|
||||
place->func = new_func_env.get();
|
||||
@@ -451,7 +470,7 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _
|
||||
auto func_block_env = new_func_env->alloc_env<BlockEnv>(new_func_env.get(), "#f");
|
||||
func_block_env->return_value = return_reg;
|
||||
func_block_env->end_label = Label(new_func_env.get());
|
||||
func_block_env->emit(std::make_unique<IR_ValueReset>(args_for_coloring));
|
||||
func_block_env->emit(std::make_unique<IR_ValueReset>(reset_args_for_coloring));
|
||||
|
||||
// compile the function!
|
||||
Val* result = nullptr;
|
||||
|
||||
@@ -30,59 +30,60 @@
|
||||
(show-paren-mode 1)
|
||||
;; the default color is hard to see - make it the most painful color possible.
|
||||
(custom-set-faces
|
||||
'(show-paren-match ((t (:foreground "white" :background "red")))))
|
||||
'(show-paren-match ((t (:foreground "white" :background "red")))))
|
||||
)
|
||||
|
||||
;; very simple syntax highlighting (sometimes fails)
|
||||
;; these are added to lisp-mode's highlighting.
|
||||
(font-lock-add-keywords 'lisp-mode
|
||||
'(("set!" . font-lock-keyword-face)
|
||||
("local-vars" . font-lock-keyword-face)
|
||||
("until" . font-lock-keyword-face)
|
||||
("car" . font-lock-keyword-face)
|
||||
("cdr" . font-lock-keyword-face)
|
||||
("->" . font-lock-keyword-face)
|
||||
("else" . font-lock-constant-face)
|
||||
("nop!" . font-lock-builtin-face)
|
||||
;;("and" . font-lock-keyword-face)
|
||||
;;("or" . font-lock-keyword-face)
|
||||
'(("set!" . font-lock-keyword-face)
|
||||
("local-vars" . font-lock-keyword-face)
|
||||
("until" . font-lock-keyword-face)
|
||||
("car" . font-lock-keyword-face)
|
||||
("cdr" . font-lock-keyword-face)
|
||||
("->" . font-lock-keyword-face)
|
||||
("else" . font-lock-constant-face)
|
||||
("nop!" . font-lock-builtin-face)
|
||||
;;("and" . font-lock-keyword-face)
|
||||
;;("or" . font-lock-keyword-face)
|
||||
("format" . font-lock-builtin-face)
|
||||
("defun-debug" . font-lock-keyword-face)
|
||||
("defbehavior" . font-lock-keyword-face)
|
||||
("defenum" . font-lock-keyword-face)
|
||||
("begin" . font-lock-keyword-face)
|
||||
("zero?" . font-lock-builtin-face)
|
||||
("#f" . font-lock-constant-face)
|
||||
("#t" . font-lock-constant-face)))
|
||||
("begin" . font-lock-keyword-face)
|
||||
("zero?" . font-lock-builtin-face)
|
||||
("#f" . font-lock-constant-face)
|
||||
("#t" . font-lock-constant-face)))
|
||||
|
||||
;; more advanced syntax highlighting. These still fail sometimes, but look nicer.
|
||||
(defconst scheme-font-lock-keywords-1
|
||||
(eval-when-compile
|
||||
(list
|
||||
;;
|
||||
;; Declarations. Hannes Haug <hannes.haug@student.uni-tuebingen.de> says
|
||||
;; this works for SOS, STklos, SCOOPS, Meroon and Tiny CLOS.
|
||||
(list (concat "(\\(define\\*?\\("
|
||||
;; Function names.
|
||||
"\\(\\|-public\\|-method\\|-generic\\(-procedure\\)?\\)\\|"
|
||||
;; Macro names, as variable names. A bit dubious, this.
|
||||
"\\(-syntax\\|-macro\\)\\|"
|
||||
;; Class names.
|
||||
"-class"
|
||||
;; Guile modules.
|
||||
"\\|-module"
|
||||
"\\)\\)\\>"
|
||||
;; Any whitespace and declared object.
|
||||
;; The "(*" is for curried definitions, e.g.,
|
||||
;; (define ((sum a) b) (+ a b))
|
||||
"[ \t]*(*"
|
||||
"\\(\\sw+\\)?")
|
||||
'(1 font-lock-keyword-face)
|
||||
'(6 (cond ((match-beginning 3) font-lock-function-name-face)
|
||||
((match-beginning 5) font-lock-variable-name-face)
|
||||
(t font-lock-type-face))
|
||||
nil t))
|
||||
))
|
||||
"Subdued expressions to highlight in Scheme modes.")
|
||||
(eval-when-compile
|
||||
(list
|
||||
;;
|
||||
;; Declarations. Hannes Haug <hannes.haug@student.uni-tuebingen.de> says
|
||||
;; this works for SOS, STklos, SCOOPS, Meroon and Tiny CLOS.
|
||||
(list (concat "(\\(define\\*?\\("
|
||||
;; Function names.
|
||||
"\\(\\|-public\\|-method\\|-generic\\(-procedure\\)?\\)\\|"
|
||||
;; Macro names, as variable names. A bit dubious, this.
|
||||
"\\(-syntax\\|-macro\\)\\|"
|
||||
;; Class names.
|
||||
"-class"
|
||||
;; Guile modules.
|
||||
"\\|-module"
|
||||
"\\)\\)\\>"
|
||||
;; Any whitespace and declared object.
|
||||
;; The "(*" is for curried definitions, e.g.,
|
||||
;; (define ((sum a) b) (+ a b))
|
||||
"[ \t]*(*"
|
||||
"\\(\\sw+\\)?")
|
||||
'(1 font-lock-keyword-face)
|
||||
'(6 (cond ((match-beginning 3) font-lock-function-name-face)
|
||||
((match-beginning 5) font-lock-variable-name-face)
|
||||
(t font-lock-type-face))
|
||||
nil t))
|
||||
))
|
||||
"Subdued expressions to highlight in Scheme modes.")
|
||||
(font-lock-add-keywords 'lisp-mode scheme-font-lock-keywords-1)
|
||||
|
||||
;; make gc files use lisp-mode
|
||||
@@ -103,6 +104,7 @@
|
||||
(put 'countdown 'common-lisp-indent-function 1)
|
||||
(put 'defun-debug 'common-lisp-indent-function 2)
|
||||
(put 'defenum 'common-lisp-indent-function 2)
|
||||
(put 'defbehavior 'common-lisp-indent-function 3)
|
||||
|
||||
;; indent for common lisp, this makes if's look nicer
|
||||
(custom-set-variables '(lisp-indent-function 'common-lisp-indent-function))
|
||||
@@ -130,10 +132,10 @@
|
||||
(let ((buffer (comint-check-proc "OpenGOAL")))
|
||||
;; create or reuse a buffer and make it visible
|
||||
(pop-to-buffer
|
||||
(if (or buffer (not (derived-mode-p 'opengoal-mode))
|
||||
(comint-check-proc (current-buffer)))
|
||||
(get-buffer-create (or buffer "*OpenGOAL*"))
|
||||
(current-buffer)))
|
||||
(if (or buffer (not (derived-mode-p 'opengoal-mode))
|
||||
(comint-check-proc (current-buffer)))
|
||||
(get-buffer-create (or buffer "*OpenGOAL*"))
|
||||
(current-buffer)))
|
||||
|
||||
(unless buffer
|
||||
;; start the compiler
|
||||
@@ -159,10 +161,10 @@
|
||||
(defun opengoal-compile-current-file (mode)
|
||||
"Check if the current file is saved. If so, compile it!"
|
||||
(cond
|
||||
((buffer-modified-p)
|
||||
(message "Cannot compile current file: save your changes first!"))
|
||||
(t
|
||||
(opengoal-compile-file (buffer-file-name) mode))))
|
||||
((buffer-modified-p)
|
||||
(message "Cannot compile current file: save your changes first!"))
|
||||
(t
|
||||
(opengoal-compile-file (buffer-file-name) mode))))
|
||||
|
||||
(defun opengoal-m-file ()
|
||||
"Make the current file."
|
||||
|
||||
@@ -102,3 +102,15 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro defbehavior (name process-type bindings &rest body)
|
||||
(if (and
|
||||
(> (length body) 1) ;; more than one thing in function
|
||||
(string? (first body)) ;; first thing is a string
|
||||
)
|
||||
;; then it's a docstring and we ignore it.
|
||||
`(define ,name (lambda :name ,name :behavior ,process-type ,bindings ,@(cdr body)))
|
||||
;; otherwise don't ignore it.
|
||||
`(define ,name (lambda :name ,name :behavior ,process-type ,bindings ,@body))
|
||||
)
|
||||
)
|
||||
@@ -17,7 +17,7 @@
|
||||
:size-assert #x134
|
||||
:flag-assert #xe00000134
|
||||
(:methods
|
||||
(new (symbol type process) _type_ 0)
|
||||
(new (symbol type process) _type_ :behavior process-drawable 0)
|
||||
(dummy-9 () none 9)
|
||||
(dummy-10 () none 10)
|
||||
(dummy-11 () none 11)
|
||||
@@ -47,23 +47,25 @@
|
||||
new
|
||||
align-control
|
||||
((allocation symbol) (type-to-make type) (arg0 process))
|
||||
(with-pp
|
||||
(let
|
||||
((obj
|
||||
(object-new allocation type-to-make (the-as int (-> type-to-make size)))
|
||||
)
|
||||
(let
|
||||
((obj
|
||||
(object-new allocation type-to-make (the-as int (-> type-to-make size)))
|
||||
)
|
||||
(when (zero? obj)
|
||||
(let ((t9-1 (the-as (function object object) enter-state))
|
||||
(a0-1 "memory")
|
||||
)
|
||||
(set! (-> pp next-state) process-drawable-art-error)
|
||||
(t9-1 a0-1)
|
||||
)
|
||||
(return (the-as align-control 0))
|
||||
)
|
||||
(set! (-> obj process) arg0)
|
||||
obj
|
||||
)
|
||||
(when (zero? obj)
|
||||
(let ((t9-1 (the-as (function object object) enter-state))
|
||||
(a0-1 "memory")
|
||||
)
|
||||
(set! (-> self next-state) process-drawable-art-error)
|
||||
(t9-1 a0-1)
|
||||
)
|
||||
(return (the-as align-control 0))
|
||||
)
|
||||
(set! (-> obj process) arg0)
|
||||
obj
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -59,23 +59,22 @@
|
||||
;; definition for function camera-teleport-to-entity
|
||||
;; INFO: Return type mismatch int vs none.
|
||||
;; Used lq/sq
|
||||
(defun camera-teleport-to-entity ((arg0 entity-actor))
|
||||
(with-pp (let ((gp-0 (new 'stack 'transformq)))
|
||||
(set!
|
||||
(-> gp-0 trans quad)
|
||||
(-> (the-as transform (-> arg0 extra)) scale quad)
|
||||
)
|
||||
(quaternion-copy! (-> gp-0 quat) (-> arg0 quat))
|
||||
(vector-identity! (-> gp-0 scale))
|
||||
(let ((a1-2 (new 'stack-no-clear 'event-message-block)))
|
||||
(set! (-> a1-2 from) pp)
|
||||
(set! (-> a1-2 num-params) 1)
|
||||
(set! (-> a1-2 message) 'teleport-to-transformq)
|
||||
(set! (-> a1-2 param 0) (the-as uint gp-0))
|
||||
(send-event-function *camera* a1-2)
|
||||
)
|
||||
)
|
||||
0
|
||||
(none)
|
||||
(defbehavior camera-teleport-to-entity process ((arg0 entity-actor))
|
||||
(let ((gp-0 (new 'stack 'transformq)))
|
||||
(set!
|
||||
(-> gp-0 trans quad)
|
||||
(-> (the-as transform (-> arg0 extra)) scale quad)
|
||||
)
|
||||
(quaternion-copy! (-> gp-0 quat) (-> arg0 quat))
|
||||
(vector-identity! (-> gp-0 scale))
|
||||
(let ((a1-2 (new 'stack-no-clear 'event-message-block)))
|
||||
(set! (-> a1-2 from) self)
|
||||
(set! (-> a1-2 num-params) 1)
|
||||
(set! (-> a1-2 message) 'teleport-to-transformq)
|
||||
(set! (-> a1-2 param 0) (the-as uint gp-0))
|
||||
(send-event-function *camera* a1-2)
|
||||
)
|
||||
)
|
||||
0
|
||||
(none)
|
||||
)
|
||||
|
||||
@@ -197,93 +197,92 @@
|
||||
)
|
||||
|
||||
;; definition for function remove-exit
|
||||
(defun remove-exit ()
|
||||
(with-pp (when (-> pp stack-frame-top)
|
||||
(let ((v0-0 (-> pp stack-frame-top next)))
|
||||
(set! (-> pp stack-frame-top) v0-0)
|
||||
v0-0
|
||||
)
|
||||
)
|
||||
(defbehavior remove-exit process ()
|
||||
(when (-> self stack-frame-top)
|
||||
(let ((v0-0 (-> self stack-frame-top next)))
|
||||
(set! (-> self stack-frame-top) v0-0)
|
||||
v0-0
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; definition (debug) for function stream<-process-mask
|
||||
;; INFO: Return type mismatch int vs object.
|
||||
(defun-debug stream<-process-mask ((arg0 object) (arg1 int))
|
||||
(defun-debug stream<-process-mask ((arg0 object) (arg1 process-mask))
|
||||
(let ((s4-0 arg1))
|
||||
(if (= (logand #x1000000 s4-0) #x1000000)
|
||||
(if (= (logand #x1000000 (the-as int s4-0)) (process-mask death))
|
||||
(format arg0 "death ")
|
||||
)
|
||||
(if (= (logand #x800000 s4-0) #x800000)
|
||||
(if (= (logand #x800000 (the-as int s4-0)) (process-mask attackable))
|
||||
(format arg0 "attackable ")
|
||||
)
|
||||
(if (= (logand #x400000 s4-0) #x400000)
|
||||
(if (= (logand #x400000 (the-as int s4-0)) (process-mask projectile))
|
||||
(format arg0 "projectile ")
|
||||
)
|
||||
(if (= (logand #x200000 s4-0) #x200000)
|
||||
(if (= (logand #x200000 (the-as int s4-0)) (process-mask entity))
|
||||
(format arg0 "entity ")
|
||||
)
|
||||
(if (= (logand #x100000 s4-0) #x100000)
|
||||
(if (= (logand #x100000 (the-as int s4-0)) (process-mask ambient))
|
||||
(format arg0 "ambient ")
|
||||
)
|
||||
(if (= (logand #x80000 s4-0) #x80000)
|
||||
(if (= (logand #x80000 (the-as int s4-0)) (process-mask platform))
|
||||
(format arg0 "platform ")
|
||||
)
|
||||
(if (= (logand #x40000 s4-0) #x40000)
|
||||
(if (= (logand #x40000 (the-as int s4-0)) (process-mask camera))
|
||||
(format arg0 "camera ")
|
||||
)
|
||||
(if (= (logand #x20000 s4-0) #x20000)
|
||||
(if (= (logand #x20000 (the-as int s4-0)) (process-mask enemy))
|
||||
(format arg0 "enemy ")
|
||||
)
|
||||
(if (= (logand #x10000 s4-0) #x10000)
|
||||
(if (= (logand #x10000 (the-as int s4-0)) (process-mask collectable))
|
||||
(format arg0 "collectable ")
|
||||
)
|
||||
(if (= (logand s4-0 #x8000) #x8000)
|
||||
(if (= (logand s4-0 (process-mask crate)) (process-mask crate))
|
||||
(format arg0 "crate ")
|
||||
)
|
||||
(if (= (logand s4-0 #x4000) #x4000)
|
||||
(if (= (logand s4-0 (process-mask sidekick)) (process-mask sidekick))
|
||||
(format arg0 "sidekick ")
|
||||
)
|
||||
(if (= (logand s4-0 8192) 8192)
|
||||
(if (= (logand s4-0 (process-mask target)) (process-mask target))
|
||||
(format arg0 "target ")
|
||||
)
|
||||
(if (= (logand s4-0 4096) 4096)
|
||||
(if
|
||||
(= (logand s4-0 (process-mask movie-subject)) (process-mask movie-subject))
|
||||
(format arg0 "movie-subject ")
|
||||
)
|
||||
(if (= (logand s4-0 2048) 2048)
|
||||
(if (= (logand s4-0 (process-mask movie)) (process-mask movie))
|
||||
(format arg0 "movie ")
|
||||
)
|
||||
(if (= (logand s4-0 1024) 1024)
|
||||
(if (= (logand s4-0 (process-mask going)) (process-mask going))
|
||||
(format arg0 "going ")
|
||||
)
|
||||
(if (= (logand s4-0 512) 512)
|
||||
(if (= (logand s4-0 (process-mask heap-shrunk)) (process-mask heap-shrunk))
|
||||
(format arg0 "heap-shrunk ")
|
||||
)
|
||||
(if (= (logand s4-0 256) 256)
|
||||
(if (= (logand s4-0 (process-mask process-tree)) (process-mask process-tree))
|
||||
(format arg0 "process-tree ")
|
||||
)
|
||||
(if (= (logand s4-0 128) 128)
|
||||
(if (= (logand s4-0 (process-mask sleep-code)) (process-mask sleep-code))
|
||||
(format arg0 "sleep-code ")
|
||||
)
|
||||
(if (= (logand s4-0 64) 64)
|
||||
(if (= (logand s4-0 (process-mask sleep)) (process-mask sleep))
|
||||
(format arg0 "sleep ")
|
||||
)
|
||||
(if (= (logand s4-0 32) 32)
|
||||
(if (= (logand s4-0 (process-mask actor-pause)) (process-mask actor-pause))
|
||||
(format arg0 "actor-pause ")
|
||||
)
|
||||
(if (= (logand s4-0 16) 16)
|
||||
(if (= (logand s4-0 (process-mask progress)) (process-mask progress))
|
||||
(format arg0 "progress ")
|
||||
)
|
||||
(if (= (logand s4-0 8) 8)
|
||||
(if (= (logand s4-0 (process-mask menu)) (process-mask menu))
|
||||
(format arg0 "menu ")
|
||||
)
|
||||
(if (= (logand s4-0 4) 4)
|
||||
(if (= (logand s4-0 (process-mask pause)) (process-mask pause))
|
||||
(format arg0 "pause ")
|
||||
)
|
||||
(if (= (logand s4-0 2) 2)
|
||||
(if (= (logand s4-0 (process-mask draw)) (process-mask draw))
|
||||
(format arg0 "draw ")
|
||||
)
|
||||
(if (= (logand s4-0 1) 1)
|
||||
(if (= (logand s4-0 (process-mask execute)) (process-mask execute))
|
||||
(format arg0 "execute ")
|
||||
)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
(deftype behavior-test-type (basic)
|
||||
((foo int))
|
||||
(:methods
|
||||
(test (_type_ basic) int :behavior behavior-test-type)
|
||||
)
|
||||
)
|
||||
|
||||
(defmethod test behavior-test-type ((obj behavior-test-type) (arg0 basic))
|
||||
(format #t "method obj: ~D self: ~D~%" (-> obj foo) (-> self foo))
|
||||
0
|
||||
)
|
||||
|
||||
(defbehavior test-bfunc behavior-test-type ((arg0 basic))
|
||||
(format #t "function self: ~D~%" (-> self foo))
|
||||
)
|
||||
|
||||
|
||||
(let ((obj1 (new 'static 'behavior-test-type :foo 123))
|
||||
(obj2 (new 'static 'behavior-test-type :foo 456)))
|
||||
(with-pp
|
||||
(protect (pp)
|
||||
(set! pp (the process obj1))
|
||||
(test-bfunc #f)
|
||||
(test obj2 #f)
|
||||
)
|
||||
)
|
||||
)
|
||||
0
|
||||
@@ -780,6 +780,12 @@ TEST_F(WithGameTests, MethodReplace) {
|
||||
{"relocate! foo: 123 heap: 1 name: 2\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, Behaviors) {
|
||||
runner.run_static_test(env, testCategory, "test-behaviors.gc",
|
||||
{"function self: 123\n"
|
||||
"method obj: 456 self: 123\n0\n"});
|
||||
}
|
||||
|
||||
TEST(TypeConsistency, TypeConsistency) {
|
||||
Compiler compiler;
|
||||
compiler.enable_throw_on_redefines();
|
||||
|
||||
+207
-1
@@ -1,6 +1,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/Trie.h"
|
||||
@@ -12,6 +13,7 @@
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "common/util/print_float.h"
|
||||
#include "common/util/CopyOnWrite.h"
|
||||
#include "common/util/SmallVector.h"
|
||||
|
||||
TEST(CommonUtil, get_file_path) {
|
||||
std::vector<std::string> test = {"cabbage", "banana", "apple"};
|
||||
@@ -182,4 +184,208 @@ TEST(CommonUtil, CopyOnWrite) {
|
||||
EXPECT_EQ(*x, 3);
|
||||
EXPECT_EQ(*y, 3);
|
||||
EXPECT_EQ(*z, 15);
|
||||
}
|
||||
}
|
||||
|
||||
namespace cu {
|
||||
namespace test {
|
||||
|
||||
class ThrowOnDefaultConstruct {
|
||||
public:
|
||||
ThrowOnDefaultConstruct() {
|
||||
throw std::runtime_error("ThrowOnDefaultConstruct was default constructed.");
|
||||
}
|
||||
};
|
||||
|
||||
class ThrowOnDestruct {
|
||||
public:
|
||||
~ThrowOnDestruct() {
|
||||
// not a good idea to throw.
|
||||
exit(-1);
|
||||
}
|
||||
};
|
||||
|
||||
struct RuleOfFiveExample {
|
||||
// if we fail to call the destructor we'll leak memory, which will get caught with valgrind.
|
||||
RuleOfFiveExample() { mem = new int; }
|
||||
RuleOfFiveExample(const RuleOfFiveExample& other) {
|
||||
if (&other != this) {
|
||||
mem = new int;
|
||||
}
|
||||
}
|
||||
RuleOfFiveExample(RuleOfFiveExample&& other) noexcept {
|
||||
if (&other != this) {
|
||||
mem = other.mem;
|
||||
other.mem = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RuleOfFiveExample& operator=(const RuleOfFiveExample& other) {
|
||||
if (&other != this) {
|
||||
delete mem;
|
||||
mem = new int;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
RuleOfFiveExample& operator=(RuleOfFiveExample&& other) noexcept {
|
||||
if (&other != this) {
|
||||
mem = other.mem;
|
||||
other.mem = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~RuleOfFiveExample() { delete mem; }
|
||||
int value = 12;
|
||||
int* mem;
|
||||
};
|
||||
|
||||
TEST(SmallVector, NoConstruction) {
|
||||
// Confirm that an empty vector constructs nothing.
|
||||
SmallVector<ThrowOnDefaultConstruct, 128> empty;
|
||||
EXPECT_EQ(empty.size(), 0);
|
||||
EXPECT_TRUE(empty.empty());
|
||||
|
||||
// should also destroy nothing
|
||||
SmallVector<ThrowOnDestruct> empty2;
|
||||
}
|
||||
|
||||
TEST(SmallVector, ConstructWithSize) {
|
||||
// Test construction calls default constructors.
|
||||
SmallVector<RuleOfFiveExample, 1> heap_no_stack(12);
|
||||
SmallVector<RuleOfFiveExample, 12> full_stack(12);
|
||||
SmallVector<RuleOfFiveExample, 12> not_full_stack(11);
|
||||
SmallVector<RuleOfFiveExample, 12> overflow_to_heap(13);
|
||||
|
||||
// size
|
||||
EXPECT_EQ(heap_no_stack.size(), 12);
|
||||
EXPECT_EQ(full_stack.size(), 12);
|
||||
EXPECT_EQ(not_full_stack.size(), 11);
|
||||
EXPECT_EQ(overflow_to_heap.size(), 13);
|
||||
|
||||
// capacity
|
||||
EXPECT_EQ(heap_no_stack.capacity(), 12);
|
||||
EXPECT_EQ(full_stack.capacity(), 12);
|
||||
EXPECT_EQ(not_full_stack.capacity(), 12);
|
||||
EXPECT_EQ(overflow_to_heap.capacity(), 13);
|
||||
|
||||
// were they constructed?
|
||||
int i = 0;
|
||||
for (auto& obj : heap_no_stack) {
|
||||
EXPECT_EQ(obj.value, 12);
|
||||
i++;
|
||||
}
|
||||
EXPECT_EQ(i, 12);
|
||||
|
||||
for (auto vec : {full_stack, not_full_stack, overflow_to_heap}) {
|
||||
int j = 0;
|
||||
for (auto& obj : heap_no_stack) {
|
||||
EXPECT_EQ(obj.value, 12);
|
||||
j++;
|
||||
}
|
||||
EXPECT_EQ(j, 12);
|
||||
}
|
||||
}
|
||||
|
||||
// small std::string's aren't heap allocated.
|
||||
constexpr const char* long_string_1 = "this-is-a-string-thats-long-enough-to-go-on-the-heap!";
|
||||
constexpr const char* long_string_2 = "another-string-thats-long-enough-to-go-on-the-heap!";
|
||||
constexpr const char* long_string_3 = "also-long-enough-to-go-on-the-heap!";
|
||||
|
||||
TEST(SmallVector, ConstructByCopying) {
|
||||
// test that we copy the input properly.
|
||||
SmallVector<std::string> strings(20, long_string_1);
|
||||
EXPECT_EQ(strings[0], long_string_1);
|
||||
strings[0] = long_string_2;
|
||||
EXPECT_EQ(strings[1], long_string_1);
|
||||
EXPECT_EQ(strings.size(), 20);
|
||||
}
|
||||
|
||||
TEST(SmallVector, ConstructFromIterator) {
|
||||
std::unordered_set<std::string> stuff;
|
||||
for (auto x : Range(10, 20)) {
|
||||
stuff.insert(long_string_1 + std::to_string(x));
|
||||
}
|
||||
|
||||
// iterators into unordered set can't be subtracted, but this should still work.
|
||||
SmallVector<std::string> strings(stuff.begin(), stuff.end());
|
||||
EXPECT_EQ(strings.size(), 10);
|
||||
std::unordered_set<std::string> stuff2(strings.begin(), strings.end());
|
||||
EXPECT_EQ(stuff, stuff2);
|
||||
|
||||
// these can be subtracted.
|
||||
SmallVector<std::string> strings2(strings.begin(), strings.end());
|
||||
EXPECT_EQ(strings, strings2);
|
||||
EXPECT_EQ(strings.at(1), strings2.at(1));
|
||||
strings.at(1) = long_string_2;
|
||||
EXPECT_TRUE(strings.at(1) != strings2.at(1));
|
||||
}
|
||||
|
||||
TEST(SmallVector, ConstructFromCopy) {
|
||||
SmallVector<std::string> one = {long_string_1, long_string_2, long_string_3};
|
||||
SmallVector<std::string> two(one);
|
||||
EXPECT_EQ(two.at(2), long_string_3);
|
||||
two.at(2) = "four";
|
||||
EXPECT_EQ(one.at(2), long_string_3);
|
||||
}
|
||||
|
||||
TEST(SmallVector, ConstructFromMoveInline) {
|
||||
// stack move
|
||||
SmallVector<std::string, 20> one = {long_string_1, long_string_2, long_string_3};
|
||||
SmallVector<std::string, 20> two(std::move(one));
|
||||
EXPECT_TRUE(one.empty()); // this is the convention of SmallVector.
|
||||
EXPECT_EQ(two.at(2), long_string_3);
|
||||
}
|
||||
|
||||
TEST(SmallVector, ConstructFromMoveHeap) {
|
||||
// heap move
|
||||
SmallVector<std::string, 1> one = {long_string_1, long_string_2, long_string_3};
|
||||
SmallVector<std::string, 1> two(std::move(one));
|
||||
EXPECT_TRUE(one.empty()); // this is the convention of SmallVector.
|
||||
EXPECT_EQ(two.at(2), long_string_3);
|
||||
EXPECT_EQ(two.size(), 3);
|
||||
}
|
||||
|
||||
TEST(SmallVector, ConstructFromInitList) {
|
||||
SmallVector<std::string, 1> one({long_string_1, long_string_2, long_string_3});
|
||||
EXPECT_EQ(one.at(2), long_string_3);
|
||||
EXPECT_EQ(one.size(), 3);
|
||||
}
|
||||
|
||||
/*
|
||||
TEST(SmallVector, SelfCopyAndMoveAssignment) {
|
||||
SmallVector<std::string, 0> one({long_string_1, long_string_2, long_string_3});
|
||||
one = one;
|
||||
one = std::move(one);
|
||||
EXPECT_EQ(one.at(2), long_string_3);
|
||||
EXPECT_EQ(one.size(), 3);
|
||||
}
|
||||
*/
|
||||
|
||||
TEST(SmallVector, CopyAssign) {
|
||||
// heap -> heap
|
||||
SmallVector<std::string, 1> heap_one({long_string_1, long_string_2, long_string_3}),
|
||||
heap_two({"a", "b"});
|
||||
heap_one = heap_two;
|
||||
EXPECT_TRUE(heap_one.size() == 2);
|
||||
EXPECT_TRUE(heap_one[0] == "a");
|
||||
EXPECT_TRUE(heap_one[1] == "b");
|
||||
}
|
||||
|
||||
TEST(SmallVector, Construction) {
|
||||
SmallVector<ThrowOnDefaultConstruct, 128> empty;
|
||||
EXPECT_EQ(empty.size(), 0);
|
||||
EXPECT_TRUE(empty.empty());
|
||||
empty.reserve(256);
|
||||
EXPECT_EQ(empty.size(), 0);
|
||||
EXPECT_TRUE(empty.empty());
|
||||
empty.shrink_to_fit();
|
||||
EXPECT_EQ(empty.capacity(), 128);
|
||||
|
||||
SmallVector<int, 2> one(1);
|
||||
EXPECT_EQ(one.size(), 1);
|
||||
EXPECT_FALSE(one.empty());
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace cu
|
||||
Reference in New Issue
Block a user