From b60154cf9a25daa1301e077f726383e42421ea71 Mon Sep 17 00:00:00 2001 From: JaDogg Date: Sun, 17 Mar 2024 17:57:09 +0000 Subject: [PATCH] feat: directives to use minimal mode --- compiler/src/builtins/builtins.cpp | 25 +++++++++++++------ compiler/src/compiler/def_class_visitor.cpp | 2 +- .../compiler/function_datatype_extractor.h | 2 +- compiler/src/compiler/multifile_compiler.cpp | 6 ++++- compiler/src/compiler/to_c_compiler.cpp | 25 ++++++++++++++++--- compiler/src/compiler/to_c_compiler.h | 2 +- compiler/src/compiler/type_checker.cpp | 16 +++++++++--- compiler/src/compiler/type_checker.h | 2 +- compiler/src/compiler/usage_analyser.cpp | 10 +++++++- compiler/src/compiler/usage_analyser.h | 1 + .../directives/minimal_mode.yaka | 25 +++++++++++++++++++ .../directives/minimal_mode.yaka.c | 19 ++++++++++++++ compiler/tests/test_compiler.cpp | 3 +++ 13 files changed, 117 insertions(+), 21 deletions(-) create mode 100644 compiler/test_data/compiler_tests/directives/minimal_mode.yaka create mode 100644 compiler/test_data/compiler_tests/directives/minimal_mode.yaka.c diff --git a/compiler/src/builtins/builtins.cpp b/compiler/src/builtins/builtins.cpp index bae67ff4..951c145f 100644 --- a/compiler/src/builtins/builtins.cpp +++ b/compiler/src/builtins/builtins.cpp @@ -195,6 +195,7 @@ struct builtin_arrsetlencap : builtin { return {code.str(), o}; } bool require_stdlib() override { return true; } + private: std::string func_name_; }; @@ -274,6 +275,7 @@ struct builtin_print : builtin { return {code.str(), o}; } bool require_stdlib() override { return true; } + private: std::string func_name_; }; @@ -1249,8 +1251,10 @@ struct builtin_iif : builtin { *args[2].datatype_->const_unwrap()) { o.string_val_ = "Second and third argument to iif() must be of same type"; } else if (args[1].is_a_function()) { - ykdatatype *arg1_dt = dt_slot_matcher->function_to_datatype(args[1]); - ykdatatype *arg2_dt = dt_slot_matcher->function_to_datatype(args[2]); + ykdatatype *arg1_dt = + dt_slot_matcher->function_to_datatype_or_null(args[1]); + ykdatatype *arg2_dt = + dt_slot_matcher->function_to_datatype_or_null(args[2]); if (arg1_dt != nullptr && arg2_dt != nullptr && *(arg1_dt->const_unwrap()) == *(arg2_dt->const_unwrap())) { o = ykobject(arg1_dt); @@ -1351,7 +1355,7 @@ struct builtin_functional : builtin { ykdatatype *dt; ykdatatype *return_val_type; if (args[1].second.is_a_function()) { - dt = fnc_dt_extractor->function_to_datatype(args[1].second); + dt = fnc_dt_extractor->function_to_datatype_or_null(args[1].second); } else { dt = args[1].second.datatype_; } @@ -1463,6 +1467,7 @@ struct builtin_functional : builtin { return {code.str(), ykobject(return_val_type)}; } bool require_stdlib() override { return true; } + private: void write_statement(std::stringstream &read_stream, statement_writer *st_writer) { @@ -1483,7 +1488,13 @@ struct builtin_functional : builtin { if (function.datatype_->is_function()) { dt = function.datatype_; } else { - dt = dt_slot_matcher->function_to_datatype(function); + dt = dt_slot_matcher->function_to_datatype_or_null(function); + } + if (dt == nullptr) { + ykobject o = ykobject(dt_pool); + o.string_val_ = "Function argument for builtin " + name_ + " if invalid"; + o.object_type_ = object_type::ERROR_DETECTED; + return o; } ykobject o = ykobject(dt_pool); ykdatatype *template_dt = array.datatype_->args_[0]; @@ -1761,15 +1772,15 @@ ykobject builtins::verify( const std::unordered_map &import_aliases, const std::string &filepath, slot_matcher *dt_slot_matcher, bool no_stdlib) { - auto& builtin_object = builtins_[name]; + auto &builtin_object = builtins_[name]; if (builtin_object->require_stdlib() && no_stdlib) { auto o = ykobject(dt_pool_); - o.string_val_ = "Builtin '" + name + "' does not work without stdlib."; + o.string_val_ = "Builtin '" + name + "' does not work without stdlib."; o.object_type_ = object_type::ERROR_DETECTED; return o; } return builtin_object->verify(args, arg_expressions, this, dt_pool_, - import_aliases, filepath, dt_slot_matcher); + import_aliases, filepath, dt_slot_matcher); } std::pair builtins::compile( const std::string &name, diff --git a/compiler/src/compiler/def_class_visitor.cpp b/compiler/src/compiler/def_class_visitor.cpp index 39733cdf..60c13f25 100644 --- a/compiler/src/compiler/def_class_visitor.cpp +++ b/compiler/src/compiler/def_class_visitor.cpp @@ -367,7 +367,7 @@ bool def_class_visitor::has_zero_arg_directive(directive_stmt *obj) { cf_->directives_.no_main_ = true; } // (Global flag) Directly substitute '@nativedefine' or 'native constants' - if (directive_type == "apply_nativedefine") { // 🔴 + if (directive_type == "apply_nativedefine") {// 🔴 zero_arg_directive = true; cf_->directives_.apply_native_define_ = true; } diff --git a/compiler/src/compiler/function_datatype_extractor.h b/compiler/src/compiler/function_datatype_extractor.h index 61b798af..55814023 100644 --- a/compiler/src/compiler/function_datatype_extractor.h +++ b/compiler/src/compiler/function_datatype_extractor.h @@ -51,7 +51,7 @@ namespace yaksha { * @param arg argument * @return nullptr if failed, else a valid datatype */ - virtual ykdatatype *function_to_datatype(const ykobject &arg) = 0; + virtual ykdatatype *function_to_datatype_or_null(const ykobject &arg) = 0; }; }// namespace yaksha #endif diff --git a/compiler/src/compiler/multifile_compiler.cpp b/compiler/src/compiler/multifile_compiler.cpp index c7be014f..25cc65f8 100644 --- a/compiler/src/compiler/multifile_compiler.cpp +++ b/compiler/src/compiler/multifile_compiler.cpp @@ -231,7 +231,11 @@ comp_result multifile_compiler::compile_all(codegen *code_generator) { // Statement usage analysis // So we know which 'functions / classes / consts' are actually used usage_analyser ua{main_file_info}; - ua.analyse(); + if (cf_->directives_.no_main_) { + ua.analyse_no_main(); + } else { + ua.analyse(); + } if (!ua.errors_.empty()) { error_printer_.print_errors(ua.errors_); LOG_COMP("usage analyser found errors"); diff --git a/compiler/src/compiler/to_c_compiler.cpp b/compiler/src/compiler/to_c_compiler.cpp index 418c397e..678c8011 100644 --- a/compiler/src/compiler/to_c_compiler.cpp +++ b/compiler/src/compiler/to_c_compiler.cpp @@ -417,6 +417,10 @@ void to_c_compiler::visit_fncall_expr(fncall_expr *obj) { LOG_COMP("module function: " << name << " file=" << module_file << " fn=" << module_fn << " prefix=" << module_prefix); auto fndef = module_info->data_->dsv_->get_function(module_fn); + if (cf_->directives_.apply_native_define_ && + fndef->annotations_.native_define_) { + prefixed_fn_name = fndef->annotations_.native_define_arg_; + } auto fn_return = fndef->return_type_; std::vector params{}; params.reserve(fndef->params_.size()); @@ -426,12 +430,17 @@ void to_c_compiler::visit_fncall_expr(fncall_expr *obj) { } else if (defs_classes_.has_function(name)) { LOG_COMP("local_function: " << name); auto fn_def = defs_classes_.get_function(name); + std::string prefixed_name = prefix(name, prefix_val_); + if (cf_->directives_.apply_native_define_ && + fn_def->annotations_.native_define_) { + prefixed_name = fn_def->annotations_.native_define_arg_; + } auto return_type = fn_def->return_type_; std::vector params{}; params.reserve(fn_def->params_.size()); for (auto &p : fn_def->params_) { params.emplace_back(p.data_type_); } - compile_function_call(obj, prefix(name, prefix_val_), code, return_type, - params, fn_def->annotations_.varargs_); + compile_function_call(obj, prefixed_name, code, return_type, params, + fn_def->annotations_.varargs_); } else if (defs_classes_.has_class(name)) { LOG_COMP("local_class:" << name); auto class_ = defs_classes_.get_class(name); @@ -475,6 +484,7 @@ void to_c_compiler::compile_function_call( bool first = true; size_t arg_size = obj->args_.size(); size_t param_size = parameters.size(); + // --- for (size_t i = 0; i < arg_size; i++) { auto arg = obj->args_[i]; ykdatatype *param; @@ -823,6 +833,9 @@ void to_c_compiler::visit_def_stmt(def_stmt *obj) { LOG_COMP("compiling def_stmt: " << obj->name_->token_); auto name = prefix(obj->name_->token_, prefix_val_); if (obj->annotations_.native_define_) { + if (cf_->directives_.apply_native_define_) { + return;// no need to write native_defines + } struct_forward_declarations_ << "#define " << name << " " << obj->annotations_.native_define_arg_ << "\n"; @@ -1009,7 +1022,11 @@ void to_c_compiler::visit_let_stmt(let_stmt *obj) { visited_expr = true; resulting_pair = compile_expression(obj->expression_); if (resulting_pair.second.is_a_function()) { - obj->data_type_ = function_to_datatype(resulting_pair.second); + obj->data_type_ = function_to_datatype_or_null(resulting_pair.second); + if (obj->data_type_ == nullptr) { + error(obj->name_, "Failed to derive data type of function pointer"); + return; + } } else { obj->data_type_ = resulting_pair.second.datatype_ ->const_unwrap(); /* set our data type here */ @@ -1704,7 +1721,7 @@ void to_c_compiler::write_statement_no_end(std::string code_line) { body_ << code_line << "\n"; } void to_c_compiler::visit_runtimefeature_stmt(runtimefeature_stmt *obj) {} -ykdatatype *to_c_compiler::function_to_datatype(const ykobject &arg) { +ykdatatype *to_c_compiler::function_to_datatype_or_null(const ykobject &arg) { def_stmt *funct; if (arg.object_type_ == object_type::FUNCTION) { funct = defs_classes_.get_function(arg.string_val_); diff --git a/compiler/src/compiler/to_c_compiler.h b/compiler/src/compiler/to_c_compiler.h index 86d8a803..899d0152 100644 --- a/compiler/src/compiler/to_c_compiler.h +++ b/compiler/src/compiler/to_c_compiler.h @@ -66,7 +66,7 @@ namespace yaksha { entry_struct_func_compiler *esc, gc_pool *token_pool); ~to_c_compiler() override; compiler_output compile(codefiles *cf, file_info *fi); - ykdatatype *function_to_datatype(const ykobject &arg) override; + ykdatatype *function_to_datatype_or_null(const ykobject &arg) override; void visit_assign_expr(assign_expr *obj) override; void visit_binary_expr(binary_expr *obj) override; void visit_fncall_expr(fncall_expr *obj) override; diff --git a/compiler/src/compiler/type_checker.cpp b/compiler/src/compiler/type_checker.cpp index 4c852cbe..2307a355 100644 --- a/compiler/src/compiler/type_checker.cpp +++ b/compiler/src/compiler/type_checker.cpp @@ -70,7 +70,14 @@ void type_checker::visit_assign_expr(assign_expr *obj) { if (scope_.is_defined(name)) { object = scope_.get(name); } else { - if (rhs.is_a_function()) { rhs.datatype_ = function_to_datatype(rhs); } + if (rhs.is_a_function()) { + rhs.datatype_ = function_to_datatype_or_null(rhs); + if (rhs.datatype_ == nullptr) { + error(obj->right_->locate(), + "Failed to derive data type of function pointer"); + return; + } + } if (rhs.datatype_->is_none()) { error(obj->opr_, "Cannot infer type when RHS is None"); } @@ -309,7 +316,8 @@ void type_checker::visit_fncall_expr(fncall_expr *obj) { arguments.push_back(pop()); } auto result = builtins_.verify(name.string_val_, arguments, obj->args_, - import_stmts_alias_, filepath_, this, cf_->directives_.no_stdlib_); + import_stmts_alias_, filepath_, this, + cf_->directives_.no_stdlib_); // Error when calling builtin, if so return None as data type if (result.object_type_ == object_type::ERROR_DETECTED) { error(obj->paren_token_, result.string_val_); @@ -1002,7 +1010,7 @@ void type_checker::visit_const_stmt(const_stmt *obj) { } bool type_checker::slot_match(const ykobject &arg, ykdatatype *datatype) { if (arg.is_a_function() && datatype->is_function()) { - ykdatatype *arg_datatype = function_to_datatype(arg); + ykdatatype *arg_datatype = function_to_datatype_or_null(arg); return arg_datatype != nullptr && *arg_datatype == *datatype; } if (arg.is_primitive_or_obj() && @@ -1024,7 +1032,7 @@ bool type_checker::slot_match(const ykobject &arg, ykdatatype *datatype) { } return false; } -ykdatatype *type_checker::function_to_datatype(const ykobject &arg) { +ykdatatype *type_checker::function_to_datatype_or_null(const ykobject &arg) { def_stmt *funct; if (arg.object_type_ == object_type::FUNCTION) { funct = defs_classes_->get_function(arg.string_val_); diff --git a/compiler/src/compiler/type_checker.h b/compiler/src/compiler/type_checker.h index 44127e2d..c2492914 100644 --- a/compiler/src/compiler/type_checker.h +++ b/compiler/src/compiler/type_checker.h @@ -54,7 +54,7 @@ namespace yaksha { gc_pool *token_pool); ~type_checker() override; bool slot_match(const ykobject &arg, ykdatatype *datatype) override; - ykdatatype *function_to_datatype(const ykobject &arg) override; + ykdatatype *function_to_datatype_or_null(const ykobject &arg) override; void check(const std::vector &statements); void visit_assign_expr(assign_expr *obj) override; void visit_binary_expr(binary_expr *obj) override; diff --git a/compiler/src/compiler/usage_analyser.cpp b/compiler/src/compiler/usage_analyser.cpp index adc9121e..125df6a9 100644 --- a/compiler/src/compiler/usage_analyser.cpp +++ b/compiler/src/compiler/usage_analyser.cpp @@ -59,6 +59,14 @@ void usage_analyser::analyse() { import_stack_.clear(); object_stack_.clear(); } +void usage_analyser::analyse_no_main() { + for (std::string &name : main_->data_->dsv_->function_names_) { + LOG_COMP("usage analyser (no main) - found: " << name); + main_->data_->dsv_->get_function(name)->accept(this); + } + import_stack_.clear(); + object_stack_.clear(); +} void usage_analyser::visit_assign_expr(assign_expr *obj) { obj->hits_++; obj->right_->accept(this); @@ -445,4 +453,4 @@ void usage_analyser::visit_cfor_stmt(cfor_stmt *obj) { } void usage_analyser::visit_enum_stmt(enum_stmt *obj) { obj->hits_++; } void usage_analyser::visit_union_stmt(union_stmt *obj) { obj->hits_++; } -void usage_analyser::visit_directive_stmt(directive_stmt *obj) {} +void usage_analyser::visit_directive_stmt(directive_stmt *obj) { obj->hits_++; } diff --git a/compiler/src/compiler/usage_analyser.h b/compiler/src/compiler/usage_analyser.h index ab245e0a..a1d7fb3e 100644 --- a/compiler/src/compiler/usage_analyser.h +++ b/compiler/src/compiler/usage_analyser.h @@ -46,6 +46,7 @@ namespace yaksha { struct usage_analyser : expr_visitor, stmt_visitor { explicit usage_analyser(file_info *main_file_info); void analyse(); + void analyse_no_main(); void visit_assign_expr(assign_expr *obj) override; void visit_assign_arr_expr(assign_arr_expr *obj) override; void visit_assign_member_expr(assign_member_expr *obj) override; diff --git a/compiler/test_data/compiler_tests/directives/minimal_mode.yaka b/compiler/test_data/compiler_tests/directives/minimal_mode.yaka new file mode 100644 index 00000000..49259a3c --- /dev/null +++ b/compiler/test_data/compiler_tests/directives/minimal_mode.yaka @@ -0,0 +1,25 @@ +directive no_main +directive no_stdlib +directive apply_nativedefine +directive ccode """ +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif +""" + +# Below wil be inlined +@nativedefine("max") +def max(a: i32, b: i32) -> i32: + pass + +@nativedefine("min") +def min(a: i32, b: i32) -> i32: + pass + +def add(a: i32, b: i32) -> i32: + return min(a, b) + max(a, b) + diff --git a/compiler/test_data/compiler_tests/directives/minimal_mode.yaka.c b/compiler/test_data/compiler_tests/directives/minimal_mode.yaka.c new file mode 100644 index 00000000..6061d256 --- /dev/null +++ b/compiler/test_data/compiler_tests/directives/minimal_mode.yaka.c @@ -0,0 +1,19 @@ +// YK +// --forward declarations-- +int32_t yy__add(int32_t, int32_t); +// --structs-- +// --functions-- + +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif +; +int32_t yy__add(int32_t yy__a, int32_t yy__b) +{ + int32_t t__0 = (min(yy__a, yy__b) + max(yy__a, yy__b)); + return t__0; +} diff --git a/compiler/tests/test_compiler.cpp b/compiler/tests/test_compiler.cpp index 569d8264..56244772 100644 --- a/compiler/tests/test_compiler.cpp +++ b/compiler/tests/test_compiler.cpp @@ -379,4 +379,7 @@ TEST_CASE("compiler: bug-fix - access struct str member") { } TEST_CASE("compiler: directive - ccode") { 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"); } \ No newline at end of file