Add deftype (#48)

* initial deftype implementation

* fix library setup for windows

* implement deftype

* fix memory bug

* fix formatting
This commit is contained in:
water111
2020-09-17 21:47:52 -04:00
committed by GitHub
parent 8d322dd7fc
commit abcd444a3b
38 changed files with 601 additions and 32 deletions
+3
View File
@@ -52,6 +52,9 @@ include_directories(./)
# build asset packer/unpacker
add_subdirectory(asset_tool)
# build goos
add_subdirectory(common/goos)
# build type_system library for compiler/decompiler
add_subdirectory(common/type_system)
+2 -2
View File
@@ -19,8 +19,8 @@
#include <utility>
#include <unordered_map>
#include "goalc/goos/Object.h"
#include "goalc/goos/TextDB.h"
#include "common/goos/Object.h"
#include "common/goos/TextDB.h"
namespace goos {
+1 -1
View File
@@ -22,7 +22,7 @@
#include <unordered_map>
#include <memory>
#include "goalc/goos/Object.h"
#include "common/goos/Object.h"
namespace goos {
/*!
+3 -2
View File
@@ -2,6 +2,7 @@ add_library(type_system
SHARED
TypeSystem.cpp
Type.cpp
TypeSpec.cpp)
TypeSpec.cpp
deftype.cpp)
target_link_libraries(type_system fmt)
target_link_libraries(type_system fmt goos)
+1 -1
View File
@@ -467,7 +467,7 @@ void StructureType::inherit(StructureType* parent) {
}
bool StructureType::operator==(const Type& other) const {
if (typeid(StructureType) != typeid(other)) {
if (typeid(*this) != typeid(other)) {
return false;
}
+1 -1
View File
@@ -215,7 +215,7 @@ class StructureType : public ReferenceType {
bool pack = false);
std::string print() const override;
void inherit(StructureType* parent);
const std::vector<Field>& fields() { return m_fields; }
const std::vector<Field>& fields() const { return m_fields; }
bool operator==(const Type& other) const override;
int get_size_in_memory() const override;
int get_offset() const override;
+1
View File
@@ -591,6 +591,7 @@ void TypeSystem::add_builtin_types() {
// TYPE
builtin_structure_inherit(type_type);
add_method(type_type, "new", make_function_typespec({"symbol", "type", "int"}, "_type_"));
add_field_to_type(type_type, "symbol", make_typespec("symbol"));
add_field_to_type(type_type, "parent", make_typespec("type"));
add_field_to_type(type_type, "allocated-size", make_typespec("uint16")); // todo, u16 or s16?
+1 -1
View File
@@ -76,6 +76,7 @@ class TypeSystem {
bool print_on_error = true,
bool throw_on_error = true) const;
std::vector<std::string> get_path_up_tree(const std::string& type);
int get_next_method_id(Type* type);
/*!
* Get a type by name and cast to a child class of Type*. Must succeed.
@@ -99,7 +100,6 @@ class TypeSystem {
int get_size_in_type(const Field& field);
int get_alignment_in_type(const Field& field);
Field lookup_field(const std::string& type_name, const std::string& field_name);
int get_next_method_id(Type* type);
StructureType* add_builtin_structure(const std::string& parent,
const std::string& type_name,
bool boxed = false);
+325
View File
@@ -0,0 +1,325 @@
#include "deftype.h"
#include "third-party/fmt/core.h"
/*!
* Missing Features
* - Bitfields
* - Int128 children
* - Refer to yourself (structure/basic only)
* - Method List
* - Evaluate constants.
*
*/
namespace {
const goos::Object& car(const goos::Object* x) {
if (!x->is_pair()) {
throw std::runtime_error("invalid deftype form");
}
return x->as_pair()->car;
}
const goos::Object* cdr(const goos::Object* x) {
if (!x->is_pair()) {
throw std::runtime_error("invalid deftype form");
}
return &x->as_pair()->cdr;
}
std::string deftype_parent_list(const goos::Object& list) {
if (!list.is_pair()) {
throw std::runtime_error("invalid parent list in deftype: " + list.print());
}
auto parent = list.as_pair()->car;
auto rest = list.as_pair()->cdr;
if (!rest.is_empty_list()) {
throw std::runtime_error("invalid parent list in deftype - can only have one parent");
}
if (!parent.is_symbol()) {
throw std::runtime_error("invalid parent in deftype parent list");
}
return parent.as_symbol()->name;
}
bool is_type(const std::string& expected, const TypeSpec& actual, const TypeSystem* ts) {
return ts->typecheck(ts->make_typespec(expected), actual, "", false, false);
}
template <typename T>
void for_each_in_list(const goos::Object& list, T f) {
const goos::Object* iter = &list;
while (iter->is_pair()) {
auto lap = iter->as_pair();
f(lap->car);
iter = &lap->cdr;
}
if (!iter->is_empty_list()) {
throw std::runtime_error("invalid list in for_each_in_list: " + list.print());
}
}
std::string symbol_string(const goos::Object& obj) {
if (obj.is_symbol()) {
return obj.as_symbol()->name;
}
throw std::runtime_error(obj.print() + " was supposed to be a symbol, but isn't");
}
int64_t get_int(const goos::Object& obj) {
if (obj.is_int()) {
return obj.integer_obj.value;
}
throw std::runtime_error(obj.print() + " was supposed to be an integer, but isn't");
}
TypeSpec parse_typespec(TypeSystem* type_system, const goos::Object& src) {
if (src.is_symbol()) {
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);
for_each_in_list(rest,
[&](const goos::Object& o) { ts.add_arg(parse_typespec(type_system, o)); });
return ts;
} else {
throw std::runtime_error("invalid typespec: " + src.print());
}
assert(false);
return {};
}
void add_field(StructureType* structure, TypeSystem* ts, const goos::Object& def) {
auto rest = &def;
auto name = symbol_string(car(rest));
rest = cdr(rest);
auto type = parse_typespec(ts, car(rest));
rest = cdr(rest);
int array_size = -1;
bool is_inline = false;
bool is_dynamic = false;
int offset_override = -1;
int offset_assert = -1;
if (!rest->is_empty_list()) {
if (car(rest).is_int()) {
array_size = car(rest).integer_obj.value;
rest = cdr(rest);
}
while (!rest->is_empty_list()) {
auto opt_name = symbol_string(car(rest));
rest = cdr(rest);
if (opt_name == ":inline") {
is_inline = true;
} else if (opt_name == ":dynamic") {
is_dynamic = true;
} else if (opt_name == ":offset") {
offset_override = get_int(car(rest));
rest = cdr(rest);
} else if (opt_name == ":offset-assert") {
offset_assert = get_int(car(rest));
if (offset_assert == -1) {
throw std::runtime_error("Cannot use -1 as offset-assert");
}
rest = cdr(rest);
} else {
throw std::runtime_error("Invalid option in field specification: " + opt_name);
}
}
}
int actual_offset = ts->add_field_to_type(structure, name, type, is_inline, is_dynamic,
array_size, offset_override);
if (offset_assert != -1 && actual_offset != offset_assert) {
throw std::runtime_error("Field " + name + " was placed at " + std::to_string(actual_offset) +
" but offset-assert was set to " + std::to_string(offset_assert));
}
}
void declare_method(StructureType* type, TypeSystem* type_system, const goos::Object& def) {
for_each_in_list(def, [&](const goos::Object& _obj) {
auto obj = &_obj;
// (name args return-type [id])
auto method_name = symbol_string(car(obj));
obj = cdr(obj);
auto& args = car(obj);
obj = cdr(obj);
auto& return_type = car(obj);
obj = cdr(obj);
int id = -1;
if (!obj->is_empty_list()) {
auto& id_obj = car(obj);
id = get_int(id_obj);
if (!cdr(obj)->is_empty_list()) {
throw std::runtime_error("too many things in method def: " + def.print());
}
}
std::vector<std::string> arg_types;
for_each_in_list(args, [&](const goos::Object& o) {
if (o.is_symbol()) {
arg_types.emplace_back(symbol_string(o));
} else {
auto next = cdr(&o);
arg_types.emplace_back(symbol_string(car(next)));
if (!cdr(next)->is_empty_list()) {
throw std::runtime_error("too many things in method def arg type: " + def.print());
};
}
});
auto info = type_system->add_method(
type, method_name,
type_system->make_function_typespec(arg_types, symbol_string(return_type)));
// check the method assert
if (id != -1) {
// method id assert!
if (id != info.id) {
printf("WARNING - ID assert failed on method %s of type %s (wanted %d got %d)\n",
method_name.c_str(), type->get_name().c_str(), id, info.id);
throw std::runtime_error("Method ID assert failed");
}
}
});
}
TypeFlags parse_structure_def(StructureType* type,
TypeSystem* ts,
const goos::Object& fields,
const goos::Object& options) {
(void)options;
for_each_in_list(fields, [&](const goos::Object& o) { add_field(type, ts, o); });
TypeFlags flags;
flags.heap_base = 0;
flags.size = type->get_size_in_memory();
flags.pad = 0;
auto* rest = &options;
int size_assert = -1;
int method_count_assert = -1;
uint64_t flag_assert = 0;
bool flag_assert_set = false;
while (!rest->is_empty_list()) {
if (car(rest).is_pair()) {
auto opt_list = &car(rest);
auto& first = car(opt_list);
opt_list = cdr(opt_list);
if (symbol_string(first) == ":methods") {
declare_method(type, ts, *opt_list);
} else {
throw std::runtime_error("Invalid option list in field specification: " +
car(rest).print());
}
rest = cdr(rest);
} else {
auto opt_name = symbol_string(car(rest));
rest = cdr(rest);
if (opt_name == ":size-assert") {
size_assert = get_int(car(rest));
if (size_assert == -1) {
throw std::runtime_error("Cannot use -1 as size-assert");
}
rest = cdr(rest);
} else if (opt_name == ":method-count-assert") {
method_count_assert = get_int(car(rest));
if (method_count_assert == -1) {
throw std::runtime_error("Cannot use -1 as method_count_assert");
}
rest = cdr(rest);
} else if (opt_name == ":flag-assert") {
flag_assert = get_int(car(rest));
flag_assert_set = true;
rest = cdr(rest);
} else {
throw std::runtime_error("Invalid option in field specification: " + opt_name);
}
}
}
if (size_assert != -1 && flags.size != u16(size_assert)) {
throw std::runtime_error("Type " + type->get_name() + " came out to size " +
std::to_string(int(flags.size)) + " but size-assert was set to " +
std::to_string(size_assert));
}
flags.methods = ts->get_next_method_id(type);
if (method_count_assert != -1 && flags.methods != u16(method_count_assert)) {
throw std::runtime_error(
"Type " + type->get_name() + " has " + std::to_string(int(flags.methods)) +
" methods, but method-count-assert was set to " + std::to_string(method_count_assert));
}
if (flag_assert_set && (flags.flag != flag_assert)) {
throw std::runtime_error(
fmt::format("Type {} has flag 0x{:x} but flag-assert was set to 0x{:x}", type->get_name(),
flags.flag, flag_assert));
}
return flags;
}
} // namespace
DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts) {
auto iter = &deftype;
auto& type_name_obj = car(iter);
iter = cdr(iter);
auto& parent_list_obj = car(iter);
iter = cdr(iter);
auto& field_list_obj = car(iter);
iter = cdr(iter);
auto& options_obj = *iter;
if (!type_name_obj.is_symbol()) {
throw std::runtime_error("deftype must be given a symbol as the type name");
}
auto name = type_name_obj.as_symbol()->name;
auto parent_type_name = deftype_parent_list(parent_list_obj);
auto parent_type = ts->make_typespec(parent_type_name);
DeftypeResult result;
if (is_type("basic", parent_type, ts)) {
auto new_type = std::make_unique<BasicType>(parent_type_name, name);
auto pto = dynamic_cast<BasicType*>(ts->lookup_type(parent_type));
assert(pto);
new_type->inherit(pto);
result.flags = parse_structure_def(new_type.get(), ts, field_list_obj, options_obj);
ts->add_type(name, std::move(new_type));
} else if (is_type("structure", parent_type, ts)) {
auto new_type = std::make_unique<StructureType>(parent_type_name, name);
auto pto = dynamic_cast<StructureType*>(ts->lookup_type(parent_type));
assert(pto);
new_type->inherit(pto);
result.flags = parse_structure_def(new_type.get(), ts, field_list_obj, options_obj);
ts->add_type(name, std::move(new_type));
} else if (is_type("integer", parent_type, ts)) {
throw std::runtime_error("Creating a child type of integer is not supported yet.");
} else {
throw std::runtime_error("Creating a child type from " + parent_type.print() +
" is not allowed or not supported yet.");
}
result.type = ts->make_typespec(name);
result.type_info = ts->lookup_type(result.type);
return result;
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "TypeSystem.h"
#include "common/goos/Object.h"
struct TypeFlags {
union {
uint64_t flag = 0;
struct {
uint16_t size;
uint16_t heap_base;
uint16_t methods;
uint16_t pad;
};
};
};
struct DeftypeResult {
TypeFlags flags;
TypeSpec type;
Type* type_info = nullptr;
};
DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts);
+5 -4
View File
@@ -22,10 +22,11 @@ std::string file_util::get_project_path() {
0, pos + 11); // + 12 to include "\jak-project" in the returned filepath
#else
// do Linux stuff
char buffer[FILENAME_MAX];
readlink("/proc/self/exe", buffer,
FILENAME_MAX); // /proc/self acts like a "virtual folder" containing information about
// the current process
char buffer[FILENAME_MAX + 1];
auto len = readlink("/proc/self/exe", buffer,
FILENAME_MAX); // /proc/self acts like a "virtual folder" containing
// information about the current process
buffer[len] = '\0';
std::string::size_type pos =
std::string(buffer).rfind("jak-project"); // Strip file path down to /jak-project/ directory
return std::string(buffer).substr(
+42 -4
View File
@@ -100,7 +100,7 @@
(defun mod ((a integer) (b integer))
"Compute mod. It does what you expect for positive numbers. For negative numbers, nobody knows what to expect.
This is a 32-bit operation. It uses an idiv on x86 and gets the remainder."
;; The original implementation is div, mfhi
;; todo - verify this is exactly the same as the PS2.
(mod a b)
@@ -109,7 +109,7 @@
(defun rem ((a integer) (b integer))
"Compute remainder (32-bit). It is identical to mod. It uses a idiv and gets the remainder"
;; The original implementation is div, mfhi
;; todo - verify this is exactly the same as the PS2.
(mod a b)
@@ -206,5 +206,43 @@
;; or 128-bit arguments (unimplemented in C Kernel), but both of these were never finished.
(define format _format)
;; todo bfloat
;; todo pinrt bfloat
;; TODO - vec4s
;; The "boxed float" type bfloat is just a float wrapped in a basic (structure type that has runtime type information)
;; define a type called "bfloat" which is a child of "basic".
;; "basic" is just a type with structures and runtime type information
(deftype bfloat (basic)
;; the field list.
;; there is a single field named data, of type float.
;; the :offset-assert makes sure that OpenGOAL's type layout places the field at the given offset.
;; if not, it creates a compiler error. This is used to make sure we exactly copy the game's memory layout,
;; as we can get the exact offset of fields from the disassembly
((data float :offset-assert 4))
;; declare methods. If you are overriding a parent method, you don't have to declare it, but you can if you want
;; The number after the return type is the method ID, that can be checked against the disassembly to make sure
;; the type and method hierarchy is correct. If OpenGOAL's method table layout doesn't match, it will create
;; compiler error.
(:methods (print (_type_) _type_ 2) ;; we will override print later on
(inspect (_type_) _type_ 3) ;; this is a parent method we won't override. It's fine to put it here anyway.
)
;; Note that the special type "_type_" can be used in methods to indicate "the type of the object method is called on".
;; this is used for 2 things:
;; 1. Child who overrides it can use their own type as an argument, rather than a less specific parent type.
;; 2. Caller who calls an overriden method and knows it at compile time can know a return type more specifically.
;; make sure the size of the type is correct (this is stored in the type structure, so we can check it)
:size-assert 8
;; make sure method count is correct (again, stored in the type structure)
:method-count-assert 9
;; flags passed to the new_type function in the runtime.
:flag-assert #x900000008
)
;; todo print bfloat
+1 -1
View File
@@ -1,4 +1,3 @@
add_subdirectory(goos)
add_subdirectory(listener)
add_library(compiler
@@ -22,6 +21,7 @@ add_library(compiler
compiler/compilation/Define.cpp
compiler/compilation/Function.cpp
compiler/compilation/ControlFlow.cpp
compiler/compilation/Type.cpp
compiler/Util.cpp
logger/Logger.cpp
regalloc/IRegister.cpp
+1 -1
View File
@@ -200,7 +200,7 @@ std::vector<std::string> Compiler::run_test(const std::string& source_code) {
return m_listener.stop_recording_messages();
} catch (std::exception& e) {
fmt::print("[Compiler] Failed to compile test program {}: {}\n", source_code, e.what());
return {};
throw e;
}
}
+8 -1
View File
@@ -6,7 +6,7 @@
#include "common/type_system/TypeSystem.h"
#include "Env.h"
#include "goalc/listener/Listener.h"
#include "goalc/goos/Interpreter.h"
#include "common/goos/Interpreter.h"
#include "goalc/compiler/IR.h"
#include "CompilerSettings.h"
@@ -105,6 +105,10 @@ class Compiler {
Val* compile_variable_shift(const RegVal* in, const RegVal* sa, Env* env, IntegerMathKind kind);
RegVal* compile_get_method_of_type(const TypeSpec& type,
const std::string& method_name,
Env* env);
public:
// Atoms
@@ -163,6 +167,9 @@ class Compiler {
Val* compile_lambda(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_inline(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_declare(const goos::Object& form, const goos::Object& rest, Env* env);
// Type
Val* compile_deftype(const goos::Object& form, const goos::Object& rest, Env* env);
};
#endif // JAK_COMPILER_H
+1 -1
View File
@@ -5,7 +5,7 @@
#include <unordered_map>
#include <string>
#include "goalc/goos/Object.h"
#include "common/goos/Object.h"
class CompilerSettings {
public:
+1 -1
View File
@@ -14,7 +14,7 @@
#include <vector>
#include "common/type_system/TypeSpec.h"
#include "goalc/regalloc/allocate.h"
#include "goalc/goos/Object.h"
#include "common/goos/Object.h"
#include "StaticObject.h"
#include "Label.h"
#include "Val.h"
+34
View File
@@ -665,4 +665,38 @@ void IR_ConditionalBranch::do_codegen(emitter::ObjectGenerator* gen,
auto jump_rec = gen->add_instr(jump_instr, irec);
gen->link_instruction_jump(jump_rec, gen->get_future_ir_record_in_same_func(irec, label.idx));
}
/////////////////////
// LoadConstantOffset
/////////////////////
IR_LoadConstOffset::IR_LoadConstOffset(const RegVal* dest,
int offset,
const RegVal* base,
MemLoadInfo info)
: m_dest(dest), m_offset(offset), m_base(base), m_info(info) {}
std::string IR_LoadConstOffset::print() {
return fmt::format("mov {}, [{} + {}]\n", m_dest->print(), m_base->print(), m_offset);
}
RegAllocInstr IR_LoadConstOffset::to_rai() {
RegAllocInstr rai;
rai.write.push_back(m_dest->ireg());
rai.read.push_back(m_base->ireg());
return rai;
}
void IR_LoadConstOffset::do_codegen(emitter::ObjectGenerator* gen,
const AllocationResult& allocs,
emitter::IR_Record irec) {
if (m_dest->ireg().kind == emitter::RegKind::GPR) {
gen->add_instr(IGen::load_goal_gpr(get_reg(m_dest, allocs, irec), get_reg(m_base, allocs, irec),
emitter::gRegInfo.get_offset_reg(), m_offset, m_info.size,
m_info.sign_extend),
irec);
} else {
throw std::runtime_error("IR_LoadConstOffset::do_codegen xmm not supported");
}
}
+16
View File
@@ -270,4 +270,20 @@ class IR_ConditionalBranch : public IR {
bool m_resolved = false;
};
class IR_LoadConstOffset : public IR {
public:
IR_LoadConstOffset(const RegVal* dest, int offset, const RegVal* base, MemLoadInfo info);
std::string print() override;
RegAllocInstr to_rai() override;
void do_codegen(emitter::ObjectGenerator* gen,
const AllocationResult& allocs,
emitter::IR_Record irec) override;
private:
const RegVal* m_dest = nullptr;
int m_offset = 0;
const RegVal* m_base = nullptr;
MemLoadInfo m_info;
};
#endif // JAK_IR_H
+1 -1
View File
@@ -3,7 +3,7 @@
#ifndef JAK_LAMBDA_H
#define JAK_LAMBDA_H
#include "goalc/goos/Object.h"
#include "common/goos/Object.h"
// note - we cannot easily reuse the GOOS argument system because GOAL's is slightly different.
// there's no rest or keyword support.
+6
View File
@@ -91,4 +91,10 @@ RegVal* FloatConstantVal::to_reg(Env* fe) {
auto re = fe->make_xmm(m_ts);
fe->emit(std::make_unique<IR_StaticVarLoad>(re, m_value));
return re;
}
RegVal* MemoryOffsetConstantVal::to_reg(Env* fe) {
auto re = fe->make_gpr(deref_type);
fe->emit(std::make_unique<IR_LoadConstOffset>(re, offset, base, info));
return re;
}
+27
View File
@@ -125,6 +125,33 @@ class StaticVal : public Val {
RegVal* to_reg(Env* fe) override;
};
struct MemLoadInfo {
bool sign_extend = false;
int size = -1;
};
class MemoryOffsetConstantVal : public Val {
public:
MemoryOffsetConstantVal(TypeSpec ts,
RegVal* _base,
int _offset,
MemLoadInfo _info,
TypeSpec _deref_type)
: Val(std::move(ts)),
base(_base),
offset(_offset),
info(_info),
deref_type(std::move(_deref_type)) {}
std::string print() const override {
return "(" + base->print() + " + " + std::to_string(offset) + ")";
}
RegVal* to_reg(Env* fe) override;
RegVal* base = nullptr;
int offset = 0;
MemLoadInfo info;
TypeSpec deref_type;
};
// MemOffConstant
// MemOffVar
// MemDeref
+2 -7
View File
@@ -55,13 +55,9 @@ static const std::unordered_map<
// {"defun-extern", &Compiler::compile_defun_extern},
// {"declare-method", &Compiler::compile_declare_method},
//
// // DEFTYPE
// {"deftype", &Compiler::compile_deftype},
//
// // ENUM
// TYPE
{"deftype", &Compiler::compile_deftype},
// {"defenum", &Compiler::compile_defenum},
//
// // Field Access
// {"->", &Compiler::compile_deref},
// {"&", &Compiler::compile_addr_of},
//
@@ -72,7 +68,6 @@ static const std::unordered_map<
{"inline", &Compiler::compile_inline},
// {"with-inline", &Compiler::compile_with_inline},
// {"rlet", &Compiler::compile_rlet},
// {"get-ra-ptr", &Compiler::compile_get_ra_ptr},
//
//
+2 -1
View File
@@ -341,7 +341,8 @@ Val* Compiler::compile_real_function_call(const goos::Object& form,
printf("got type %s\n", function->type().print().c_str());
throw_compile_error(form, "invalid number of arguments to function call: got " +
std::to_string(args.size()) + " and expected " +
std::to_string(function->type().arg_count() - 1));
std::to_string(function->type().arg_count() - 1) + " for " +
function->type().print());
}
for (uint32_t i = 0; i < args.size(); i++) {
typecheck(form, function->type().get_arg(i), args.at(i)->type(), "function argument");
+40
View File
@@ -0,0 +1,40 @@
#include "goalc/compiler/Compiler.h"
#include "common/type_system/deftype.h"
RegVal* Compiler::compile_get_method_of_type(const TypeSpec& type,
const std::string& method_name,
Env* env) {
auto info = m_ts.lookup_method(type.base_type(), method_name);
auto offset_of_method = 16 + 4 * info.id; // todo, something more flexible?
auto fe = get_parent_env_of_type<FunctionEnv>(env);
auto typ = compile_get_symbol_value(type.base_type(), env)->to_gpr(env);
MemLoadInfo load_info;
load_info.sign_extend = false;
load_info.size = POINTER_SIZE;
return fe
->alloc_val<MemoryOffsetConstantVal>(typ->type(), typ, offset_of_method, load_info, info.type)
->to_reg(env);
}
Val* Compiler::compile_deftype(const goos::Object& form, const goos::Object& rest, Env* env) {
(void)form;
(void)env;
auto result = parse_deftype(rest, &m_ts);
auto kv = m_symbol_types.find(result.type.base_type());
if (kv != m_symbol_types.end() && kv->second.base_type() != "type") {
fmt::print("[Warning] deftype will redefined {} from {} to a type.\n", result.type.base_type(),
kv->second.print());
}
m_symbol_types[result.type.base_type()] = m_ts.make_typespec("type");
auto new_type_method = compile_get_method_of_type(m_ts.make_typespec("type"), "new", env);
auto new_type_symbol = compile_get_sym_obj(result.type.base_type(), env)->to_gpr(env);
auto parent_type = compile_get_symbol_value(result.type_info->get_parent(), env)->to_gpr(env);
auto flags_int = compile_integer(result.flags.flag, env)->to_gpr(env);
return compile_real_function_call(form, new_type_method,
{new_type_symbol, parent_type, flags_int}, env);
}
+1
View File
@@ -14,6 +14,7 @@ add_executable(goalc-test
test_emitter_integer_math.cpp
test_common_util.cpp
test_compiler_and_runtime.cpp
test_deftype.cpp
)
enable_testing()
+48
View File
@@ -0,0 +1,48 @@
#include "gtest/gtest.h"
#include "common/type_system/TypeSystem.h"
#include "common/type_system/deftype.h"
#include "common/goos/Reader.h"
#include "third-party/fmt/core.h"
TEST(Deftype, deftype) {
TypeSystem ts;
ts.add_builtin_types();
std::string input =
"(deftype my-type (basic) ((f1 int64) (f2 string) (f3 int8) (f4 type :inline)))";
goos::Reader reader;
auto in = reader.read_from_string(input).as_pair()->cdr.as_pair()->car.as_pair()->cdr;
auto result = parse_deftype(in, &ts);
auto& f = dynamic_cast<StructureType*>(ts.lookup_type(result.type))->fields();
EXPECT_EQ(f.size(), 5);
auto& tf = f.at(0);
EXPECT_EQ(tf.name(), "type");
EXPECT_EQ(tf.offset(), 0);
EXPECT_EQ(tf.type().print(), "type");
EXPECT_EQ(tf.is_inline(), false);
auto& f1 = f.at(1);
EXPECT_EQ(f1.name(), "f1");
EXPECT_EQ(f1.offset(), 8);
EXPECT_EQ(f1.type().print(), "int64");
EXPECT_EQ(f1.is_inline(), false);
auto& f2 = f.at(2);
EXPECT_EQ(f2.name(), "f2");
EXPECT_EQ(f2.offset(), 16);
EXPECT_EQ(f2.type().print(), "string");
EXPECT_EQ(f2.is_inline(), false);
auto& f3 = f.at(3);
EXPECT_EQ(f3.name(), "f3");
EXPECT_EQ(f3.offset(), 20);
EXPECT_EQ(f3.type().print(), "int8");
EXPECT_EQ(f3.is_inline(), false);
auto& f4 = f.at(4);
EXPECT_EQ(f4.name(), "f4");
EXPECT_EQ(f4.offset(), 32);
EXPECT_EQ(f4.type().print(), "type");
EXPECT_EQ(f4.is_inline(), true);
}
+1 -1
View File
@@ -4,7 +4,7 @@
*/
#include "gtest/gtest.h"
#include "goalc/goos/Interpreter.h"
#include "common/goos/Interpreter.h"
using namespace goos;
+1 -1
View File
@@ -5,7 +5,7 @@
*/
#include "gtest/gtest.h"
#include "goalc/goos/Reader.h"
#include "common/goos/Reader.h"
#include "common/util/FileUtil.h"
using namespace goos;
+1
View File
@@ -315,4 +315,5 @@ TEST(TypeSystem, lca) {
.print(),
"(pointer object)");
}
// TODO - a big test to make sure all the builtin types are what we expect.