diff --git a/compiler/src/ast/codefiles.h b/compiler/src/ast/codefiles.h index 5119110b..7efdc34a 100644 --- a/compiler/src/ast/codefiles.h +++ b/compiler/src/ast/codefiles.h @@ -56,7 +56,7 @@ namespace yaksha { // Flags bool no_main_{false}; bool apply_native_define_{false}; - bool no_libs_{false}; + bool no_stdlib_{false}; // Conditional directives std::vector include_paths_{}; std::vector library_paths_{}; diff --git a/compiler/src/ast/parser.cpp b/compiler/src/ast/parser.cpp index 6b04b3b0..db85bb76 100644 --- a/compiler/src/ast/parser.cpp +++ b/compiler/src/ast/parser.cpp @@ -965,7 +965,7 @@ stmt *parser::import_statement() { } // We can see if we encountered this in pre_parsing // If we did, then we can use the pre-parsed data - // during preparsing, this section will do nothing as there are no pre_parse stuff + // during preparsing, this section will do nothing if there are no pre_parse stuff if (pre_parse_import_stmts_alias_.find(name_token->token_) != pre_parse_import_stmts_alias_.end()) { auto pre_parse_import = pre_parse_import_stmts_alias_[name_token->token_]; diff --git a/compiler/src/builtins/builtin.h b/compiler/src/builtins/builtin.h index 46f46d48..d46c18fe 100644 --- a/compiler/src/builtins/builtin.h +++ b/compiler/src/builtins/builtin.h @@ -52,6 +52,7 @@ namespace yaksha { struct builtin { virtual ~builtin() = default; + virtual bool require_stdlib() = 0; virtual ykobject verify(const std::vector &args, const std::vector &arg_expressions, diff --git a/compiler/src/builtins/builtins.cpp b/compiler/src/builtins/builtins.cpp index 23c7d575..bae67ff4 100644 --- a/compiler/src/builtins/builtins.cpp +++ b/compiler/src/builtins/builtins.cpp @@ -99,6 +99,7 @@ struct builtin_arrput : builtin { } return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐┬─┐┬─┐┌─┐┌─┐┌─┐ @@ -141,6 +142,7 @@ struct builtin_arrpop : builtin { code << "yk__arrpop(" << args[0].first << ")"; return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐┬─┐┬─┐┌─┐┌─┐┌┬┐┌─┐┌─┐┌─┐ @@ -192,7 +194,7 @@ struct builtin_arrsetlencap : builtin { << ")"; return {code.str(), o}; } - + bool require_stdlib() override { return true; } private: std::string func_name_; }; @@ -271,7 +273,7 @@ struct builtin_print : builtin { } return {code.str(), o}; } - + bool require_stdlib() override { return true; } private: std::string func_name_; }; @@ -342,6 +344,7 @@ struct builtin_len : builtin { o = ykobject(dt_pool->create("int")); return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐┬ ┬┌─┐┬─┐┌─┐┌┬┐ @@ -389,6 +392,7 @@ struct builtin_charat : builtin { o = ykobject(dt_pool->create("int")); return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐┌─┐┌┬┐┬─┐┌─┐┌─┐ @@ -432,6 +436,7 @@ struct builtin_getref : builtin { o = ykobject(dt); return {code.str(), o}; } + bool require_stdlib() override { return false; } }; // // ┬ ┬┌┐┌┬─┐┌─┐┌─┐ @@ -471,6 +476,7 @@ struct builtin_unref : builtin { o = ykobject(args[0].second.datatype_->args_[0]); return {code.str(), o}; } + bool require_stdlib() override { return false; } }; // // ┌─┐┬ ┬┌┐┌┌─┐┬ ┬ @@ -510,6 +516,7 @@ struct builtin_shnew : builtin { code << "yk__sh_new_strdup(" << args[0].first << ")"; return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐┬ ┬┌─┐┌─┐┌┬┐ @@ -557,6 +564,7 @@ struct builtin_shget : builtin { o = ykobject(args[0].second.datatype_->args_[0]->args_[0]); return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐┬ ┬┌─┐┌─┐┌┬┐┬ @@ -604,6 +612,7 @@ struct builtin_shgeti : builtin { o = ykobject(dt_pool->create("int")); return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐┬ ┬┌─┐┬ ┬┌┬┐ @@ -653,6 +662,7 @@ struct builtin_shput : builtin { code << ", " << args[2].first << ")"; return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐┌─┐┌─┐┌┬┐ @@ -718,6 +728,7 @@ struct builtin_cast : builtin { o = ykobject(out_dt); return {code.str(), o}; } + bool require_stdlib() override { return false; } }; // // ┬ ┬┌┬┐┌┐┌┌─┐┬ ┬ @@ -755,6 +766,7 @@ struct builtin_hmnew : builtin { auto o = ykobject(dt_pool); return {"", o}; } + bool require_stdlib() override { return true; } }; // // ┬ ┬┌┬┐┌─┐┌─┐┌┬┐ @@ -800,6 +812,7 @@ struct builtin_hmget : builtin { o = ykobject(args[0].second.datatype_->args_[0]->args_[0]); return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┬ ┬┌┬┐┌─┐┌─┐┌┬┐┬ @@ -845,6 +858,7 @@ struct builtin_hmgeti : builtin { o = ykobject(dt_pool->create("int")); return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┬ ┬┌┬┐┌─┐┬ ┬┌┬┐ @@ -894,6 +908,7 @@ struct builtin_hmput : builtin { << args[2].first << ")"; return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐ ┌─┐┌─┐┬─┐┌┬┐ @@ -965,6 +980,7 @@ struct builtin_qsort : builtin { << ") == 0)"; return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐┬─┐┬─┐┌┐┌┌─┐┬ ┬ @@ -1033,6 +1049,7 @@ struct builtin_arrnew : builtin { o = ykobject(array_dt); return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┌─┐┬ ┐ ┬┌─┐┌┬┐┌─┐┬─┐┬─┐ @@ -1120,6 +1137,7 @@ struct builtin_fixed_arr : builtin { o = ykobject(array_dt); return {code.str(), o}; } + bool require_stdlib() override { return false; } }; // // ┌─┐┬─┐┬─┐┌─┐┬ ┬ @@ -1208,6 +1226,7 @@ struct builtin_array : builtin { o = ykobject(array_dt); return {code.str(), o}; } + bool require_stdlib() override { return true; } }; // // ┬┬┌─┐ @@ -1275,6 +1294,7 @@ struct builtin_iif : builtin { } return {code.str(), o}; } + bool require_stdlib() override { return false; } }; // // ┌─┐┌─┐┬─┐┌─┐┌─┐┌─┐┬ ┬ @@ -1442,7 +1462,7 @@ struct builtin_functional : builtin { code << return_temp; 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) { @@ -1546,6 +1566,7 @@ struct builtin_binarydata : builtin { dt_parser->parse("Const[Ptr[Const[u8]]]", import_aliases, filepath)); return {esc->compile_binary_data(raw_string), o}; } + bool require_stdlib() override { return false; } }; // // ┌┬┐┌─┐┬┌─┌─┐ @@ -1620,6 +1641,7 @@ struct builtin_make : builtin { o.datatype_ = dt; return {code.str(), o}; } + bool require_stdlib() override { return false; } }; // // ┬┌┐┌┬ ┬┌┐┌┌─┐┌─┐ @@ -1683,6 +1705,7 @@ struct builtin_inlinec : builtin { o.datatype_ = parsed_dt; return {string_utils::unescape(code->literal_token_->token_), o}; } + bool require_stdlib() override { return false; } }; //======================================= builtins::builtins(ykdt_pool *dt_pool, gc_pool *token_pool) @@ -1736,8 +1759,16 @@ ykobject builtins::verify( const std::string &name, const std::vector &args, const std::vector &arg_expressions, const std::unordered_map &import_aliases, - const std::string &filepath, slot_matcher *dt_slot_matcher) { - return builtins_[name]->verify(args, arg_expressions, this, dt_pool_, + const std::string &filepath, slot_matcher *dt_slot_matcher, + bool no_stdlib) { + 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.object_type_ = object_type::ERROR_DETECTED; + return o; + } + return builtin_object->verify(args, arg_expressions, this, dt_pool_, import_aliases, filepath, dt_slot_matcher); } std::pair builtins::compile( diff --git a/compiler/src/builtins/builtins.h b/compiler/src/builtins/builtins.h index 470d9b24..89168780 100644 --- a/compiler/src/builtins/builtins.h +++ b/compiler/src/builtins/builtins.h @@ -76,7 +76,8 @@ namespace yaksha { verify(const std::string &name, const std::vector &args, const std::vector &arg_expressions, const std::unordered_map &import_aliases, - const std::string &filepath, slot_matcher *dt_slot_matcher); + const std::string &filepath, slot_matcher *dt_slot_matcher, + bool no_stdlib); /** * Compile a builtin function call. * This assumes verify and has builtin is called. diff --git a/compiler/src/compiler/codegen_c.cpp b/compiler/src/compiler/codegen_c.cpp index 45150b3c..30e9ec73 100644 --- a/compiler/src/compiler/codegen_c.cpp +++ b/compiler/src/compiler/codegen_c.cpp @@ -104,7 +104,11 @@ comp_result codegen_c::emit(codefiles *cf, gc_pool *token_pool, } c_code << "#"; } - c_code << "\n#include \"yk__lib.h\"\n"; + c_code << "\n"; + if (!cf->directives_.no_stdlib_) { + LOG_COMP("no_stdlib mode"); + c_code << "#include \"yk__lib.h\"\n"; + } c_code << "// --forward declarations-- \n"; c_code << global_consts.str(); if (cf->esc_->has_bin_data()) { cf->esc_->compile_binary_data_to(c_code); } @@ -122,9 +126,13 @@ comp_result codegen_c::emit(codefiles *cf, gc_pool *token_pool, if (cf->esc_->has_structures()) { cf->esc_->compile_structures(c_code); } c_code << "// --functions-- \n"; c_code << function_body.str(); - c_code << "#if defined(YK__MINIMAL_MAIN)\n"; - c_code << "int main(void) { return yy__main(); }\n"; - c_code << "#endif"; + if (!cf->directives_.no_main_) { + LOG_COMP("no_main directive is set, no need add the minimal main to the " + "generated code."); + c_code << "#if defined(YK__MINIMAL_MAIN)\n"; + c_code << "int main(void) { return yy__main(); }\n"; + c_code << "#endif"; + } LOG_COMP("c code generated"); return {false, c_code.str()}; } diff --git a/compiler/src/compiler/def_class_visitor.cpp b/compiler/src/compiler/def_class_visitor.cpp index 1036b791..39733cdf 100644 --- a/compiler/src/compiler/def_class_visitor.cpp +++ b/compiler/src/compiler/def_class_visitor.cpp @@ -360,20 +360,21 @@ bool def_class_visitor::has_zero_arg_directive(directive_stmt *obj) { auto directive_type = obj->directive_type_->token_; /* these must have no STR argument */ bool zero_arg_directive = false; - // (Global flag) No need to check for main() 🔴 + // (Global flag) No need to check for main() 🟡 if (directive_type == "no_main") { + // TODO verify parameter count is zero zero_arg_directive = true; 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; } - // (Global flag) no yaksha runtime / libs, 🔴 - if (directive_type == "no_libs") { + // (Global flag) no yaksha runtime / libs, 🟡 + if (directive_type == "no_stdlib") { zero_arg_directive = true; - cf_->directives_.no_libs_ = true; + cf_->directives_.no_stdlib_ = true; } return zero_arg_directive; } diff --git a/compiler/src/compiler/multifile_compiler.cpp b/compiler/src/compiler/multifile_compiler.cpp index e97d8c54..c7be014f 100644 --- a/compiler/src/compiler/multifile_compiler.cpp +++ b/compiler/src/compiler/multifile_compiler.cpp @@ -184,6 +184,13 @@ comp_result multifile_compiler::compile_all(codegen *code_generator) { } delete builtins_obj; } + has_errors |= has_not_allowed_imports_for_no_std_lib(); + if (has_errors) { return {true, ""}; } + if (cf_->directives_.no_main_ || !this->main_required_) { + LOG_COMP("main is not required"); + this->main_required_ = false; + cf_->directives_.no_main_ = true; + } LOG_COMP("post loop"); has_errors |= has_invalid_main_func(main_file_info); if (has_errors) { @@ -353,6 +360,29 @@ bool multifile_compiler::has_invalid_main_func( } multifile_compiler::~multifile_compiler() { delete cf_; } codefiles &multifile_compiler::get_codefiles() const { return *cf_; } +bool multifile_compiler::has_not_allowed_imports_for_no_std_lib() { + if (!cf_->directives_.no_stdlib_) { + LOG_COMP("stdlib is allowed"); + return false; + } + for (auto f : cf_->files_) { + for (auto imp : f->data_->parser_->import_stmts_) { + // allow for imports 'libs', 'w4', 'libs.c' + if (!imp->import_names_.empty() && + imp->import_names_[0]->token_ == "raylib") { + LOG_COMP("in nostdlib mode - raylib is not allowed"); + return true; + } + if (imp->import_names_.size() > 1 && + imp->import_names_[0]->token_ == "libs" && + imp->import_names_[1]->token_ != "c") { + LOG_COMP("in nostdlib mode only libs,w4,libs.c are allowed") + return true; + } + } + } + return false; +} comp_result do_nothing_codegen::emit(codefiles *cf, gc_pool *token_pool, errors::error_printer *ep) { return comp_result{false, ""}; diff --git a/compiler/src/compiler/multifile_compiler.h b/compiler/src/compiler/multifile_compiler.h index c21e115d..7956288d 100644 --- a/compiler/src/compiler/multifile_compiler.h +++ b/compiler/src/compiler/multifile_compiler.h @@ -81,6 +81,7 @@ namespace yaksha { [[nodiscard]] bool has_invalid_main_func(file_info *main_file_info) const; [[nodiscard]] bool all_success() const; codefiles *cf_{nullptr}; + bool has_not_allowed_imports_for_no_std_lib(); }; }// namespace yaksha #endif diff --git a/compiler/src/compiler/to_c_compiler.cpp b/compiler/src/compiler/to_c_compiler.cpp index 5c0a3421..418c397e 100644 --- a/compiler/src/compiler/to_c_compiler.cpp +++ b/compiler/src/compiler/to_c_compiler.cpp @@ -1275,10 +1275,19 @@ std::string to_c_compiler::convert_dt(ykdatatype *basic_dt, return convert_dt(basic_dt->args_[0], datatype_location::STRUCT, "", "") + " const "; } else if (basic_dt->is_str()) { + if (this->cf_->directives_.no_stdlib_) { + error("no stdlib mode does not allow str"); + } return "yk__sds"; } else if (basic_dt->is_sr()) { + if (this->cf_->directives_.no_stdlib_) { + error("no stdlib mode does not allow sr"); + } return "struct yk__bstr"; } else if (basic_dt->is_string_literal()) { + if (this->cf_->directives_.no_stdlib_) { + error("no stdlib mode does not allow string literal"); + } return "char const*"; } else if (basic_dt->is_i8()) { return "int8_t"; @@ -1309,6 +1318,9 @@ std::string to_c_compiler::convert_dt(ykdatatype *basic_dt, } else if (basic_dt->is_any_ptr_to_const()) { return "void const*"; } else if (basic_dt->is_sm_entry() || basic_dt->is_m_entry()) { + if (this->cf_->directives_.no_stdlib_) { + error("no stdlib mode does not allow MEntry/SMEntry"); + } // Handle SMEntry and Entry return esc_->compile(basic_dt, this); } else if (basic_dt->is_function()) { @@ -1916,6 +1928,7 @@ void to_c_compiler::visit_cfor_stmt(cfor_stmt *obj) { void to_c_compiler::visit_enum_stmt(enum_stmt *obj) {} void to_c_compiler::visit_union_stmt(union_stmt *obj) {} void to_c_compiler::visit_directive_stmt(directive_stmt *obj) { + // TODO add support for parameters if (obj->directive_type_->token_ == "ccode") { write_indent(body_); body_ << ::string_utils::unescape(obj->directive_val_->token_); diff --git a/compiler/src/compiler/type_checker.cpp b/compiler/src/compiler/type_checker.cpp index 65d4fe3b..4c852cbe 100644 --- a/compiler/src/compiler/type_checker.cpp +++ b/compiler/src/compiler/type_checker.cpp @@ -309,7 +309,7 @@ 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); + 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_);