From 4ebf134cf2cb9505e5b27fef9c4ccb3f811d0127 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 3 Feb 2023 19:56:38 -0700 Subject: [PATCH] More class gen fixes --- tool/cppcgen.cpp | 273 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 229 insertions(+), 44 deletions(-) diff --git a/tool/cppcgen.cpp b/tool/cppcgen.cpp index 9b88f5c..ce44a00 100644 --- a/tool/cppcgen.cpp +++ b/tool/cppcgen.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2017-2022 Jonathan Müller and cppast contributors // SPDX-License-Identifier: MIT +#include #include "cppast/cpp_member_function.hpp" #include @@ -8,6 +9,7 @@ #include // for generate_code() #include // for generate_code() +#include // for generate_code() #include // for the cpp_entity_kind definition #include // for is_definition() #include // for cpp_namespace @@ -18,6 +20,63 @@ std::string CLASS_SUFFIX = "Ext"; std::string FUNC_SUFFIX = "Func"; std::string FUNC_VAR_PREFIX = "m_"; std::string FUNC_PARAM_PREFIX = "a_"; +std::string PREFIX_SPACING = " "; + +std::vector resplit(const std::string &s, const std::regex &sep_regex = std::regex{"\\s+"}) { + std::sregex_token_iterator iter(s.begin(), s.end(), sep_regex, -1); + std::sregex_token_iterator end; + return {iter, end}; +} + + +namespace std +{ + +template +std::basic_string regex_replace_lambda(BidirIt first, BidirIt last, + const std::basic_regex& re, UnaryFunction f) +{ + std::basic_string s; + + typename std::match_results::difference_type + positionOfLastMatch = 0; + auto endOfLastMatch = first; + + auto callback = [&](const std::match_results& match) + { + auto positionOfThisMatch = match.position(0); + auto diff = positionOfThisMatch - positionOfLastMatch; + + auto startOfThisMatch = endOfLastMatch; + std::advance(startOfThisMatch, diff); + + s.append(endOfLastMatch, startOfThisMatch); + s.append(f(match)); + + auto lengthOfMatch = match.length(0); + + positionOfLastMatch = positionOfThisMatch + lengthOfMatch; + + endOfLastMatch = startOfThisMatch; + std::advance(endOfLastMatch, lengthOfMatch); + }; + + std::regex_iterator begin(first, last, re), end; + std::for_each(begin, end, callback); + + s.append(endOfLastMatch, last); + + return s; +} + +template +std::string regex_replace_lambda(const std::string& s, + const std::basic_regex& re, UnaryFunction f) +{ + return regex_replace_lambda(s.cbegin(), s.cend(), re, f); +} + +} // namespace std bool is_excluded_synopsis(const cppast::cpp_entity& e, cppast::cpp_access_specifier_kind access) { @@ -31,6 +90,78 @@ void print_help(const cxxopts::Options& options) std::cout << options.help({"", "compilation"}) << '\n'; } +const std::map operator_map{ + {"+", "_plus"}, + {"-", "_minus"}, + {"*", "_mul"}, + {"/", "_div"}, + {"%", "_mod"}, + {"^", "_xor"}, + {"&", "_and"}, + {"|", "_or"}, + {"~", "_complement"}, + {"!", "_not"}, + {"=", "_eq"}, + {"<", "_lt"}, + {">", "_gt"}, + {"+=", "_plus_eq"}, + {"-=", "_minus_eq"}, + {"*=", "_mul_eq"}, + {"/=", "_div_eq"}, + {"%=", "_mod_eq"}, + {"^=", "_xor_eq"}, + {"&=", "_and_eq"}, + {"|=", "_or_eq"}, + {"<<", "_shl"}, + {">>", "_shr"}, + {">>=", "_shr_eq"}, + {"<<=", "_shl_eq"}, + {"==", "_dbl_eq"}, + {"!=", "_neq"}, + {"<=", "_lt_eq"}, + {">=", "_gt_eq"}, + {"<=>", "_three_way_comp"}, + {"&&", "_dbl_and"}, + {"||", "_dbl_or"}, + {"++", "_increment"}, + {"--", "_decrement"}, + {",", "_comma"}, + {"->*", "_ptr_accessor_star"}, + {"->", "_ptr_accessor"}, + {"()", "_call"}, + {"[]", "_index"}, + {"co_await", "_co_await"}, + {"new", "_new"}, + {"new []", "_new_index"}, + {"delete", "_delete"}, + {"delete []", "_delete_index"}, +}; + +std::string sanitize_method_name(const std::string& str) { + + std::string result; + auto reg = R"(operator\s*(\+|-|\*|/|%|\^|&|\||~|\!|\=|<|>|\+\=|-=|\*\=|/\=|%\=|\^\=|&\=|\|=|<<|>>|>>\=|<<\=|\=\=|\!\=|<\=|>\=|<\=>|&&|\|\||\+\+|--|,|->\*|->|\(\s*\)|\[\s*\]|new|new\s*\[\]|delete|delete\s*\[\]|\"\"\s*_\w+|co_await|\w+)\s*$)"; + result = std::regex_replace_lambda( + str, + std::regex(reg), + [](const std::smatch& m){ + std::string match = m.str(1); + //std::cerr << "MATCH: " << match << std::endl; + if (operator_map.find(match) == operator_map.end()) { + // doesn't exist + if (match[0] == '"') { + std::string suffix = resplit(match).back(); + return "_operator_suffix_" + suffix; + } + return "_operator_type_" + match; + } + return "_operator" + operator_map.at(match); + } + ); + + return result; +} + // print error message void print_error(const std::string& msg) { @@ -128,10 +259,10 @@ static inline void rtrim(std::string &s) { void output_member_function_typedef(std::stringstream& out, const cppast::cpp_entity& e, std::string& current_class) { if (e.kind() != cppast::cpp_entity_kind::member_function_t) return; - auto& mf = static_cast(e); + auto& mf = static_cast(e); out << "typedef "; out << cppast::to_string(mf.return_type()) << " "; - out << "(*"<< current_class << mf.name() << FUNC_SUFFIX << ")(const " << current_class << "*" << " self"; + out << "(*"<< current_class << sanitize_method_name(mf.name()) << FUNC_SUFFIX << ")(const " << current_class << "*" << " self"; int i = 0; for (auto& param : mf.parameters()) { if (i == 0) { @@ -148,16 +279,16 @@ void output_member_function_typedef(std::stringstream& out, const cppast::cpp_en void output_member_function_param(std::stringstream& out, const cppast::cpp_entity& e, std::string& current_class) { if (e.kind() != cppast::cpp_entity_kind::member_function_t) return; - std::string mpar_name = FUNC_PARAM_PREFIX + e.name(); - out << current_class << e.name() << FUNC_SUFFIX << " " << mpar_name; + std::string mpar_name = FUNC_PARAM_PREFIX + sanitize_method_name(e.name()); + out << current_class << sanitize_method_name(e.name()) << FUNC_SUFFIX << " " << mpar_name; out << ", "; } void output_member_function_var_init(std::stringstream& out, std::string& prefix, const cppast::cpp_entity& e, std::string& current_class) { if (e.kind() != cppast::cpp_entity_kind::member_function_t) return; - std::string mpar_name = FUNC_PARAM_PREFIX + e.name(); - std::string mvar_name = FUNC_VAR_PREFIX + current_class + e.name(); + std::string mpar_name = FUNC_PARAM_PREFIX + sanitize_method_name(e.name()); + std::string mvar_name = FUNC_VAR_PREFIX + current_class + sanitize_method_name(e.name()); out << prefix << prefix << mvar_name << " = " << mpar_name << ";\n"; } @@ -174,11 +305,12 @@ void output_member_function(std::stringstream& out, const cppast::cpp_entity& e, } std::string method_stub = generate_synopsis(e); + method_stub = std::regex_replace(method_stub, std::regex(R"((\s*=\s*[a-zA-Z0-9_]+)?;)"), "", std::regex_constants::format_first_only); rtrim(method_stub); - method_stub.pop_back(); // get rid of semicolon + //method_stub.pop_back(); // get rid of semicolon - std::string mvar_name = FUNC_VAR_PREFIX + current_class + mf.name(); - std::string func_type = current_class + mf.name() + FUNC_SUFFIX; + std::string mvar_name = FUNC_VAR_PREFIX + current_class + sanitize_method_name(mf.name()); + std::string func_type = current_class + sanitize_method_name(mf.name()) + FUNC_SUFFIX; std::stringstream all_params_stream; /* std::unique_ptr ty = cppast::cpp_user_defined_type::build(cppast::cpp_type_ref(cppast::cpp_entity_id(func_type), func_type)); */ /* auto fp = cppast::cpp_function_parameter::build(std::string("callback"), std::move(ty), nullptr); */ @@ -211,7 +343,7 @@ void output_member_function(std::stringstream& out, const cppast::cpp_entity& e, } out << "\n"; out << prefix << "{\n"; - out << prefix << prefix; + out << prefix << PREFIX_SPACING; std::string ret_type = cppast::to_string(mf.return_type()); bool is_void = ret_type == "void"; @@ -224,9 +356,9 @@ void output_member_function(std::stringstream& out, const cppast::cpp_entity& e, /* out << prefix << prefix; */ /* out << "std::cout << \"" << mvar_name << " is NULL? \" << " << "(*" << mvar_name << " == NULL)" << " << \"" << R"(\n)" << "\";\n"; */ - out << prefix << prefix; + out << prefix << PREFIX_SPACING; out << "if (*" << mvar_name << " != NULL){\n"; - out << prefix << prefix << prefix; + out << prefix << PREFIX_SPACING << PREFIX_SPACING; out << "return "; if (i > 0) { out << mvar_name << "(this, " << all_params << ");\n"; @@ -234,14 +366,14 @@ void output_member_function(std::stringstream& out, const cppast::cpp_entity& e, else { out << mvar_name << "(this);\n"; } - out << prefix << prefix; + out << prefix << PREFIX_SPACING; out << "}\n"; // endif if (!is_void) { - out << prefix << prefix; + out << prefix << PREFIX_SPACING; out << "else {\n"; - out << prefix << prefix << prefix; + out << prefix << PREFIX_SPACING << PREFIX_SPACING; out << "return res;\n"; - out << prefix << prefix; + out << prefix << PREFIX_SPACING; out << "}\n"; } @@ -286,7 +418,7 @@ void output_consdes(std::stringstream& out, const cppast::cpp_entity& e, std::st bool is_constructor = e.kind() == cppast::cpp_entity_kind::constructor_t; if (!is_destructor && !is_constructor) return; - auto& mf = static_cast(e); + auto& mf = static_cast(e); int j = 0; for (auto& param : mf.parameters()) { if (param.name().empty()) { @@ -296,8 +428,10 @@ void output_consdes(std::stringstream& out, const cppast::cpp_entity& e, std::st } std::string method_stub = generate_synopsis(e); + // get rid of the ' = 0;' or ' = default;' in the constructor + method_stub = std::regex_replace(method_stub, std::regex(R"((\s*=\s*[a-zA-Z0-9_]+)?;)"), "", std::regex_constants::format_first_only); rtrim(method_stub); - method_stub.pop_back(); // get rid of semicolon + //method_stub.pop_back(); // get rid of semicolon /* std::string mvar_name = FUNC_VAR_PREFIX + (is_constructor ? "new" : "destroy") + current_class; */ std::string func_type = current_class + FUNC_SUFFIX; @@ -324,7 +458,7 @@ void output_consdes(std::stringstream& out, const cppast::cpp_entity& e, std::st // Maybe don't need to? // generate method - auto new_stub = std::regex_replace(method_stub, std::regex(base_class), current_class); + auto new_stub = std::regex_replace(method_stub, std::regex(base_class), current_class, std::regex_constants::format_first_only); out << prefix << new_stub; // call super if (is_constructor) { @@ -352,19 +486,17 @@ void print_ast(std::ostream& out, const cppast::cpp_file& file) { // print file name std::cerr << "AST for '" << file.name() << "':\n"; - out << "#include \n#include \n\n"; std::string prefix; // the current prefix string std::string current_class; // the current class std::string base_class; // the current base class // - std::cerr << "AST for '" << file.name() << "':\n"; std::string fname = file.name(); auto pos = fname.rfind("wx/"); auto len_to_end = fname.length() - pos; std::string include_path = fname.substr(pos, len_to_end); std::string guard_name = "_" + std::regex_replace(include_path, std::regex("/|\\."), "_") + "_EXT_"; std::transform(guard_name.begin(), guard_name.end(), guard_name.begin(), [](char c) { return std::toupper(c); }); - std::cerr << guard_name; + //std::cerr << guard_name; out << "#ifndef " << guard_name << "\n"; out << "#define " << guard_name << "\n\n"; @@ -377,6 +509,9 @@ void print_ast(std::ostream& out, const cppast::cpp_file& file) std::stringstream init_params; std::stringstream var_init; std::stringstream class_stream; + std::stringstream class_close_stream; + bool in_class = false; + bool has_class_ending = false; // - Forward declare the class // - typedef all of the virtual methods with class as first arg @@ -387,7 +522,7 @@ void print_ast(std::ostream& out, const cppast::cpp_file& file) cppast::visit(file, [](const cppast::cpp_entity& e, cppast::cpp_access_specifier_kind access) { // only visit non-templated class definitions - return (!cppast::is_templated(e) && !cppast::is_template(e.kind()) && + return (!cppast::is_templated(e) && !cppast::is_template(e.kind()) && e.kind() != cppast::cpp_entity_kind::class_template_t && access != cppast::cpp_private && ( (e.kind() == cppast::cpp_entity_kind::class_t && cppast::is_definition(e)) || @@ -395,7 +530,8 @@ void print_ast(std::ostream& out, const cppast::cpp_file& file) (e.kind() == cppast::cpp_entity_kind::constructor_t) || (e.kind() == cppast::cpp_entity_kind::destructor_t && static_cast(e).is_virtual()) || e.kind() == cppast::cpp_entity_kind::base_class_t || - e.kind() == cppast::cpp_entity_kind::macro_definition_t + e.kind() == cppast::cpp_entity_kind::macro_definition_t || + e.kind() == cppast::cpp_entity_kind::type_alias_t ) ); }, @@ -403,40 +539,62 @@ void print_ast(std::ostream& out, const cppast::cpp_file& file) [&](const cppast::cpp_entity& e, cppast::visitor_info info) { if (info.event == cppast::visitor_info::container_entity_enter) { if (e.kind() == cppast::cpp_entity_kind::class_t) { + if (e.name().empty()) { + // it was a fake exit event + //std::cerr << "FAAAAAAAAAAAKE\n"; + //std::cerr << current_class << "\n\n"; + + in_class = true; + has_class_ending = true; + return true; + } + else if (has_class_ending) { + auto param_str = init_params.str(); + auto var_init_str = var_init.str(); + if (param_str.length() >= 2) { + // delete comma and space + param_str.pop_back(); + param_str.pop_back(); + } + output_constructor(class_stream, var_init_str, param_str, prefix, base_class, current_class); + init_params.str(std::string()); // clear + var_init.str(std::string()); // clear + class_stream << "};\n\n"; + // we have visited all children of a container, + // remove prefix + prefix.pop_back(); + prefix.pop_back(); + has_class_ending = false; + //std::cerr << "EXIT CLASS\n"; + //std::cerr << current_class << "\n\n"; + } base_class = e.name(); current_class = e.name() + CLASS_SUFFIX; + //std::cerr << "ENTER CLASS\n"; + //std::cerr << current_class << "\n\n"; forward_decls << "class " << current_class << ";\n"; class_stream << "class " << current_class << ": public " << base_class; class_stream << "\n{\n"; class_stream << "public:\n"; + prefix += PREFIX_SPACING; + in_class = true; } - prefix += " "; } else if (info.event == cppast::visitor_info::container_entity_exit) { - if (e.kind() == cppast::cpp_entity_kind::class_t) { - auto param_str = init_params.str(); - auto var_init_str = var_init.str(); - if (param_str.length() >= 2) { - // delete comma and space - param_str.pop_back(); - param_str.pop_back(); - } - output_constructor(class_stream, var_init_str, param_str, prefix, base_class, current_class); - init_params.str(std::string()); // clear - var_init.str(std::string()); // clear - class_stream << "};\n\n"; + if (e.kind() == cppast::cpp_entity_kind::class_t && !has_class_ending) { + has_class_ending = true; + in_class = false; } - // we have visited all children of a container, - // remove prefix - prefix.pop_back(); - prefix.pop_back(); } else { + if (!in_class) { + return true; + } if (e.kind() == cppast::cpp_entity_kind::constructor_t) { - auto& mf = static_cast(e); + auto& mf = static_cast(e); int j = 0; for (auto& param : mf.parameters()) { if (param.name().empty()) { @@ -448,7 +606,7 @@ void print_ast(std::ostream& out, const cppast::cpp_file& file) output_consdes(class_stream, e, prefix, base_class, current_class); } else if (e.kind() == cppast::cpp_entity_kind::destructor_t) { - auto& mf = static_cast(e); + auto& mf = static_cast(e); int j = 0; for (auto& param : mf.parameters()) { if (param.name().empty()) { @@ -460,7 +618,11 @@ void print_ast(std::ostream& out, const cppast::cpp_file& file) output_consdes(class_stream, e, prefix, base_class, current_class); } else if (e.kind() == cppast::cpp_entity_kind::member_function_t) { - auto& mf = static_cast(e); + auto& mf = static_cast(e); + //std::cerr << sanitize_method_name(mf.name()) << "\n\n"; + /* if (mf.is_definition()) { */ + /* return true; */ + /* } */ int j = 0; for (auto& param : mf.parameters()) { if (param.name().empty()) { @@ -473,6 +635,10 @@ void print_ast(std::ostream& out, const cppast::cpp_file& file) output_member_function_typedef(forward_decls, e, current_class); output_member_function(class_stream, e, prefix, base_class, current_class); } + /* else if (e.kind() == cppast::cpp_entity_kind::type_alias_t) { */ + /* auto& alias = static_cast(e); */ + /* std::cerr << "DFSFSDF " << alias.name() << "\n\n"; */ + /* } */ /* else if (e.kind() == cppast::cpp_entity_kind::macro_definition_t) { */ /* auto& mf = static_cast(e); */ /* std::cerr << e.name() << "\n"; */ @@ -481,6 +647,25 @@ void print_ast(std::ostream& out, const cppast::cpp_file& file) return true; }); + + if (has_class_ending) { + auto param_str = init_params.str(); + auto var_init_str = var_init.str(); + if (param_str.length() >= 2) { + // delete comma and space + param_str.pop_back(); + param_str.pop_back(); + } + output_constructor(class_stream, var_init_str, param_str, prefix, base_class, current_class); + init_params.str(std::string()); // clear + var_init.str(std::string()); // clear + class_stream << "};\n\n"; + // we have visited all children of a container, + // remove prefix + prefix.pop_back(); + prefix.pop_back(); + has_class_ending = false; + } out << forward_decls.str() << "\n"; out << class_stream.str() << "\n\n"; out << "#endif" << "\n";