diff --git a/.gitignore b/.gitignore index f85781e7..beccdb69 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ cmake-* ./build **/.vscode **/.run +_.*.yaka \ No newline at end of file diff --git a/compiler/src/compiler/to_c_compiler.cpp b/compiler/src/compiler/to_c_compiler.cpp index 65047200..e4fb2145 100644 --- a/compiler/src/compiler/to_c_compiler.cpp +++ b/compiler/src/compiler/to_c_compiler.cpp @@ -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_) { @@ -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()); @@ -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; @@ -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_; diff --git a/compiler/src/compiler/type_checker.cpp b/compiler/src/compiler/type_checker.cpp index 4f8af3e5..dd714cb0 100644 --- a/compiler/src/compiler/type_checker.cpp +++ b/compiler/src/compiler/type_checker.cpp @@ -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) { @@ -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); @@ -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 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); @@ -716,6 +733,12 @@ void type_checker::check(const std::vector &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); @@ -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 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); @@ -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; @@ -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) { @@ -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 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"); @@ -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) { @@ -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 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) {} diff --git a/compiler/src/compiler/usage_analyser.cpp b/compiler/src/compiler/usage_analyser.cpp index 0319b0e4..60ebbe36 100644 --- a/compiler/src/compiler/usage_analyser.cpp +++ b/compiler/src/compiler/usage_analyser.cpp @@ -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); @@ -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; @@ -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) { diff --git a/compiler/src/utilities/ykdatatype.cpp b/compiler/src/utilities/ykdatatype.cpp index d85c2d2b..e685b708 100644 --- a/compiler/src/utilities/ykdatatype.cpp +++ b/compiler/src/utilities/ykdatatype.cpp @@ -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_); } @@ -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) { diff --git a/compiler/src/utilities/ykdatatype.h b/compiler/src/utilities/ykdatatype.h index a1199507..7ffbe562 100644 --- a/compiler/src/utilities/ykdatatype.h +++ b/compiler/src/utilities/ykdatatype.h @@ -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 @@ -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; diff --git a/compiler/src/utilities/ykobject.h b/compiler/src/utilities/ykobject.h index 4abfc419..7277128c 100644 --- a/compiler/src/utilities/ykobject.h +++ b/compiler/src/utilities/ykobject.h @@ -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, diff --git a/compiler/test_data/compiler_tests/integer_enums/my_enum.yaka b/compiler/test_data/compiler_tests/integer_enums/my_enum.yaka new file mode 100644 index 00000000..0f7928ff --- /dev/null +++ b/compiler/test_data/compiler_tests/integer_enums/my_enum.yaka @@ -0,0 +1,4 @@ +enum SampleType: + A + B + C \ No newline at end of file diff --git a/compiler/test_data/compiler_tests/integer_enums/sample.yaka b/compiler/test_data/compiler_tests/integer_enums/sample.yaka new file mode 100644 index 00000000..bb5ac1b1 --- /dev/null +++ b/compiler/test_data/compiler_tests/integer_enums/sample.yaka @@ -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 diff --git a/compiler/test_data/compiler_tests/integer_enums/sample.yaka.c b/compiler/test_data/compiler_tests/integer_enums/sample.yaka.c new file mode 100644 index 00000000..22c64d74 --- /dev/null +++ b/compiler/test_data/compiler_tests/integer_enums/sample.yaka.c @@ -0,0 +1,63 @@ +// YK +#include "yk__lib.h" +struct yy__My; +struct yy__My { + int32_t yy__a; + int32_t yy__b; + int32_t yy__c; + int32_t yy__d; +}; +int32_t yy__main(); +int32_t yy__main() +{ + struct yy__My* t__0 = calloc(1, sizeof(struct yy__My)); + t__0->yy__a = (INT32_C(0)); + t__0->yy__b = (INT32_C(0)); + t__0->yy__c = (0); + t__0->yy__d = (0); + struct yy__My* yy__g = t__0; + int32_t yy__a = 0; + int32_t yy__b = 1; + int32_t yy__c = 2; + int32_t yy__a0 = 0; + int32_t yy__bx = 0; + int32_t yy__by = 1; + int32_t yy__d = INT32_C(0); + if (yy__a == yy__a0) + { + yy__d += INT32_C(2); + } + if (yy__a != yy__b) + { + yy__d += INT32_C(3); + } + else + { + yy__d += INT32_C(100); + } + if (yy__c == yy__c) + { + yy__d += INT32_C(1); + } + if (yy__c != yy__c) + { + yy__d += INT32_C(1000); + } + if ((yy__bx == 0) && (yy__bx == yy__g->yy__c)) + { + yy__d += INT32_C(5); + } + if (yy__bx != yy__by) + { + yy__d += INT32_C(7); + } + else + { + yy__d += INT32_C(10000); + } + int32_t t__1 = yy__d; + return t__1; +} +#if defined(YK__MINIMAL_MAIN) +int main(void) { return yy__main(); } +#endif \ No newline at end of file diff --git a/compiler/tests/test_compiler.cpp b/compiler/tests/test_compiler.cpp index 5bcbcbc1..b24afb8f 100644 --- a/compiler/tests/test_compiler.cpp +++ b/compiler/tests/test_compiler.cpp @@ -44,7 +44,8 @@ using namespace yaksha; static void test_compile_yaka_file(const std::string &yaka_code_file) { std::string exe_path = get_my_exe_path(); - auto libs_path = std::filesystem::path(exe_path).parent_path().parent_path() / "libs"; + auto libs_path = + std::filesystem::path(exe_path).parent_path().parent_path() / "libs"; multifile_compiler mc{}; codegen_c cg{}; auto result = mc.compile(yaka_code_file, libs_path.string(), &cg); @@ -383,11 +384,18 @@ TEST_CASE("compiler: bug-fix - cast string literal should work as expected") { test_compile_yaka_file("../test_data/bug_fixes/easy_cstr.yaka"); } TEST_CASE("compiler: directive - ccode") { - test_compile_yaka_file("../test_data/compiler_tests/directives/directive_ccode.yaka"); + test_compile_yaka_file( + "../test_data/compiler_tests/directives/directive_ccode.yaka"); } TEST_CASE("compiler: directive - no_main/no_stdlib") { - test_compile_yaka_file("../test_data/compiler_tests/directives/minimal_mode.yaka"); + test_compile_yaka_file( + "../test_data/compiler_tests/directives/minimal_mode.yaka"); } TEST_CASE("compiler: structures - depends on other structures") { - test_compile_yaka_file("../test_data/compiler_tests/structs_arrays/cat_game.yaka"); + test_compile_yaka_file( + "../test_data/compiler_tests/structs_arrays/cat_game.yaka"); } +TEST_CASE("compiler: enums - import and use enum") { + test_compile_yaka_file( + "../test_data/compiler_tests/integer_enums/sample.yaka"); +} \ No newline at end of file diff --git a/compiler/tests/test_type_checker.cpp b/compiler/tests/test_type_checker.cpp index 3d6ebf4b..622800fd 100644 --- a/compiler/tests/test_type_checker.cpp +++ b/compiler/tests/test_type_checker.cpp @@ -111,8 +111,6 @@ static void test_typechecker_snippet_full_ok(const std::string &S) { REQUIRE(mc.error_printer_.has_no_errors()); } TEST_CASE("type checker: Bad function for qsort") { - // TODO Note that the error here does not really make sense - // as I renamed SortArg --> AnyPtrToConst test_typechecker_yaka_file( "../test_data/bad_inputs/bad_input_sort_with_wrong_args.yaka", "Comparison must match with " @@ -243,6 +241,124 @@ TEST_CASE("type checker: str constants are not supported") { " return 0", "Only number/bool/sr constants are supported."); } +TEST_CASE("type checker: two different enums cannot be compared") { + test_typechecker_snippet_full("enum A:\n" + " a\n" + "enum B:\n" + " b\n" + "def main() -> int:\n" + " c: bool = A.a == B.b\n" + " 0\n", + "Cannot compare between two data types"); +} +TEST_CASE("type checker: enum cannot be compared with integer") { + test_typechecker_snippet_full("enum A:\n" + " a\n" + "def main() -> int:\n" + " c: bool = A.a == 0\n" + " 0\n", + "Cannot compare between two data types"); +} +TEST_CASE("type checker: different enums cannot be assigned") { + test_typechecker_snippet_full( + "enum A:\n" + " a\n" + "enum B:\n" + " b\n" + "def main() -> int:\n" + " a: A = A.a\n" + " a = B.b\n" + " 0\n", + "Cannot assign between 2 different data types. lhs: A, rhs: B"); +} +TEST_CASE( + "type checker: different enums cannot be assigned ('a' basic inference)") { + test_typechecker_snippet_full( + "enum A:\n" + " a\n" + "enum B:\n" + " b\n" + "def main() -> int:\n" + " a = A.a\n" + " a = B.b\n" + " 0\n", + "Cannot assign between 2 different data types. lhs: A, rhs: B"); +} +TEST_CASE("type checker: compare inferred enums") { + test_typechecker_snippet_full("enum A:\n" + " a\n" + "enum B:\n" + " b\n" + "def main() -> int:\n" + " a = A.a\n" + " b = B.b\n" + " iif(a == b, 0, 1)\n", + "Cannot compare between two data types"); +} +TEST_CASE("type checker: duplicated enum values") { + test_typechecker_snippet_full("enum A:\n" + " a\n" + " a\n" + "def main() -> int:\n" + " a = A.a\n" + " iif(a == A.a, 0, 1)\n", + "Duplicate enum value 'a' in enum 'A'"); +} +TEST_CASE("type checker: duplicate class fields") { + test_typechecker_snippet_full( + "class A:\n" + " a: int\n" + " a: int\n" + "def main() -> int:\n" + " a = A{a: 1}\n" + " iif(a.a == 1, 0, 1)\n", + "Duplicate member name: 'a' in class/struct: 'A'"); +} +TEST_CASE("type checker: duplicate function parameters") { + // Both errors should be raised + test_typechecker_snippet_full("def fnc(a: int, a: int) -> None:\n" + " pass\n" + "def main() -> int:\n" + " fnc(1, 2)\n" + " 0\n", + "Parameter redefinition is not allowed: 'a'"); + test_typechecker_snippet_full("def fnc(b: int, b: int) -> None:\n" + " pass\n" + "def main() -> int:\n" + " fnc(1, 2)\n" + " 0\n", + "Parameter shadows outer scope name: b"); +} +TEST_CASE("type checker: duplicate name and value for an enum") { + test_typechecker_snippet_full_ok("enum A:\n" + " A\n" + "def main() -> int:\n" + " iif(A.A == A.A, 0, 1)\n"); +} +TEST_CASE("type checker: duplicate name and value for class field") { + test_typechecker_snippet_full_ok("class A:\n" + " A: int\n" + "def main() -> int:\n" + " a = A{A: 1}\n" + " iif(a.A == 1, 0, 1)\n"); +} +TEST_CASE("type checker: duplicate name and parameter name for function") { + test_typechecker_snippet_full("def fnc(fnc: int) -> None:\n" + " pass\n" + "def main() -> int:\n" + " fnc(1)\n" + " 0\n", + "Parameter shadows outer scope name: fnc"); +} +TEST_CASE("type checker: function name is same as class member") { + test_typechecker_snippet_full_ok("class A:\n" + " a: int\n" + "def a(b: int) -> None:\n" + " pass\n" + "def main() -> int:\n" + " a0 = A{a: 1}\n" + " 0\n"); +} TEST_CASE("type checker: sr constants are supported") { test_typechecker_snippet_full_ok("A:Const[sr] = \"hello\"\n" "def main() -> int:\n" @@ -768,7 +884,7 @@ TEST_CASE("type checker: Import shadows a foreach variable") { } TEST_CASE("type checker: Create a primitive using {} init") { test_typechecker_snippet("a = int{x: 0}", - "Invalid data type for {} initialization"); + "Invalid datatype for {} initialization"); } TEST_CASE("type checker: Invalid fields in struct") { test_typechecker_snippet_full("struct P:\n" diff --git a/compiler/tests/test_yaksha_lisp.cpp b/compiler/tests/test_yaksha_lisp.cpp index 56ec5e02..bf8ea6ca 100644 --- a/compiler/tests/test_yaksha_lisp.cpp +++ b/compiler/tests/test_yaksha_lisp.cpp @@ -153,9 +153,7 @@ void no_crash_test(std::string text) { auto env = yaksha_envmap{&yaksha_lisp_m}; env.setup_builtins(); env.eval(parser.exprs_); - } catch (const parsing_error &ex) { - intentionally_ignored(ex); - } + } catch (const parsing_error &ex) { intentionally_ignored(ex); } } TEST_CASE("yaksha_lisp: expected ')'") { test_snippet_execute_unhappy("(+ 1 2", "Expected a terminal");