diff --git a/examples/user-defined-metafunction/build.cpp2 b/examples/user-defined-metafunction/build.cpp2 new file mode 100644 index 0000000..9c5b5a3 --- /dev/null +++ b/examples/user-defined-metafunction/build.cpp2 @@ -0,0 +1,5 @@ +import cpp2b.build; + +build: (inout b: cpp2b::build) -> void = { + b.cppfront_reflect_inject("metafunction.h2"); +} diff --git a/examples/user-defined-metafunction/example-with-user-defined-metafunction.cpp2 b/examples/user-defined-metafunction/example-with-user-defined-metafunction.cpp2 new file mode 100644 index 0000000..f8a7db1 --- /dev/null +++ b/examples/user-defined-metafunction/example-with-user-defined-metafunction.cpp2 @@ -0,0 +1,10 @@ +named_coords: @example::printable type = { + x: f32; + y: f32; + name: std::string; +} + +main: () = { + a: named_coords = (10, 42, "Zeke"); + a.print_fields(); +} diff --git a/examples/user-defined-metafunction/metafunction.h2 b/examples/user-defined-metafunction/metafunction.h2 new file mode 100644 index 0000000..bd431ac --- /dev/null +++ b/examples/user-defined-metafunction/metafunction.h2 @@ -0,0 +1,5 @@ +example: namespace = { + printable: (inout t: meta::type_declaration) = { + + } +} diff --git a/share/cpp2b.build.cppm.tpl b/share/cpp2b.build.cppm.tpl index 587e09c..85b5e1d 100644 --- a/share/cpp2b.build.cppm.tpl +++ b/share/cpp2b.build.cppm.tpl @@ -15,6 +15,9 @@ CPP2B_BUILD_API void (*cpp2b_detail_build_binary_name)( cpp2b_detail_build_impl *, std::filesystem::path, std::string_view) = nullptr; +CPP2B_BUILD_API void (*cpp2b_detail_build_cppfront_reflect_inject)( + cpp2b_detail_build_impl *, std::filesystem::path) = nullptr; + export namespace cpp2b { class build { cpp2b_detail_build_impl *impl; @@ -34,6 +37,24 @@ public: return (*cpp2b_detail_build_binary_name)(impl, binary_source_path, new_binary_name); } + + /** + * Parses the .h2 header looking for functions with any of these signatures: + * + * (inout _: cpp2::meta::declaration) -> void + * (inout _: cpp2::meta::type_declaration) -> void + * (inout _: cpp2::meta::function_declaration) -> void + * (inout _: cpp2::meta::object_declaration) -> void + * (inout _: cpp2::meta::alias_declaration) -> void + * + * Every function with one of these signatures are injected into cppfront's + * reflect.h2 `apply_metafunctions`, making them available as user-defined + * metafunctions. Additionally the header_path is injected as an #include at + * the top of the reflect.h2 header. + */ + inline auto cppfront_reflect_inject(std::filesystem::path header_path) -> void { + return (*cpp2b_detail_build_cppfront_reflect_inject)(impl, header_path); + } }; } // namespace cpp2b diff --git a/src/main.cpp2 b/src/main.cpp2 index 36c936e..0c76c21 100644 --- a/src/main.cpp2 +++ b/src/main.cpp2 @@ -635,10 +635,15 @@ build_binary: (info: cpp2b_source_binary_info) -> build_binary_result = { return result; } +cpp2b_reflect_header_info: @struct type = { + public header_paths: std::vector; + public metafunction_names: std::vector; +} cpp2b_detail_build_impl: @struct type = { public build_cpp2_dir: fs::path; public bins: *std::vector; + public reflect_inject: *cpp2b_reflect_header_info; } cpp2b_detail_build_binary_name: ( copy impl: *cpp2b_detail_build_impl, @@ -660,6 +665,21 @@ cpp2b_detail_build_binary_name: ( bin*.preferred_name = (build_cpp2_dir / new_binary_name).generic_string(); } + +cpp2b_detail_build_cppfront_reflect_inject: ( + copy impl: *cpp2b_detail_build_impl, + copy header_path: std::filesystem::path, +) = { + build_cpp2_dir: fs::path = impl*.build_cpp2_dir; + p := build_cpp2_dir / header_path; + + if !fs::exists(header_path) { + return; + } + + impl*.reflect_inject*.header_paths.emplace_back(header_path); +} + cpp2b_detail_build: (copy _impl: *cpp2b_detail_build_impl) -> void = { // empty. this is just so we can decltype the signature } @@ -752,6 +772,8 @@ full_build_info: @struct type = { bin_results: std::vector = (); + reflect_inject: cpp2b_reflect_header_info = (); + empty: (this) -> bool = { return bin_results.empty(); } @@ -782,6 +804,7 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c transpile_source_dir: fs::path = ".cache/cpp2/source"; cpp2_source_files: std::vector = (); + cpp2_header_files: std::vector = (); cpp1_module_source_files: std::vector = (); ec: std::error_code = (); @@ -803,6 +826,15 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c } cpp2_source_files.emplace_back(rel_path); + } else if p.extension() == ".h2" { + rel_path := fs::relative(p, fs::current_path()); + for rel_path do(rel_path_comp) { + if rel_path_comp.string().starts_with(".") { + continue src_loop; + } + } + + cpp2_header_files.emplace_back(rel_path); } else if p.extension() == ".cppm" { rel_path := fs::relative(p, fs::current_path()); for rel_path do(rel_path_comp) { @@ -922,10 +954,12 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c build_dylib.assert_symbol("cpp2b_detail_build"); build_dylib.assert_symbol("cpp2b_detail_build_binary_name"); + build_dylib.assert_symbol("cpp2b_detail_build_cppfront_reflect_inject"); build_dylib.get_variable("cpp2b_detail_build_binary_name") = cpp2b_detail_build_binary_name&; + build_dylib.get_variable("cpp2b_detail_build_binary_name") = cpp2b_detail_build_cppfront_reflect_inject&; - b: cpp2b_detail_build_impl = (build_script.src.parent_path(), stuff.bins&); + b: cpp2b_detail_build_impl = (build_script.src.parent_path(), stuff.bins&, stuff.reflect_inject&); build_dylib.get_function("cpp2b_detail_build")(b&); }