Skip to content

Commit

Permalink
feat: type checked simple enums (compiles to integers)
Browse files Browse the repository at this point in the history
  • Loading branch information
JaDogg committed May 27, 2024
1 parent a179285 commit eaadb8b
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 26 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ cmake-*
./build
**/.vscode
**/.run
_.*.yaka
41 changes: 41 additions & 0 deletions compiler/src/compiler/to_c_compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,11 @@ void to_c_compiler::visit_variable_expr(variable_expr *obj) {
b.object_type_ = object_type::CLASS;
push(obj->name_->token_, b);
return;
} else if (defs_classes_.has_enum(obj->name_->token_)) {
auto b = yk_object(dt_pool_);
b.object_type_ = object_type::ENUM;
push(obj->name_->token_, b);
return;
}
auto object = scope_.get(name);
if (object.desugar_rewrite_needed_) {
Expand Down Expand Up @@ -1351,11 +1356,14 @@ std::string to_c_compiler::convert_dt(yk_datatype *basic_dt,
auto module = cf_->get_or_null(basic_dt->module_);
auto imported_module_prefix = module->prefix_;
auto class_info = module->data_->dsv_->get_class(dt);
auto enum_info = module->data_->dsv_->get_enum(dt);
if (class_info != nullptr) {
auto class_name = prefix(dt, imported_module_prefix);
if (class_info->annotations_.native_define_) { return class_name; }
if (class_info->annotations_.on_stack_) { return "struct " + class_name; }
return "struct " + class_name + "*";
} else if (enum_info != nullptr) {
return "int32_t";// Just an integer
}
}
error("Failed to compile data type:" + basic_dt->as_string());
Expand Down Expand Up @@ -1495,6 +1503,11 @@ void to_c_compiler::visit_get_expr(get_expr *obj) {
mod_obj.string_val_ = member_item->token_;
mod_obj.module_file_ = lhs.second.string_val_;
mod_obj.module_name_ = lhs.second.module_name_;
} else if (has_enum) {
mod_obj.object_type_ = object_type::MODULE_ENUM;
mod_obj.string_val_ = member_item->token_;
mod_obj.module_file_ = lhs.second.string_val_;
mod_obj.module_name_ = lhs.second.module_name_;
} else if (has_const) {
auto glob = imported->data_->dsv_->get_const(member_item->token_);
mod_obj.object_type_ = object_type::PRIMITIVE_OR_OBJ;
Expand Down Expand Up @@ -1523,6 +1536,34 @@ void to_c_compiler::visit_get_expr(get_expr *obj) {
push("<><>", mod_obj);
return;
}
if (lhs.second.object_type_ == object_type::ENUM ||
lhs.second.object_type_ == object_type::MODULE_ENUM) {
auto enum_item = obj->item_;
enum_stmt *enum_statement = nullptr;
std::string module;
if (lhs.second.object_type_ == object_type::ENUM) {
enum_statement = this->defs_classes_.get_enum(lhs.first);
module = filepath_;
} else {
auto imported = cf_->get_or_null(lhs.second.module_file_);
enum_statement = imported->data_->dsv_->get_enum(lhs.second.string_val_);
module = lhs.second.module_file_;
}
int index = 0;
for (const auto &item : enum_statement->members_) {
if (item.name_->token_ == enum_item->token_) {
auto enum_object = yk_object(dt_pool_);
enum_object.object_type_ = object_type::PRIMITIVE_OR_OBJ;
enum_object.string_val_ = item.name_->token_;
enum_object.datatype_ =
dt_pool_->create(enum_statement->name_->token_, module);
push(std::to_string(index),
enum_object);// index is the value of enum (only used at C level)
return;
}
index++;
}
}
auto item = obj->item_->token_;
auto user_defined_type = lhs.second.datatype_->type_;
auto module_file = lhs.second.datatype_->module_;
Expand Down
98 changes: 94 additions & 4 deletions compiler/src/compiler/type_checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,13 @@ void type_checker::visit_fncall_expr(fncall_expr *obj) {
// Creating a custom object from user defined type / class;
return;
}
// Enums are not allowed to be created
if (name.object_type_ == object_type::ENUM ||
name.object_type_ == object_type::MODULE_ENUM) {
error(obj->paren_token_, "Enum object creation is not supported");
push(yk_object(dt_pool_));
return;
}
// Functions
if (name.object_type_ == object_type::FUNCTION ||
name.object_type_ == object_type::MODULE_FUNCTION) {
Expand Down Expand Up @@ -508,7 +515,8 @@ void type_checker::visit_variable_expr(variable_expr *obj) {
auto value = scope_.get(name);
// Preserve function name so we can access it
if (value.object_type_ == object_type::FUNCTION ||
value.object_type_ == object_type::CLASS) {
value.object_type_ == object_type::CLASS ||
value.object_type_ == object_type::ENUM) {
value.string_val_ = name;
}
push(value);
Expand All @@ -534,11 +542,20 @@ void type_checker::visit_def_stmt(def_stmt *obj) {
push_scope_type(ast_type::STMT_DEF);
push_function(obj->name_->token_);
scope_.push();
std::unordered_set<std::string> param_names{};
for (auto param : obj->params_) {
auto name = param.name_->token_;
if (param_names.find(name) != param_names.end()) {
message << "Parameter redefinition is not allowed: '" << name << "'";
error(param.name_, message.str());
message.str("");
} else {
param_names.insert(name);
}
if (scope_.is_defined(name)) {
message << "Parameter shadows outer scope name: " << name;
error(param.name_, message.str());
message.str("");
} else {
auto data = yk_object(param.data_type_);
scope_.define(name, data);
Expand Down Expand Up @@ -716,6 +733,12 @@ void type_checker::check(const std::vector<stmt *> &statements) {
class_placeholder_object.object_type_ = object_type::CLASS;
scope_.define_global(class_name, class_placeholder_object);
}
// Define enums
for (const auto &enum_name : defs_classes_->enum_names_) {
auto enum_placeholder_object = yk_object(dt_pool_);
enum_placeholder_object.object_type_ = object_type::ENUM;
scope_.define_global(enum_name, enum_placeholder_object);
}
// Define global constants
for (const auto &constant_name : defs_classes_->global_const_names_) {
auto constant_definition = defs_classes_->get_const(constant_name);
Expand Down Expand Up @@ -781,7 +804,18 @@ void type_checker::visit_defer_stmt(defer_stmt *obj) {
}
}
void type_checker::visit_class_stmt(class_stmt *obj) {
// TODO check validity of types
// Check for duplicate fields
std::unordered_set<std::string> members{};
for (const auto &member : obj->members_) {
if (members.find(member.name_->token_) != members.end()) {
std::stringstream message{};
message << "Duplicate member name: '" << member.name_->token_ << "' ";
message << "in class/struct: '" << obj->name_->token_ << "'";
error(member.name_, message.str());
} else {
members.insert(member.name_->token_);
}
}
}
void type_checker::visit_del_stmt(del_stmt *obj) {
obj->expression_->accept(this);
Expand Down Expand Up @@ -825,6 +859,7 @@ void type_checker::handle_dot_operator(expr *lhs_expr, token *dot,
bool has_const = imported->data_->dsv_->has_const(member_item->token_);
bool has_native_const =
imported->data_->dsv_->has_native_const(member_item->token_);
bool has_enum = imported->data_->dsv_->has_enum(member_item->token_);
auto obj = yk_object(dt_pool_);
if (has_class) {
obj.object_type_ = object_type::MODULE_CLASS;
Expand All @@ -837,6 +872,11 @@ void type_checker::handle_dot_operator(expr *lhs_expr, token *dot,
obj.string_val_ = member_item->token_;
obj.module_file_ = lhs.string_val_;
obj.module_name_ = lhs.module_name_;
} else if (has_enum) {
obj.object_type_ = object_type::MODULE_ENUM;
obj.string_val_ = member_item->token_;// enum name
obj.module_file_ = lhs.string_val_;
obj.module_name_ = lhs.module_name_;
} else if (has_const || has_native_const) {
yk_datatype *dt;
if (has_const) {
Expand All @@ -862,6 +902,44 @@ void type_checker::handle_dot_operator(expr *lhs_expr, token *dot,
push(obj);
return;
}
// --- access enum values ---
if (lhs.object_type_ == object_type::ENUM ||
lhs.object_type_ == object_type::MODULE_ENUM) {
enum_stmt *enum_statement;
std::string module_file;
if (lhs.object_type_ == object_type::ENUM) {
enum_statement = defs_classes_->get_enum(lhs.string_val_);
module_file = filepath_;
} else {
auto imp = cf_->get_or_null(lhs.module_file_);
enum_statement = imp->data_->dsv_->get_enum(lhs.string_val_);
module_file = lhs.module_file_;
}
for (const auto &member : enum_statement->members_) {
if (member.name_->token_ == member_item->token_) {
auto placeholder =
yk_object(dt_pool_->create(lhs.string_val_, module_file));
push(placeholder);
return;
}
}
// -- bad enum value --
std::vector<std::string> members{};
members.reserve(enum_statement->members_.size());
for (const auto &member : enum_statement->members_) {
members.push_back(member.name_->token_);
}
auto closest = find_closest(member_item->token_, members);
if (closest.empty()) {
error(dot, "Enum value not found");
} else {
error(dot, "Enum value not found. Perhaps '" + closest +
"' is what you "
"meant?");
}
push(yk_object(dt_pool_));
return;
}
if (!lhs.is_primitive_or_obj() ||
lhs.datatype_->const_unwrap()->is_primitive()) {
error(dot, "Invalid dot operator, LHS need to be an object");
Expand Down Expand Up @@ -1414,7 +1492,7 @@ void type_checker::visit_curly_call_expr(curly_call_expr *obj) {
/* ----------------------------------------- */
push(data);
} else {
error(obj->curly_open_, "Invalid data type for {} initialization");
error(obj->curly_open_, "Invalid datatype for {} initialization");
}
}
void type_checker::visit_macro_call_expr(macro_call_expr *obj) {
Expand All @@ -1436,5 +1514,17 @@ void type_checker::visit_cfor_stmt(cfor_stmt *obj) {
scope_.pop();
pop_scope_type();
}
void type_checker::visit_enum_stmt(enum_stmt *obj) {}
void type_checker::visit_enum_stmt(enum_stmt *obj) {
std::unordered_set<std::string> names{};
for (auto nv : obj->members_) {
if (names.find(nv.name_->token_) != names.end()) {
std::stringstream message{};
message << "Duplicate enum value '" << nv.name_->token_ << "' ";
message << "in enum '" << obj->name_->token_ << "'";
error(nv.name_, message.str());
break;
}
names.insert(nv.name_->token_);
}
}
void type_checker::visit_directive_stmt(directive_stmt *obj) {}
23 changes: 21 additions & 2 deletions compiler/src/compiler/usage_analyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ void usage_analyser::visit_get_expr(get_expr *obj) {
o.module_file_ = import_st->data_->filepath_.string();
o.string_val_ = obj->item_->token_;
push_object(o);
} else if (import_st->data_->data_->dsv_->has_enum(obj->item_->token_)) {
auto e = import_st->data_->data_->dsv_->get_enum(obj->item_->token_);
push_import(import_st);
e->accept(this);
pop_import();
auto o = yk_object();
o.object_type_ = object_type::MODULE_ENUM;
o.module_file_ = import_st->data_->filepath_.string();
o.string_val_ = obj->item_->token_;
push_object(o);
} else if (import_st->data_->data_->dsv_->has_const(obj->item_->token_)) {
auto c = import_st->data_->data_->dsv_->get_const(obj->item_->token_);
push_import(import_st);
Expand Down Expand Up @@ -269,6 +279,11 @@ void usage_analyser::visit_variable_expr(variable_expr *obj) {
auto imp = peek_file_info()->data_->parser_->import_stmts_alias_[name];
o.module_file_ = imp->data_->filepath_.string();
imp->accept(this);
} else if (peek_file_info()->data_->dsv_->has_enum(name)) {
o.string_val_ = name;
o.object_type_ = object_type::ENUM;
peek_file_info()->data_->dsv_->get_enum(name)->accept(this);
o.module_file_ = "!enum";
} else {
o.string_val_ = name;
o.object_type_ = object_type::PRIMITIVE_OR_OBJ;
Expand Down Expand Up @@ -405,13 +420,17 @@ void usage_analyser::visit_data_type(yk_datatype *dt, token *token_for_err) {
dsv = import_st->data_->data_->dsv_;
}
}
if (!dsv->has_class(dt->type_)) {
if (!dsv->has_class(dt->type_) && !dsv->has_enum(dt->type_)) {
error(token_for_err,
"cannot find class " + dt->type_ + " in " + dt->module_);
if (pop_import_stack) { pop_import(); }
return;
}
dsv->get_class(dt->type_)->accept(this);
if (dsv->has_enum(dt->type_)) {
dsv->get_enum(dt->type_)->accept(this);
} else {
dsv->get_class(dt->type_)->accept(this);
}
if (pop_import_stack) { pop_import(); }
}
void usage_analyser::push_import(const import_stmt *import_st) {
Expand Down
5 changes: 0 additions & 5 deletions compiler/src/utilities/ykdatatype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,6 @@ void yk_datatype::find_builtin_or_primitive() {
builtin_type_ = yk_builtin::TUPLE;
} else if (token_->token_ == "FixedArr") {
builtin_type_ = yk_builtin::FIXED_ARRAY;
} else if (token_->token_ == ":enum:") {
builtin_type_ = yk_builtin::ENUM_CONTAINER;
}
}
yk_datatype::~yk_datatype() { delete (token_); }
Expand Down Expand Up @@ -327,9 +325,6 @@ bool yk_datatype::is_fixed_size_array() const {
bool yk_datatype::is_dimension() const {
return !is_primitive() && builtin_type_ == yk_builtin::DIMENSION;
}
bool yk_datatype::is_enum_container() const {
return !is_primitive() && builtin_type_ == yk_builtin::ENUM_CONTAINER;
}
bool yaksha::internal_is_identical_type(yk_datatype *required_datatype,
yk_datatype *provided_datatype) {
if (required_datatype != nullptr && provided_datatype != nullptr) {
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/utilities/ykdatatype.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ namespace yaksha {
PTR_TO_ANY, // void *
PTR_TO_CONST_ANY,// void const *
TUPLE,
ENUM_CONTAINER,// Enum[EnumClass,Value]
// Function[In[str],Out]
FUNCTION,
F_IN, // ----- special metadata
Expand Down Expand Up @@ -139,7 +138,6 @@ namespace yaksha {
[[nodiscard]] bool is_tuple() const;
[[nodiscard]] bool is_fixed_size_array() const;
[[nodiscard]] bool is_dimension() const;
[[nodiscard]] bool is_enum_container() const;
// match a combination of primitives
[[nodiscard]] bool is_a_number() const;
[[nodiscard]] bool is_an_integer() const;
Expand Down
8 changes: 5 additions & 3 deletions compiler/src/utilities/ykobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,15 @@ namespace yaksha {
token *token_{nullptr};
};
enum class object_type {
PRIMITIVE_OR_OBJ,
FUNCTION,
PRIMITIVE_OR_OBJ,// int, float, bool, string, custom object
BUILTIN_FUNCTION,// reference to a builtin function
FUNCTION, // reference to a function
CLASS,
BUILTIN_FUNCTION,
ENUM,
MODULE,
MODULE_CLASS,
MODULE_FUNCTION,
MODULE_ENUM,
// --------------------
// Only used for constant folding
CONST_FOLD_VALUE,
Expand Down
4 changes: 4 additions & 0 deletions compiler/test_data/compiler_tests/integer_enums/my_enum.yaka
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
enum SampleType:
A
B
C
39 changes: 39 additions & 0 deletions compiler/test_data/compiler_tests/integer_enums/sample.yaka
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import my_enum as me

enum Banana:
X
Y
Z

class My:
a: int
b: int
c: Banana
d: me.SampleType

def main() -> int:
g = My{a: 0, b: 0, c: Banana.X, d: me.SampleType.A}
a: me.SampleType = me.SampleType.A
b = me.SampleType.B
c = me.SampleType.C
a0 = me.SampleType.A
bx = Banana.X
by = Banana.Y
d = 0
if a == a0:
d += 2
if a != b:
d += 3
else:
d += 100
if c == c:
d += 1
if c != c:
d += 1000
if bx == Banana.X and bx == g.c:
d += 5
if bx != by:
d += 7
else:
d += 10000
d
Loading

0 comments on commit eaadb8b

Please sign in to comment.