diff --git a/include/cpp2reflect.h b/include/cpp2reflect.h new file mode 100644 index 0000000000..7a0221d1be --- /dev/null +++ b/include/cpp2reflect.h @@ -0,0 +1,887 @@ + +#ifndef CPP2REFLECT_H_CPP2 +#define CPP2REFLECT_H_CPP2 + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "reflect.h2" + +#line 19 "reflect.h2" +namespace cpp2 { + +namespace meta { + +#line 28 "reflect.h2" +class source_position; + +#line 44 "reflect.h2" +class passing_style; + +#line 83 "reflect.h2" +class compiler_services; + +#line 276 "reflect.h2" +class declaration_base; + +#line 318 "reflect.h2" +class declaration; + +#line 403 "reflect.h2" +class function_declaration; + +#line 494 "reflect.h2" +class object_declaration; + +#line 530 "reflect.h2" +class type_declaration; + +#line 668 "reflect.h2" +class alias_declaration; + +#line 1118 "reflect.h2" +class value_member_info; + +#line 1634 "reflect.h2" +} + +} + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "reflect.h2" + +// Copyright (c) Herb Sutter +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#line 14 "reflect.h2" +//=========================================================================== +// Reflection and meta +//=========================================================================== + +#line 19 "reflect.h2" +namespace cpp2 { + +namespace meta { + +#line 24 "reflect.h2" +using lineno_t = std::int32_t; +using colno_t = std::int32_t; // not int16_t... encountered >80,000 char line during testing +using index_t = std::int32_t; + +class CPPFRONTAPI source_position + { + public: lineno_t lineno {1}; // one-based offset into program source + public: colno_t colno {1}; // one-based offset into line + + public: source_position(); + public: source_position(cpp2::impl::in l, cpp2::impl::in c = 1); + +#line 40 "reflect.h2" + public: [[nodiscard]] auto to_string() const& -> std::string; + public: [[nodiscard]] auto operator<=>(source_position const& that) const& -> std::strong_ordering = default; +public: source_position(source_position const& that); + +public: auto operator=(source_position const& that) -> source_position& ; +public: source_position(source_position&& that) noexcept; +public: auto operator=(source_position&& that) noexcept -> source_position& ; + +#line 41 "reflect.h2" +}; + +#line 44 "reflect.h2" +class CPPFRONTAPI passing_style + { +private: cpp2::i8 _value; private: constexpr passing_style(cpp2::impl::in _val); + +private: constexpr auto operator=(cpp2::impl::in _val) -> passing_style& ; +public: static const passing_style in; +public: static const passing_style in_ref; +public: static const passing_style copy; +public: static const passing_style inout; +public: static const passing_style out; +public: static const passing_style move; +public: static const passing_style forward; +public: static const passing_style forward_ref; +public: static const passing_style invalid; +public: [[nodiscard]] constexpr auto get_raw_value() const& -> cpp2::i8; +public: constexpr explicit passing_style(); +public: constexpr passing_style(passing_style const& that); +public: constexpr auto operator=(passing_style const& that) -> passing_style& ; +public: constexpr passing_style(passing_style&& that) noexcept; +public: constexpr auto operator=(passing_style&& that) noexcept -> passing_style& ; +public: [[nodiscard]] auto operator<=>(passing_style const& that) const& -> std::strong_ordering = default; +public: [[nodiscard]] auto to_string_impl(cpp2::impl::in prefix) const& -> std::string; +public: [[nodiscard]] auto to_string() const& -> std::string; +public: [[nodiscard]] auto to_code() const& -> std::string; +public: [[nodiscard]] static auto from_string(cpp2::impl::in s) -> passing_style; +public: [[nodiscard]] static auto from_code(cpp2::impl::in s) -> passing_style; + +#line 55 "reflect.h2" +}; + +// Not `passing_style::from_string`, +// as it will not return `invalid` (does this call for a `from_string_or`?), +// and to workaround #555. +[[nodiscard]] auto to_passing_style(cpp2::impl::in s) -> passing_style; + +#line 76 "reflect.h2" +//----------------------------------------------------------------------- +// +// Compiler services +// +//----------------------------------------------------------------------- +// + +class CPPFRONTAPI compiler_services + { + // Common data members + // + private: std::any data_; + private: [[nodiscard]] auto data() const& -> decltype(auto); + private: [[nodiscard]] auto data() & -> decltype(auto); + + // Constructor + // + public: explicit compiler_services( + + cpp2::impl::in data_v + ); + +#line 105 "reflect.h2" + // Common API + // + public: auto set_metafunction_name(cpp2::impl::in name, cpp2::impl::in> args) & -> void; + +#line 113 "reflect.h2" + public: [[nodiscard]] auto get_metafunction_name() const& -> std::string_view; + + public: [[nodiscard]] auto get_argument(cpp2::impl::in index) & -> std::string; + +#line 123 "reflect.h2" + public: [[nodiscard]] auto get_arguments() & -> std::vector; + +#line 128 "reflect.h2" + public: [[nodiscard]] auto arguments_were_used() const& -> bool; + + protected: [[nodiscard]] auto parse_statement( + + std::string_view source + ) & -> auto; + +#line 184 "reflect.h2" + public: auto add_runtime_support_include(cpp2::impl::in s) & -> void; + + public: [[nodiscard]] virtual auto position() const -> source_position; + +#line 192 "reflect.h2" + // Error diagnosis and handling, integrated with compiler output + // Unlike a contract violation, .requires continues further processing + // + public: auto require( + + cpp2::impl::in b, + cpp2::impl::in msg + ) const& -> void; + +#line 206 "reflect.h2" + public: auto error(cpp2::impl::in msg) const& -> void; + +#line 215 "reflect.h2" + // Enable custom contracts on this object, integrated with compiler output + // Unlike .requires, a contract violation stops further processing + // + public: auto report_violation(auto const& msg) const& -> void; + +#line 226 "reflect.h2" + public: [[nodiscard]] auto is_active() const& -> bool; + public: virtual ~compiler_services() noexcept; +public: compiler_services(compiler_services const& that); + +#line 227 "reflect.h2" +}; + +#line 230 "reflect.h2" +/* +//----------------------------------------------------------------------- +// +// Type IDs +// +//----------------------------------------------------------------------- +// + +// All type_ids are wrappers around a pointer to node +// +type_id: @dll_visible @polymorphic_base @copyable type = +{ + this: compiler_services = (); + + n: type_id_node; + + protected operator=: ( + out this, + n_: type_id_node, + s : compiler_services + ) + = { + compiler_services = s; + n = n_; + assert( n, "a meta::type_id must point to a valid type_id_node, not null" ); + } + + is_wildcard : (this) -> bool = n.is_wildcard(); + is_pointer_qualified: (this) -> bool = n.is_pointer_qualified(); + template_args_count : (this) -> int = n.template_arguments().ssize(); + to_string : (this) -> std::string = n.to_string(); + + position: (override this) -> source_position = n.position(); +} +*/ + +#line 267 "reflect.h2" +//----------------------------------------------------------------------- +// +// Declarations +// +//----------------------------------------------------------------------- +// + +// All declarations are wrappers around a pointer to node +// +class CPPFRONTAPI declaration_base +: public compiler_services { + +#line 280 "reflect.h2" + public: class node_pointer + { + private: void* n; + + public: template node_pointer( + + T const& n_ + ); +#line 284 "reflect.h2" + public: template auto operator=( + + T const& n_ + ) -> node_pointer& ; + +#line 294 "reflect.h2" + public: [[nodiscard]] auto operator*() const& -> decltype(auto); + public: node_pointer(node_pointer const& that); + +public: auto operator=(node_pointer const& that) -> node_pointer& ; +public: node_pointer(node_pointer&& that) noexcept; +public: auto operator=(node_pointer&& that) noexcept -> node_pointer& ; + +#line 295 "reflect.h2" + }; + + protected: node_pointer n; + + protected: explicit declaration_base( + + cpp2::impl::in n_, + cpp2::impl::in s + ); + +#line 309 "reflect.h2" + public: [[nodiscard]] auto position() const -> source_position override; + + public: [[nodiscard]] auto print() const& -> std::string; + public: virtual ~declaration_base() noexcept; +public: declaration_base(declaration_base const& that); + +#line 312 "reflect.h2" +}; + +#line 315 "reflect.h2" +//----------------------------------------------------------------------- +// All declarations +// +class CPPFRONTAPI declaration +: public declaration_base { + +#line 322 "reflect.h2" + public: explicit declaration( + + cpp2::impl::in n_, + cpp2::impl::in s + ); + +#line 331 "reflect.h2" + public: [[nodiscard]] auto is_public() const& -> bool; + public: [[nodiscard]] auto is_protected() const& -> bool; + public: [[nodiscard]] auto is_private() const& -> bool; + public: [[nodiscard]] auto is_default_access() const& -> bool; + + public: auto default_to_public() & -> void; + public: auto default_to_protected() & -> void; + public: auto default_to_private() & -> void; + + public: [[nodiscard]] auto make_public() & -> bool; + public: [[nodiscard]] auto make_protected() & -> bool; + public: [[nodiscard]] auto make_private() & -> bool; + + public: [[nodiscard]] auto is_dll_visible() const& -> bool; + public: [[nodiscard]] auto make_dll_visible() & -> bool; + + public: [[nodiscard]] auto has_name() const& -> bool; + public: [[nodiscard]] auto has_name(cpp2::impl::in s) const& -> bool; + + public: [[nodiscard]] auto name() const& -> std::string_view; + +#line 355 "reflect.h2" + public: [[nodiscard]] auto has_initializer() const& -> bool; + + public: [[nodiscard]] auto is_global() const& -> bool; + public: [[nodiscard]] auto is_function() const& -> bool; + public: [[nodiscard]] auto is_object() const& -> bool; + public: [[nodiscard]] auto is_base_object() const& -> bool; + public: [[nodiscard]] auto is_member_object() const& -> bool; + public: [[nodiscard]] auto is_type() const& -> bool; + public: [[nodiscard]] auto is_namespace() const& -> bool; + public: [[nodiscard]] auto is_alias() const& -> bool; + + public: [[nodiscard]] auto is_type_alias() const& -> bool; + public: [[nodiscard]] auto is_namespace_alias() const& -> bool; + public: [[nodiscard]] auto is_object_alias() const& -> bool; + + public: [[nodiscard]] auto is_function_expression() const& -> bool; + + public: [[nodiscard]] auto as_function() const& -> function_declaration; + public: [[nodiscard]] auto as_object() const& -> object_declaration; + public: [[nodiscard]] auto as_type() const& -> type_declaration; + public: [[nodiscard]] auto as_alias() const& -> alias_declaration; + + public: [[nodiscard]] auto get_parent() const& -> declaration; + + public: [[nodiscard]] auto parent_is_function() const& -> bool; + public: [[nodiscard]] auto parent_is_object() const& -> bool; + public: [[nodiscard]] auto parent_is_type() const& -> bool; + public: [[nodiscard]] auto parent_is_namespace() const& -> bool; + public: [[nodiscard]] auto parent_is_alias() const& -> bool; + + public: [[nodiscard]] auto parent_is_type_alias() const& -> bool; + public: [[nodiscard]] auto parent_is_namespace_alias() const& -> bool; + public: [[nodiscard]] auto parent_is_object_alias() const& -> bool; + + public: [[nodiscard]] auto parent_is_polymorphic() const& -> bool; + + public: auto mark_for_removal_from_enclosing_type() & -> void; + public: virtual ~declaration() noexcept; +public: declaration(declaration const& that); + + // this precondition should be sufficient ... + +#line 397 "reflect.h2" +}; + +#line 400 "reflect.h2" +//----------------------------------------------------------------------- +// Function declarations +// +class CPPFRONTAPI function_declaration +: public declaration { + +#line 407 "reflect.h2" + public: explicit function_declaration( + + cpp2::impl::in n_, + cpp2::impl::in s + ); + +#line 417 "reflect.h2" + public: [[nodiscard]] auto index_of_parameter_named(cpp2::impl::in s) const& -> int; + public: [[nodiscard]] auto has_parameter_named(cpp2::impl::in s) const& -> bool; + public: [[nodiscard]] auto has_in_parameter_named(cpp2::impl::in s) const& -> bool; + public: [[nodiscard]] auto has_in_ref_parameter_named(cpp2::impl::in s) const& -> bool; + public: [[nodiscard]] auto has_copy_parameter_named(cpp2::impl::in s) const& -> bool; + public: [[nodiscard]] auto has_inout_parameter_named(cpp2::impl::in s) const& -> bool; + public: [[nodiscard]] auto has_out_parameter_named(cpp2::impl::in s) const& -> bool; + public: [[nodiscard]] auto has_move_parameter_named(cpp2::impl::in s) const& -> bool; + public: [[nodiscard]] auto has_forward_parameter_named(cpp2::impl::in s) const& -> bool; + public: [[nodiscard]] auto first_parameter_name() const& -> std::string; + + public: [[nodiscard]] auto has_parameter_with_name_and_pass(cpp2::impl::in s, cpp2::impl::in pass) const& -> bool; + + public: [[nodiscard]] auto is_function_with_this() const& -> bool; + public: [[nodiscard]] auto is_virtual() const& -> bool; + public: [[nodiscard]] auto is_defaultable() const& -> bool; + public: [[nodiscard]] auto is_constructor() const& -> bool; + public: [[nodiscard]] auto is_default_constructor() const& -> bool; + public: [[nodiscard]] auto is_move() const& -> bool; + public: [[nodiscard]] auto is_swap() const& -> bool; + public: [[nodiscard]] auto is_constructor_with_that() const& -> bool; + public: [[nodiscard]] auto is_constructor_with_in_that() const& -> bool; + public: [[nodiscard]] auto is_constructor_with_move_that() const& -> bool; + public: [[nodiscard]] auto is_assignment() const& -> bool; + public: [[nodiscard]] auto is_assignment_with_that() const& -> bool; + public: [[nodiscard]] auto is_assignment_with_in_that() const& -> bool; + public: [[nodiscard]] auto is_assignment_with_move_that() const& -> bool; + public: [[nodiscard]] auto is_destructor() const& -> bool; + + public: [[nodiscard]] auto is_copy_or_move() const& -> bool; + + public: [[nodiscard]] auto has_declared_return_type() const& -> bool; + public: [[nodiscard]] auto has_deduced_return_type() const& -> bool; + public: [[nodiscard]] auto has_bool_return_type() const& -> bool; + public: [[nodiscard]] auto has_non_void_return_type() const& -> bool; + + public: [[nodiscard]] auto unnamed_return_type() const& -> std::string; + + public: [[nodiscard]] auto get_parameters() const& -> std::vector; + +#line 465 "reflect.h2" + public: [[nodiscard]] auto is_binary_comparison_function() const& -> bool; + + public: auto default_to_virtual() & -> void; + + public: [[nodiscard]] auto make_virtual() & -> bool; + + public: auto add_initializer(cpp2::impl::in source) & -> void; + public: function_declaration(function_declaration const& that); + + +#line 488 "reflect.h2" +}; + +#line 491 "reflect.h2" +//----------------------------------------------------------------------- +// Object declarations +// +class CPPFRONTAPI object_declaration +: public declaration { + +#line 498 "reflect.h2" + public: explicit object_declaration( + + cpp2::impl::in n_, + cpp2::impl::in s + ); + +#line 508 "reflect.h2" + public: [[nodiscard]] auto is_const() const& -> bool; + public: [[nodiscard]] auto has_wildcard_type() const& -> bool; + + public: [[nodiscard]] auto type() const& -> std::string; + +#line 518 "reflect.h2" + public: [[nodiscard]] auto initializer() const& -> std::string; + public: object_declaration(object_declaration const& that); + + +#line 524 "reflect.h2" +}; + +#line 527 "reflect.h2" +//----------------------------------------------------------------------- +// Type declarations +// +class CPPFRONTAPI type_declaration +: public declaration { + +#line 534 "reflect.h2" + public: explicit type_declaration( + + cpp2::impl::in n_, + cpp2::impl::in s + ); + +#line 544 "reflect.h2" + public: auto reserve_names(cpp2::impl::in name, auto&& ...etc) const& -> void; + +#line 558 "reflect.h2" + public: [[nodiscard]] auto is_polymorphic() const& -> bool; + public: [[nodiscard]] auto is_final() const& -> bool; + public: [[nodiscard]] auto make_final() & -> bool; + + public: [[nodiscard]] auto get_member_functions() const& -> std::vector; + +#line 573 "reflect.h2" + public: [[nodiscard]] auto get_member_functions_needing_initializer() const& -> std::vector; + +#line 588 "reflect.h2" + public: [[nodiscard]] auto get_member_objects() const& -> std::vector; + +#line 598 "reflect.h2" + public: [[nodiscard]] auto get_member_types() const& -> std::vector; + +#line 608 "reflect.h2" + public: [[nodiscard]] auto get_member_aliases() const& -> std::vector; + +#line 618 "reflect.h2" + public: [[nodiscard]] auto get_members() const& -> std::vector; +struct query_declared_value_set_functions_ret { bool out_this_in_that; bool out_this_move_that; bool inout_this_in_that; bool inout_this_move_that; }; + + + +#line 628 "reflect.h2" + public: [[nodiscard]] auto query_declared_value_set_functions() const& -> query_declared_value_set_functions_ret; + +#line 644 "reflect.h2" + public: auto add_member(cpp2::impl::in source) & -> void; + +#line 658 "reflect.h2" + public: auto remove_marked_members() & -> void; + public: auto remove_all_members() & -> void; + + public: auto disable_member_function_generation() & -> void; + public: type_declaration(type_declaration const& that); + +#line 662 "reflect.h2" +}; + +#line 665 "reflect.h2" +//----------------------------------------------------------------------- +// Alias declarations +// +class CPPFRONTAPI alias_declaration +: public declaration { + +#line 672 "reflect.h2" + public: explicit alias_declaration( + + cpp2::impl::in n_, + cpp2::impl::in s + ); + public: alias_declaration(alias_declaration const& that); + + +#line 681 "reflect.h2" +}; + +#line 684 "reflect.h2" +//----------------------------------------------------------------------- +// +// Metafunctions - these are hardwired for now until we get to the +// step of writing a Cpp2 interpreter to run inside the compiler +// +//----------------------------------------------------------------------- +// + +//----------------------------------------------------------------------- +// Some common metafunction helpers (metafunctions are just functions, +// so they can be factored as usual) +// +auto add_virtual_destructor(meta::type_declaration& t) -> void; + +#line 702 "reflect.h2" +//----------------------------------------------------------------------- +// +// "... an abstract base class defines an interface ..." +// +// -- Stroustrup (The Design and Evolution of C++, 12.3.1) +// +//----------------------------------------------------------------------- +// +// interface +// +// an abstract base class having only pure virtual functions +// +CPPFRONTAPI auto interface(meta::type_declaration& t) -> void; + +#line 741 "reflect.h2" +//----------------------------------------------------------------------- +// +// "C.35: A base class destructor should be either public and +// virtual, or protected and non-virtual." +// +// "[C.43] ... a base class should not be copyable, and so does not +// necessarily need a default constructor." +// +// -- Stroustrup, Sutter, et al. (C++ Core Guidelines) +// +//----------------------------------------------------------------------- +// +// polymorphic_base +// +// A pure polymorphic base type that is not copyable, and whose +// destructor is either public and virtual or protected and nonvirtual. +// +// Unlike an interface, it can have nonpublic and nonvirtual functions. +// +CPPFRONTAPI auto polymorphic_base(meta::type_declaration& t) -> void; + +#line 785 "reflect.h2" +//----------------------------------------------------------------------- +// +// "... A totally ordered type ... requires operator<=> that +// returns std::strong_ordering. If the function is not +// user-written, a lexicographical memberwise implementation +// is generated by default..." +// +// -- P0707R4, section 3 +// +// Note: This feature derived from Cpp2 was already adopted +// into Standard C++ via paper P0515, so most of the +// heavy lifting is done by the Cpp1 C++20/23 compiler, +// including the memberwise default semantics +// (In contrast, cppfront has to do the work itself for +// default memberwise semantics for operator= assignment +// as those aren't yet part of Standard C++) +// +//----------------------------------------------------------------------- +// + +auto ordered_impl( + meta::type_declaration& t, + cpp2::impl::in ordering// must be "strong_ordering" etc. +) -> void; + +#line 829 "reflect.h2" +//----------------------------------------------------------------------- +// ordered - a totally ordered type +// +// Note: the ordering that should be encouraged as default gets the nice name +// +CPPFRONTAPI auto ordered(meta::type_declaration& t) -> void; + +#line 839 "reflect.h2" +//----------------------------------------------------------------------- +// weakly_ordered - a weakly ordered type +// +CPPFRONTAPI auto weakly_ordered(meta::type_declaration& t) -> void; + +#line 847 "reflect.h2" +//----------------------------------------------------------------------- +// partially_ordered - a partially ordered type +// +CPPFRONTAPI auto partially_ordered(meta::type_declaration& t) -> void; + +#line 856 "reflect.h2" +//----------------------------------------------------------------------- +// +// "A value is ... a regular type. It must have all public +// default construction, copy/move construction/assignment, +// and destruction, all of which are generated by default +// if not user-written; and it must not have any protected +// or virtual functions (including the destructor)." +// +// -- P0707R4, section 3 +// +//----------------------------------------------------------------------- +// +// copyable +// +// A type with (copy and move) x (construction and assignment) +// +CPPFRONTAPI auto copyable(meta::type_declaration& t) -> void; + +#line 899 "reflect.h2" +//----------------------------------------------------------------------- +// +// hashable +// +// A memberwise hashable type +// +CPPFRONTAPI auto hashable(meta::type_declaration& t) -> void; + +#line 931 "reflect.h2" +//----------------------------------------------------------------------- +// +// basic_value +// +// A regular type: copyable, plus has public default construction +// and no protected or virtual functions +// +CPPFRONTAPI auto basic_value(meta::type_declaration& t) -> void; + +#line 956 "reflect.h2" +//----------------------------------------------------------------------- +// +// "A 'value' is a totally ordered basic_value..." +// +// -- P0707R4, section 3 +// +// value - a value type that is totally ordered +// +// Note: the ordering that should be encouraged as default gets the nice name +// +CPPFRONTAPI auto value(meta::type_declaration& t) -> void; + +#line 972 "reflect.h2" +CPPFRONTAPI auto weakly_ordered_value(meta::type_declaration& t) -> void; + +#line 978 "reflect.h2" +CPPFRONTAPI auto partially_ordered_value(meta::type_declaration& t) -> void; + +#line 985 "reflect.h2" +//----------------------------------------------------------------------- +// +// C.20: If you can avoid defining default operations, do +// +// ##### Reason +// +// It's the simplest and gives the cleanest semantics. +// +// ... +// +// This is known as "the rule of zero". +// +// -- C++ Core Guidelines +// C.20: If you can avoid defining any default operations, do +// +// +//----------------------------------------------------------------------- +// +// cpp1_rule_of_zero +// +// a type without declared copy/move/destructor functions +// +CPPFRONTAPI auto cpp1_rule_of_zero(meta::type_declaration& t) -> void; + +#line 1019 "reflect.h2" +//----------------------------------------------------------------------- +// +// "By definition, a `struct` is a `class` in which members +// are by default `public`; that is, +// +// struct s { ... +// +// is simply shorthand for +// +// class s { public: ... +// +// ... Which style you use depends on circumstances and taste. +// I usually prefer to use `struct` for classes that have all +// data `public`." +// +// -- Stroustrup (The C++ Programming Language, 3rd ed., p. 234) +// +//----------------------------------------------------------------------- +// +// struct +// +// a type with only public bases, objects, and functions, +// no virtual functions, and no user-defined constructors +// (i.e., no invariants) or assignment or destructors. +// +// For GCC 10 compatibility, optionally allow passing struct +// that generates a memberwise constructor with a generic deduced +// parameters instead of concrete forwarding parameters (mainly used +// for cppfront internal use, so cppfront builds under GCC 10) +// +CPPFRONTAPI auto cpp2_struct(meta::type_declaration& t) -> void; + +#line 1101 "reflect.h2" +//----------------------------------------------------------------------- +// +// "C enumerations constitute a curiously half-baked concept. ... +// the cleanest way out was to deem each enumeration a separate type." +// +// -- Stroustrup (The Design and Evolution of C++, 11.7) +// +// "An enumeration is a distinct type ... with named constants" +// +// -- ISO C++ Standard +// +//----------------------------------------------------------------------- +// +// basic_enum +// +// a type together with named constants that are its possible values +// +class value_member_info { + public: std::string name; + public: std::string type; + public: std::string value; + public: value_member_info(auto const& name_, auto const& type_, auto const& value_); + +#line 1122 "reflect.h2" +}; + +auto basic_enum( + meta::type_declaration& t, + auto const& nextval, + cpp2::impl::in bitwise + ) -> void; + +#line 1379 "reflect.h2" +//----------------------------------------------------------------------- +// +// "An enum[...] is a totally ordered value type that stores a +// value of its enumerators's type, and otherwise has only public +// member variables of its enumerator's type, all of which are +// naturally scoped because they are members of a type." +// +// -- P0707R4, section 3 +// +CPPFRONTAPI auto cpp2_enum(meta::type_declaration& t) -> void; + +#line 1405 "reflect.h2" +//----------------------------------------------------------------------- +// +// "flag_enum expresses an enumeration that stores values +// corresponding to bitwise-or'd enumerators. The enumerators must +// be powers of two, and are automatically generated [...] A none +// value is provided [...] Operators | and & are provided to +// combine and extract values." +// +// -- P0707R4, section 3 +// +CPPFRONTAPI auto flag_enum(meta::type_declaration& t) -> void; + +#line 1437 "reflect.h2" +//----------------------------------------------------------------------- +// +// "As with void*, programmers should know that unions [...] are +// inherently dangerous, should be avoided wherever possible, +// and should be handled with special care when actually needed." +// +// -- Stroustrup (The Design and Evolution of C++, 14.3.4.1) +// +// "C++17 needs a type-safe union... The implications of the +// consensus `variant` design are well understood and have been +// explored over several LEWG discussions, over a thousand emails, +// a joint LEWG/EWG session, and not to mention 12 years of +// experience with Boost and other libraries." +// +// -- Axel Naumann, in P0088 (wg21.link/p0088), +// the adopted proposal for C++17 std::variant +// +//----------------------------------------------------------------------- +// +// union +// +// a type that contains exactly one of a fixed set of values at a time +// + +CPPFRONTAPI auto cpp2_union(meta::type_declaration& t) -> void; + +#line 1608 "reflect.h2" +//----------------------------------------------------------------------- +// +// print - output a pretty-printed visualization of t +// +CPPFRONTAPI auto print(cpp2::impl::in t) -> void; + +#line 1618 "reflect.h2" +//----------------------------------------------------------------------- +// +// dll_visible - makes t visible in a DLL +// +CPPFRONTAPI auto dll_visible(meta::type_declaration& t) -> void; + +#line 1627 "reflect.h2" +CPPFRONTAPI auto dll_visible(meta::function_declaration& t) -> void; + +#line 1634 "reflect.h2" +} + +} + +#endif diff --git a/include/cpp2util.h b/include/cpp2util.h index 6a817aeb57..4ae5020b53 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -375,6 +375,16 @@ constexpr auto gcc_clang_msvc_min_versions( #define CPP2_CONSTEXPR constexpr #endif + +#if defined(_WIN32) +#define CPPFRONTAPI __declspec(dllexport) +#else +#define CPPFRONTAPI __attribute__ ((visibility ("default"))) +#endif + +#define CPP2_C_API extern "C" CPPFRONTAPI + + namespace cpp2 { @@ -2911,4 +2921,15 @@ using cpp2::cpp2_new; #pragma clang diagnostic pop #endif +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) +#endif + +#include "cpp2reflect.h" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #endif // CPP2_CPP2UTIL_H diff --git a/source/common.h b/source/common.h index 5839b4cd80..7db8b980f1 100644 --- a/source/common.h +++ b/source/common.h @@ -14,7 +14,7 @@ // We want cppfront to build cleanly at very high warning levels, with warnings // as errors -- so disable a handful that fire incorrectly due to compiler bugs #ifdef _MSC_VER - #pragma warning(disable: 4456 4706) + #pragma warning(disable: 4456 4706 4996) #endif #if defined(__GNUC__) && __GNUC__ >= 13 && !defined(__clang_major__) #pragma GCC diagnostic ignored "-Wdangling-reference" @@ -103,28 +103,11 @@ struct source_line }; -using lineno_t = int32_t; -using colno_t = int32_t; // not int16_t... encountered >80,000 char line during testing -using index_t = int32_t; +using cpp2::meta::lineno_t; +using cpp2::meta::colno_t; +using cpp2::meta::index_t; -struct source_position -{ - lineno_t lineno; // one-based offset into program source - colno_t colno; // one-based offset into line - - source_position(lineno_t l = 1, colno_t c = 1 ) - : lineno{ l }, colno{ c } - { - } - - auto operator<=>(source_position const&) const = default; - - auto to_string() const - -> std::string - { - return "(" + std::to_string(lineno) + "," + std::to_string(colno) + ")"; - } -}; +using cpp2::meta::source_position; struct comment { @@ -521,6 +504,36 @@ auto to_upper_and_underbar(std::string_view s) } +auto to_lower_and_collapsed_underbar( + std::string_view s, + bool prev_was_underbar = false, + bool drop_back_underbar = false + ) + -> std::string +{ + auto ret = std::string{}; + for (char c : s) { + if (std::isalnum(c)) { + ret.push_back(string_util::safe_tolower(c)); + prev_was_underbar = false; + } + else if (!prev_was_underbar) { + ret.push_back('_'); + prev_was_underbar = true; + } + } + if ( + drop_back_underbar + && !ret.empty() + && ret.back() == '_' + ) + { + ret.pop_back(); + } + return ret; +} + + auto is_empty_or_a_decimal_number(std::string_view s) -> bool { diff --git a/source/cpp2reflect.hpp b/source/cpp2reflect.hpp new file mode 100644 index 0000000000..029515ad32 --- /dev/null +++ b/source/cpp2reflect.hpp @@ -0,0 +1,1590 @@ + +#ifndef CPP2REFLECT_H_CPP2 +#error This file is part of a '.h2' header compiled to be consumed from another -pure-cpp2 file. To use this file, write '#include "cpp2reflect.h2"' in a '.h2' or '.cpp2' file compiled with -pure-cpp2. +#endif + +#ifndef CPP2REFLECT_HPP_CPP2 +#define CPP2REFLECT_HPP_CPP2 + + + +//=== Cpp2 function definitions ================================================= + +#line 1 "reflect.h2" + +#line 19 "reflect.h2" +namespace cpp2 { + +namespace meta { + +#line 33 "reflect.h2" + source_position::source_position(){} +#line 34 "reflect.h2" + source_position::source_position(cpp2::impl::in l, cpp2::impl::in c) + : lineno{ l } + , colno{ c } +#line 35 "reflect.h2" + { + +#line 38 "reflect.h2" + } + +#line 40 "reflect.h2" + [[nodiscard]] auto source_position::to_string() const& -> std::string { return "(" + cpp2::to_string(lineno) + "," + cpp2::to_string(colno) + ")"; } + + + source_position::source_position(source_position const& that) + : lineno{ that.lineno } + , colno{ that.colno }{} + +auto source_position::operator=(source_position const& that) -> source_position& { + lineno = that.lineno; + colno = that.colno; + return *this;} +source_position::source_position(source_position&& that) noexcept + : lineno{ std::move(that).lineno } + , colno{ std::move(that).colno }{} +auto source_position::operator=(source_position&& that) noexcept -> source_position& { + lineno = std::move(that).lineno; + colno = std::move(that).colno; + return *this;} +constexpr passing_style::passing_style(cpp2::impl::in _val) + : _value{ cpp2::unchecked_narrow(_val) } { } +constexpr auto passing_style::operator=(cpp2::impl::in _val) -> passing_style& { + _value = cpp2::unchecked_narrow(_val); + return *this; } +inline CPP2_CONSTEXPR passing_style passing_style::in{ 0 }; + +inline CPP2_CONSTEXPR passing_style passing_style::in_ref{ 1 }; + +inline CPP2_CONSTEXPR passing_style passing_style::copy{ 2 }; + +inline CPP2_CONSTEXPR passing_style passing_style::inout{ 3 }; + +inline CPP2_CONSTEXPR passing_style passing_style::out{ 4 }; + +inline CPP2_CONSTEXPR passing_style passing_style::move{ 5 }; + +inline CPP2_CONSTEXPR passing_style passing_style::forward{ 6 }; + +inline CPP2_CONSTEXPR passing_style passing_style::forward_ref{ 7 }; + +inline CPP2_CONSTEXPR passing_style passing_style::invalid{ 8 }; + +[[nodiscard]] constexpr auto passing_style::get_raw_value() const& -> cpp2::i8 { return _value; } +constexpr passing_style::passing_style() + : _value{ in._value }{} +constexpr passing_style::passing_style(passing_style const& that) + : _value{ that._value }{} +constexpr auto passing_style::operator=(passing_style const& that) -> passing_style& { + _value = that._value; + return *this;} +constexpr passing_style::passing_style(passing_style&& that) noexcept + : _value{ std::move(that)._value }{} +constexpr auto passing_style::operator=(passing_style&& that) noexcept -> passing_style& { + _value = std::move(that)._value; + return *this;} +[[nodiscard]] auto passing_style::to_string_impl(cpp2::impl::in prefix) const& -> std::string{ + + auto pref {cpp2::to_string(prefix)}; + if ((*this) == in) {return pref + "in"; } + if ((*this) == in_ref) {return pref + "in_ref"; } + if ((*this) == copy) {return pref + "copy"; } + if ((*this) == inout) {return pref + "inout"; } + if ((*this) == out) {return pref + "out"; } + if ((*this) == move) {return pref + "move"; } + if ((*this) == forward) {return pref + "forward"; } + if ((*this) == forward_ref) {return pref + "forward_ref"; } + if ((*this) == invalid) {return cpp2::move(pref) + "invalid"; } + return "invalid passing_style value"; + } + + [[nodiscard]] auto passing_style::to_string() const& -> std::string { return to_string_impl(""); } +[[nodiscard]] auto passing_style::to_code() const& -> std::string { return to_string_impl("passing_style::"); } +[[nodiscard]] auto passing_style::from_string(cpp2::impl::in s) -> passing_style{ + + auto x {s}; + if ("in" == x) {return in; } + else {if ("in_ref" == x) {return in_ref; } + else {if ("copy" == x) {return copy; } + else {if ("inout" == x) {return inout; } + else {if ("out" == x) {return out; } + else {if ("move" == x) {return move; } + else {if ("forward" == x) {return forward; } + else {if ("forward_ref" == x) {return forward_ref; } + else {if ("invalid" == cpp2::move(x)) {return invalid; } +#line 1 "reflect.h2" +}}}}}}}} +CPP2_UFCS(report_violation)(cpp2::type_safety, CPP2_UFCS(c_str)(("can't convert string '" + cpp2::to_string(s) + "' to enum of type passing_style"))); +return in; +} + +[[nodiscard]] auto passing_style::from_code(cpp2::impl::in s) -> passing_style{ +std::string str {s}; return from_string(cpp2::string_util::replace_all(cpp2::move(str), "passing_style::", "")); } + +#line 60 "reflect.h2" +[[nodiscard]] auto to_passing_style(cpp2::impl::in s) -> passing_style +{ + return [&] () -> passing_style { auto&& _expr = s; + if (cpp2::impl::is(_expr, (CPP2_UFCS(to_string)(passing_style::in)))) { if constexpr( requires{passing_style::in;} ) if constexpr( std::is_convertible_v ) return passing_style::in; else return passing_style{}; else return passing_style{}; } + else if (cpp2::impl::is(_expr, CPP2_UFCS(to_string)(passing_style::in_ref))) { if constexpr( requires{passing_style::in_ref;} ) if constexpr( std::is_convertible_v ) return passing_style::in_ref; else return passing_style{}; else return passing_style{}; } + else if (cpp2::impl::is(_expr, CPP2_UFCS(to_string)(passing_style::copy))) { if constexpr( requires{passing_style::copy;} ) if constexpr( std::is_convertible_v ) return passing_style::copy; else return passing_style{}; else return passing_style{}; } + else if (cpp2::impl::is(_expr, CPP2_UFCS(to_string)(passing_style::inout))) { if constexpr( requires{passing_style::inout;} ) if constexpr( std::is_convertible_v ) return passing_style::inout; else return passing_style{}; else return passing_style{}; } + else if (cpp2::impl::is(_expr, CPP2_UFCS(to_string)(passing_style::out))) { if constexpr( requires{passing_style::out;} ) if constexpr( std::is_convertible_v ) return passing_style::out; else return passing_style{}; else return passing_style{}; } + else if (cpp2::impl::is(_expr, CPP2_UFCS(to_string)(passing_style::move))) { if constexpr( requires{passing_style::move;} ) if constexpr( std::is_convertible_v ) return passing_style::move; else return passing_style{}; else return passing_style{}; } + else if (cpp2::impl::is(_expr, CPP2_UFCS(to_string)(passing_style::forward))) { if constexpr( requires{passing_style::forward;} ) if constexpr( std::is_convertible_v ) return passing_style::forward; else return passing_style{}; else return passing_style{}; } + else if (cpp2::impl::is(_expr, CPP2_UFCS(to_string)(passing_style::forward_ref))) { if constexpr( requires{passing_style::forward_ref;} ) if constexpr( std::is_convertible_v ) return passing_style::forward_ref; else return passing_style{}; else return passing_style{}; } + else return passing_style::invalid; } + (); +} + +#line 88 "reflect.h2" + [[nodiscard]] auto compiler_services::data() const& -> decltype(auto) { return std::any_cast>(data_); } +#line 89 "reflect.h2" + [[nodiscard]] auto compiler_services::data() & -> decltype(auto) { return std::any_cast>(data_); } + +#line 93 "reflect.h2" + compiler_services::compiler_services( + + cpp2::impl::in data_v + ) + : data_{ data_v } +#line 97 "reflect.h2" + { + + if (cpp2::type_safety.is_active() && !(CPP2_UFCS(type)(data_) == typeid(compiler_services_data)) ) { cpp2::type_safety.report_violation(CPP2_CONTRACT_MSG("parameter 'data_v' must store a 'compiler_services_data'")); } + +#line 103 "reflect.h2" + } + +#line 107 "reflect.h2" + auto compiler_services::set_metafunction_name(cpp2::impl::in name, cpp2::impl::in> args) & -> void{ + data().metafunction_name = name; + data().metafunction_args = args; + data().metafunctions_used = CPP2_UFCS(empty)(args); + } + +#line 113 "reflect.h2" + [[nodiscard]] auto compiler_services::get_metafunction_name() const& -> std::string_view { return data().metafunction_name; } + +#line 115 "reflect.h2" + [[nodiscard]] auto compiler_services::get_argument(cpp2::impl::in index) & -> std::string{ + data().metafunctions_used = true; + if (([_0 = 0, _1 = index, _2 = CPP2_UFCS(ssize)(data().metafunction_args)]{ return cpp2::impl::cmp_less_eq(_0,_1) && cpp2::impl::cmp_less(_1,_2); }())) { + return CPP2_ASSERT_IN_BOUNDS(data().metafunction_args, index); + } + return ""; + } + +#line 123 "reflect.h2" + [[nodiscard]] auto compiler_services::get_arguments() & -> std::vector{ + data().metafunctions_used = true; + return data().metafunction_args; + } + +#line 128 "reflect.h2" + [[nodiscard]] auto compiler_services::arguments_were_used() const& -> bool { return data().metafunctions_used; } + +#line 130 "reflect.h2" + [[nodiscard]] auto compiler_services::parse_statement( + + std::string_view source + ) & -> auto + + { + auto original_source {source}; + + CPP2_UFCS(push_back)(generated_lines, std::vector()); + auto lines {&CPP2_UFCS(back)(generated_lines)}; + + auto add_line {[&, _1 = lines](cpp2::impl::in s) mutable -> void{ + static_cast(CPP2_UFCS(emplace_back)((*cpp2::impl::assert_not_null(_1)), s, source_line::category::cpp2)); + }}; +{ +auto newline_pos{CPP2_UFCS(find)(source, '\n')}; + + // First split this string into source_lines + // + +#line 148 "reflect.h2" + if ( cpp2::impl::cmp_greater(CPP2_UFCS(ssize)(source),1) + && newline_pos != source.npos) + { + while( newline_pos != source.npos ) + { + add_line(CPP2_UFCS(substr)(source, 0, newline_pos)); + CPP2_UFCS(remove_prefix)(source, newline_pos + 1); + newline_pos = CPP2_UFCS(find)(source, '\n'); + } + } +} + +#line 159 "reflect.h2" + if (!(CPP2_UFCS(empty)(source))) { + cpp2::move(add_line)(cpp2::move(source)); + } + + // Now lex this source fragment to generate + // a single grammar_map entry, whose .second + // is the vector of tokens + static_cast(CPP2_UFCS(emplace_back)(generated_lexers, *cpp2::impl::assert_not_null(data().errors))); + auto tokens {&CPP2_UFCS(back)(generated_lexers)}; + CPP2_UFCS(lex)((*cpp2::impl::assert_not_null(tokens)), *cpp2::impl::assert_not_null(cpp2::move(lines)), true); + + if (cpp2::cpp2_default.is_active() && !(std::ssize(CPP2_UFCS(get_map)((*cpp2::impl::assert_not_null(tokens)))) == 1) ) { cpp2::cpp2_default.report_violation(""); } + + // Now parse this single declaration from + // the lexed tokens + auto ret {CPP2_UFCS(parse_one_declaration)(data().parser, + (*cpp2::impl::assert_not_null(CPP2_UFCS(begin)(CPP2_UFCS(get_map)(*cpp2::impl::assert_not_null(cpp2::move(tokens)))))).second, + *cpp2::impl::assert_not_null(data().generated_tokens) + )}; + if (!(ret)) { + error("parse failed - the source string is not a valid statement:\n" + cpp2::to_string(cpp2::move(original_source)) + ""); + } + return ret; + } + +#line 184 "reflect.h2" + auto compiler_services::add_runtime_support_include(cpp2::impl::in s) & -> void{static_cast(CPP2_UFCS(emplace)((*cpp2::impl::assert_not_null(data().includes)), s)); } + +#line 186 "reflect.h2" + [[nodiscard]] auto compiler_services::position() const -> source_position + + { + return { }; + } + +#line 195 "reflect.h2" + auto compiler_services::require( + + cpp2::impl::in b, + cpp2::impl::in msg + ) const& -> void + { + if (!(b)) { + error(msg); + } + } + +#line 206 "reflect.h2" + auto compiler_services::error(cpp2::impl::in msg) const& -> void + { + auto message {cpp2::impl::as_(msg)}; + if (!(CPP2_UFCS(empty)(data().metafunction_name))) { + message = "while applying @" + cpp2::to_string(data().metafunction_name) + " - " + cpp2::to_string(message) + ""; + } + static_cast(CPP2_UFCS(emplace_back)((*cpp2::impl::assert_not_null(data().errors)), position(), cpp2::move(message))); + } + +#line 218 "reflect.h2" + auto compiler_services::report_violation(auto const& msg) const& -> void{ + error(msg); + throw(std::runtime_error( + " ==> programming bug found in metafunction @" + cpp2::to_string(data().metafunction_name) + " " + "- contract violation - see previous errors" + )); + } + +#line 226 "reflect.h2" + [[nodiscard]] auto compiler_services::is_active() const& -> bool { return true; } + + compiler_services::~compiler_services() noexcept{} +compiler_services::compiler_services(compiler_services const& that) + : data_{ that.data_ }{} + +#line 284 "reflect.h2" + template declaration_base::node_pointer::node_pointer( + + T const& n_ + ) + : n{ n_ } +#line 288 "reflect.h2" + { + + if (cpp2::null_safety.is_active() && !(n_) ) { cpp2::null_safety.report_violation(CPP2_CONTRACT_MSG("a meta::declaration must point to a valid declaration_node, not null")); } + static_assert(std::is_same_v); + } +#line 284 "reflect.h2" + template auto declaration_base::node_pointer::operator=( + +#line 286 "reflect.h2" + T const& n_ + ) -> node_pointer& + { + n = n_; + +#line 290 "reflect.h2" + if (cpp2::null_safety.is_active() && !(n_) ) { cpp2::null_safety.report_violation(CPP2_CONTRACT_MSG("a meta::declaration must point to a valid declaration_node, not null")); } + static_assert(std::is_same_v); + return *this; +#line 292 "reflect.h2" + } + +#line 294 "reflect.h2" + [[nodiscard]] auto declaration_base::node_pointer::operator*() const& -> decltype(auto) { return *cpp2::impl::assert_not_null(cpp2::unchecked_cast(n)); } + + declaration_base::node_pointer::node_pointer(node_pointer const& that) + : n{ that.n }{} + +auto declaration_base::node_pointer::operator=(node_pointer const& that) -> node_pointer& { + n = that.n; + return *this;} +declaration_base::node_pointer::node_pointer(node_pointer&& that) noexcept + : n{ std::move(that).n }{} +auto declaration_base::node_pointer::operator=(node_pointer&& that) noexcept -> node_pointer& { + n = std::move(that).n; + return *this;} +#line 299 "reflect.h2" + declaration_base::declaration_base( + + cpp2::impl::in n_, + cpp2::impl::in s + ) + : compiler_services{ s } + , n{ n_ } +#line 304 "reflect.h2" + { + +#line 307 "reflect.h2" + } + +#line 309 "reflect.h2" + [[nodiscard]] auto declaration_base::position() const -> source_position { return CPP2_UFCS(position)((*cpp2::impl::assert_not_null(n))); } + +#line 311 "reflect.h2" + [[nodiscard]] auto declaration_base::print() const& -> std::string { return CPP2_UFCS(pretty_print_visualize)((*cpp2::impl::assert_not_null(n)), 0); } + + declaration_base::~declaration_base() noexcept{} +declaration_base::declaration_base(declaration_base const& that) + : compiler_services{ static_cast(that) } + , n{ that.n }{} + +#line 322 "reflect.h2" + declaration::declaration( + + cpp2::impl::in n_, + cpp2::impl::in s + ) + : declaration_base{ n_, s } +#line 327 "reflect.h2" + { + + } + +#line 331 "reflect.h2" + [[nodiscard]] auto declaration::is_public() const& -> bool { return CPP2_UFCS(is_public)((*cpp2::impl::assert_not_null(n))); } +#line 332 "reflect.h2" + [[nodiscard]] auto declaration::is_protected() const& -> bool { return CPP2_UFCS(is_protected)((*cpp2::impl::assert_not_null(n))); } +#line 333 "reflect.h2" + [[nodiscard]] auto declaration::is_private() const& -> bool { return CPP2_UFCS(is_private)((*cpp2::impl::assert_not_null(n))); } +#line 334 "reflect.h2" + [[nodiscard]] auto declaration::is_default_access() const& -> bool { return CPP2_UFCS(is_default_access)((*cpp2::impl::assert_not_null(n))); } + +#line 336 "reflect.h2" + auto declaration::default_to_public() & -> void{static_cast(CPP2_UFCS(make_public)((*cpp2::impl::assert_not_null(n))));} +#line 337 "reflect.h2" + auto declaration::default_to_protected() & -> void{static_cast(CPP2_UFCS(make_protected)((*cpp2::impl::assert_not_null(n)))); } +#line 338 "reflect.h2" + auto declaration::default_to_private() & -> void{static_cast(CPP2_UFCS(make_private)((*cpp2::impl::assert_not_null(n))));} + +#line 340 "reflect.h2" + [[nodiscard]] auto declaration::make_public() & -> bool { return CPP2_UFCS(make_public)((*cpp2::impl::assert_not_null(n))); } +#line 341 "reflect.h2" + [[nodiscard]] auto declaration::make_protected() & -> bool { return CPP2_UFCS(make_protected)((*cpp2::impl::assert_not_null(n))); } +#line 342 "reflect.h2" + [[nodiscard]] auto declaration::make_private() & -> bool { return CPP2_UFCS(make_private)((*cpp2::impl::assert_not_null(n))); } + +#line 344 "reflect.h2" + [[nodiscard]] auto declaration::is_dll_visible() const& -> bool { return CPP2_UFCS(is_dll_visible)((*cpp2::impl::assert_not_null(n))); } +#line 345 "reflect.h2" + [[nodiscard]] auto declaration::make_dll_visible() & -> bool { return CPP2_UFCS(make_dll_visible)((*cpp2::impl::assert_not_null(n))); } + +#line 347 "reflect.h2" + [[nodiscard]] auto declaration::has_name() const& -> bool { return CPP2_UFCS(has_name)((*cpp2::impl::assert_not_null(n))); } +#line 348 "reflect.h2" + [[nodiscard]] auto declaration::has_name(cpp2::impl::in s) const& -> bool { return CPP2_UFCS(has_name)((*cpp2::impl::assert_not_null(n)), s); } + +#line 350 "reflect.h2" + [[nodiscard]] auto declaration::name() const& -> std::string_view{ + if (has_name()) {return CPP2_UFCS(as_string_view)((*cpp2::impl::assert_not_null(CPP2_UFCS(name)(*cpp2::impl::assert_not_null(n))))); } + else { return ""; } + } + +#line 355 "reflect.h2" + [[nodiscard]] auto declaration::has_initializer() const& -> bool { return CPP2_UFCS(has_initializer)((*cpp2::impl::assert_not_null(n))); } + +#line 357 "reflect.h2" + [[nodiscard]] auto declaration::is_global() const& -> bool { return CPP2_UFCS(is_global)((*cpp2::impl::assert_not_null(n))); } +#line 358 "reflect.h2" + [[nodiscard]] auto declaration::is_function() const& -> bool { return CPP2_UFCS(is_function)((*cpp2::impl::assert_not_null(n))); } +#line 359 "reflect.h2" + [[nodiscard]] auto declaration::is_object() const& -> bool { return CPP2_UFCS(is_object)((*cpp2::impl::assert_not_null(n))); } +#line 360 "reflect.h2" + [[nodiscard]] auto declaration::is_base_object() const& -> bool { return CPP2_UFCS(is_base_object)((*cpp2::impl::assert_not_null(n))); } +#line 361 "reflect.h2" + [[nodiscard]] auto declaration::is_member_object() const& -> bool { return CPP2_UFCS(is_member_object)((*cpp2::impl::assert_not_null(n))); } +#line 362 "reflect.h2" + [[nodiscard]] auto declaration::is_type() const& -> bool { return CPP2_UFCS(is_type)((*cpp2::impl::assert_not_null(n))); } +#line 363 "reflect.h2" + [[nodiscard]] auto declaration::is_namespace() const& -> bool { return CPP2_UFCS(is_namespace)((*cpp2::impl::assert_not_null(n))); } +#line 364 "reflect.h2" + [[nodiscard]] auto declaration::is_alias() const& -> bool { return CPP2_UFCS(is_alias)((*cpp2::impl::assert_not_null(n))); } + +#line 366 "reflect.h2" + [[nodiscard]] auto declaration::is_type_alias() const& -> bool { return CPP2_UFCS(is_type_alias)((*cpp2::impl::assert_not_null(n))); } +#line 367 "reflect.h2" + [[nodiscard]] auto declaration::is_namespace_alias() const& -> bool { return CPP2_UFCS(is_namespace_alias)((*cpp2::impl::assert_not_null(n))); } +#line 368 "reflect.h2" + [[nodiscard]] auto declaration::is_object_alias() const& -> bool { return CPP2_UFCS(is_object_alias)((*cpp2::impl::assert_not_null(n))); } + +#line 370 "reflect.h2" + [[nodiscard]] auto declaration::is_function_expression() const& -> bool { return CPP2_UFCS(is_function_expression)((*cpp2::impl::assert_not_null(n))); } + +#line 372 "reflect.h2" + [[nodiscard]] auto declaration::as_function() const& -> function_declaration { return function_declaration(n, (*this)); } +#line 373 "reflect.h2" + [[nodiscard]] auto declaration::as_object() const& -> object_declaration { return object_declaration(n, (*this)); } +#line 374 "reflect.h2" + [[nodiscard]] auto declaration::as_type() const& -> type_declaration { return type_declaration(n, (*this)); } +#line 375 "reflect.h2" + [[nodiscard]] auto declaration::as_alias() const& -> alias_declaration { return alias_declaration(n, (*this)); } + +#line 377 "reflect.h2" + [[nodiscard]] auto declaration::get_parent() const& -> declaration { return declaration((*cpp2::impl::assert_not_null(n)).parent_declaration, (*this)); } + +#line 379 "reflect.h2" + [[nodiscard]] auto declaration::parent_is_function() const& -> bool { return CPP2_UFCS(parent_is_function)((*cpp2::impl::assert_not_null(n))); } +#line 380 "reflect.h2" + [[nodiscard]] auto declaration::parent_is_object() const& -> bool { return CPP2_UFCS(parent_is_object)((*cpp2::impl::assert_not_null(n))); } +#line 381 "reflect.h2" + [[nodiscard]] auto declaration::parent_is_type() const& -> bool { return CPP2_UFCS(parent_is_type)((*cpp2::impl::assert_not_null(n))); } +#line 382 "reflect.h2" + [[nodiscard]] auto declaration::parent_is_namespace() const& -> bool { return CPP2_UFCS(parent_is_namespace)((*cpp2::impl::assert_not_null(n))); } +#line 383 "reflect.h2" + [[nodiscard]] auto declaration::parent_is_alias() const& -> bool { return CPP2_UFCS(parent_is_alias)((*cpp2::impl::assert_not_null(n))); } + +#line 385 "reflect.h2" + [[nodiscard]] auto declaration::parent_is_type_alias() const& -> bool { return CPP2_UFCS(parent_is_type_alias)((*cpp2::impl::assert_not_null(n))); } +#line 386 "reflect.h2" + [[nodiscard]] auto declaration::parent_is_namespace_alias() const& -> bool { return CPP2_UFCS(parent_is_namespace_alias)((*cpp2::impl::assert_not_null(n))); } +#line 387 "reflect.h2" + [[nodiscard]] auto declaration::parent_is_object_alias() const& -> bool { return CPP2_UFCS(parent_is_object_alias)((*cpp2::impl::assert_not_null(n))); } + +#line 389 "reflect.h2" + [[nodiscard]] auto declaration::parent_is_polymorphic() const& -> bool { return CPP2_UFCS(parent_is_polymorphic)((*cpp2::impl::assert_not_null(n))); } + +#line 391 "reflect.h2" + auto declaration::mark_for_removal_from_enclosing_type() & -> void + + { + if (cpp2::type_safety.is_active() && !(parent_is_type()) ) { cpp2::type_safety.report_violation(""); } +#line 394 "reflect.h2" + auto test {CPP2_UFCS(type_member_mark_for_removal)((*cpp2::impl::assert_not_null(n)))}; + if (cpp2::cpp2_default.is_active() && !(cpp2::move(test)) ) { cpp2::cpp2_default.report_violation(""); }// ... to ensure this assert is true + } + + declaration::~declaration() noexcept{} +declaration::declaration(declaration const& that) + : declaration_base{ static_cast(that) }{} + +#line 407 "reflect.h2" + function_declaration::function_declaration( + + cpp2::impl::in n_, + cpp2::impl::in s + ) + : declaration{ n_, s } +#line 412 "reflect.h2" + { + + if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(is_function)((*cpp2::impl::assert_not_null(n)))) ) { cpp2::cpp2_default.report_violation(""); } + } + +#line 417 "reflect.h2" + [[nodiscard]] auto function_declaration::index_of_parameter_named(cpp2::impl::in s) const& -> int { return CPP2_UFCS(index_of_parameter_named)((*cpp2::impl::assert_not_null(n)), s); } +#line 418 "reflect.h2" + [[nodiscard]] auto function_declaration::has_parameter_named(cpp2::impl::in s) const& -> bool { return CPP2_UFCS(has_parameter_named)((*cpp2::impl::assert_not_null(n)), s); } +#line 419 "reflect.h2" + [[nodiscard]] auto function_declaration::has_in_parameter_named(cpp2::impl::in s) const& -> bool { return CPP2_UFCS(has_in_parameter_named)((*cpp2::impl::assert_not_null(n)), s); } +#line 420 "reflect.h2" + [[nodiscard]] auto function_declaration::has_in_ref_parameter_named(cpp2::impl::in s) const& -> bool { return CPP2_UFCS(has_in_ref_parameter_named)((*cpp2::impl::assert_not_null(n)), s); } +#line 421 "reflect.h2" + [[nodiscard]] auto function_declaration::has_copy_parameter_named(cpp2::impl::in s) const& -> bool { return CPP2_UFCS(has_copy_parameter_named)((*cpp2::impl::assert_not_null(n)), s); } +#line 422 "reflect.h2" + [[nodiscard]] auto function_declaration::has_inout_parameter_named(cpp2::impl::in s) const& -> bool { return CPP2_UFCS(has_inout_parameter_named)((*cpp2::impl::assert_not_null(n)), s); } +#line 423 "reflect.h2" + [[nodiscard]] auto function_declaration::has_out_parameter_named(cpp2::impl::in s) const& -> bool { return CPP2_UFCS(has_out_parameter_named)((*cpp2::impl::assert_not_null(n)), s); } +#line 424 "reflect.h2" + [[nodiscard]] auto function_declaration::has_move_parameter_named(cpp2::impl::in s) const& -> bool { return CPP2_UFCS(has_move_parameter_named)((*cpp2::impl::assert_not_null(n)), s); } +#line 425 "reflect.h2" + [[nodiscard]] auto function_declaration::has_forward_parameter_named(cpp2::impl::in s) const& -> bool { return CPP2_UFCS(has_forward_parameter_named)((*cpp2::impl::assert_not_null(n)), s); } +#line 426 "reflect.h2" + [[nodiscard]] auto function_declaration::first_parameter_name() const& -> std::string { return CPP2_UFCS(first_parameter_name)((*cpp2::impl::assert_not_null(n))); } + +#line 428 "reflect.h2" + [[nodiscard]] auto function_declaration::has_parameter_with_name_and_pass(cpp2::impl::in s, cpp2::impl::in pass) const& -> bool { + return CPP2_UFCS(has_parameter_with_name_and_pass)((*cpp2::impl::assert_not_null(n)), s, pass); } +#line 430 "reflect.h2" + [[nodiscard]] auto function_declaration::is_function_with_this() const& -> bool { return CPP2_UFCS(is_function_with_this)((*cpp2::impl::assert_not_null(n))); } +#line 431 "reflect.h2" + [[nodiscard]] auto function_declaration::is_virtual() const& -> bool { return CPP2_UFCS(is_virtual_function)((*cpp2::impl::assert_not_null(n))); } +#line 432 "reflect.h2" + [[nodiscard]] auto function_declaration::is_defaultable() const& -> bool { return CPP2_UFCS(is_defaultable_function)((*cpp2::impl::assert_not_null(n))); } +#line 433 "reflect.h2" + [[nodiscard]] auto function_declaration::is_constructor() const& -> bool { return CPP2_UFCS(is_constructor)((*cpp2::impl::assert_not_null(n))); } +#line 434 "reflect.h2" + [[nodiscard]] auto function_declaration::is_default_constructor() const& -> bool { return CPP2_UFCS(is_default_constructor)((*cpp2::impl::assert_not_null(n))); } +#line 435 "reflect.h2" + [[nodiscard]] auto function_declaration::is_move() const& -> bool { return CPP2_UFCS(is_move)((*cpp2::impl::assert_not_null(n))); } +#line 436 "reflect.h2" + [[nodiscard]] auto function_declaration::is_swap() const& -> bool { return CPP2_UFCS(is_swap)((*cpp2::impl::assert_not_null(n))); } +#line 437 "reflect.h2" + [[nodiscard]] auto function_declaration::is_constructor_with_that() const& -> bool { return CPP2_UFCS(is_constructor_with_that)((*cpp2::impl::assert_not_null(n))); } +#line 438 "reflect.h2" + [[nodiscard]] auto function_declaration::is_constructor_with_in_that() const& -> bool { return CPP2_UFCS(is_constructor_with_in_that)((*cpp2::impl::assert_not_null(n))); } +#line 439 "reflect.h2" + [[nodiscard]] auto function_declaration::is_constructor_with_move_that() const& -> bool { return CPP2_UFCS(is_constructor_with_move_that)((*cpp2::impl::assert_not_null(n))); } +#line 440 "reflect.h2" + [[nodiscard]] auto function_declaration::is_assignment() const& -> bool { return CPP2_UFCS(is_assignment)((*cpp2::impl::assert_not_null(n))); } +#line 441 "reflect.h2" + [[nodiscard]] auto function_declaration::is_assignment_with_that() const& -> bool { return CPP2_UFCS(is_assignment_with_that)((*cpp2::impl::assert_not_null(n))); } +#line 442 "reflect.h2" + [[nodiscard]] auto function_declaration::is_assignment_with_in_that() const& -> bool { return CPP2_UFCS(is_assignment_with_in_that)((*cpp2::impl::assert_not_null(n))); } +#line 443 "reflect.h2" + [[nodiscard]] auto function_declaration::is_assignment_with_move_that() const& -> bool { return CPP2_UFCS(is_assignment_with_move_that)((*cpp2::impl::assert_not_null(n))); } +#line 444 "reflect.h2" + [[nodiscard]] auto function_declaration::is_destructor() const& -> bool { return CPP2_UFCS(is_destructor)((*cpp2::impl::assert_not_null(n))); } + +#line 446 "reflect.h2" + [[nodiscard]] auto function_declaration::is_copy_or_move() const& -> bool { return is_constructor_with_that() || is_assignment_with_that(); } + +#line 448 "reflect.h2" + [[nodiscard]] auto function_declaration::has_declared_return_type() const& -> bool { return CPP2_UFCS(has_declared_return_type)((*cpp2::impl::assert_not_null(n))); } +#line 449 "reflect.h2" + [[nodiscard]] auto function_declaration::has_deduced_return_type() const& -> bool { return CPP2_UFCS(has_deduced_return_type)((*cpp2::impl::assert_not_null(n))); } +#line 450 "reflect.h2" + [[nodiscard]] auto function_declaration::has_bool_return_type() const& -> bool { return CPP2_UFCS(has_bool_return_type)((*cpp2::impl::assert_not_null(n))); } +#line 451 "reflect.h2" + [[nodiscard]] auto function_declaration::has_non_void_return_type() const& -> bool { return CPP2_UFCS(has_non_void_return_type)((*cpp2::impl::assert_not_null(n))); } + +#line 453 "reflect.h2" + [[nodiscard]] auto function_declaration::unnamed_return_type() const& -> std::string { return CPP2_UFCS(unnamed_return_type_to_string)((*cpp2::impl::assert_not_null(n))); } + +#line 455 "reflect.h2" + [[nodiscard]] auto function_declaration::get_parameters() const& -> std::vector + + { + std::vector ret {}; + for ( auto const& param : CPP2_UFCS(get_function_parameters)((*cpp2::impl::assert_not_null(n))) ) { + static_cast(CPP2_UFCS(emplace_back)(ret, &*cpp2::impl::assert_not_null((*cpp2::impl::assert_not_null(param)).declaration), (*this))); + } + return ret; + } + +#line 465 "reflect.h2" + [[nodiscard]] auto function_declaration::is_binary_comparison_function() const& -> bool { return CPP2_UFCS(is_binary_comparison_function)((*cpp2::impl::assert_not_null(n))); } + +#line 467 "reflect.h2" + auto function_declaration::default_to_virtual() & -> void{static_cast(CPP2_UFCS(make_function_virtual)((*cpp2::impl::assert_not_null(n))));} + +#line 469 "reflect.h2" + [[nodiscard]] auto function_declaration::make_virtual() & -> bool { return CPP2_UFCS(make_function_virtual)((*cpp2::impl::assert_not_null(n))); } + +#line 471 "reflect.h2" + auto function_declaration::add_initializer(cpp2::impl::in source) & -> void + +#line 474 "reflect.h2" + { + if ((*this).is_active() && !(!(has_initializer())) ) { (*this).report_violation(CPP2_CONTRACT_MSG("cannot add an initializer to a function that already has one")); } + if ((*this).is_active() && !(parent_is_type()) ) { (*this).report_violation(CPP2_CONTRACT_MSG("cannot add an initializer to a function that isn't in a type scope")); } + //require( !has_initializer(), + // "cannot add an initializer to a function that already has one"); + //require( parent_is_type(), + // "cannot add an initializer to a function that isn't in a type scope"); + +#line 480 "reflect.h2" + auto stmt {parse_statement(source)}; + if (!((cpp2::impl::as_(stmt)))) { + error("cannot add an initializer that is not a valid statement"); + return ; + } + require(CPP2_UFCS(add_function_initializer)((*cpp2::impl::assert_not_null(n)), std::move(cpp2::move(stmt))), + std::string("unexpected error while attempting to add initializer")); + } + + function_declaration::function_declaration(function_declaration const& that) + : declaration{ static_cast(that) }{} + +#line 498 "reflect.h2" + object_declaration::object_declaration( + + cpp2::impl::in n_, + cpp2::impl::in s + ) + : declaration{ n_, s } +#line 503 "reflect.h2" + { + + if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(is_object)((*cpp2::impl::assert_not_null(n)))) ) { cpp2::cpp2_default.report_violation(""); } + } + +#line 508 "reflect.h2" + [[nodiscard]] auto object_declaration::is_const() const& -> bool { return CPP2_UFCS(is_const)((*cpp2::impl::assert_not_null(n))); } +#line 509 "reflect.h2" + [[nodiscard]] auto object_declaration::has_wildcard_type() const& -> bool { return CPP2_UFCS(has_wildcard_type)((*cpp2::impl::assert_not_null(n))); } + +#line 511 "reflect.h2" + [[nodiscard]] auto object_declaration::type() const& -> std::string{ + auto ret {CPP2_UFCS(object_type)((*cpp2::impl::assert_not_null(n)))}; + require(!(contains(ret, "(*ERROR*)")), + "cannot to_string this type: " + ret); + return ret; + } + +#line 518 "reflect.h2" + [[nodiscard]] auto object_declaration::initializer() const& -> std::string{ + auto ret {CPP2_UFCS(object_initializer)((*cpp2::impl::assert_not_null(n)))}; + require(!(contains(ret, "(*ERROR*)")), + "cannot to_string this initializer: " + ret); + return ret; + } + + object_declaration::object_declaration(object_declaration const& that) + : declaration{ static_cast(that) }{} + +#line 534 "reflect.h2" + type_declaration::type_declaration( + + cpp2::impl::in n_, + cpp2::impl::in s + ) + : declaration{ n_, s } +#line 539 "reflect.h2" + { + + if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(is_type)((*cpp2::impl::assert_not_null(n)))) ) { cpp2::cpp2_default.report_violation(""); } + } + +#line 544 "reflect.h2" + auto type_declaration::reserve_names(cpp2::impl::in name, auto&& ...etc) const& -> void + { // etc is not declared ':string_view' for compatibility with GCC 10.x + for ( + auto const& m : get_members() ) { + CPP2_UFCS(require)(m, !(CPP2_UFCS(has_name)(m, name)), + "in a '" + cpp2::to_string(get_metafunction_name()) + "' type, the name '" + cpp2::to_string(name) + "' " + "is reserved for use by the '" + cpp2::to_string(get_metafunction_name()) + "' implementation" + ); + } + if constexpr (!(CPP2_PACK_EMPTY(etc))) { + reserve_names(CPP2_FORWARD(etc)...); + } + } + +#line 558 "reflect.h2" + [[nodiscard]] auto type_declaration::is_polymorphic() const& -> bool { return CPP2_UFCS(is_polymorphic)((*cpp2::impl::assert_not_null(n))); } +#line 559 "reflect.h2" + [[nodiscard]] auto type_declaration::is_final() const& -> bool { return CPP2_UFCS(is_type_final)((*cpp2::impl::assert_not_null(n))); } +#line 560 "reflect.h2" + [[nodiscard]] auto type_declaration::make_final() & -> bool { return CPP2_UFCS(make_type_final)((*cpp2::impl::assert_not_null(n))); } + +#line 562 "reflect.h2" + [[nodiscard]] auto type_declaration::get_member_functions() const& -> std::vector + + { + std::vector ret {}; + for ( + auto const& d : CPP2_UFCS(get_type_scope_declarations)((*cpp2::impl::assert_not_null(n)), declaration_node::functions) ) { + static_cast(CPP2_UFCS(emplace_back)(ret, d, (*this))); + } + return ret; + } + +#line 573 "reflect.h2" + [[nodiscard]] auto type_declaration::get_member_functions_needing_initializer() const& -> std::vector + + { + std::vector ret {}; + for ( + auto const& d : CPP2_UFCS(get_type_scope_declarations)((*cpp2::impl::assert_not_null(n)), declaration_node::functions) ) + if ( !(CPP2_UFCS(has_initializer)((*cpp2::impl::assert_not_null(d)))) + && !(CPP2_UFCS(is_virtual_function)((*cpp2::impl::assert_not_null(d)))) + && !(CPP2_UFCS(is_defaultable_function)((*cpp2::impl::assert_not_null(d))))) + { + static_cast(CPP2_UFCS(emplace_back)(ret, d, (*this))); + } + return ret; + } + +#line 588 "reflect.h2" + [[nodiscard]] auto type_declaration::get_member_objects() const& -> std::vector + + { + std::vector ret {}; + for ( auto const& d : CPP2_UFCS(get_type_scope_declarations)((*cpp2::impl::assert_not_null(n)), declaration_node::objects) ) { + static_cast(CPP2_UFCS(emplace_back)(ret, d, (*this))); + } + return ret; + } + +#line 598 "reflect.h2" + [[nodiscard]] auto type_declaration::get_member_types() const& -> std::vector + + { + std::vector ret {}; + for ( auto const& d : CPP2_UFCS(get_type_scope_declarations)((*cpp2::impl::assert_not_null(n)), declaration_node::types) ) { + static_cast(CPP2_UFCS(emplace_back)(ret, d, (*this))); + } + return ret; + } + +#line 608 "reflect.h2" + [[nodiscard]] auto type_declaration::get_member_aliases() const& -> std::vector + + { + std::vector ret {}; + for ( auto const& d : CPP2_UFCS(get_type_scope_declarations)((*cpp2::impl::assert_not_null(n)), declaration_node::aliases) ) { + static_cast(CPP2_UFCS(emplace_back)(ret, d, (*this))); + } + return ret; + } + +#line 618 "reflect.h2" + [[nodiscard]] auto type_declaration::get_members() const& -> std::vector + + { + std::vector ret {}; + for ( auto const& d : CPP2_UFCS(get_type_scope_declarations)((*cpp2::impl::assert_not_null(n)), declaration_node::all) ) { + static_cast(CPP2_UFCS(emplace_back)(ret, d, (*this))); + } + return ret; + } + +#line 628 "reflect.h2" + [[nodiscard]] auto type_declaration::query_declared_value_set_functions() const& -> query_declared_value_set_functions_ret + +#line 635 "reflect.h2" + { + cpp2::impl::deferred_init out_this_in_that; + cpp2::impl::deferred_init out_this_move_that; + cpp2::impl::deferred_init inout_this_in_that; + cpp2::impl::deferred_init inout_this_move_that; +#line 636 "reflect.h2" + auto declared {CPP2_UFCS(find_declared_value_set_functions)((*cpp2::impl::assert_not_null(n)))}; + out_this_in_that.construct(declared.out_this_in_that != nullptr); + out_this_move_that.construct(declared.out_this_move_that != nullptr); + inout_this_in_that.construct(declared.inout_this_in_that != nullptr); + inout_this_move_that.construct(cpp2::move(declared).inout_this_move_that != nullptr); + return { std::move(out_this_in_that.value()), std::move(out_this_move_that.value()), std::move(inout_this_in_that.value()), std::move(inout_this_move_that.value()) }; // NOLINT(performance-move-const-arg) + } + +#line 644 "reflect.h2" + auto type_declaration::add_member(cpp2::impl::in source) & -> void + { + auto decl {parse_statement(source)}; + if (!((cpp2::impl::as_(decl)))) { + error("the provided source string is not a valid statement"); + return ; + } + if (!(CPP2_UFCS(is_declaration)((*cpp2::impl::assert_not_null(decl))))) { + error("cannot add a member that is not a declaration"); + } + require(CPP2_UFCS(add_type_member)((*cpp2::impl::assert_not_null(n)), std::move(cpp2::move(decl))), + std::string("unexpected error while attempting to add member:\n") + source); + } + +#line 658 "reflect.h2" + auto type_declaration::remove_marked_members() & -> void{CPP2_UFCS(type_remove_marked_members)((*cpp2::impl::assert_not_null(n))); } +#line 659 "reflect.h2" + auto type_declaration::remove_all_members() & -> void{CPP2_UFCS(type_remove_all_members)((*cpp2::impl::assert_not_null(n)));} + +#line 661 "reflect.h2" + auto type_declaration::disable_member_function_generation() & -> void{CPP2_UFCS(type_disable_member_function_generation)((*cpp2::impl::assert_not_null(n))); } + + type_declaration::type_declaration(type_declaration const& that) + : declaration{ static_cast(that) }{} + +#line 672 "reflect.h2" + alias_declaration::alias_declaration( + + cpp2::impl::in n_, + cpp2::impl::in s + ) + : declaration{ n_, s } +#line 677 "reflect.h2" + { + + if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(is_alias)((*cpp2::impl::assert_not_null(n)))) ) { cpp2::cpp2_default.report_violation(""); } + } + + alias_declaration::alias_declaration(alias_declaration const& that) + : declaration{ static_cast(that) }{} + +#line 696 "reflect.h2" +auto add_virtual_destructor(meta::type_declaration& t) -> void +{ + CPP2_UFCS(add_member)(t, "operator=: (virtual move this) = { }"); +} + +#line 714 "reflect.h2" +CPPFRONTAPI auto interface(meta::type_declaration& t) -> void +{ + auto has_dtor {false}; + + for ( auto& m : CPP2_UFCS(get_members)(t) ) + { + CPP2_UFCS(require)(m, !(CPP2_UFCS(is_object)(m)), + "interfaces may not contain data objects"); + if (CPP2_UFCS(is_function)(m)) { + auto mf {CPP2_UFCS(as_function)(m)}; + CPP2_UFCS(require)(mf, !(CPP2_UFCS(is_copy_or_move)(mf)), + "interfaces may not copy or move; consider a virtual clone() instead"); + CPP2_UFCS(require)(mf, !(CPP2_UFCS(has_initializer)(mf)), + "interface functions must not have a function body; remove the '=' initializer"); + CPP2_UFCS(require)(mf, CPP2_UFCS(make_public)(mf), + "interface functions must be public"); + CPP2_UFCS(default_to_virtual)(mf); + has_dtor |= CPP2_UFCS(is_destructor)(cpp2::move(mf)); + } + } + + if (!(cpp2::move(has_dtor))) { + CPP2_UFCS(add_virtual_destructor)(t); + } +} + +#line 760 "reflect.h2" +CPPFRONTAPI auto polymorphic_base(meta::type_declaration& t) -> void +{ + auto has_dtor {false}; + + for ( auto& mf : CPP2_UFCS(get_member_functions)(t) ) + { + if (CPP2_UFCS(is_default_access)(mf)) { + CPP2_UFCS(default_to_public)(mf); + } + CPP2_UFCS(require)(mf, !(CPP2_UFCS(is_copy_or_move)(mf)), + "polymorphic base types may not copy or move; consider a virtual clone() instead"); + if (CPP2_UFCS(is_destructor)(mf)) { + has_dtor = true; + CPP2_UFCS(require)(mf, ((CPP2_UFCS(is_public)(mf) || CPP2_UFCS(is_default_access)(mf)) && CPP2_UFCS(is_virtual)(mf)) + || (CPP2_UFCS(is_protected)(mf) && !(CPP2_UFCS(is_virtual)(mf))), + "a polymorphic base type destructor must be public and virtual, or protected and nonvirtual"); + } + } + + if (!(cpp2::move(has_dtor))) { + CPP2_UFCS(add_virtual_destructor)(t); + } +} + +#line 805 "reflect.h2" +auto ordered_impl( + meta::type_declaration& t, + cpp2::impl::in ordering +) -> void +{ + auto has_spaceship {false}; + + for ( auto& mf : CPP2_UFCS(get_member_functions)(t) ) + { + if (CPP2_UFCS(has_name)(mf, "operator<=>")) { + has_spaceship = true; + auto return_name {CPP2_UFCS(unnamed_return_type)(mf)}; + if (CPP2_UFCS(find)(return_name, ordering) == return_name.npos) + { + CPP2_UFCS(error)(mf, "operator<=> must return std::" + cpp2::impl::as_(ordering)); + } + } + } + + if (!(cpp2::move(has_spaceship))) { + CPP2_UFCS(add_member)(t, "operator<=>: (this, that) -> std::" + (cpp2::impl::as_(ordering)) + ";"); + } +} + +#line 834 "reflect.h2" +CPPFRONTAPI auto ordered(meta::type_declaration& t) -> void +{ + ordered_impl(t, "strong_ordering"); +} + +#line 842 "reflect.h2" +CPPFRONTAPI auto weakly_ordered(meta::type_declaration& t) -> void +{ + ordered_impl(t, "weak_ordering"); +} + +#line 850 "reflect.h2" +CPPFRONTAPI auto partially_ordered(meta::type_declaration& t) -> void +{ + ordered_impl(t, "partial_ordering"); +} + +#line 872 "reflect.h2" +CPPFRONTAPI auto copyable(meta::type_declaration& t) -> void +{ + // If the user explicitly wrote any of the copy/move functions, + // they must also have written the most general one - we can't + // assume we can safely generate it for them since they've opted + // into customized semantics + auto smfs {CPP2_UFCS(query_declared_value_set_functions)(t)}; + if ( !(smfs.out_this_in_that) + && ( + smfs.out_this_move_that + || smfs.inout_this_in_that + || smfs.inout_this_move_that)) + + { + CPP2_UFCS(error)(t, + "this type is partially copyable/movable - when you provide " + "any of the more-specific operator= signatures, you must also provide " + "the one with the general signature (out this, that); alternatively, " + "consider removing all the operator= functions and let them all be " + "generated for you with default memberwise semantics" + ); + } + else {if (!(cpp2::move(smfs).out_this_in_that)) { + CPP2_UFCS(add_member)(t, "operator=: (out this, that) = { }"); + }} +} + +#line 905 "reflect.h2" +CPPFRONTAPI auto hashable(meta::type_declaration& t) -> void +{ + CPP2_UFCS(require)(t, !(CPP2_UFCS(empty)(CPP2_UFCS(get_member_objects)(t))), + "a hashable type must have at least one data member"); + + std::string hash {" hash: (this) -> size_t = {\n" + " ret: size_t = 0;"}; + + for ( + auto const& o : CPP2_UFCS(get_member_objects)(t) ) + { + cpp2::impl::deferred_init o_hash; + if (CPP2_UFCS(name)(o) == "this") { + o_hash.construct("" + cpp2::to_string(CPP2_UFCS(type)(o)) + "::hash()"); + } + else { + o_hash.construct("std::hash<" + cpp2::to_string(CPP2_UFCS(type)(o)) + ">()(" + cpp2::to_string(CPP2_UFCS(name)(o)) + ")"); + } + + hash += "\n cpp2::hash_combine( ret, " + cpp2::to_string(cpp2::move(o_hash.value())) + " );"; + } + + CPP2_UFCS(add_member)(t, cpp2::move(hash) + "\n return ret;\n }"); +} + +#line 938 "reflect.h2" +CPPFRONTAPI auto basic_value(meta::type_declaration& t) -> void +{ + CPP2_UFCS(copyable)(t); + + auto has_default_ctor {false}; + for ( auto& mf : CPP2_UFCS(get_member_functions)(t) ) { + has_default_ctor |= CPP2_UFCS(is_default_constructor)(mf); + CPP2_UFCS(require)(mf, !(CPP2_UFCS(is_protected)(mf)) && !(CPP2_UFCS(is_virtual)(mf)), + "a value type may not have a protected or virtual function"); + CPP2_UFCS(require)(mf, !(CPP2_UFCS(is_destructor)(mf)) || CPP2_UFCS(is_public)(mf) || CPP2_UFCS(is_default_access)(mf), + "a value type may not have a non-public destructor"); + } + + if (!(cpp2::move(has_default_ctor))) { + CPP2_UFCS(add_member)(t, "operator=: (out this) = { }"); + } +} + +#line 966 "reflect.h2" +CPPFRONTAPI auto value(meta::type_declaration& t) -> void +{ + CPP2_UFCS(ordered)(t); + CPP2_UFCS(basic_value)(t); +} + +#line 972 "reflect.h2" +CPPFRONTAPI auto weakly_ordered_value(meta::type_declaration& t) -> void +{ + CPP2_UFCS(weakly_ordered)(t); + CPP2_UFCS(basic_value)(t); +} + +#line 978 "reflect.h2" +CPPFRONTAPI auto partially_ordered_value(meta::type_declaration& t) -> void +{ + CPP2_UFCS(partially_ordered)(t); + CPP2_UFCS(basic_value)(t); +} + +#line 1007 "reflect.h2" +CPPFRONTAPI auto cpp1_rule_of_zero(meta::type_declaration& t) -> void +{ + for ( auto& mf : CPP2_UFCS(get_member_functions)(t) ) + { + CPP2_UFCS(require)(t, !(CPP2_UFCS(is_constructor_with_that)(mf)) + && !(CPP2_UFCS(is_assignment_with_that)(mf)) + && !(CPP2_UFCS(is_destructor)(mf)), + "the rule of zero requires no copy/move/destructor functions"); + } + CPP2_UFCS(disable_member_function_generation)(t); +} + +#line 1049 "reflect.h2" +CPPFRONTAPI auto cpp2_struct(meta::type_declaration& t) -> void +{ + std::string ctor_params {}; + std::string ctor_inits {}; + + auto found_member_without_initializer {false}; + + for ( auto& m : CPP2_UFCS(get_members)(t) ) + { + CPP2_UFCS(require)(m, CPP2_UFCS(make_public)(m), + "all struct members must be public"); + if (CPP2_UFCS(is_function)(m)) { + auto mf {CPP2_UFCS(as_function)(m)}; + CPP2_UFCS(require)(t, !(CPP2_UFCS(is_virtual)(mf)), + "a struct may not have a virtual function"); + CPP2_UFCS(require)(t, !(CPP2_UFCS(has_name)(cpp2::move(mf), "operator=")), + "a struct may not have a user-defined operator="); + } + else {if (CPP2_UFCS(is_object)(m)) { + auto mo {CPP2_UFCS(as_object)(m)}; + if (CPP2_UFCS(name)(mo) != "this") { + if (CPP2_UFCS(get_argument)(t, 0) == "noforward") { + ctor_params += "" + cpp2::to_string(CPP2_UFCS(name)(mo)) + "_, "; + } + else { + ctor_params += "forward " + cpp2::to_string(CPP2_UFCS(name)(mo)) + "_ : " + cpp2::to_string(CPP2_UFCS(type)(mo)) + ", "; + } + ctor_inits += "" + cpp2::to_string(CPP2_UFCS(name)(mo)) + " = " + cpp2::to_string(CPP2_UFCS(name)(mo)) + "_; "; + } + else { + ctor_inits += "" + cpp2::to_string(CPP2_UFCS(type)(mo)) + " = (" + cpp2::to_string(CPP2_UFCS(initializer)(mo)) + "); "; + } + found_member_without_initializer |= !(CPP2_UFCS(has_initializer)(cpp2::move(mo))); + }} + } + CPP2_UFCS(cpp1_rule_of_zero)(t); + + // If we found any data members + if (!(CPP2_UFCS(empty)(ctor_params))) + { + // Then to enable construction from corresponding values + // requires a constructor... an exception to the rule of zero + CPP2_UFCS(add_member)(t, " operator=: (implicit out this, " + cpp2::to_string(cpp2::move(ctor_params)) + ") = { " + cpp2::to_string(cpp2::move(ctor_inits)) + " }"); + + // And if all members had initializers, we need a default constructor + if (!(cpp2::move(found_member_without_initializer))) { + CPP2_UFCS(add_member)(t, " operator=: (implicit out this) = { }"); + } + } +} + +value_member_info::value_member_info(auto const& name_, auto const& type_, auto const& value_) + : name{ name_ } + , type{ type_ } + , value{ value_ }{} + +#line 1124 "reflect.h2" +auto basic_enum( + meta::type_declaration& t, + auto const& nextval, + cpp2::impl::in bitwise + ) -> void +{ + std::vector enumerators {}; + cpp2::i64 min_value {}; + cpp2::i64 max_value {}; + cpp2::impl::deferred_init underlying_type; + + CPP2_UFCS(reserve_names)(t, "operator=", "operator<=>"); + if (bitwise) { + CPP2_UFCS(reserve_names)(t, "has", "set", "clear", "to_string", "get_raw_value", "none"); + } + + // 1. Gather: The names of all the user-written members, and find/compute the type + + underlying_type.construct(CPP2_UFCS(get_argument)(t, 0));// use the first template argument, if there was one + + auto found_non_numeric {false}; +{ +std::string value{"-1"}; + +#line 1147 "reflect.h2" + for ( + auto const& m : CPP2_UFCS(get_members)(t) ) + if ( CPP2_UFCS(is_member_object)(m)) + { + CPP2_UFCS(require)(m, CPP2_UFCS(is_public)(m) || CPP2_UFCS(is_default_access)(m), + "an enumerator cannot be protected or private"); + + auto mo {CPP2_UFCS(as_object)(m)}; + if (!(CPP2_UFCS(has_wildcard_type)(mo))) { + CPP2_UFCS(error)(mo, + "an explicit underlying type should be specified as a compile-time argument " + "to the metafunction - try 'enum' or 'flag_enum'" + ); + } + + auto init {CPP2_UFCS(initializer)(mo)}; + + auto is_default_or_numeric {is_empty_or_a_decimal_number(init)}; + found_non_numeric |= !(CPP2_UFCS(empty)(init)) && !(is_default_or_numeric); + CPP2_UFCS(require)(m, !(cpp2::move(is_default_or_numeric)) || !(found_non_numeric) || CPP2_UFCS(has_name)(mo, "none"), + "" + cpp2::to_string(CPP2_UFCS(name)(mo)) + ": enumerators with non-numeric values must come after all default and numeric values"); + + nextval(value, cpp2::move(init)); + + auto v {std::strtoll(&CPP2_ASSERT_IN_BOUNDS_LITERAL(value, 0), nullptr, 10)}; // for non-numeric values we'll just get 0 which is okay for now + if (cpp2::impl::cmp_less(v,min_value)) { + min_value = v; + } + if (cpp2::impl::cmp_greater(v,max_value)) { + max_value = cpp2::move(v); + } + + // Adding local variable 'e' to work around a Clang warning + value_member_info e {cpp2::impl::as_(CPP2_UFCS(name)(mo)), "", value}; + CPP2_UFCS(push_back)(enumerators, cpp2::move(e)); + + CPP2_UFCS(mark_for_removal_from_enclosing_type)(mo); + static_cast(cpp2::move(mo)); + } +} + +#line 1187 "reflect.h2" + if ((CPP2_UFCS(empty)(enumerators))) { + CPP2_UFCS(error)(t, "an enumeration must contain at least one enumerator value"); + return ; + } + + // Compute the default underlying type, if it wasn't explicitly specified + if (underlying_type.value() == "") + { + CPP2_UFCS(require)(t, !(cpp2::move(found_non_numeric)), + "if you write an enumerator with a non-numeric-literal value, " + "you must specify the enumeration's underlying type" + ); + + if (!(bitwise)) { + if (cpp2::impl::cmp_greater_eq(min_value,std::numeric_limits::min()) && cpp2::impl::cmp_less_eq(max_value,std::numeric_limits::max())) { + underlying_type.value() = "i8"; + } + else {if (cpp2::impl::cmp_greater_eq(min_value,std::numeric_limits::min()) && cpp2::impl::cmp_less_eq(max_value,std::numeric_limits::max())) { + underlying_type.value() = "i16"; + } + else {if (cpp2::impl::cmp_greater_eq(min_value,std::numeric_limits::min()) && cpp2::impl::cmp_less_eq(max_value,std::numeric_limits::max())) { + underlying_type.value() = "i32"; + } + else {if (cpp2::impl::cmp_greater_eq(cpp2::move(min_value),std::numeric_limits::min()) && cpp2::impl::cmp_less_eq(cpp2::move(max_value),std::numeric_limits::max())) { + underlying_type.value() = "i64"; + } + else { + CPP2_UFCS(error)(t, + "values are outside the range representable by the " + "largest supported underlying signed type (i64)" + ); + }}}} + } + else { + auto umax {cpp2::move(max_value) * cpp2::impl::as_()}; + if (cpp2::impl::cmp_less_eq(umax,std::numeric_limits::max())) { + underlying_type.value() = "u8"; + } + else {if (cpp2::impl::cmp_less_eq(umax,std::numeric_limits::max())) { + underlying_type.value() = "u16"; + } + else {if (cpp2::impl::cmp_less_eq(cpp2::move(umax),std::numeric_limits::max())) { + underlying_type.value() = "u32"; + } + else { + underlying_type.value() = "u64"; + }}} + } + } + +#line 1238 "reflect.h2" + // 2. Replace: Erase the contents and replace with modified contents + // + // Note that most values and functions are declared as '==' compile-time values, i.e. Cpp1 'constexpr' + + CPP2_UFCS(remove_marked_members)(t); + + // Generate the 'none' value if appropriate, and use that or + // else the first enumerator as the default-constructed value + auto default_value {CPP2_ASSERT_IN_BOUNDS_LITERAL(enumerators, 0).name}; + if (bitwise) { + default_value = "none"; + value_member_info e {"none", "", "0"}; + CPP2_UFCS(push_back)(enumerators, cpp2::move(e)); + } + + // Generate all the private implementation + CPP2_UFCS(add_member)(t, " _value : " + cpp2::to_string(underlying_type.value()) + ";"); + CPP2_UFCS(add_member)(t, " private operator= : (implicit out this, _val: i64) == " + "_value = cpp2::unchecked_narrow<" + cpp2::to_string(underlying_type.value()) + ">(_val);"); + + // Generate the bitwise operations + if (bitwise) { + CPP2_UFCS(add_member)(t, " operator|=: ( inout this, that ) == _value |= that._value;"); + CPP2_UFCS(add_member)(t, " operator&=: ( inout this, that ) == _value &= that._value;"); + CPP2_UFCS(add_member)(t, " operator^=: ( inout this, that ) == _value ^= that._value;"); + CPP2_UFCS(add_member)(t, " operator| : ( this, that ) -> " + cpp2::to_string(CPP2_UFCS(name)(t)) + " == _value | that._value;"); + CPP2_UFCS(add_member)(t, " operator& : ( this, that ) -> " + cpp2::to_string(CPP2_UFCS(name)(t)) + " == _value & that._value;"); + CPP2_UFCS(add_member)(t, " operator^ : ( this, that ) -> " + cpp2::to_string(CPP2_UFCS(name)(t)) + " == _value ^ that._value;"); + CPP2_UFCS(add_member)(t, " has : ( this, that ) -> bool == _value & that._value;"); + CPP2_UFCS(add_member)(t, " set : ( inout this, that ) == { _value |= that._value; }"); + CPP2_UFCS(add_member)(t, " clear : ( inout this, that ) == { _value &= that._value~; }"); + } + + // Add the enumerators + for ( auto const& e : enumerators ) { + CPP2_UFCS(add_member)(t, " " + cpp2::to_string(e.name) + " : " + cpp2::to_string(CPP2_UFCS(name)(t)) + " == " + cpp2::to_string(e.value) + ";"); + } + + // Generate the common functions + CPP2_UFCS(add_member)(t, " get_raw_value : (this) -> " + cpp2::to_string(cpp2::move(underlying_type.value())) + " == _value;"); + CPP2_UFCS(add_member)(t, " operator= : (out this) == { _value = " + cpp2::to_string(default_value) + "._value; }"); + CPP2_UFCS(add_member)(t, " operator= : (out this, that) == { }"); + CPP2_UFCS(add_member)(t, " operator<=> : (this, that) -> std::strong_ordering;"); +{ +std::string to_string_impl{" to_string_impl: (this, prefix: std::string_view"}; + + // Provide 'to_string' and 'to_code' functions to print enumerator + // name(s) as human-readable strings or as code expressions + +#line 1285 "reflect.h2" + { + if (bitwise) { + to_string_impl += ", separator: std::string_view ) -> std::string = { \n" + " ret : std::string = \"(\";\n" + " sep : std::string = ();\n" + " if this == none { return \"(none)\"; }\n"; + } + else { + to_string_impl += ") -> std::string = { \n"; + } + + to_string_impl += " pref := cpp2::to_string(prefix);\n"; + + for ( + auto const& e : enumerators ) { + if (e.name != "_") {// ignore unnamed values + if (bitwise) { + if (e.name != "none") { + to_string_impl += " if (this & " + cpp2::to_string(e.name) + ") == " + cpp2::to_string(e.name) + " { " + "ret += sep + pref + \"" + cpp2::to_string(e.name) + "\"; sep = separator; " + "}\n"; + } + } + else { + to_string_impl += " if this == " + cpp2::to_string(e.name) + " { return pref + \"" + cpp2::to_string(e.name) + "\"; }\n"; + } + } + } + + if (bitwise) { + to_string_impl += " return ret+\")\";\n}\n"; + } + else { + to_string_impl += " return \"invalid " + cpp2::to_string(CPP2_UFCS(name)(t)) + " value\";\n}\n"; + } + + CPP2_UFCS(add_member)(t, cpp2::move(to_string_impl)); + } +} + +#line 1324 "reflect.h2" + if (bitwise) { + CPP2_UFCS(add_member)(t, " to_string: (this) -> std::string = to_string_impl( \"\", \", \" );"); + CPP2_UFCS(add_member)(t, " to_code : (this) -> std::string = to_string_impl( \"" + cpp2::to_string(CPP2_UFCS(name)(t)) + "::\", \" | \" );"); + } + else { + CPP2_UFCS(add_member)(t, " to_string: (this) -> std::string = to_string_impl( \"\" );"); + CPP2_UFCS(add_member)(t, " to_code : (this) -> std::string = to_string_impl( \"" + cpp2::to_string(CPP2_UFCS(name)(t)) + "::\" );"); + } +{ +std::string from_string{" from_string: (s: std::string_view) -> " + cpp2::to_string(CPP2_UFCS(name)(t)) + " = { \n"}; + + // Provide a 'from_string' function to parse strings into enumerators + +#line 1335 "reflect.h2" + { + std::string_view prefix {""}; + std::string_view combine_op {"return"}; + + // For flags, accept a list that we break apart and then |= together + if (bitwise) + { + prefix = "flag_"; + combine_op = "ret |="; + + from_string += " ret := none;\n" + " outer: do {\n" + " for cpp2::string_util::split_string_list(s) do (x) {\n"; + } + // Otherwise, accept just a single string + else { + from_string += " x := s;\n"; + } +{ +std::string_view else_{""}; + +#line 1355 "reflect.h2" + for ( + auto const& e : cpp2::move(enumerators) ) { + from_string += " " + cpp2::to_string(else_) + "if \"" + cpp2::to_string(e.name) + "\" == x { " + cpp2::to_string(combine_op) + " " + cpp2::to_string(e.name) + "; }\n"; + else_ = "else "; + } +} + +#line 1361 "reflect.h2" + if (bitwise) { + from_string += " else { break outer; }\n" + " }\n" + " return ret;\n" + " } while false;\n"; + } + + from_string += " cpp2::type_safety.report_violation( (\"can't convert string '\" + cpp2::to_string(s) + \"' to " + cpp2::to_string(cpp2::move(prefix)) + "enum of type " + cpp2::to_string(CPP2_UFCS(name)(t)) + "\").c_str() );\n" + " return " + cpp2::to_string(cpp2::move(default_value)) + ";\n" + " }\n\n"; + + CPP2_UFCS(add_member)(t, cpp2::move(from_string)); + } +} + +#line 1375 "reflect.h2" + CPP2_UFCS(add_member)(t, " from_code: (s: std::string_view) -> " + cpp2::to_string(CPP2_UFCS(name)(t)) + " = { str: std::string = s; return from_string( cpp2::string_util::replace_all(str, \"" + cpp2::to_string(CPP2_UFCS(name)(t)) + "::\", \"\" ) ); }"); +} + +#line 1388 "reflect.h2" +CPPFRONTAPI auto cpp2_enum(meta::type_declaration& t) -> void +{ + // Let basic_enum do its thing, with an incrementing value generator + CPP2_UFCS(basic_enum)(t, + [](std::string& value, cpp2::impl::in specified_value) -> void{ + if (!(CPP2_UFCS(empty)(specified_value))) { + value = specified_value; + }else { + auto v {std::strtoll(&CPP2_ASSERT_IN_BOUNDS_LITERAL(value, 0), nullptr, 10)}; + value = cpp2::impl::as_((cpp2::move(v) + 1)); + } + }, + false // disable bitwise operations + ); +} + +#line 1415 "reflect.h2" +CPPFRONTAPI auto flag_enum(meta::type_declaration& t) -> void +{ + // Let basic_enum do its thing, with a power-of-two value generator + CPP2_UFCS(basic_enum)(t, + [](std::string& value, cpp2::impl::in specified_value) -> void{ + if (!(CPP2_UFCS(empty)(specified_value))) { + value = specified_value; + }else { + auto v {std::strtoll(&CPP2_ASSERT_IN_BOUNDS_LITERAL(value, 0), nullptr, 10)}; + if (cpp2::impl::cmp_less(v,1)) { + value = "1"; + } + else { + value = cpp2::impl::as_((cpp2::move(v) * 2)); + } + } + }, + true // enable bitwise operations + ); +} + +#line 1461 "reflect.h2" +CPPFRONTAPI auto cpp2_union(meta::type_declaration& t) -> void +{ + std::vector alternatives {}; +{ +auto value{0}; + + // 1. Gather: All the user-written members, and find/compute the max size + +#line 1468 "reflect.h2" + for ( + + auto const& m : CPP2_UFCS(get_members)(t) ) { do + if ( CPP2_UFCS(is_member_object)(m)) + { + CPP2_UFCS(require)(m, CPP2_UFCS(is_public)(m) || CPP2_UFCS(is_default_access)(m), + "a union alternative cannot be protected or private" + ); + + CPP2_UFCS(require)(m, !(CPP2_UFCS(starts_with)(CPP2_UFCS(name)(m), "is_")) + && !(CPP2_UFCS(starts_with)(CPP2_UFCS(name)(m), "set_")), + "a union alternative's name cannot start with 'is_' or 'set_' - that could cause " + "user confusion with the 'is_alternative' and 'set_alternative' generated functions" + ); + + auto mo {CPP2_UFCS(as_object)(m)}; + CPP2_UFCS(require)(mo, CPP2_UFCS(empty)(CPP2_UFCS(initializer)(mo)), + "a union alternative cannot have an initializer" + ); + + // Adding local variable 'e' to work around a Clang warning + value_member_info e {cpp2::impl::as_(CPP2_UFCS(name)(mo)), CPP2_UFCS(type)(mo), cpp2::impl::as_(value)}; + CPP2_UFCS(push_back)(alternatives, cpp2::move(e)); + + CPP2_UFCS(mark_for_removal_from_enclosing_type)(mo); + static_cast(cpp2::move(mo)); + } while (false); ++value; } +} + +#line 1496 "reflect.h2" + std::string discriminator_type {}; + if (cpp2::impl::cmp_less(CPP2_UFCS(ssize)(alternatives),std::numeric_limits::max())) { + discriminator_type = "i8"; + } + else {if (cpp2::impl::cmp_less(CPP2_UFCS(ssize)(alternatives),std::numeric_limits::max())) { + discriminator_type = "i16"; + } + else {if (cpp2::impl::cmp_less(CPP2_UFCS(ssize)(alternatives),std::numeric_limits::max())) { + discriminator_type = "i32"; + } + else { + discriminator_type = "i64"; + }}} + +#line 1511 "reflect.h2" + // 2. Replace: Erase the contents and replace with modified contents + + CPP2_UFCS(remove_marked_members)(t); +{ +std::string storage{" _storage: cpp2::aligned_storage bool = _discriminator == " + cpp2::to_string(a.value) + ";\n"); + + CPP2_UFCS(add_member)(t, " " + cpp2::to_string(a.name) + ": (this) -> forward " + cpp2::to_string(a.type) + " pre(is_" + cpp2::to_string(a.name) + "()) = " + "reinterpret_cast<* const " + cpp2::to_string(a.type) + ">(_storage&)*;\n" + ); + + CPP2_UFCS(add_member)(t, " " + cpp2::to_string(a.name) + ": (inout this) -> forward " + cpp2::to_string(a.type) + " pre(is_" + cpp2::to_string(a.name) + "()) = " + "reinterpret_cast<*" + cpp2::to_string(a.type) + ">(_storage&)*;\n" + ); + + CPP2_UFCS(add_member)(t, " set_" + cpp2::to_string(a.name) + ": (inout this, _value: " + cpp2::to_string(a.type) + ") = { " + "if !is_" + cpp2::to_string(a.name) + "() { _destroy(); std::construct_at( reinterpret_cast<*" + cpp2::to_string(a.type) + ">(_storage&), _value); } " + "else { reinterpret_cast<*" + cpp2::to_string(a.type) + ">(_storage&)* = _value; } " + "_discriminator = " + cpp2::to_string(a.value) + "; " + "}\n" + ); + + CPP2_UFCS(add_member)(t, " set_" + cpp2::to_string(a.name) + ": (inout this, forward _args...: _) = { " + "if !is_" + cpp2::to_string(a.name) + "() { _destroy(); std::construct_at( reinterpret_cast<*" + cpp2::to_string(a.type) + ">(_storage&), _args...); } " + " else { reinterpret_cast<*" + cpp2::to_string(a.type) + ">(_storage&)* = :" + cpp2::to_string(a.type) + " = (_args...); } " + "_discriminator = " + cpp2::to_string(a.value) + "; " + "}\n" + ); + } +{ +std::string destroy{" private _destroy: (inout this) = {\n"}; + + // Add destroy + +#line 1568 "reflect.h2" + { + for ( + auto const& a : alternatives ) { + destroy += " if _discriminator == " + cpp2::to_string(a.value) + " { std::destroy_at( reinterpret_cast<*" + cpp2::to_string(a.type) + ">(_storage&) ); }\n"; + } + + destroy += " _discriminator = -1;\n" + " }\n"; + CPP2_UFCS(add_member)(t, cpp2::move(destroy)); + } +} + + // Add the destructor +#line 1580 "reflect.h2" + CPP2_UFCS(add_member)(t, " operator=: (move this) = { _destroy(); _ = this; }"); + + // Add default constructor + CPP2_UFCS(add_member)(t, " operator=: (out this) = { }"); +{ +std::string value_set{""}; + + // Add copy/move construction and assignment + +#line 1587 "reflect.h2" + { + for ( + auto const& a : cpp2::move(alternatives) ) { + value_set += " if that.is_" + cpp2::to_string(a.name) + "() { set_" + cpp2::to_string(a.name) + "( that." + cpp2::to_string(a.name) + "() ); }\n"; + } + value_set += " }\n"; + + CPP2_UFCS(add_member)(t, " operator=: (out this, that) = {\n" + " _storage = ();\n" + " _discriminator = -1;\n" + + value_set + ); + CPP2_UFCS(add_member)(t, " operator=: (inout this, that) = {\n" + " _storage = _;\n" + " _discriminator = _;\n" + + cpp2::move(value_set) + ); + } +} +#line 1605 "reflect.h2" +} + +#line 1612 "reflect.h2" +CPPFRONTAPI auto print(cpp2::impl::in t) -> void +{ + std::cout << CPP2_UFCS(print)(t) << "\n"; +} + +#line 1622 "reflect.h2" +CPPFRONTAPI auto dll_visible(meta::type_declaration& t) -> void +{ + CPP2_UFCS(require)(t, CPP2_UFCS(make_dll_visible)(t), + "dll_visible can only be applied to a namespace-scope name"); +} +#line 1627 "reflect.h2" +CPPFRONTAPI auto dll_visible(meta::function_declaration& t) -> void +{ + CPP2_UFCS(require)(t, CPP2_UFCS(make_dll_visible)(t), + "dll_visible can only be applied to a namespace-scope name"); +} + +#line 1634 "reflect.h2" +} + +} + +#endif diff --git a/source/parse.h b/source/parse.h index a8ad4bd9e6..79c37ddd54 100644 --- a/source/parse.h +++ b/source/parse.h @@ -19,6 +19,10 @@ #define CPP2_PARSE_H #include "lex.h" +#include +#include +#include +#include namespace cpp2 { @@ -689,33 +693,8 @@ auto binary_expression_node::is_standalone_expression() const } -enum class passing_style : u8 { in=0, in_ref, copy, inout, out, move, forward, forward_ref, invalid }; -auto to_passing_style(token const& t) -> passing_style { - if (t.type() == lexeme::Identifier) { - if (t == "in" ) { return passing_style::in; } - if (t == "in_ref" ) { return passing_style::in_ref; } - if (t == "copy" ) { return passing_style::copy; } - if (t == "inout" ) { return passing_style::inout; } - if (t == "out" ) { return passing_style::out; } - if (t == "move" ) { return passing_style::move; } - if (t == "forward") { return passing_style::forward; } - if (t == "forward_ref") { return passing_style::forward_ref; } - } - return passing_style::invalid; -} -auto to_string_view(passing_style pass) -> std::string_view { - switch (pass) { - break;case passing_style::in : return "in"; - break;case passing_style::in_ref : return "in_ref"; - break;case passing_style::copy : return "copy"; - break;case passing_style::inout : return "inout"; - break;case passing_style::out : return "out"; - break;case passing_style::move : return "move"; - break;case passing_style::forward : return "forward"; - break;case passing_style::forward_ref: return "forward_ref"; - break;default : return "INVALID passing_style"; - } -} +using meta::passing_style; +using meta::to_passing_style; struct expression_list_node @@ -726,7 +705,7 @@ struct expression_list_node bool default_initializer = false; struct term { - passing_style pass = {}; + passing_style pass = passing_style{}; std::unique_ptr expr; auto visit(auto& v, int depth) -> void @@ -2549,7 +2528,7 @@ struct function_type_node if (auto t = std::get_if(&returns)) { ret += " -> "; - ret += to_string_view(t->pass); + ret += t->pass.to_string(); ret += " " + t->type->to_string(); } else if (auto t = std::get_if(&returns)) { @@ -2628,6 +2607,12 @@ struct function_type_node auto is_destructor() const -> bool; + auto is_metafunction() const + -> bool; + + auto is_const_metafunction() const + -> bool; + auto has_declared_return_type() const -> bool { @@ -2985,6 +2970,7 @@ struct declaration_node source_position pos; bool is_variadic = false; bool is_constexpr = false; + bool is_dll_visible_ = false; std::unique_ptr identifier; accessibility access = accessibility::default_; @@ -2998,6 +2984,7 @@ struct declaration_node > type; std::vector> metafunctions; + std::vector metafunction_lookup_checks; std::unique_ptr template_parameters; source_position requires_pos = {}; std::unique_ptr requires_clause_expression; @@ -3183,6 +3170,21 @@ struct declaration_node return set_access( accessibility::private_ ); } + auto is_dll_visible() const + -> bool + { + return is_dll_visible_; + } + + auto make_dll_visible() + -> bool + { + if (!parent_is_namespace()) { + return false; + } + return is_dll_visible_ = true; + } + auto has_name() const -> bool { @@ -3211,6 +3213,23 @@ struct declaration_node ; } + auto fully_qualified_name() const + -> std::string + { + if (!has_name()) { + return {}; + } + + auto res = std::string{}; + for (auto n = this; n; n = n->parent_declaration) + { + assert(n->identifier); + res.insert(0, n->identifier->to_string()); + res.insert(0, "::"); + } + return res; + } + auto has_initializer() const -> bool { @@ -3975,6 +3994,26 @@ struct declaration_node return ""; } + auto is_metafunction() const + -> bool + { + if (auto func = std::get_if(&type)) { + return (*func)->is_metafunction(); + } + // else + return false; + } + + auto is_const_metafunction() const + -> bool + { + if (auto func = std::get_if(&type)) { + return (*func)->is_const_metafunction(); + } + // else + return false; + } + auto is_binary_comparison_function() const -> bool { @@ -4091,7 +4130,7 @@ auto parameter_declaration_node::to_string() const ; } - ret += to_string_view(pass) + declaration->to_string(); + ret += pass.to_string() + declaration->to_string(); return ret; } @@ -4511,6 +4550,32 @@ auto function_type_node::is_destructor() const } +auto function_type_node::is_metafunction() const + -> bool +{ + if ( + (*parameters).ssize() == 1 + && this->nth_parameter_type_name(1) == "cpp2::meta::type_declaration" + && ( + (*parameters)[0]->direction() == passing_style::in + || (*parameters)[0]->direction() == passing_style::inout + ) + ) + { + return true; + } + return false; +} + + +auto function_type_node::is_const_metafunction() const + -> bool +{ + return is_metafunction() + && (*parameters)[0]->direction() == passing_style::in; +} + + auto primary_expression_node::template_arguments() const -> std::vector const& { @@ -4734,6 +4799,7 @@ auto parameter_declaration_node::visit(auto& v, int depth) struct translation_unit_node { + bool has_interface = false; std::vector< std::unique_ptr > declarations; auto position() const -> source_position @@ -5046,7 +5112,7 @@ auto pretty_print_visualize(expression_list_node const& n, int indent) || expr.pass == passing_style::forward ) { - ret += to_string_view(expr.pass) + std::string{" "}; + ret += expr.pass.to_string() + std::string{" "}; } ret += pretty_print_visualize(*expr.expr, indent); if (++i < std::ssize(n.expressions)) { @@ -5490,7 +5556,7 @@ auto pretty_print_visualize(parameter_declaration_node const& n, int indent, boo break;default: ; // none } - ret += to_string_view(n.pass); + ret += n.pass.to_string(); ret += " "; } @@ -5541,7 +5607,7 @@ auto pretty_print_visualize(function_type_node const& n, int indent) ret += try_pretty_print_visualize(n.returns, indent+1); if (n.returns.index() == function_type_node::id) { auto& single = std::get(n.returns); - ret += to_string_view(single.pass) + ret += single.pass.to_string() + std::string{" "} + pretty_print_visualize(*single.type, indent+1); } } @@ -5773,6 +5839,118 @@ auto pretty_print_visualize(translation_unit_node const& n) } +// Consider moving these `stack` functions to `common.h` to enable more general use. + +template +auto stack_value( + T& var, + std::type_identity_t const& value +) + -> auto +{ + return finally([&var, old = std::exchange(var, value)]() { + var = old; + }); +} + +template +auto stack_element( + std::vector& cont, + std::type_identity_t const& value +) + -> auto +{ + cont.push_back(value); + return finally([&]{ cont.pop_back(); }); +} + +template +auto stack_size(std::vector& cont) + -> auto +{ + return finally([&, size = cont.size()]{ cont.resize(size); }); +} + +template +auto stack_size_if( + std::vector& cont, + bool cond +) + -> std::optional +{ + if (cond) { + return stack_size(cont); + } + return {}; +} + + +struct active_using_declaration { + qualified_id_node const* id = {}; + + explicit active_using_declaration(using_statement_node const& n) { + if (auto id_ = get_if(&n.id->id)) { + id = &**id_; + } + } + + auto introduced_identifier() const + -> std::string_view + { + if (id) { + return *id->ids.back().id->identifier; + } + // else + return {}; + } + + auto to_string() const + -> std::string + { + if (id) { + return id->to_string(); + } + // else + return {}; + } +}; + +using source_order_name_lookup_res = + std::optional>; + +using current_names_span = std::span; + +auto source_order_name_lookup(current_names_span current_names, std::string_view id) + -> source_order_name_lookup_res +{ + for ( + auto first = current_names.rbegin(), last = current_names.rend() - 1; + first != last; + ++first + ) + { + if ( + auto decl = get_if(&*first); + decl + && *decl + && (*decl)->has_name(id) + ) + { + return *decl; + } + else if ( + auto using_ = get_if(&*first); + using_ + && using_->introduced_identifier() == id + ) + { + return *using_; + } + } + + return {}; +} + //----------------------------------------------------------------------- // // parser: parses a section of Cpp2 code @@ -5807,8 +5985,12 @@ class parser } }; - // Keep a stack of currently active declarations (still being parsed) - std::vector current_declarations = { nullptr }; + // Stack of the currently active nested declarations we're inside (still being parsed) + std::vector current_declarations = { {} }; + + // Stack of the currently active names for source order name lookup: + // Like 'current_declarations' + also parameters and using declarations + std::vector current_names = { {} }; struct current_declarations_stack_guard { @@ -5909,12 +6091,15 @@ class parser // parser( std::vector& errors_, - std::set& includes_ + std::set& includes_, + bool translation_unit_has_interface_ ) : errors{ errors_ } , includes{ includes_ } , parse_tree{std::make_unique()} - { } + { + parse_tree->has_interface = translation_unit_has_interface_; + } parser( parser const& that ) : errors{ that.errors } @@ -5923,6 +6108,9 @@ class parser { } + auto translation_unit_has_interface() -> bool { return parse_tree->has_interface; } + + //----------------------------------------------------------------------- // parse // @@ -8022,6 +8210,10 @@ class parser } next(); + if (!n->for_namespace()) { + current_names.push_back(active_using_declaration{*n}); + } + return n; } @@ -8059,6 +8251,8 @@ class parser auto n = std::make_unique(compound_parent); + auto guard_parameters = stack_size_if(current_names, /*bool(n->parameters)*/ true); + // If a parameter list is allowed here, try to parse one if (parameters_allowed) { n->parameters = parameter_declaration_list(false, true, false, true); @@ -8076,6 +8270,9 @@ class parser } } } + if (!n->parameters) { + guard_parameters.reset(); + } // Now handle the rest of the statement @@ -8396,20 +8593,25 @@ class parser && n->pass != passing_style::copy ) { - switch (n->pass) { - break;case passing_style::in: + if (n->pass == passing_style::in) { error( "an 'in' parameter is always const, 'const' isn't needed and isn't allowed", false ); - break;case passing_style::in_ref: + } + else if (n->pass == passing_style::in_ref) { error( "an 'in_ref' parameter is always const, 'const' isn't needed and isn't allowed", false ); - break;case passing_style::inout: + } + else if (n->pass == passing_style::inout) { error( "an 'inout' parameter can't be const, if you do want it to be const then use 'in' instead", false ); - break;case passing_style::out: + } + else if (n->pass == passing_style::out) { error( "an 'out' parameter can't be const, otherwise it can't be initialized in the function body", false ); - break;case passing_style::move: + } + else if (n->pass == passing_style::move) { error( "a 'move' parameter can't be const, otherwise it can't be moved from in the function body", false ); - break;case passing_style::forward: + } + else if (n->pass == passing_style::forward) { error( "a 'forward' parameter shouldn't be const, because it passes along the argument's actual const-ness (and actual value category)", false ); - break;default: + } + else { assert (false && "ICE: missing case"); } return {}; @@ -8435,6 +8637,7 @@ class parser } } + current_names.push_back(&*n->declaration); return n; } @@ -8746,7 +8949,7 @@ class parser } else { auto msg = std::string("'"); - msg += to_string_view(pass); + msg += pass.to_string(); error(msg + "' must be followed by a type-id"); return {}; } @@ -8821,6 +9024,8 @@ class parser auto apply_type_metafunctions( declaration_node& decl ) -> bool; + auto apply_function_metafunctions( declaration_node& decl ) + -> bool; //G unnamed-declaration: @@ -8995,6 +9200,9 @@ class parser n->template_parameters = std::move(template_parameters); } + current_names.push_back(&*n); + auto guard_function = stack_size_if(current_names, /*n->is_function()*/ true); + // Next is an an optional type auto deduced_type = false; @@ -9027,18 +9235,16 @@ class parser } // Or a function type, declaring a function - and tell the function whether it's in a user-defined type - else if (auto t = function_type(n.get(), named)) + else if (auto t = function_type(n.get(), named); + t + || ( + guard_function.reset(), + false + ) + ) { n->type = std::move(t); assert (n->is_function()); - - if (!n->metafunctions.empty()) { - errors.emplace_back( - n->metafunctions.front()->position(), - "(temporary alpha limitation) metafunctions are currently not supported on functions, only on types" - ); - return {}; - } } // Or a namespace @@ -9334,6 +9540,16 @@ class parser return {}; } } + // If this is a function with metafunctions, apply those + else if (n->is_function()) { + if (!apply_function_metafunctions(*n)) { + error( + "error encountered while applying function metafunctions", + false, {}, true + ); + return {}; + } + } if ( n->is_function() @@ -9619,6 +9835,7 @@ class parser // Remember current position, because we need to look ahead auto start_pos = pos; + auto start_current_names_size = current_names.size(); auto n = std::unique_ptr{}; @@ -9773,6 +9990,7 @@ class parser ); if (!n) { pos = start_pos; // backtrack + current_names.resize(start_current_names_size); return {}; } } @@ -10013,7 +10231,7 @@ class parse_tree_printer : printing_visitor if (n.returns.index() == function_type_node::id) { auto& r = std::get(n.returns); if (r.pass != passing_style::invalid) { - o << pre(indent+1) << "returns by: " << to_string_view(r.pass) << "\n"; + o << pre(indent+1) << "returns by: " << r.pass.to_string() << "\n"; } } } @@ -10085,7 +10303,7 @@ class parse_tree_printer : printing_visitor { o << pre(indent) << "parameter-declaration\n"; - o << pre(indent+1) << to_string_view(n.pass); + o << pre(indent+1) << n.pass.to_string(); o << pre(indent+1); switch (n.mod) { diff --git a/source/reflect.h2 b/source/reflect.h2 index 734fa728a9..30bc0eaa98 100644 --- a/source/reflect.h2 +++ b/source/reflect.h2 @@ -15,15 +15,64 @@ // Reflection and meta //=========================================================================== -#include "parse.h" -#include "cpp2regex.h" -using namespace cpp2::regex; cpp2: namespace = { meta: namespace = { +lineno_t: type == std::int32_t; +colno_t: type == std::int32_t; // not int16_t... encountered >80,000 char line during testing +index_t: type == std::int32_t; + +source_position: @dll_visible @value type = +{ + public lineno: lineno_t = 1; // one-based offset into program source + public colno: colno_t = 1; // one-based offset into line + + operator=: (implicit out this) = { } + operator=: (implicit out this, l: lineno_t, c: colno_t = 1) = + { + lineno = l; + colno = c; + } + + to_string: (this) -> std::string = "((lineno)$,(colno)$)"; +} + + +passing_style: @dll_visible @enum type = +{ + in; + in_ref; + copy; + inout; + out; + move; + forward; + forward_ref; + invalid; +} + +// Not `passing_style::from_string`, +// as it will not return `invalid` (does this call for a `from_string_or`?), +// and to workaround #555. +to_passing_style: (s: std::string_view) -> passing_style = +{ + return inspect s -> passing_style { + is (passing_style::in.to_string()) = passing_style::in; + is (passing_style::in_ref.to_string()) = passing_style::in_ref; + is (passing_style::copy.to_string()) = passing_style::copy; + is (passing_style::inout.to_string()) = passing_style::inout; + is (passing_style::out.to_string()) = passing_style::out; + is (passing_style::move.to_string()) = passing_style::move; + is (passing_style::forward.to_string()) = passing_style::forward; + is (passing_style::forward_ref.to_string()) = passing_style::forward_ref; + is _ = passing_style::invalid; + }; +} + + //----------------------------------------------------------------------- // // Compiler services @@ -31,65 +80,58 @@ meta: namespace = { //----------------------------------------------------------------------- // -compiler_services: @polymorphic_base @copyable type = +compiler_services: @dll_visible @polymorphic_base @copyable type = { // Common data members // - errors : *std::vector; - includes : *std::set; - errors_original_size : int; - generated_tokens : *stable_vector; - parser : cpp2::parser; - metafunction_name : std::string = (); - metafunction_args : std::vector = (); - metafunctions_used : bool = false; + data_: std::any; + private data: (this) -> forward _ = std::any_cast>(data_); + private data: (inout this) -> forward _ = std::any_cast>(data_); // Constructor // operator=: ( out this, - errors_ : *std::vector, - includes_ : *std::set, - generated_tokens_: *stable_vector + data_v: std::any ) = { - errors = errors_; - includes = includes_; - errors_original_size = cpp2::unchecked_narrow(std::ssize(errors*)); - generated_tokens = generated_tokens_; - parser = (errors*, includes*); + data_ = data_v; + assert( + data_.type() == typeid(compiler_services_data), + "parameter 'data_v' must store a 'compiler_services_data'" + ); } // Common API // set_metafunction_name: (inout this, name: std::string_view, args: std::vector) = { - metafunction_name = name; - metafunction_args = args; - metafunctions_used = args.empty(); + data().metafunction_name = name; + data().metafunction_args = args; + data().metafunctions_used = args.empty(); } - get_metafunction_name: (this) -> std::string_view = metafunction_name; + get_metafunction_name: (this) -> std::string_view = data().metafunction_name; get_argument: (inout this, index: int) -> std::string = { - metafunctions_used = true; - if (0 <= index < metafunction_args.ssize()) { - return metafunction_args[index]; + data().metafunctions_used = true; + if (0 <= index < data().metafunction_args.ssize()) { + return data().metafunction_args[index]; } return ""; } get_arguments: (inout this) -> std::vector = { - metafunctions_used = true; - return metafunction_args; + data().metafunctions_used = true; + return data().metafunction_args; } - arguments_were_used: (this) -> bool = metafunctions_used; + arguments_were_used: (this) -> bool = data().metafunctions_used; protected parse_statement: ( inout this, copy source: std::string_view ) - -> (ret: std::unique_ptr) + -> _ = { original_source := source; @@ -121,7 +163,7 @@ compiler_services: @polymorphic_base @copyable type = // Now lex this source fragment to generate // a single grammar_map entry, whose .second // is the vector of tokens - _ = generated_lexers.emplace_back( errors* ); + _ = generated_lexers.emplace_back( data().errors* ); tokens := generated_lexers.back()&; tokens*.lex( lines*, true ); @@ -129,16 +171,17 @@ compiler_services: @polymorphic_base @copyable type = // Now parse this single declaration from // the lexed tokens - ret = parser.parse_one_declaration( - tokens*.get_map().begin()*.second, - generated_tokens* - ); + ret := data().parser.parse_one_declaration( + tokens*.get_map().begin()*.second, + data().generated_tokens* + ); if !ret { error( "parse failed - the source string is not a valid statement:\n(original_source)$"); } + return ret; } - add_runtime_support_include: (inout this, s: std::string_view) = _=includes*.emplace( s ); + add_runtime_support_include: (inout this, s: std::string_view) = { _=data().includes*.emplace( s ); } position: (virtual this) -> source_position @@ -163,10 +206,10 @@ compiler_services: @polymorphic_base @copyable type = error: (this, msg: std::string_view) = { message := msg as std::string; - if !metafunction_name.empty() { - message = "while applying @(metafunction_name)$ - (message)$"; + if !data().metafunction_name.empty() { + message = "while applying @(data().metafunction_name)$ - (message)$"; } - _ = errors*.emplace_back( position(), message); + _ = data().errors*.emplace_back( position(), message); } // Enable custom contracts on this object, integrated with compiler output @@ -175,12 +218,12 @@ compiler_services: @polymorphic_base @copyable type = report_violation: (this, msg) = { error(msg); throw( std::runtime_error( - " ==> programming bug found in metafunction @(metafunction_name)$ " + " ==> programming bug found in metafunction @(data().metafunction_name)$ " "- contract violation - see previous errors" ) ); } - is_active:(this) = true; + is_active:(this) -> bool = true; } @@ -194,7 +237,7 @@ compiler_services: @polymorphic_base @copyable type = // All type_ids are wrappers around a pointer to node // -type_id: @polymorphic_base @copyable type = +type_id: @dll_visible @polymorphic_base @copyable type = { this: compiler_services = (); @@ -230,21 +273,37 @@ type_id: @polymorphic_base @copyable type = // All declarations are wrappers around a pointer to node // -declaration_base: @polymorphic_base @copyable type = +declaration_base: @dll_visible @polymorphic_base @copyable type = { this: compiler_services = (); - protected n: *declaration_node; + node_pointer: @copyable type = + { + n: *void; + + operator=: ( + implicit out this, + n_: T + ) + = { + n = n_; + assert( n_, "a meta::declaration must point to a valid declaration_node, not null" ); + static_assert( std::is_same_v ); + } + + operator*: (this) -> forward _ = unchecked_cast<*declaration_node>(n)*; + } + + protected n: node_pointer; protected operator=: ( out this, - n_: *declaration_node, + n_: node_pointer, s : compiler_services ) = { compiler_services = s; n = n_; - assert( n, "a meta::declaration must point to a valid declaration_node, not null" ); } position: (override this) -> source_position = n*.position(); @@ -256,13 +315,13 @@ declaration_base: @polymorphic_base @copyable type = //----------------------------------------------------------------------- // All declarations // -declaration: @polymorphic_base @copyable type = +declaration: @dll_visible @polymorphic_base @copyable type = { this: declaration_base = (); operator=: ( out this, - n_: *declaration_node, + n_: declaration_base::node_pointer, s : compiler_services ) = { @@ -274,14 +333,17 @@ declaration: @polymorphic_base @copyable type = is_private : (this) -> bool = n*.is_private(); is_default_access: (this) -> bool = n*.is_default_access(); - default_to_public : (inout this) = _ = n*.make_public(); - default_to_protected: (inout this) = _ = n*.make_protected(); - default_to_private : (inout this) = _ = n*.make_private(); + default_to_public : (inout this) = { _ = n*.make_public(); } + default_to_protected: (inout this) = { _ = n*.make_protected(); } + default_to_private : (inout this) = { _ = n*.make_private(); } make_public : (inout this) -> bool = n*.make_public(); make_protected : (inout this) -> bool = n*.make_protected(); make_private : (inout this) -> bool = n*.make_private(); + is_dll_visible : (this) -> bool = n*.is_dll_visible(); + make_dll_visible : (inout this) -> bool = n*.make_dll_visible(); + has_name : (this) -> bool = n*.has_name(); has_name : (this, s: std::string_view) -> bool = n*.has_name(s); @@ -338,13 +400,13 @@ declaration: @polymorphic_base @copyable type = //----------------------------------------------------------------------- // Function declarations // -function_declaration: @copyable type = +function_declaration: @dll_visible @copyable type = { this: declaration = (); operator=: ( out this, - n_: *declaration_node, + n_: declaration_base::node_pointer, s : compiler_services ) = { @@ -402,7 +464,7 @@ function_declaration: @copyable type = is_binary_comparison_function: (this) -> bool = n*.is_binary_comparison_function(); - default_to_virtual : (inout this) = _ = n*.make_function_virtual(); + default_to_virtual : (inout this) = { _ = n*.make_function_virtual(); } make_virtual : (inout this) -> bool = n*.make_function_virtual(); @@ -429,13 +491,13 @@ function_declaration: @copyable type = //----------------------------------------------------------------------- // Object declarations // -object_declaration: @copyable type = +object_declaration: @dll_visible @copyable type = { this: declaration = (); operator=: ( out this, - n_: *declaration_node, + n_: declaration_base::node_pointer, s : compiler_services ) = { @@ -465,13 +527,13 @@ object_declaration: @copyable type = //----------------------------------------------------------------------- // Type declarations // -type_declaration: @copyable type = +type_declaration: @dll_visible @copyable type = { this: declaration = (); operator=: ( out this, - n_: *declaration_node, + n_: declaration_base::node_pointer, s : compiler_services ) = { @@ -593,23 +655,23 @@ type_declaration: @copyable type = std::string("unexpected error while attempting to add member:\n") + source ); } - remove_marked_members: (inout this) = n*.type_remove_marked_members(); - remove_all_members : (inout this) = n*.type_remove_all_members(); + remove_marked_members: (inout this) = { n*.type_remove_marked_members(); } + remove_all_members : (inout this) = { n*.type_remove_all_members(); } - disable_member_function_generation: (inout this) = n*.type_disable_member_function_generation(); + disable_member_function_generation: (inout this) = { n*.type_disable_member_function_generation(); } } //----------------------------------------------------------------------- // Alias declarations // -alias_declaration: @copyable type = +alias_declaration: @dll_visible @copyable type = { this: declaration = (); operator=: ( out this, - n_: *declaration_node, + n_: declaration_base::node_pointer, s : compiler_services ) = { @@ -649,7 +711,7 @@ add_virtual_destructor: (inout t: meta::type_declaration) = // // an abstract base class having only pure virtual functions // -interface: (inout t: meta::type_declaration) = +interface: @dll_visible (inout t: meta::type_declaration) = { has_dtor := false; @@ -695,7 +757,7 @@ interface: (inout t: meta::type_declaration) = // // Unlike an interface, it can have nonpublic and nonvirtual functions. // -polymorphic_base: (inout t: meta::type_declaration) = +polymorphic_base: @dll_visible (inout t: meta::type_declaration) = { has_dtor := false; @@ -769,7 +831,7 @@ ordered_impl: ( // // Note: the ordering that should be encouraged as default gets the nice name // -ordered: (inout t: meta::type_declaration) = +ordered: @dll_visible (inout t: meta::type_declaration) = { ordered_impl( t, "strong_ordering" ); } @@ -777,7 +839,7 @@ ordered: (inout t: meta::type_declaration) = //----------------------------------------------------------------------- // weakly_ordered - a weakly ordered type // -weakly_ordered: (inout t: meta::type_declaration) = +weakly_ordered: @dll_visible (inout t: meta::type_declaration) = { ordered_impl( t, "weak_ordering" ); } @@ -785,7 +847,7 @@ weakly_ordered: (inout t: meta::type_declaration) = //----------------------------------------------------------------------- // partially_ordered - a partially ordered type // -partially_ordered: (inout t: meta::type_declaration) = +partially_ordered: @dll_visible (inout t: meta::type_declaration) = { ordered_impl( t, "partial_ordering" ); } @@ -807,7 +869,7 @@ partially_ordered: (inout t: meta::type_declaration) = // // A type with (copy and move) x (construction and assignment) // -copyable: (inout t: meta::type_declaration) = +copyable: @dll_visible (inout t: meta::type_declaration) = { // If the user explicitly wrote any of the copy/move functions, // they must also have written the most general one - we can't @@ -840,7 +902,7 @@ copyable: (inout t: meta::type_declaration) = // // A memberwise hashable type // -hashable: (inout t: meta::type_declaration) = +hashable: @dll_visible (inout t: meta::type_declaration) = { t.require( !t.get_member_objects().empty(), "a hashable type must have at least one data member"); @@ -873,7 +935,7 @@ hashable: (inout t: meta::type_declaration) = // A regular type: copyable, plus has public default construction // and no protected or virtual functions // -basic_value: (inout t: meta::type_declaration) = +basic_value: @dll_visible (inout t: meta::type_declaration) = { t.copyable(); @@ -901,19 +963,19 @@ basic_value: (inout t: meta::type_declaration) = // // Note: the ordering that should be encouraged as default gets the nice name // -value: (inout t: meta::type_declaration) = +value: @dll_visible (inout t: meta::type_declaration) = { t.ordered(); t.basic_value(); } -weakly_ordered_value: (inout t: meta::type_declaration) = +weakly_ordered_value: @dll_visible (inout t: meta::type_declaration) = { t.weakly_ordered(); t.basic_value(); } -partially_ordered_value: (inout t: meta::type_declaration) = +partially_ordered_value: @dll_visible (inout t: meta::type_declaration) = { t.partially_ordered(); t.basic_value(); @@ -942,7 +1004,7 @@ partially_ordered_value: (inout t: meta::type_declaration) = // // a type without declared copy/move/destructor functions // -cpp1_rule_of_zero: (inout t: meta::type_declaration) = +cpp1_rule_of_zero: @dll_visible (inout t: meta::type_declaration) = { for t.get_member_functions() do (inout mf) { @@ -984,7 +1046,7 @@ cpp1_rule_of_zero: (inout t: meta::type_declaration) = // parameters instead of concrete forwarding parameters (mainly used // for cppfront internal use, so cppfront builds under GCC 10) // -struct: (inout t: meta::type_declaration) = +struct: @dll_visible (inout t: meta::type_declaration) = { ctor_params: std::string = (); ctor_inits : std::string = (); @@ -1323,7 +1385,7 @@ basic_enum: ( // // -- P0707R4, section 3 // -enum: (inout t: meta::type_declaration) = +enum: @dll_visible (inout t: meta::type_declaration) = { // Let basic_enum do its thing, with an incrementing value generator t.basic_enum( @@ -1350,7 +1412,7 @@ enum: (inout t: meta::type_declaration) = // // -- P0707R4, section 3 // -flag_enum: (inout t: meta::type_declaration) = +flag_enum: @dll_visible (inout t: meta::type_declaration) = { // Let basic_enum do its thing, with a power-of-two value generator t.basic_enum( @@ -1396,7 +1458,7 @@ flag_enum: (inout t: meta::type_declaration) = // a type that contains exactly one of a fixed set of values at a time // -union: (inout t : meta::type_declaration) +union: @dll_visible (inout t : meta::type_declaration) = { alternatives : std::vector = (); @@ -1547,7 +1609,7 @@ union: (inout t : meta::type_declaration) // // print - output a pretty-printed visualization of t // -print: (t: meta::type_declaration) = +print: @dll_visible (t: meta::type_declaration) = { std::cout << t.print() << "\n"; } @@ -1555,2236 +1617,17 @@ print: (t: meta::type_declaration) = //----------------------------------------------------------------------- // -// regex - creates regular expressions from members -// -// Each member that starts with `regex` is replaced by a regular expression -// of the initializer string. E.g.: -// ``` -// regex := "ab"; -// ``` -// is replaced with -// ``` -// regex := ::cpp2::regex::regular_expression<...>; -// ``` -// -error_func: type == std::function< (x: std::string) -> void >; - -// Possible modifiers for a regular expression. -// -expression_flags: @flag_enum type = -{ - case_insensitive; // mod: i - multiple_lines; // mod: m - single_line; // mod: s - no_group_captures; // mod: n - perl_code_syntax; // mod: x - perl_code_syntax_in_classes; // mod: xx -} - - -// Tokens for regular expressions. -// - -// Basic class for a regex token. -// -regex_token: @polymorphic_base type = -{ - public string_rep: std::string; - - operator=:(out this, str: std::string) = { - string_rep = str; - } - - operator=:(out this) = { - string_rep = ""; - } - - //parse: (inout ctx: parse_context) -> token_ptr; - generate_code: (virtual this, inout _: generation_context); // Generate the matching code. - - add_groups: (virtual this, inout _: std::set) = {} // Adds all group indices to the set. - to_string: (this) -> std::string = { return string_rep; } // Create a string representation. - set_string: (inout this, s: std::string) = { string_rep = s; } // Set the string representation. -} - -token_ptr : type == std::shared_ptr; -token_vec: type == std::vector; - - -// Adds a check in code generation. -// -regex_token_check: @polymorphic_base type = -{ - this: regex_token; - - check: std::string; - - operator=:(out this, str: std::string, check_: std::string) = { - regex_token = (str); - check = check_; - } - - generate_code: (override this, inout ctx: generation_context) = { - ctx..add_check(check + "(" + ctx..match_parameters() + ")"); - } -} - - -// Adds code in code generation. -// -regex_token_code: @polymorphic_base type = -{ - this: regex_token; - - code: std::string; - - operator=:(out this, str: std::string, code_: std::string) = { - regex_token = (str); - code = code_; - } - - generate_code: (override this, inout ctx: generation_context) = { - ctx..add(code); - } -} - - -// Token that does not influence the matching. E.g. comment. -// -regex_token_empty: @polymorphic_base type = -{ - this: regex_token; - - operator=:(out this, str: std::string) = { - regex_token = (str); - } - - generate_code: (override this, inout _: generation_context) = { - // Nothing. - } -} - - -// Represents a list of regex tokens as one token. -// -regex_token_list: @polymorphic_base type = -{ - this: regex_token; - - public tokens: token_vec; - - operator=:(out this, t: token_vec) = { - regex_token = (gen_string(t)); - tokens = t; - } - - generate_code: (override this, inout ctx: generation_context) = { - for tokens do (token) { - token*..generate_code(ctx); - } - } - - add_groups: (override this, inout groups: std::set) = { - for tokens do (token) { - token*..add_groups(groups); - } - } - - gen_string: (vec: token_vec) -> std::string = { - r : std::string = ""; - for vec do (token) { - r += token*..to_string(); - } - return r; - } -} - - -// -// Parse and generation context. -// - -// State of the current capturing group. See '()' -// -parse_context_group_state: @struct type = -{ - cur_match_list: token_vec = (); // Current list of matchers. - alternate_match_lists: token_vec = (); // List of alternate matcher lists. E.g. ab|cd|xy. - modifiers : expression_flags = (); // Current modifiers for the group/regular expression. - - // Start a new alternative. - next_alternative: (inout this) = { - new_list: token_vec = (); - std::swap(new_list, cur_match_list); - post_process_list(new_list); - _ = alternate_match_lists..insert(alternate_match_lists..end(), shared.new(new_list)); - } - - // Swap this state with the other one. NOLINTNEXTLINE(performance-noexcept-swap) - swap: (inout this, inout t: parse_context_group_state) = { // NOLINT(performance-noexcept-swap) - std::swap(cur_match_list, t.cur_match_list); - std::swap(alternate_match_lists, t.alternate_match_lists); - std::swap(modifiers, t.modifiers); - } - - // Convert this state into a regex token. - get_as_token: (inout this) -> token_ptr = { - if alternate_match_lists..empty() { - post_process_list(cur_match_list); - return shared.new(cur_match_list); - } - else { - next_alternative(); - return shared.new(alternate_match_lists); - } - } - - // Add a token to the current matcher list. - add: (inout this, token: token_ptr) = { - cur_match_list..push_back(token); - } - - // True if current matcher list is empty. - empty: (this) -> bool = cur_match_list..empty(); - - - // Apply optimizations to the matcher list. - post_process_list: (inout list: token_vec) = { - // Merge all characters - merge_pos := list..begin(); - while merge_pos != list..end() next (merge_pos++) { - if merge_pos** is char_token { - combine_pos := merge_pos + 1; - while combine_pos != list..end() && combine_pos** is char_token { // The erase advances combine_pos - (merge_pos** as char_token)..append(combine_pos** as char_token); - combine_pos = list..erase(combine_pos); - } - } - } - } -} - - -// State for the branch reset. Takes care of the group numbering. See '(|)'. +// dll_visible - makes t visible in a DLL // -parse_context_branch_reset_state: @struct type = +dll_visible: @dll_visible (inout t: meta::type_declaration) = { - is_active : bool = false; // If we have a branch reset group. - cur_group : int = 1; // Next group identifier. 0 == global capture group. - max_group : int = 1; // Maximum group identifier generated. - from : int = 1; // Starting identifier on new alternative branch. - - // Next group identifier. - next: (inout this) -> int = { - g := cur_group; - cur_group += 1; - max_group = max(max_group, cur_group); - - return g; - } - - // Set next group identifier. - set_next: (inout this, g: int) = { - cur_group = g; - max_group = max(max_group, g); - } - - // Start a new alternative branch. - next_alternative: (inout this) = { - if is_active { - cur_group = from; - } - } - - // Initialize for a branch reset group. - set_active_reset: (inout this, restart: int) = { - is_active = true; - cur_group = restart; - from = restart; - max_group = restart; - } + t.require( t.make_dll_visible(), + "dll_visible can only be applied to a namespace-scope name"); } - - -// Context during parsing of the regular expressions. -// -// Keeps track of the distributed group identifiers, current parsed group and branch resets. -// -parse_context: type = +dll_visible: @dll_visible (inout t: meta::function_declaration) = { - regex: std::string_view; // Regular expression string. - pos: size_t = 0; // Current parsing position. - root: token_ptr; // Token representing the regular expression. - - cur_group_state: parse_context_group_state = (); - cur_branch_reset_state: parse_context_branch_reset_state = (); - - - public named_groups: std::map = (); - - error_out: error_func; // TODO: Declaring std::function fails for cpp2. - has_error: bool = false; - - operator=:(out this, r: std::string_view, e) = { - regex = r; - root = shared.new(""); - error_out = e; - } - - // State management functions - // - - // Returned group state needs to be stored and provided in `end_group`. - start_group: (inout this) -> parse_context_group_state = - { - old_state: parse_context_group_state = (); - old_state..swap(cur_group_state); - cur_group_state.modifiers = old_state.modifiers; - - return old_state; - } - - // `old_state` argument needs to be from start group. - end_group: (inout this, old_state: parse_context_group_state) -> token_ptr = - { - inner := cur_group_state..get_as_token(); - cur_group_state = old_state; - return inner; - } - - get_modifiers: (this) -> expression_flags = { - return cur_group_state.modifiers; - } - - set_modifiers: (inout this, mod: expression_flags) = { - cur_group_state.modifiers = mod; - } - - // Branch reset management functions - // - - branch_reset_new_state: (inout this) -> parse_context_branch_reset_state = - { - old_state: parse_context_branch_reset_state = (); - std::swap(old_state, cur_branch_reset_state); - - cur_branch_reset_state..set_active_reset(old_state.cur_group); - return old_state; - } - - branch_reset_restore_state: (inout this, old_state: parse_context_branch_reset_state) = - { - max_group := cur_branch_reset_state.max_group; - cur_branch_reset_state = old_state; - cur_branch_reset_state..set_next(max_group); - } - - next_alternative: (inout this) = - { - cur_group_state..next_alternative(); - cur_branch_reset_state..next_alternative(); - } - - // Regex token management - // - add_token: (inout this, token: token_ptr) = { - cur_group_state..add(token); - } - - has_token: (this) -> bool = { - return !cur_group_state..empty(); - } - - pop_token: (inout this) -> token_ptr = - { - r : token_ptr = nullptr; - if has_token() { - r = cur_group_state.cur_match_list..back(); - cur_group_state.cur_match_list..pop_back(); - } - - return r; - } - - get_as_token: (inout this) -> token_ptr = { - return root; - } - - // Group management - // - get_cur_group: (this) -> int = { - return cur_branch_reset_state.cur_group; - } - - next_group: (inout this) -> int = { - return cur_branch_reset_state..next(); - } - - set_named_group: (inout this, name: std::string, id: int) = - { - if !named_groups..contains(name) { // Redefinition of group name is not an error. The left most one is retained. - named_groups[name] = id; - } - } - - get_named_group: (this, name: std::string) -> int = - { - iter := named_groups..find(name); - if iter == named_groups..end() { - return -1; - } - else { - return iter*.second; - } - } - - // Position management functions - // - current: (this) -> char = { return regex[pos]; } - - // Get the next token in the regex, skipping spaces according to the parameters. See `x` and `xx` modifiers. - private get_next_position: (in this, in_class: bool, no_skip: bool) -> size_t = - { - perl_syntax := false; - if !no_skip { - if in_class { - perl_syntax = get_modifiers()..has(expression_flags::perl_code_syntax) && get_modifiers()..has(expression_flags::perl_code_syntax_in_classes); - } - else { - perl_syntax = get_modifiers()..has(expression_flags::perl_code_syntax); - } - } - cur := pos + 1; - if perl_syntax { - while cur < regex..size() next (cur += 1) { - n: = regex[cur]; - - if space_class::includes(n) { - continue; - } - else if !in_class && '#' == n { - cur = regex..find('\n', cur); - if std::string::npos == cur { - // No new line, comment runs until the end of the pattern - cur = regex..size(); - } - } - else { // None space none comment char - break; - } - } - } - - // Check for end of file. - if cur > regex..size() { - cur = regex..size(); - } - return cur; - } - - // Return true if next token is available. - private next_impl: (inout this, in_class: bool, no_skip: bool) -> bool = - { - pos = get_next_position(in_class, no_skip); - if pos != regex..size() { - return true; - } - else { - return false; - } - } - - next : (inout this) = next_impl(false, false); - next_in_class: (inout this) = next_impl( true, false); - next_no_skip : (inout this) = next_impl(false, true); - - next_n: (inout this, n: int) -> bool = { - r := true; - cur := 0; - while r && cur < n next (r = next()) { - cur += 1; - } - return r; - } - - has_next: (this) -> bool = { return pos < regex..size(); } - - private grab_until_impl: (inout this, in e: std::string, out r: std::string, any: bool) -> bool = - { - end:= pos; // NOLINT(clang-analyzer-deadcode.DeadStores) - if any { - end = regex..find_first_of(e, pos); - } - else { - end = regex..find(e, pos); - } - - if end != std::string_view::npos { - r = regex..substr(pos, end - pos); - pos = end; - return true; - } - else { - r = ""; - return false; - } - } - - grab_until: (inout this, in e: std::string, out r: std::string) = grab_until_impl(e, out r, false); - grab_until: (inout this, in e: char, out r: std::string) = grab_until_impl(std::string(1, e), out r, false); - grab_until_one_of: (inout this, in e: std::string, out r: std::string) = grab_until_impl(e, out r, true); - - grab_n: (inout this, in n: int, out r: std::string) -> bool = - { - if pos + n as size_t <= regex..size() { - r = regex..substr(pos, n as size_t); - pos += (n as size_t) - 1; - return true; - } - else { - r = ""; - return false; - } - } - - grab_number: (inout this) -> std::string = - { - start := pos; - start_search := pos; - if regex[start_search] == '-' { - start_search += 1; - } - end := regex..find_first_not_of("1234567890", start_search); - - r : std::string; - if end != std::string::npos { - r = regex..substr(start, end - start); - pos = end - 1; - } - else { - r = regex..substr(start); - pos = regex..size() - 1; - } - return r; - } - - private peek_impl: (in this, in_class: bool) -> char = { - next_pos := get_next_position(in_class, false); - if next_pos < regex..size() { - return regex[next_pos]; - } - else { - return '\0'; - } - } - - peek : (in this) = peek_impl(false); - peek_in_class: (in this) = peek_impl( true); - - - // Parsing functions - // - parser_group_modifiers: (inout this, change_str: std::string, inout parser_modifiers: expression_flags) -> bool = - { - is_negative := false; - is_reset := false; - - apply := :(flag: expression_flags) = { - if is_negative&$* { - parser_modifiers&$*..clear(flag); - } - else { - parser_modifiers&$*..set(flag); - } - }; - - iter := change_str..begin(); - while iter != change_str..end() next (iter++) - { - cur := iter*; - if cur == '^' { - is_reset = true; - parser_modifiers = expression_flags::none; - } - else if cur == '-' { - if is_reset { _= error("No negative modifier allowed."); return false; } - is_negative = true; - } - else if cur == 'i' { apply(expression_flags::case_insensitive); } - else if cur == 'm' { apply(expression_flags::multiple_lines); } - else if cur == 's' { apply(expression_flags::single_line); } - else if cur == 'n' { apply(expression_flags::no_group_captures); } - else if cur == 'x' { - if (iter + 1) == change_str..end() || (iter + 1)* != 'x' { - // x modifier - apply(expression_flags::perl_code_syntax); - - // Just x unsets xx and remove x also removes xx - parser_modifiers..clear(expression_flags::perl_code_syntax_in_classes); - } - else { // xx modifier - // xx also sets or unsets x - apply(expression_flags::perl_code_syntax); - apply(expression_flags::perl_code_syntax_in_classes); - - iter++; // Skip the second x - } - } - else { - _= error("Unknown modifier: (cur)$"); return false; - } - } - - return true; - } - - parse_until:(inout this, term: char) -> bool = { - cur_token: token_ptr = (); - - while valid() next _ = next() - { - if term == current() { break; } - - cur_token = nullptr; - - if !cur_token && valid() { cur_token = alternative_token::parse(this); } - if !cur_token && valid() { cur_token = any_token::parse(this); } - if !cur_token && valid() { cur_token = class_token::parse(this); } - if !cur_token && valid() { cur_token = escape_token_parse(this); } - if !cur_token && valid() { cur_token = global_group_reset_token_parse(this); } - if !cur_token && valid() { cur_token = group_ref_token::parse(this); } - if !cur_token && valid() { cur_token = group_token::parse(this); } - if !cur_token && valid() { cur_token = hexadecimal_token_parse(this); } - if !cur_token && valid() { cur_token = line_end_token_parse(this); } - if !cur_token && valid() { cur_token = line_start_token_parse(this); } - if !cur_token && valid() { cur_token = named_class_token_parse(this); } - if !cur_token && valid() { cur_token = octal_token_parse(this); } - if !cur_token && valid() { cur_token = range_token::parse(this); } - if !cur_token && valid() { cur_token = special_range_token::parse(this); } - if !cur_token && valid() { cur_token = word_boundary_token_parse(this); } - - // Everything else is matched as it is. - if !cur_token && valid() { cur_token = char_token::parse(this); } - - if cur_token && valid() { - add_token(cur_token); - } else { - return false; - } - } - - return true; - } - - parse: (inout this, modifiers: std::string) -> bool = - { - - flags : expression_flags = (); - if !parser_group_modifiers(modifiers, flags) { return false; } - set_modifiers(flags); - - r := parse_until('\0'); - if r { - root = cur_group_state..get_as_token(); - } - - return r; - } - - // Misc functions - - get_pos: (this) = pos; - get_range: (this, start: size_t, end: size_t) = std::string(regex..substr(start, end - start + 1)); - valid: (this) -> bool = { return has_next() && !has_error; } - - error: (inout this, err: std::string) -> token_ptr = { - has_error = true; - error_out("Error during parsing of regex '(regex)$' at position '(pos)$': (err)$"); - return nullptr; - } -} - - -// Context for one function generation. Generation of functions can be interleaved, -// therefore we buffer the code for one function here. -// -generation_function_context: @struct type = { - code: std::string = ""; - tabs: std::string = ""; - - add_tabs: (inout this, c: int) = { - i: int = 0; - while i < c next i += 1 { - tabs += " "; - } - } - - remove_tabs: (inout this, c: int) = { - tabs = tabs..substr(0, (c as size_t) * 2); - } -} - - -// Context for generating the state machine. -generation_context: type = -{ - gen_stack: std::vector = (1); // Element 0 contains all the code. - - matcher_func: int = 0; - reset_func: int = 0; - temp_name: int = 0; - entry_func: std::string = ""; - - // Generation helpers - // - match_parameters: (this) -> std::string = { return "r.pos, ctx"; } - - // Code generation. - - // Add code line. - add: (inout this, s: std::string) = { - cur := get_current(); - cur*.code += "(cur*.tabs)$(s)$\n"; - } - - // Add check for token. The check needs to be a function call that returns a boolean. - add_check: (inout this, check: std::string) = { - cur := get_current(); - cur*.code += "(cur*.tabs)$if !cpp2::regex::(check)$ { r.matched = false; break; }\n"; - } - - // Add a stateful check. The check needs to return a `match_return`. - add_statefull: (inout this, next_func: std::string, check: std::string) = - { - end_func_statefull(check); - - name := next_func..substr(0, next_func..size() - 2); - start_func_named(name); - } - - protected start_func_named: (inout this, name: std::string) = - { - cur := new_context(); - - cur*.code += "(cur*.tabs)$(name)$: @struct type = {\n"; - cur*.code += "(cur*.tabs)$ operator(): (this, cur: Iter, inout ctx: context, other) -> cpp2::regex::match_return = {\n"; - cur*.code += "(cur*.tabs)$ r := ctx..pass(cur);\n"; - cur*.code += "(cur*.tabs)$ do {\n"; - cur*..add_tabs(3); - } - - protected start_func: (inout this) -> std::string = - { - name := gen_func_name(); - start_func_named(name); - return name + "()"; - } - - protected end_func_statefull: (inout this, s: std::string) = - { - cur := get_current(); - cur*..remove_tabs(3); - cur*.code += "\n"; - cur*.code += "(cur*.tabs)$ } while false;\n"; - cur*.code += "(cur*.tabs)$ if r.matched {\n"; - cur*.code += "(cur*.tabs)$ r = (s)$;\n"; - cur*.code += "(cur*.tabs)$ }\n"; - cur*.code += "(cur*.tabs)$ else {\n"; - cur*.code += "(cur*.tabs)$ r.pos = ctx.end;\n"; - cur*.code += "(cur*.tabs)$ }\n"; - cur*.code += "(cur*.tabs)$ return r;\n"; - cur*.code += "(cur*.tabs)$ }\n"; - cur*.code += "(cur*.tabs)$}\n"; - - finish_context(); - } - - // Generate the function for a token. - generate_func: (inout this, token: token_ptr) -> std::string = - { - name := start_func(); - token*..generate_code(this); - end_func_statefull("other((match_parameters())$)"); - - return name; - } - - // Generate the reset for a list of group identifiers. - generate_reset: (inout this, groups: std::set) -> std::string = - { - if groups..empty() { - return "cpp2::regex::no_reset()"; - } - - name := gen_reset_func_name(); - cur := new_context(); - - cur*.code += "(cur*.tabs)$(name)$: @struct type = {\n"; - cur*.code += "(cur*.tabs)$ operator(): (this, inout ctx) = {\n"; - for groups do (g) { - cur*.code += "(cur*.tabs)$ ctx..set_group_invalid((g)$);\n"; - } - cur*.code += "(cur*.tabs)$ }\n"; - cur*.code += "(cur*.tabs)$}\n"; - - finish_context(); - - return name + "()"; - } - - // Name generation - // - protected gen_func_name: (inout this) -> std::string = { - cur_id : = matcher_func; - matcher_func += 1; - return "func_(cur_id)$"; - } - - next_func_name: (inout this) -> std::string = { - return gen_func_name() + "()"; - } - - protected gen_reset_func_name: (inout this) -> std::string = { - cur_id : = reset_func; - reset_func += 1; - return "reset_(cur_id)$"; - } - - gen_temp: (inout this) -> std::string = { - cur_id := temp_name; - temp_name += 1; - return "tmp_(cur_id)$"; - } - - // Context management - // - new_context: (inout this) -> *generation_function_context = { - gen_stack..push_back(generation_function_context()); - cur := get_current(); - cur*.tabs = " "; - - return cur; - } - - finish_context: (inout this) = { - cur := get_current(); - base := get_base(); - base*.code += cur*.code; - - gen_stack..pop_back(); - } - - // Misc functions - // - private get_current: (inout this) -> *generation_function_context = { - return gen_stack..back()&; - } - - private get_base: (inout this) -> *generation_function_context = { - return gen_stack[0]&; - } - - get_entry_func: (this) -> std::string = { - return entry_func; - } - - create_named_group_lookup: (this, named_groups: std::map) -> std::string = - { - res: std::string = "get_named_group_index: (name) -> int = {\n"; - - // Generate if selection. - sep: std::string = ""; - for named_groups do (cur) { - res += "(sep)$if name == \"(cur.first)$\" { return (cur.second)$; }"; - sep = "else "; - } - - // Generate else branch or return if list is empty. - if named_groups..empty() { - res += " _ = name;\n"; - res += " return -1;\n"; - } - else { - res += " else { return -1; }\n"; - } - res += "}\n"; - return res; - } - - - // Run the generation for the token. - run: (inout this, token: token_ptr) -> std::string = { - entry_func = generate_func(token); - - return get_base()*.code; - } -} - -// Regex syntax: | Example: ab|ba -// -// Non greedy implementation. First alternative that matches is chosen. -// -alternative_token: @polymorphic_base type = -{ - this: regex_token_empty = (""); // No code gen here. alternative_token_gen is created in the parse_context - - operator=:(out this) = {} - - parse: (inout ctx: parse_context) -> token_ptr = { - if ctx..current() != '|' { return nullptr; } - - if !ctx..has_token() { return ctx..error("Alternative with no content."); } - ctx..next_alternative(); - return shared.new(); - } -} - -alternative_token_gen: @polymorphic_base type = -{ - this: regex_token; - - alternatives: token_vec; - - operator=: (out this, a: token_vec) = { - regex_token = gen_string(a); - alternatives = a; - } - - generate_code: (override this, inout ctx: generation_context) = - { - functions: std::string = ""; - - for alternatives do (cur) { - groups: std::set = (); - cur*..add_groups(groups); - - functions += ", " + ctx..generate_func(cur); - functions += ", " + ctx..generate_reset(groups); - } - - next_name := ctx..next_func_name(); - - ctx..add_statefull(next_name, "cpp2::regex::alternative_token_matcher::match((ctx..match_parameters())$, other, (next_name)$ (functions)$)"); - } - - add_groups: (override this, inout groups: std::set) = - { - for alternatives do (cur) { - cur*..add_groups(groups); - } - } - - gen_string: (a: token_vec) -> std::string = - { - r: std::string = ""; - sep: std::string = ""; - - for a do (cur) { - r += sep + cur*..to_string(); - sep = "|"; - } - - return r; - } -} - - -// Regex syntax: . -// -any_token: @polymorphic_base type = -{ - this: regex_token_check = ("."); - - operator=:(out this, single_line: bool) = { - regex_token_check = (".", "any_token_matcher"); - } - - parse: (inout ctx: parse_context) -> token_ptr = { - if '.' != ctx..current() { return nullptr;} - - return shared.new(ctx..get_modifiers()..has(expression_flags::single_line)); - } -} - - -// Regex syntax: a -// -char_token: @polymorphic_base type = -{ - this: regex_token; - - token : std::string; - ignore_case: bool; - - operator=: (out this, t: char, ignore_case_: bool) = { - regex_token = (std::string(1, t)); - token = t; - ignore_case = ignore_case_; - } - - parse: (inout ctx: parse_context) -> token_ptr = { - return shared.new(ctx..current(), ctx..get_modifiers()..has(expression_flags::case_insensitive)); - } - - generate_code: (override this, inout ctx: generation_context) = - { - if ignore_case { - upper: std::string = token; - lower: std::string = token; - - (copy i: size_t = 0) while i < token..size() next i += 1 { - lower[i] = string_util::safe_tolower(token[i]); - upper[i] = string_util::safe_toupper(token[i]); - } - - if upper != lower { - gen_case_insensitive(lower, upper, ctx); - } - else { - gen_case_sensitive(ctx); - } - } - else { - gen_case_sensitive(ctx); - } - } - - gen_case_insensitive: (this, lower: std::string, upper: std::string, inout ctx: generation_context) = - { - name: std::string = "str_(ctx..gen_temp())$"; - lower_name: std::string = "lower_(name)$"; - upper_name: std::string = "upper_(name)$"; - size := token..size(); - ctx..add("(lower_name)$ : std::array = \"(add_escapes(lower))$\";"); // TODO: Add constexpr when Issue https://github.com/hsutter/cppfront/issues/1104 is resolved. - ctx..add("(upper_name)$ : std::array = \"(add_escapes(upper))$\";"); // TODO: Add constexpr when Issue https://github.com/hsutter/cppfront/issues/1104 is resolved. - ctx..add("if std::distance(r.pos, ctx.end) < (size)$ {"); - ctx..add(" r.matched = false;"); - ctx..add(" break;"); - ctx..add("}"); - ctx..add(""); - ctx..add("(copy i : int = 0) while i < (size)$ next (i += 1) {"); - ctx..add(" if !((lower_name)$[i] == r.pos[i] || (upper_name)$[i] == r.pos[i]) { r.matched = false; }"); - ctx..add("}"); - ctx..add(""); - ctx..add("if r.matched { r.pos += (size)$; }"); - ctx..add("else { break; }"); - } - - gen_case_sensitive: (this, inout ctx: generation_context) = - { - name: std::string = "str_(ctx..gen_temp())$"; - size := token..size(); - ctx..add("(name)$ : std::array = \"(add_escapes(token))$\";"); // TODO: Add constexpr when Issue https://github.com/hsutter/cppfront/issues/1104 is resolved. - ctx..add("if std::distance(r.pos, ctx.end) < (size)$ {"); - ctx..add(" r.matched = false;"); - ctx..add(" break;"); - ctx..add("}"); - ctx..add(""); - ctx..add("(copy i : int = 0) while i < (size)$ next (i += 1) {"); - ctx..add(" if (name)$[i] != r.pos[i] { r.matched = false; }"); - ctx..add("}"); - ctx..add(""); - ctx..add("if r.matched { r.pos += (size)$; }"); - ctx..add("else { break; }"); - } - - add_escapes: (this, copy str: std::string) -> std::string = - { - str = string_util::replace_all(str, "\\", "\\\\"); - str = string_util::replace_all(str, "\a", "\\a"); - str = string_util::replace_all(str, "\f", "\\f"); - str = string_util::replace_all(str, "\x1b", "\" \"\\x1b\" \""); // Generate a separated string. This prevents - // situations like `\x1bblub` from generating - // wrong hex characters. - str = string_util::replace_all(str, "\n", "\\n"); - str = string_util::replace_all(str, "\r", "\\r"); - str = string_util::replace_all(str, "\t", "\\t"); - - return str; - } - - append: (inout this, that) = { - this.token += that.token; - this.string_rep += that.string_rep; - } -} - - -// Regex syntax: [] Example: [abcx-y[:digits:]] -// -class_token: @polymorphic_base type = -{ - this : regex_token = (); - - negate : bool; - case_insensitive: bool; - class_str : std::string; - - operator=: (out this, negate_: bool, case_insensitive_: bool, class_str_: std::string, str: std::string) = - { - regex_token = str; - negate = negate_; - case_insensitive = case_insensitive_; - class_str = class_str_; - } - - // TODO: Rework class generation: Generate check functions for classes. - parse: (inout ctx: parse_context) -> token_ptr = - { - if ctx..current() != '[' { return nullptr; } - - start_pos := ctx..get_pos(); - - supported_classes: std::vector = ("alnum", "alpha", "ascii", "blank", "cntrl", "digits", "graph", - "lower", "print", "punct", "space", "upper", "word", "xdigit"); - - classes: std::vector = (); - - // First step: parse until the end bracket and push single chars, ranges or groups on the class stack. - is_negate := false; - first := true; - range := false; - while ctx..next_in_class() && (ctx..current() != ']' || first) - { - if ctx..current() == '^' - { - is_negate = true; - continue; // Skip rest of the loop. Also the first update. - } - - if ctx..current() == '[' && ctx..peek_in_class() == ':' - { - // We have a character class. - _ = ctx..next_n(2); // Skip [: - - name: std::string = ""; - if !ctx..grab_until(":]", out name) { return ctx..error("Could not find end of character class."); } - if supported_classes..end() == std::find(supported_classes..begin(), supported_classes..end(), name) { - return ctx..error("Unsupported character class. Supported ones are: (string_util::join(supported_classes))$"); - } - - classes..push_back("[:(name)$:]"); - - _ = ctx..next(); // Skip ':' pointing to the ending ']'. - } - else if ctx..current() == '\\' - { - if ctx..next_no_skip() && (ctx..current() != ']') - { - if ' ' == ctx..current() - && ctx..get_modifiers()..has(expression_flags::perl_code_syntax) - && ctx..get_modifiers()..has(expression_flags::perl_code_syntax_in_classes) - { - classes..push_back(std::string(1, ctx..current())); - } - else { - name := ""; - if 'd' == ctx..current() { name = "short_digits"; } - else if 'D' == ctx..current() { name = "short_not_digits"; } - else if 'h' == ctx..current() { name = "short_hor_space"; } - else if 'H' == ctx..current() { name = "short_not_hor_space"; } - else if 's' == ctx..current() { name = "short_space"; } - else if 'S' == ctx..current() { name = "short_not_space"; } - else if 'v' == ctx..current() { name = "short_ver_space"; } - else if 'V' == ctx..current() { name = "short_not_ver_space"; } - else if 'w' == ctx..current() { name = "short_word"; } - else if 'W' == ctx..current() { name = "short_not_word"; } - else { - return ctx..error("Unknown group escape."); - } - classes..push_back("[:(name)$:]"); - } - } else { - return ctx..error("Escape without a following character."); - } - } - else if ctx..current() == '-' - { - if first { // Literal if first entry. - classes..push_back("(ctx..current())$"); - } else { - range = true; - } - } - else - { - if range { // Modify last element to be a range. - classes..back() += "-(ctx..current())$"; - range = false; - } - else { - classes..push_back("(ctx..current())$"); - } - } - - first = false; - } - - if ctx..current() != ']' { - return ctx..error("Error end of character class definition before terminating ']'."); - } - end_pos := ctx..get_pos(); - - if range { // If '-' is last entry treat it as a literal char. - classes..push_back("-"); - } - - // Second step: Wrap the item on the class stack with corresponding class implementation. - for classes do (inout cur) - { - if cur..starts_with("[:") { - name := cur..substr(2, cur..size() - 4); - cur = create_matcher("(name)$_class", ""); - } - else if 1 != cur..size() { - cur = create_matcher("range_class_entry", "'(cur[0])$', '(cur[2])$'"); - } - else { - cur = create_matcher("single_class_entry", "'(cur)$'"); - } - } - - inner := string_util::join(classes); - string_rep := ctx..get_range(start_pos, end_pos); - return shared.new( - is_negate, - ctx..get_modifiers()..has(expression_flags::case_insensitive), - inner, - string_rep - ); - } - - generate_code: (override this, inout ctx: generation_context) = - { - ctx..add_check("class_token_matcher::match((ctx..match_parameters())$)"); - } - - private create_matcher: (name: std::string, template_arguments: std::string) -> std::string = - { - sep := ", "; - if template_arguments..empty() { sep = ""; } - - return "::cpp2::regex::(name)$"; - } -} - - -// Regex syntax: \a or \n or \[ -// -escape_token_parse: (inout ctx: parse_context) -> token_ptr = -{ - if ctx..current() != '\\' { return nullptr; } - - - if std::string::npos == std::string("afenrt^.[]()*{}?+|\\")..find(ctx..peek()) { - return nullptr; - } - - _ = ctx..next(); // Skip escape - - if std::string::npos != std::string("afenrt\\")..find(ctx..current()) - { - // Escape of string special char - t : char = '\0'; - if 'a' == ctx..current() { t = '\a'; } - else if 'f' == ctx..current() { t = '\f'; } - else if 'e' == ctx..current() { t = '\x1b'; } - else if 'n' == ctx..current() { t = '\n'; } - else if 'r' == ctx..current() { t = '\r'; } - else if 't' == ctx..current() { t = '\t'; } - else if '\\' == ctx..current() { t = '\\'; } - else { return ctx..error("Internal: missing switch case for special escape."); } - - r: = shared.new(t, false); - r*..set_string("\\(ctx..current())$"); - return r; - } - else - { - // Escape of regex special char - r := shared.new(ctx..current(), false); - r*..set_string("\\(ctx..current())$"); - return r; - } - -} - - -// Regex syntax: \K Example: ab\Kcd -// -global_group_reset_token_parse: (inout ctx: parse_context) -> token_ptr = -{ - if !(ctx..current() == '\\' && ctx..peek() == 'K') { return nullptr; } - - _ = ctx..next(); // Skip escape. - return shared.new("\\K", "ctx..set_group_start(0, r.pos);"); -} - - -// Regex syntax: \ Example: \1 -// \g{name_or_number} -// \k{name_or_number} -// \k -// \k'name_or_number' -// -group_ref_token: @polymorphic_base type = -{ - this : regex_token = (); - - id : int; - case_insensitive: bool; - - operator=:(out this, id_: int, case_insensitive_: bool, str: std::string) = - { - regex_token = str; - id = id_; - case_insensitive = case_insensitive_; - } - - parse: (inout ctx: parse_context) -> token_ptr = - { - if ctx..current() != '\\' { return nullptr; } - - str : std::string = "\\"; - group : std::string = ""; - - if '0' <= ctx..peek() <= '9' - { - _ = ctx..next(); // Skip escape - group = ctx..grab_number(); - - if group..size() >= 3 as size_t - { - // Octal syntax (\000) not a group ref matcher. - number := 0; - if !string_util::string_to_int(group, number, 8) { return ctx..error("Could not convert octal to int."); } - - number_as_char : char = unchecked_narrow(number); - - token := shared.new(number_as_char, ctx..get_modifiers()..has(expression_flags::case_insensitive)); - token*..set_string("\\(string_util::int_to_string<8>(number_as_char as int))$"); - - return token; - } - - str += group; - // Regular group ref - } - else if 'g' == ctx..peek() - { - _ = ctx..next(); // Skip escape - if !ctx..next() { return ctx..error("Group escape without a following char."); } // Skip g - - str += "g"; - - if ctx..current() == '{' { - str += "{"; - if !(ctx..next() && ctx..grab_until('}', out group)) { return ctx..error("No ending bracket."); } - - str += group + "}"; - } - else { - group = ctx..grab_number(); - str += group; - } - } - else if 'k' == ctx..peek() - { - _ = ctx..next(); // Skip escape - if !ctx..next() { return ctx..error("Group escape without a following char."); } // Skip k - - str += "k"; - - term_char := '\0'; - if ctx..current() == '{' { term_char = '}'; } - else if ctx..current() == '<' { term_char = '>'; } - else if ctx..current() == '\'' { term_char = '\''; } - else { - return ctx..error("Group escape has wrong operator."); - } - - str += ctx..current(); - - if !(ctx..next() && ctx..grab_until(term_char, out group)) { return ctx..error("No ending bracket."); } - - str += group + term_char; - } - else - { - // No group ref matcher - return nullptr; - } - - // Parse the group - group = string_util::trim_copy(group); - group_id : int = 0; - if string_util::string_to_int(group, group_id) - { - if group_id < 0 { - group_id = ctx..get_cur_group() + group_id; - - if group_id < 1 { // Negative and zero are no valid groups. - return ctx..error("Relative group reference does not reference a valid group. (Would be (group_id)$.)"); - } - } - - if group_id >= ctx..get_cur_group() { - return ctx..error("Group reference is used before the group is declared."); - } - } - else - { - // Named group - group_id = ctx..get_named_group(group); - if -1 == group_id { return ctx..error("Group names does not exist. (Name is: (group)$)");} - } - - return shared.new(group_id, ctx..get_modifiers()..has(expression_flags::case_insensitive), str); - } - - generate_code: (override this, inout ctx: generation_context) = { - ctx..add_check("group_ref_token_matcher((ctx..match_parameters())$)"); - } -} - - -// Regex syntax: () Example: (abc) -// (?:) (?i:abc) -// (?<>:) (?:abc) -// (?#) (#Step 1 finished) -// (?|) (?|(abc)|(cde)) -// (?=) (?=abc) -// (?!) (?!abc) -// (*: token_ptr = - { - _ = ctx..next(); // Skip last token defining the syntax - - r := shared.new(positive); - - old_state := ctx..start_group(); - if !ctx..parse_until(')') { return ctx..error("Lookahead without a closing bracket."); } - r*.inner = ctx..end_group(old_state); - r*..set_string("((syntax)$(r*.inner*..to_string())$)"); - - return r; - } - - parse: (inout ctx: parse_context) -> token_ptr = - { - if ctx..current() != '(' { return nullptr; } - - has_id := !ctx..get_modifiers()..has(expression_flags::no_group_captures); - has_pattern := true; - group_name : std::string = ""; - group_name_brackets := true; - modifiers : std::string = ""; - modifiers_change_to : = ctx..get_modifiers(); - - // Skip the '(' - if !ctx..next() { return ctx..error("Group without closing bracket."); } - - if ctx..current() == '?' - { - // Special group - if !ctx..next_no_skip() { return ctx..error("Missing character after group opening."); } - - if ctx..current() == '<' || ctx..current() == '\'' - { - // Named group - end_char := ctx..current(); - if end_char == '<' { - end_char = '>'; - } else { - group_name_brackets = false; - } - has_id = true; // Force id for named groups. - if !ctx..next() /* skip '<' */ { return ctx..error("Missing ending bracket for named group."); } - if !ctx..grab_until(end_char, out group_name) { return ctx..error("Missing ending bracket for named group."); } - if !ctx..next() { return ctx..error("Group without closing bracket."); } - } - else if ctx..current() == '#' - { - // Comment - comment_str : std::string = ""; - _ = ctx..next(); // Skip # - if !ctx..grab_until(")", out comment_str) { return ctx..error("Group without closing bracket."); } - // Do not add comment. Has problems with ranges. - - // Pop token and add a list. This fixes comments between a token and a range - if ctx..has_token() { - list : token_vec = (); - list..push_back(ctx..pop_token()); - list..push_back(shared.new("(?#(comment_str)$)")); - - return shared.new(list); - } - else { - return shared.new("(?#(comment_str)$)"); - } - } - else if ctx..current() == '|' - { - // Branch reset group - - if !ctx..next() /* skip '|' */ { return ctx..error("Missing ending bracket for named group."); } - - old_parser_state := ctx..start_group(); - old_branch_state := ctx..branch_reset_new_state(); - if !ctx..parse_until(')') { return nullptr; } - ctx..branch_reset_restore_state(old_branch_state); - inner_ := ctx..end_group(old_parser_state); - - list: token_vec = (shared.new("(?|"), inner_, shared.new(")")); - return shared.new(list); - } - else if ctx..current() == '=' || ctx..current() == '!' - { - return parse_lookahead(ctx, "?(ctx..current())$", ctx..current() == '='); - } - else - { - // Simple modifier - has_id = false; - if !ctx..grab_until_one_of("):", out modifiers) { return ctx..error("Missing ending bracket for group."); } - if !ctx..parser_group_modifiers(modifiers, modifiers_change_to) { - return nullptr; - } - - if ')' == ctx..current() { - has_pattern = false; - } - else { - if !ctx..next() /* skip ':' */ { return ctx..error("Missing ending bracket for group."); } - } - } - } - else if ctx..current() == '*' - { - // Named pattern - _ = ctx..next(); // Skip *. - name: std::string = ""; - if !ctx..grab_until(':', out name) { return ctx..error("Missing colon for named pattern."); } - - if name == "pla" || name == "positive_lookahead" { - return parse_lookahead(ctx, "*(name)$:", true); - } - else if name == "nla" || name == "negative_lookahead" { - return parse_lookahead(ctx, "*(name)$:", false); - } - else { - return ctx..error("Unknown named group pattern: '(name)$'"); - } - } - - if has_pattern - { - // Regular group - - r := shared.new(); - if has_id { - r*.number = ctx..next_group(); - - if 0 != group_name..size() { - ctx..set_named_group(group_name, r*.number); - } - } - - old_state := ctx..start_group(); - ctx..set_modifiers(modifiers_change_to); - if !ctx..parse_until(')') { return nullptr; } - r*.inner = ctx..end_group(old_state); - r*..set_string(gen_string(group_name, group_name_brackets, !has_id, modifiers, r*.inner)); - - return r; - } - else - { - // Only a modifier - ctx..set_modifiers(modifiers_change_to); - - return shared.new("(?(modifiers)$)"); - } - } - - gen_string: (name: std::string, name_brackets: bool, has_modifier: bool, modifiers: std::string, inner_: token_ptr) -> std::string = - { - start : std::string = "("; - if 0 != name..size() { - if name_brackets { - start += "?<(name..data())$>"; - } - else { - start += "?'(name..data())$'"; - } - } - else if has_modifier { - start += "?" + modifiers + ":"; - } - - return start + inner_*..to_string() + ")"; - } - - generate_code: (override this, inout ctx: generation_context) = - { - if -1 != number { - ctx..add("ctx..set_group_start((number)$, r.pos);"); - } - - inner*..generate_code(ctx); - if -1 != number { - ctx..add("ctx..set_group_end((number)$, r.pos);"); - tmp_name := ctx..gen_temp(); - ctx..add("(tmp_name)$_func := :() = {"); - ctx..add(" if !r&$*.matched {"); - ctx..add(" ctx&$*..set_group_invalid((number)$);"); - ctx..add(" }"); - ctx..add("};"); - ctx..add("(tmp_name)$ := cpp2::regex::make_on_return((tmp_name)$_func);"); - ctx..add("_ = (tmp_name)$;"); // Logic is done in the destructor. Same behavior as for guard objects. - } - } - - add_groups: (override this, inout groups: std::set) = - { - inner*..add_groups(groups); - if -1 != number { - _ = groups..insert(number); - } - } -} - - -// Regex syntax: \x or \x{} Example: \x{62} -// -hexadecimal_token_parse: (inout ctx: parse_context) -> token_ptr = -{ - if !(ctx..current() == '\\' && ctx..peek() == 'x') { return nullptr; } - - _ = ctx..next(); // Skip escape. - - if !ctx..next() { return ctx..error("x escape without number.");} - - has_brackets := false; - number_str: std::string = ""; - if '{' == ctx..current() { - // Bracketed - has_brackets = true; - _ = ctx..next(); // Skip '{' - if !ctx..grab_until('}', out number_str) { return ctx..error("No ending bracket for \\x"); } - } - else { - // Grab two chars - if !ctx..grab_n(2, out number_str) { return ctx..error("Missing hexadecimal digits after \\x."); } - } - - number := 0; - if !string_util::string_to_int(number_str, number, 16) { return ctx..error("Could not convert hexadecimal to int."); } - - // TODO: Change for unicode. - number_as_char : char = unchecked_narrow(number); - - syntax: std::string = string_util::int_to_string<16>(number_as_char as int); - if has_brackets { - syntax = "{(syntax)$}"; - } - syntax = "\\x(syntax)$"; - - r := shared.new(number_as_char, ctx..get_modifiers()..has(expression_flags::case_insensitive)); - r*..set_string(syntax); - return r; -} - - -// Regex syntax: $ Example: aa$ -// -line_end_token_parse: (inout ctx: parse_context) -> token_ptr = -{ - if ctx..current() == '$' || (ctx..current() == '\\' && ctx..peek() == '$') { - if (ctx..current() == '\\') { _ = ctx..next(); } // Skip escape - return shared.new("$", "line_end_token_matcher"); - } - else if ctx..current() == '\\' && (ctx..peek() == 'z' || ctx..peek() == 'Z') { - _ = ctx..next(); // Skip escape - - negate := ctx..current() == 'Z'; - return shared.new("\\(ctx..current())$", "line_end_token_matcher"); - } - else { - return nullptr; - } -} - - -// Regex syntax: ^ Example: ^aa -// -line_start_token_parse: (inout ctx: parse_context) -> token_ptr = -{ - if ctx..current() != '^' && !(ctx..current() == '\\' && ctx..peek() == 'A') { return nullptr; } - - if ctx..current() == '\\' { - _ = ctx..next(); - return shared.new("\\A", "line_start_token_matcher"); - } - else { - return shared.new("^", "line_start_token_matcher"); - } -} - - -// Regex syntax: (?=) or (?!) or (*pla), etc. Example: (?=AA) -// -// Parsed in group_token. -// -lookahead_token: @polymorphic_base type = -{ - this: regex_token = (""); - - protected positive: bool; - public inner : token_ptr = nullptr; - - operator=: (out this, positive_: bool) = { - positive = positive_; - } - - generate_code: (override this, inout ctx: generation_context) = { - inner_name := ctx..generate_func(inner); - - ctx..add_check("lookahead_token_matcher((ctx..match_parameters())$, (inner_name)$)"); - } - - add_groups: (override this, inout groups: std::set) = { - inner*..add_groups(groups); - } -} - - -// Named character classes -// -named_class_token_parse: (inout ctx: parse_context) -> token_ptr = -{ - if ctx..current() != '\\' { return nullptr; } - - name := ""; - c_next := ctx..peek(); - - if 'd' == c_next { name = "named_class_digits"; } - else if 'D' == c_next { name = "named_class_not_digits"; } - else if 'h' == c_next { name = "named_class_hor_space"; } - else if 'H' == c_next { name = "named_class_not_hor_space"; } - else if 'N' == c_next { name = "named_class_no_new_line"; } - else if 's' == c_next { name = "named_class_space"; } - else if 'S' == c_next { name = "named_class_not_space"; } - else if 'v' == c_next { name = "named_class_ver_space"; } - else if 'V' == c_next { name = "named_class_not_ver_space"; } - else if 'w' == c_next { name = "named_class_word"; } - else if 'W' == c_next { name = "named_class_not_word"; } - else { return nullptr; } - - _ = ctx..next(); // Skip escape - - return shared.new("\\(ctx..current())$", "(name)$::match"); -} - - -// Regex syntax: \o{} Example: \o{142} -// -octal_token_parse: (inout ctx: parse_context) -> token_ptr = -{ - if !(ctx..current() == '\\' && ctx..peek() == 'o') { return nullptr; } - - _ = ctx..next(); // Skip escape. - - if !ctx..next() { return ctx..error("o escape without number.");} - if ctx..current() != '{' { return ctx..error("Missing opening bracket for \\o."); } - - number_str: std::string = ""; - _ = ctx..next(); // Skip '{' - if !ctx..grab_until('}', out number_str) { return ctx..error("No ending bracket for \\o"); } - - number := 0; - if !string_util::string_to_int(number_str, number, 8) { return ctx..error("Could not convert octal to int."); } - - // TODO: Change for unicode. - number_as_char : char = unchecked_narrow(number); - - syntax: std::string = "\\o{(string_util::int_to_string<8>(number_as_char as int))$}"; - r := shared.new(number_as_char, ctx..get_modifiers()..has(expression_flags::case_insensitive)); - r*..set_string(syntax); - return r; -} - - -// Regex syntax: {min, max} Example: a{2,4} -// -range_token: @polymorphic_base type = -{ - this : regex_token = (""); - - protected min_count : int = -1; - protected max_count : int = -1; - protected kind : int = range_flags::greedy; - protected inner_token: token_ptr = nullptr; - - operator=: (out this) = {} - - parse: (inout ctx: parse_context) -> token_ptr = - { - r := shared.new(); - if ctx..current() == '{' - { - if !ctx..has_token() { return ctx..error("'{' without previous element."); } - - inner: std::string = ""; - if !ctx..grab_until('}', out inner) { return ctx..error("Missing closing bracket '}'."); } - - inner = string_util::trim_copy(inner..substr(1)); // Remove '{' and white spaces. - if inner..empty() { return ctx..error("Empty range specifier. Either '{n}', '{n,}', '{,m}' '{n,m}'"); } - - // Non-greedy or possessive - r*..parse_modifier(ctx); - - // Get range arguments - min_count_str: std::string = "-1"; - max_count_str: std::string = "-1"; - - sep: size_t = inner..find(','); - if sep == std::string::npos - { - min_count_str = inner; - max_count_str = inner; - if !string_util::string_to_int(inner, r*.min_count) { return ctx..error("Could not convert range to number."); } - r*.max_count = r*.min_count; - } - else - { - inner_first: std::string = string_util::trim_copy(inner..substr(0, sep)); - inner_last: std::string = string_util::trim_copy(inner..substr(sep + 1)); - - if (inner_first..empty() && inner_last..empty()) { - return ctx..error("Empty range specifier. Either '{n}', '{n,}', '{,m}' '{n,m}'"); - } - - if !inner_first..empty() { - min_count_str = inner_first; - if !string_util::string_to_int(inner_first, r*.min_count) { return ctx..error("Could not convert range to number."); } - } - if !inner_last..empty() { - max_count_str = inner_last; - if !string_util::string_to_int(inner_last, r*.max_count) { return ctx..error("Could not convert range to number."); } - } - } - - // Check validity of the range. - if -1 != r*.min_count { - if !(0 <= r*.min_count) { - return ctx..error("Min value in range is negative. Have (r*.min_count)$)"); - } - } - if -1 != r*.max_count { - if !(0 <= r*.max_count) { - return ctx..error("Max value in range is negative. Have (r*.max_count)$)"); - } - if -1 != r*.min_count { - if !(r*.min_count <= r*.max_count) { - return ctx..error("Min and max values in range are wrong it should hold 0 <= min <= max. Have 0 <= (r*.min_count)$ <= (r*.max_count)$"); - } - } - } - - r*.inner_token = ctx..pop_token(); - r*.string_rep = r*.inner_token*..to_string() + r*..gen_range_string() + r*..gen_mod_string(); - - return r; - } - - return nullptr; - } - - parse_modifier: (inout this, inout ctx: parse_context) = - { - if ctx..peek() == '?' { - kind = range_flags::not_greedy; - _ = ctx..next(); - } - else if ctx..peek() == '+' { - kind = range_flags::possessive; - _ = ctx..next(); - } - } - - gen_mod_string: (this) -> std::string = - { - if kind == range_flags::not_greedy { - return "?"; - } - else if kind == range_flags::possessive { - return "+"; - } - else { - return ""; - } - } - - gen_range_string: (this) -> std::string = - { - r : std::string = ""; - if min_count == max_count { - r += "{(min_count)$}"; - } - else if min_count == -1 { - r += "{,(max_count)$}"; - } - else if max_count == -1 { - r += "{(min_count)$,}"; - } - else { - r += "{(min_count)$,(max_count)$}"; - } - - return r; - } - - generate_code: (override this, inout ctx: generation_context) = - { - inner_name := ctx..generate_func(inner_token); - groups: std::set = (); - inner_token*..add_groups(groups); - reset_name := ctx..generate_reset(groups); - - next_name := ctx..next_func_name(); - ctx..add_statefull(next_name, "cpp2::regex::range_token_matcher::match((ctx..match_parameters())$, (inner_name)$, (reset_name)$, other, (next_name)$)"); - } - - add_groups: (override this, inout groups: std::set) = { - inner_token*..add_groups(groups); - } - -} - - -// Regex syntax: *, +, or ? Example: aa* -// -special_range_token: @polymorphic_base type = -{ - this : range_token = (); - - parse: (inout ctx: parse_context) -> token_ptr = - { - r := shared.new(); - symbol: char = '\0'; - if ctx..current() == '*' { - r*.min_count = 0; - r*.max_count = -1; - symbol = '*'; - } - else if ctx..current() == '+' { - r*.min_count = 1; - r*.max_count = -1; - symbol = '+'; - } else if ctx..current() == '?' { - r*.min_count = 0; - r*.max_count = 1; - symbol = '?'; - } else { - return nullptr; - } - - if !ctx..has_token() { return ctx..error("'(ctx..current())$' without previous element."); } - - - r*..parse_modifier(ctx); - - r*.inner_token = ctx..pop_token(); - r*.string_rep = r*.inner_token*..to_string() + symbol + r*..gen_mod_string(); - return r; - } -} - - -// Regex syntax: \b or \B Example: \bword\b -// -// Matches the start end end of word boundaries. -// -word_boundary_token_parse: (inout ctx: parse_context) -> token_ptr = -{ - if ctx..current() != '\\' { return nullptr; } - - if ctx..peek() == 'b' { - _ = ctx..next(); - return shared.new("\\b", "word_boundary_token_matcher"); - } - else if ctx..peek() == 'B' { - _ = ctx..next(); - return shared.new("\\B", "word_boundary_token_matcher"); - } - else { - return nullptr; - } -} - - -//----------------------------------------------------------------------- -// -// Parser for regular expression. -// -//----------------------------------------------------------------------- -// - -// Parser and generator for regular expressions. -regex_generator: type = -{ - regex: std::string_view; - modifier: std::string = ""; - modifier_escape: std::string = ""; - - error_out: Error_out; - - source: std::string = ""; - - operator=: (out this, r: std::string_view, e: Error_out) = { - regex = r; - error_out = e; - } - - parse:(inout this) -> std::string = - { - // Extract modifiers and adapt regex. - extract_modifiers(); - - parse_ctx: parse_context = (regex, error_out); - if !parse_ctx..parse(modifier) { - return ""; - } - - source += "{\n"; - source += " wrap: type = {\n"; // TODO: Remove wrapper when template template parameters are available. - source += " context: type == cpp2::regex::match_context;"; - - gen_ctx: generation_context = (); - source += gen_ctx..run(parse_ctx..get_as_token()); - source += " entry: (cur: Iter, inout ctx: context) -> cpp2::regex::match_return = {\n"; - source += " ctx..set_group_start(0, cur);\n"; - source += " r := (gen_ctx..get_entry_func())$(cur, ctx, cpp2::regex::true_end_func());\n"; - source += " if r.matched { ctx..set_group_end(0, r.pos); }\n"; - source += " return r;\n"; - source += " }\n"; - - source += gen_ctx..create_named_group_lookup(parse_ctx.named_groups); - source += "}\n"; - - string := parse_ctx..get_as_token()*..to_string(); - source += " to_string: () -> std::string = { return R\"((modifier_escape)$(string)$(modifier_escape)$(modifier)$)\"; }\n"; - source += "}\n"; - - _ = parse_ctx; - - return source; - } - - private extract_modifiers: (inout this) = - { - if regex..find_first_of("'/") == 0 { - mod_token: char = regex[0]; - - end_pos := regex..rfind(mod_token); - if end_pos != 0 { - // Found valid start end escape - modifier = regex..substr(end_pos + 1); - modifier_escape = mod_token; - regex = regex..substr(1, end_pos - 1); - } - } - } -} - -generate_regex: (regex: std::string_view, err: Err) -> std::string = -{ - parser: regex_generator = (regex, err); - r := parser..parse(); - _ = parser; - return r; -} - - - -regex_gen: (inout t: meta::type_declaration) = -{ - has_default := false; - exact_name := "regex"; - prefix := "regex_"; - expressions : std::map = (); - - for t.get_member_objects() do (inout m) - { - name: std::string = m.name(); - - if name.starts_with(prefix) || name == exact_name - { - if !m.has_initializer() { - t.error("Regular expression must have an initializer."); - } - m.mark_for_removal_from_enclosing_type(); - - if name == exact_name { - if has_default { - t.error("Type can only contain one default named regular expression."); - } - has_default = true; - } - - expr: std::string = m.initializer(); - if expr.starts_with("R\"(") && expr.ends_with(")\"") { - expr = expr.substr(3, expr.size() - 5); - } - else if string_util::is_escaped(expr) { - expr = expr.substr(1, expr.size() - 2); - } - else { - t.error("Unknown string format '(expr)$'"); - } - - expressions[name] = expr; - } - } - - t.remove_marked_members(); - - for expressions do (expr) { - regular_expression := generate_regex(expr.second, :(message) = t$.error(message);); - - if !regular_expression..empty() { - t.add_member("public (expr.first)$_matcher: type = (regular_expression)$"); - t.add_member("public (expr.first)$: cpp2::regex::regular_expression = ();"); - } - } - - t.add_runtime_support_include( "cpp2regex.h" ); -} - - -//----------------------------------------------------------------------- -// -// apply_metafunctions -// -apply_metafunctions: ( - inout n : declaration_node, - inout rtype : type_declaration, - error - ) - -> bool -= { - assert( n.is_type() ); - - // Check for _names reserved for the metafunction implementation - if !n.metafunctions.empty() - { - for rtype.get_members() - do (m) - { - m.require( - !m.name().starts_with("_") || m.name().ssize() == 1, - "a type that applies a metafunction cannot have a body that declares " - "a name that starts with '_' - those names are reserved for the " - "metafunction implementation" - ); - } - } - - // For each metafunction, apply it - for n.metafunctions - do (meta) - { - // Convert the name and any template arguments to strings - // and record that in rtype - name := meta*.to_string(); - name = name.substr(0, name.find('<')); - - args: std::vector = (); - for meta*.template_arguments() - do (arg) - args.push_back( arg.to_string() ); - - rtype.set_metafunction_name( name, args ); - - // Dispatch - // - if name == "interface" { - interface( rtype ); - } - else if name == "polymorphic_base" { - polymorphic_base( rtype ); - } - else if name == "ordered" { - ordered( rtype ); - } - else if name == "weakly_ordered" { - weakly_ordered( rtype ); - } - else if name == "partially_ordered" { - partially_ordered( rtype ); - } - else if name == "copyable" { - copyable( rtype ); - } - else if name == "hashable" { - hashable( rtype ); - } - else if name == "basic_value" { - basic_value( rtype ); - } - else if name == "value" { - value( rtype ); - } - else if name == "weakly_ordered_value" { - weakly_ordered_value( rtype ); - } - else if name == "partially_ordered_value" { - partially_ordered_value( rtype ); - } - else if name == "cpp1_rule_of_zero" { - cpp1_rule_of_zero( rtype ); - } - else if name == "struct" { - cpp2_struct( rtype ); - } - else if name == "enum" { - cpp2_enum( rtype ); - } - else if name == "flag_enum" { - flag_enum( rtype ); - } - else if name == "union" { - cpp2_union( rtype ); - } - else if name == "print" { - print( rtype ); - } - else if name == "regex" { - regex_gen( rtype ); - } - else { - error( "unrecognized metafunction name: " + name ); - error( - "(temporary alpha limitation) currently the supported names are: " - "interface, polymorphic_base, ordered, weakly_ordered, partially_ordered, " - "copyable, basic_value, value, weakly_ordered_value, partially_ordered_value, " - "struct, enum, flag_enum, union, cpp1_rule_of_zero, regex, print" - ); - return false; - } - } - - return true; + t.require( t.make_dll_visible(), + "dll_visible can only be applied to a namespace-scope name"); } diff --git a/source/reflect_impl.h b/source/reflect_impl.h new file mode 100644 index 0000000000..dd8d9d2e99 --- /dev/null +++ b/source/reflect_impl.h @@ -0,0 +1,3987 @@ + +#ifndef REFLECT_IMPL_H_CPP2 +#define REFLECT_IMPL_H_CPP2 + + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "reflect_impl.h2" + +#line 33 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 56 "reflect_impl.h2" +class dll_symbol; + + +#line 112 "reflect_impl.h2" +namespace this_execution + { + +#line 128 "reflect_impl.h2" +} + +#line 135 "reflect_impl.h2" +class diagnostic; + + +#line 139 "reflect_impl.h2" +template class expected; + + +#line 156 "reflect_impl.h2" +} + +} + +#line 412 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 424 "reflect_impl.h2" +class compiler_services_data; + +#line 474 "reflect_impl.h2" +class expression_flags; + +#line 490 "reflect_impl.h2" +class regex_token; + +#line 516 "reflect_impl.h2" +class regex_token_check; + +#line 535 "reflect_impl.h2" +class regex_token_code; + +#line 554 "reflect_impl.h2" +class regex_token_empty; + +#line 570 "reflect_impl.h2" +class regex_token_list; + +#line 609 "reflect_impl.h2" +class parse_context_group_state; + +#line 670 "reflect_impl.h2" +class parse_context_branch_reset_state; + +#line 713 "reflect_impl.h2" +class parse_context; + +#line 1111 "reflect_impl.h2" +class generation_function_context; + + +#line 1129 "reflect_impl.h2" +class generation_context; + +#line 1327 "reflect_impl.h2" +class alternative_token; + +#line 1342 "reflect_impl.h2" +class alternative_token_gen; + +#line 1394 "reflect_impl.h2" +class any_token; + +#line 1412 "reflect_impl.h2" +class char_token; + +#line 1515 "reflect_impl.h2" +class class_token; + +#line 1730 "reflect_impl.h2" +class group_ref_token; + +#line 1861 "reflect_impl.h2" +class group_token; + +#line 2148 "reflect_impl.h2" +class lookahead_token; + +#line 2229 "reflect_impl.h2" +class range_token; + +#line 2377 "reflect_impl.h2" +class special_range_token; + +#line 2444 "reflect_impl.h2" +template class regex_generator; + +#line 2745 "reflect_impl.h2" +} + +} + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "reflect_impl.h2" + +// Copyright (c) Herb Sutter +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +//=========================================================================== +// Reflection and meta +//=========================================================================== + +#include "parse.h" +#include "cpp2regex.h" +#include +#include +#include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#else +#include +#endif // _WIN32 + +using namespace cpp2::regex; + +#line 33 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 38 "reflect_impl.h2" +[[nodiscard]] auto to_type_metafunction_cast( + cpp2::impl::in name, + cpp2::impl::in is_const_metafunction + ) -> std::string; + +#line 56 "reflect_impl.h2" +class dll_symbol { + private: std::string value {}; + + public: static const std::string_view constant_prefix; + public: static const std::string_view reachable_mangle; + public: static const std::string_view const_metafunction_mangle; + + public: dll_symbol(); + public: explicit dll_symbol(cpp2::impl::in n, cpp2::impl::in is_reachable_); + +#line 78 "reflect_impl.h2" + public: explicit dll_symbol(std::string_view s); +#line 78 "reflect_impl.h2" + public: auto operator=(std::string_view s) -> dll_symbol& ; + +#line 86 "reflect_impl.h2" + public: [[nodiscard]] auto c_str() const& -> char const*; + public: [[nodiscard]] auto view() const& -> std::string_view; + public: [[nodiscard]] auto without_prefix() const& -> std::string_view; + +#line 101 "reflect_impl.h2" + public: [[nodiscard]] auto is_reachable() const& -> bool; + public: [[nodiscard]] auto is_const_metafunction() const& -> bool; + public: [[nodiscard]] auto operator<=>(dll_symbol const& that) const& -> std::strong_ordering = default; +public: dll_symbol(dll_symbol const& that); + +public: auto operator=(dll_symbol const& that) -> dll_symbol& ; +public: dll_symbol(dll_symbol&& that) noexcept; +public: auto operator=(dll_symbol&& that) noexcept -> dll_symbol& ; + +#line 106 "reflect_impl.h2" +}; + +[[nodiscard]] auto symbols_accessor(cpp2::impl::in lib_path) -> dll_symbol; + +#line 112 "reflect_impl.h2" +namespace this_execution + { + +#line 117 "reflect_impl.h2" +[[nodiscard]] auto symbols_accessor() -> dll_symbol; + +#line 128 "reflect_impl.h2" +} + +#line 135 "reflect_impl.h2" +class diagnostic { + public: std::string value; + public: diagnostic(auto&& value_) +CPP2_REQUIRES_ (std::is_convertible_v&>) ; + +public: auto operator=(auto&& value_) -> diagnostic& +CPP2_REQUIRES_ (std::is_convertible_v&>) ; + +#line 137 "reflect_impl.h2" +}; + +template class expected { + +#line 143 "reflect_impl.h2" + public: expected(T const& v); +#line 143 "reflect_impl.h2" + public: auto operator=(T const& v) -> expected& ; + public: expected(cpp2::impl::in u); +#line 144 "reflect_impl.h2" + public: auto operator=(cpp2::impl::in u) -> expected& ; + + public: template [[nodiscard]] auto and_then(F const& f) && -> std::remove_cvref_t>; + private: cpp2::aligned_storage _storage {}; private: cpp2::i8 _discriminator {-1}; public: [[nodiscard]] auto is_value() const& -> bool; +public: [[nodiscard]] auto value() const& -> T const&; +public: [[nodiscard]] auto value() & -> T&; +public: auto set_value(cpp2::impl::in _value) & -> void; +public: auto set_value(auto&& ..._args) & -> void; +public: [[nodiscard]] auto is_error() const& -> bool; +public: [[nodiscard]] auto error() const& -> diagnostic const&; +public: [[nodiscard]] auto error() & -> diagnostic&; +public: auto set_error(cpp2::impl::in _value) & -> void; +public: auto set_error(auto&& ..._args) & -> void; +private: auto _destroy() & -> void; +public: ~expected() noexcept; +public: explicit expected(); +public: expected(expected const& that); + +public: expected(expected&& that) noexcept; +public: auto operator=(expected const& that) -> expected& ; +public: auto operator=(expected&& that) noexcept -> expected& ; + +#line 154 "reflect_impl.h2" +}; + +} + +} + + +namespace cpp2::meta { + + +//----------------------------------------------------------------------- +// +// (de)mangling +// +auto mangle(std::string res) + -> std::string +{ + // Mangle (id length, id) pairs according to + // https://en.wikipedia.org/wiki/Name_mangling#Complex_example + // "... by the GNU GCC 3.x compilers, according to the IA-64 (Itanium) ABI" + auto xpos = res.size(); + auto prev_id_end = xpos; + while ((xpos = res.find_last_of(':', xpos)) != res.npos) + { + res.replace(xpos - 1, 2, std::to_string(prev_id_end - xpos - 1)); + prev_id_end = xpos - 1; + } + + return res; +} + +auto mangle(declaration_node const& n) + -> std::string +{ + assert(n.identifier); + assert(n.identifier->template_arguments().empty()); + assert(n.parent_is_namespace()); + + return mangle(n.fully_qualified_name()); +} + +auto demangle(std::string_view s) + -> std::string +{ + std::string res{}; + + // Demangle (id length, id) pairs according to + // https://en.wikipedia.org/wiki/Name_mangling#Complex_example + // "... by the GNU GCC 3.x compilers, according to the IA-64 (Itanium) ABI" + while (!s.empty()) + { + auto length = s.substr(0, s.find_first_not_of("0123456789")); + s.remove_prefix(length.size()); + auto id = s.substr(0, unchecked_narrow(std::stoi(std::string{length}))); + s.remove_prefix(id.size()); + + assert(!length.empty()); + assert(!id.empty()); + + res += id; + if (!s.empty()) { + res += "::"; + } + } + + return res; +} + + +//----------------------------------------------------------------------- +// +// dll Load DLL and its symbols with the OS API +// +class dll +{ +public: + dll(std::string const& path) + : handle_{ +#ifdef _WIN32 + static_cast(LoadLibraryA(path.c_str())) +#else + static_cast(dlopen(path.c_str(), RTLD_NOW|RTLD_LOCAL)) +#endif // _WIN32 + , +[](void* handle) + { +#ifdef _WIN32 + FreeLibrary(static_cast(handle)); +#else + dlclose(handle); +#endif // _WIN32 + } + } + { + if(!handle_) { + cpp2_default.report_violation(("failed to load DLL '" + path + "': " + get_last_error()).c_str()); + } + } + + template + auto get_alias(std::string const& name) noexcept -> T& + { +#ifdef _WIN32 + auto symbol = reinterpret_cast(GetProcAddress(static_cast(handle_.get()), name.c_str())); +#else + auto symbol = dlsym(handle_.get(), name.c_str()); + if(!symbol) + { + // Some platforms export with additional underscore, so try that + auto const us_name = "_" + name; + symbol = dlsym(handle_.get(), us_name.c_str()); + } +#endif // _WIN32 + if (!symbol) { + cpp2_default.report_violation(("failed to load DLL symbol '" + name+ "': " + get_last_error()).c_str()); + } + return **reinterpret_cast(symbol); + } +private: + std::shared_ptr handle_; + + static auto get_last_error() noexcept -> std::string + { +#ifdef _WIN32 + DWORD errorMessageID = GetLastError(); + if(errorMessageID == 0) { + return {}; // No error message has been recorded + } + LPSTR messageBuffer = nullptr; + auto size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + errorMessageID, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR)&messageBuffer, + 0, + nullptr + ); + std::string message(messageBuffer, unchecked_narrow(size)); + LocalFree(messageBuffer); + return message; +#else + return std::string{dlerror()}; +#endif // _WIN32 + } + +}; + + +//----------------------------------------------------------------------- +// +// get_reachable_metafunction_symbols +// +struct library +{ + std::string_view name; + std::vector symbols; +}; + +namespace this_execution { + +// Load Cpp2 libraries with metafunctions by opening DLL with the OS API +// +// The environment variable 'CPPFRONT_METAFUNCTION_LIBRARIES' +// is read and interpreted as ':'-separated Cpp2 metafunction library paths +std::span get_reachable_metafunction_symbols() +{ + static std::vector res = []{ + std::vector res; + + // FIXME: On Windows, using this approach with the system apis not set to utf8, will + // break if a metafunction library contains unicode codepoints in its name, a proper + // way to handle this would be to use _wgetenv and use wchar_t strings for the dll opening + // function + auto cpp1_libraries_cstr = std::getenv("CPPFRONT_METAFUNCTION_LIBRARIES"); + if ( + !cpp1_libraries_cstr + || cpp1_libraries_cstr[0] == '\0' + ) + { + return res; + } + + auto cpp1_libraries = std::string_view{cpp1_libraries_cstr}; + while (!cpp1_libraries.empty()) + { + auto colon = cpp1_libraries.find(':'); + auto lib_path = cpp1_libraries.substr(0, colon); + cpp1_libraries.remove_prefix(lib_path.size() + unsigned(colon != lib_path.npos)); + + auto lib = dll{std::string{lib_path}}; + + auto get_symbols = lib.get_alias(meta::symbols_accessor(lib_path).c_str()); + + res.push_back({lib_path, {}}); + auto c_strings = get_symbols(); + if (!c_strings || !*c_strings) { + cpp2_default.report_violation( + ("symbols accesor returns no symbols (in '" + std::string{lib_path} + "')").c_str() + ); + } + + for (; *c_strings; ++c_strings) { + auto symbol = res.back().symbols.emplace_back(*c_strings); + } + } + + return res; + }(); + + return res; +} + +} + + +//----------------------------------------------------------------------- +// +// load_metafunction +// +struct lookup_res { + std::string_view library; + dll_symbol const* symbol; +}; + +using load_metafunction_ret = std::function; + +// Load Cpp2 metafunction by opening DLL with the OS API +auto load_metafunction( + std::string const& name, + std::function(std::string const&)> lookup + ) + -> expected +{ + return lookup(name).and_then( + [](lookup_res res) + -> expected + { + auto [lib_path, cpp1_name] = res; + + auto lib = dll{std::string(lib_path)}; + return load_metafunction_ret{ + [ + fun = lib.get_alias(cpp1_name->c_str()), + lib = std::move(lib) + ] + (type_declaration& t) + -> void + { + fun(t); + } + }; + } + ); +} + +} + +#line 412 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 424 "reflect_impl.h2" +class compiler_services_data + { + +#line 428 "reflect_impl.h2" + public: std::vector* errors; + public: std::set* includes; + public: int errors_original_size; + public: stable_vector* generated_tokens; + public: cpp2::parser parser; + public: std::string metafunction_name {}; + public: std::vector metafunction_args {}; + public: bool metafunctions_used {false}; + +#line 439 "reflect_impl.h2" + public: [[nodiscard]] static auto make( + std::vector* errors_, + std::set* includes_, + stable_vector* generated_tokens_, + cpp2::impl::in translation_unit_has_interface + ) -> compiler_services_data; + +#line 453 "reflect_impl.h2" +}; + +#line 470 "reflect_impl.h2" +using error_func = std::function x)>; + +#line 474 "reflect_impl.h2" +class expression_flags + { +private: cpp2::u8 _value; private: constexpr expression_flags(cpp2::impl::in _val); + +private: constexpr auto operator=(cpp2::impl::in _val) -> expression_flags& ; +public: constexpr auto operator|=(expression_flags const& that) & -> decltype(auto); +public: constexpr auto operator&=(expression_flags const& that) & -> decltype(auto); +public: constexpr auto operator^=(expression_flags const& that) & -> decltype(auto); +public: [[nodiscard]] constexpr auto operator|(expression_flags const& that) const& -> expression_flags; +public: [[nodiscard]] constexpr auto operator&(expression_flags const& that) const& -> expression_flags; +public: [[nodiscard]] constexpr auto operator^(expression_flags const& that) const& -> expression_flags; +public: [[nodiscard]] constexpr auto has(expression_flags const& that) const& -> bool; +public: constexpr auto set(expression_flags const& that) & -> void; +public: constexpr auto clear(expression_flags const& that) & -> void; +public: static const expression_flags case_insensitive; +public: static const expression_flags multiple_lines; +public: static const expression_flags single_line; +public: static const expression_flags no_group_captures; +public: static const expression_flags perl_code_syntax; +public: static const expression_flags perl_code_syntax_in_classes; +public: static const expression_flags none; +public: [[nodiscard]] constexpr auto get_raw_value() const& -> cpp2::u8; +public: constexpr explicit expression_flags(); +public: constexpr expression_flags(expression_flags const& that); +public: constexpr auto operator=(expression_flags const& that) -> expression_flags& ; +public: constexpr expression_flags(expression_flags&& that) noexcept; +public: constexpr auto operator=(expression_flags&& that) noexcept -> expression_flags& ; +public: [[nodiscard]] auto operator<=>(expression_flags const& that) const& -> std::strong_ordering = default; +public: [[nodiscard]] auto to_string_impl(cpp2::impl::in prefix, cpp2::impl::in separator) const& -> std::string; +public: [[nodiscard]] auto to_string() const& -> std::string; +public: [[nodiscard]] auto to_code() const& -> std::string; +public: [[nodiscard]] static auto from_string(cpp2::impl::in s) -> expression_flags; +public: [[nodiscard]] static auto from_code(cpp2::impl::in s) -> expression_flags; + +#line 482 "reflect_impl.h2" +}; + +#line 490 "reflect_impl.h2" +class regex_token + { + public: std::string string_rep; + + public: explicit regex_token(cpp2::impl::in str); + +#line 498 "reflect_impl.h2" + public: explicit regex_token(); + +#line 503 "reflect_impl.h2" + public: virtual auto generate_code([[maybe_unused]] generation_context& unnamed_param_2) const -> void = 0; + + public: virtual auto add_groups([[maybe_unused]] std::set& unnamed_param_2) const -> void; + public: [[nodiscard]] auto to_string() const& -> std::string; + public: auto set_string(cpp2::impl::in s) & -> void; + public: virtual ~regex_token() noexcept; + + public: regex_token(regex_token const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(regex_token const&) -> void = delete; + +#line 508 "reflect_impl.h2" +}; + +using token_ptr = std::shared_ptr; +using token_vec = std::vector; + +#line 514 "reflect_impl.h2" +// Adds a check in code generation. +// +class regex_token_check +: public regex_token { + +#line 520 "reflect_impl.h2" + private: std::string check; + + public: explicit regex_token_check(cpp2::impl::in str, cpp2::impl::in check_); + +#line 527 "reflect_impl.h2" + public: auto generate_code(generation_context& ctx) const -> void override; + public: virtual ~regex_token_check() noexcept; + + public: regex_token_check(regex_token_check const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(regex_token_check const&) -> void = delete; + + +#line 530 "reflect_impl.h2" +}; + +#line 533 "reflect_impl.h2" +// Adds code in code generation. +// +class regex_token_code +: public regex_token { + +#line 539 "reflect_impl.h2" + private: std::string code; + + public: explicit regex_token_code(cpp2::impl::in str, cpp2::impl::in code_); + +#line 546 "reflect_impl.h2" + public: auto generate_code(generation_context& ctx) const -> void override; + public: virtual ~regex_token_code() noexcept; + + public: regex_token_code(regex_token_code const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(regex_token_code const&) -> void = delete; + + +#line 549 "reflect_impl.h2" +}; + +#line 552 "reflect_impl.h2" +// Token that does not influence the matching. E.g. comment. +// +class regex_token_empty +: public regex_token { + +#line 558 "reflect_impl.h2" + public: explicit regex_token_empty(cpp2::impl::in str); + +#line 562 "reflect_impl.h2" + public: auto generate_code([[maybe_unused]] generation_context& unnamed_param_2) const -> void override; + public: virtual ~regex_token_empty() noexcept; + + public: regex_token_empty(regex_token_empty const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(regex_token_empty const&) -> void = delete; + + +#line 565 "reflect_impl.h2" +}; + +#line 568 "reflect_impl.h2" +// Represents a list of regex tokens as one token. +// +class regex_token_list +: public regex_token { + +#line 574 "reflect_impl.h2" + public: token_vec tokens; + + public: explicit regex_token_list(cpp2::impl::in t); + +#line 581 "reflect_impl.h2" + public: auto generate_code(generation_context& ctx) const -> void override; + +#line 587 "reflect_impl.h2" + public: auto add_groups(std::set& groups) const -> void override; + +#line 593 "reflect_impl.h2" + public: [[nodiscard]] static auto gen_string(cpp2::impl::in vec) -> std::string; + public: virtual ~regex_token_list() noexcept; + + public: regex_token_list(regex_token_list const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(regex_token_list const&) -> void = delete; + + +#line 600 "reflect_impl.h2" +}; + +#line 603 "reflect_impl.h2" +// +// Parse and generation context. +// + +// State of the current capturing group. See '()' +// +class parse_context_group_state + { + public: token_vec cur_match_list {}; // Current list of matchers. + public: token_vec alternate_match_lists {}; // List of alternate matcher lists. E.g. ab|cd|xy. + public: expression_flags modifiers {}; // Current modifiers for the group/regular expression. + + // Start a new alternative. + public: auto next_alternative() & -> void; + +#line 623 "reflect_impl.h2" + // Swap this state with the other one. NOLINTNEXTLINE(performance-noexcept-swap) + public: auto swap(parse_context_group_state& t) & -> void; + +#line 630 "reflect_impl.h2" + // Convert this state into a regex token. + public: [[nodiscard]] auto get_as_token() & -> token_ptr; + +#line 642 "reflect_impl.h2" + // Add a token to the current matcher list. + public: auto add(cpp2::impl::in token) & -> void; + +#line 647 "reflect_impl.h2" + // True if current matcher list is empty. + public: [[nodiscard]] auto empty() const& -> bool; + +#line 651 "reflect_impl.h2" + // Apply optimizations to the matcher list. + public: static auto post_process_list(token_vec& list) -> void; + public: parse_context_group_state(auto const& cur_match_list_, auto const& alternate_match_lists_, auto const& modifiers_); +public: parse_context_group_state(); + + +#line 665 "reflect_impl.h2" +}; + +#line 668 "reflect_impl.h2" +// State for the branch reset. Takes care of the group numbering. See '(|)'. +// +class parse_context_branch_reset_state + { + public: bool is_active {false}; // If we have a branch reset group. + public: int cur_group {1}; // Next group identifier. 0 == global capture group. + public: int max_group {1}; // Maximum group identifier generated. + public: int from {1}; // Starting identifier on new alternative branch. + + // Next group identifier. + public: [[nodiscard]] auto next() & -> int; + +#line 686 "reflect_impl.h2" + // Set next group identifier. + public: auto set_next(cpp2::impl::in g) & -> void; + +#line 692 "reflect_impl.h2" + // Start a new alternative branch. + public: auto next_alternative() & -> void; + +#line 699 "reflect_impl.h2" + // Initialize for a branch reset group. + public: auto set_active_reset(cpp2::impl::in restart) & -> void; + public: parse_context_branch_reset_state(auto const& is_active_, auto const& cur_group_, auto const& max_group_, auto const& from_); +public: parse_context_branch_reset_state(); + + +#line 706 "reflect_impl.h2" +}; + +#line 709 "reflect_impl.h2" +// Context during parsing of the regular expressions. +// +// Keeps track of the distributed group identifiers, current parsed group and branch resets. +// +class parse_context + { + private: std::string_view regex; // Regular expression string. + private: size_t pos {0}; // Current parsing position. + private: token_ptr root; // Token representing the regular expression. + + private: parse_context_group_state cur_group_state {}; + private: parse_context_branch_reset_state cur_branch_reset_state {}; + +#line 723 "reflect_impl.h2" + public: std::map named_groups {}; + + private: error_func error_out; // TODO: Declaring std::function fails for cpp2. + private: bool has_error {false}; + + public: explicit parse_context(cpp2::impl::in r, auto const& e); + +#line 734 "reflect_impl.h2" + // State management functions + // + + // Returned group state needs to be stored and provided in `end_group`. + public: [[nodiscard]] auto start_group() & -> parse_context_group_state; + +#line 747 "reflect_impl.h2" + // `old_state` argument needs to be from start group. + public: [[nodiscard]] auto end_group(cpp2::impl::in old_state) & -> token_ptr; + +#line 755 "reflect_impl.h2" + public: [[nodiscard]] auto get_modifiers() const& -> expression_flags; + +#line 759 "reflect_impl.h2" + public: auto set_modifiers(cpp2::impl::in mod) & -> void; + +#line 763 "reflect_impl.h2" + // Branch reset management functions + // + + public: [[nodiscard]] auto branch_reset_new_state() & -> parse_context_branch_reset_state; + +#line 775 "reflect_impl.h2" + public: auto branch_reset_restore_state(cpp2::impl::in old_state) & -> void; + +#line 782 "reflect_impl.h2" + public: auto next_alternative() & -> void; + +#line 788 "reflect_impl.h2" + // Regex token management + // + public: auto add_token(cpp2::impl::in token) & -> void; + +#line 794 "reflect_impl.h2" + public: [[nodiscard]] auto has_token() const& -> bool; + +#line 798 "reflect_impl.h2" + public: [[nodiscard]] auto pop_token() & -> token_ptr; + +#line 809 "reflect_impl.h2" + public: [[nodiscard]] auto get_as_token() & -> token_ptr; + +#line 813 "reflect_impl.h2" + // Group management + // + public: [[nodiscard]] auto get_cur_group() const& -> int; + +#line 819 "reflect_impl.h2" + public: [[nodiscard]] auto next_group() & -> int; + +#line 823 "reflect_impl.h2" + public: auto set_named_group(cpp2::impl::in name, cpp2::impl::in id) & -> void; + +#line 830 "reflect_impl.h2" + public: [[nodiscard]] auto get_named_group(cpp2::impl::in name) const& -> int; + +#line 841 "reflect_impl.h2" + // Position management functions + // + public: [[nodiscard]] auto current() const& -> char; + + // Get the next token in the regex, skipping spaces according to the parameters. See `x` and `xx` modifiers. + private: [[nodiscard]] auto get_next_position(cpp2::impl::in in_class, cpp2::impl::in no_skip) const& -> size_t; + +#line 885 "reflect_impl.h2" + // Return true if next token is available. + private: [[nodiscard]] auto next_impl(cpp2::impl::in in_class, cpp2::impl::in no_skip) & -> bool; + +#line 897 "reflect_impl.h2" + public: [[nodiscard]] auto next() & -> decltype(auto); + public: [[nodiscard]] auto next_in_class() & -> decltype(auto); + public: [[nodiscard]] auto next_no_skip() & -> decltype(auto); + + public: [[nodiscard]] auto next_n(cpp2::impl::in n) & -> bool; + +#line 910 "reflect_impl.h2" + public: [[nodiscard]] auto has_next() const& -> bool; + + private: [[nodiscard]] auto grab_until_impl(cpp2::impl::in e, cpp2::impl::out r, cpp2::impl::in any) & -> bool; + +#line 933 "reflect_impl.h2" + public: [[nodiscard]] auto grab_until(cpp2::impl::in e, cpp2::impl::out r) & -> decltype(auto); + public: [[nodiscard]] auto grab_until(cpp2::impl::in e, cpp2::impl::out r) & -> decltype(auto); + public: [[nodiscard]] auto grab_until_one_of(cpp2::impl::in e, cpp2::impl::out r) & -> decltype(auto); + + public: [[nodiscard]] auto grab_n(cpp2::impl::in n, cpp2::impl::out r) & -> bool; + +#line 950 "reflect_impl.h2" + public: [[nodiscard]] auto grab_number() & -> std::string; + +#line 971 "reflect_impl.h2" + private: [[nodiscard]] auto peek_impl(cpp2::impl::in in_class) const& -> char; + +#line 981 "reflect_impl.h2" + public: [[nodiscard]] auto peek() const& -> decltype(auto); + public: [[nodiscard]] auto peek_in_class() const& -> decltype(auto); + +#line 985 "reflect_impl.h2" + // Parsing functions + // + public: [[nodiscard]] auto parser_group_modifiers(cpp2::impl::in change_str, expression_flags& parser_modifiers) & -> bool; + +#line 1041 "reflect_impl.h2" + public: [[nodiscard]] auto parse_until(cpp2::impl::in term) & -> bool; + +#line 1079 "reflect_impl.h2" + public: [[nodiscard]] auto parse(cpp2::impl::in modifiers) & -> bool; + +#line 1094 "reflect_impl.h2" + // Misc functions + + public: [[nodiscard]] auto get_pos() const& -> decltype(auto); + public: [[nodiscard]] auto get_range(cpp2::impl::in start, cpp2::impl::in end) const& -> decltype(auto); + public: [[nodiscard]] auto valid() const& -> bool; + + public: [[nodiscard]] auto error(cpp2::impl::in err) & -> token_ptr; + public: parse_context(parse_context const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(parse_context const&) -> void = delete; + + +#line 1105 "reflect_impl.h2" +}; + +#line 1108 "reflect_impl.h2" +// Context for one function generation. Generation of functions can be interleaved, +// therefore we buffer the code for one function here. +// +class generation_function_context { + public: std::string code {""}; + public: std::string tabs {""}; + + public: auto add_tabs(cpp2::impl::in c) & -> void; + +#line 1122 "reflect_impl.h2" + public: auto remove_tabs(cpp2::impl::in c) & -> void; + public: generation_function_context(auto const& code_, auto const& tabs_); +public: generation_function_context(); + + +#line 1125 "reflect_impl.h2" +}; + +#line 1128 "reflect_impl.h2" +// Context for generating the state machine. +class generation_context + { + private: std::vector gen_stack {1}; // Element 0 contains all the code. + + private: int matcher_func {0}; + private: int reset_func {0}; + private: int temp_name {0}; + private: std::string entry_func {""}; + + // Generation helpers + // + public: [[nodiscard]] auto match_parameters() const& -> std::string; + + // Code generation. + + // Add code line. + public: auto add(cpp2::impl::in s) & -> void; + +#line 1150 "reflect_impl.h2" + // Add check for token. The check needs to be a function call that returns a boolean. + public: auto add_check(cpp2::impl::in check) & -> void; + +#line 1156 "reflect_impl.h2" + // Add a stateful check. The check needs to return a `match_return`. + public: auto add_statefull(cpp2::impl::in next_func, cpp2::impl::in check) & -> void; + +#line 1165 "reflect_impl.h2" + protected: auto start_func_named(cpp2::impl::in name) & -> void; + +#line 1176 "reflect_impl.h2" + protected: [[nodiscard]] auto start_func() & -> std::string; + +#line 1183 "reflect_impl.h2" + protected: auto end_func_statefull(cpp2::impl::in s) & -> void; + +#line 1202 "reflect_impl.h2" + // Generate the function for a token. + public: [[nodiscard]] auto generate_func(cpp2::impl::in token) & -> std::string; + +#line 1212 "reflect_impl.h2" + // Generate the reset for a list of group identifiers. + public: [[nodiscard]] auto generate_reset(cpp2::impl::in> groups) & -> std::string; + +#line 1235 "reflect_impl.h2" + // Name generation + // + protected: [[nodiscard]] auto gen_func_name() & -> std::string; + +#line 1243 "reflect_impl.h2" + public: [[nodiscard]] auto next_func_name() & -> std::string; + +#line 1247 "reflect_impl.h2" + protected: [[nodiscard]] auto gen_reset_func_name() & -> std::string; + +#line 1253 "reflect_impl.h2" + public: [[nodiscard]] auto gen_temp() & -> std::string; + +#line 1259 "reflect_impl.h2" + // Context management + // + public: [[nodiscard]] auto new_context() & -> generation_function_context*; + +#line 1269 "reflect_impl.h2" + public: auto finish_context() & -> void; + +#line 1277 "reflect_impl.h2" + // Misc functions + // + private: [[nodiscard]] auto get_current() & -> generation_function_context*; + +#line 1283 "reflect_impl.h2" + private: [[nodiscard]] auto get_base() & -> generation_function_context*; + +#line 1287 "reflect_impl.h2" + public: [[nodiscard]] auto get_entry_func() const& -> std::string; + +#line 1291 "reflect_impl.h2" + public: [[nodiscard]] auto create_named_group_lookup(cpp2::impl::in> named_groups) const& -> std::string; + +#line 1315 "reflect_impl.h2" + // Run the generation for the token. + public: [[nodiscard]] auto run(cpp2::impl::in token) & -> std::string; + public: generation_context() = default; + public: generation_context(generation_context const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(generation_context const&) -> void = delete; + + +#line 1321 "reflect_impl.h2" +}; + +// Regex syntax: | Example: ab|ba +// +// Non greedy implementation. First alternative that matches is chosen. +// +class alternative_token +: public regex_token_empty { + // No code gen here. alternative_token_gen is created in the parse_context + + public: explicit alternative_token(); + + public: [[nodiscard]] static auto parse(parse_context& ctx) -> token_ptr; + public: virtual ~alternative_token() noexcept; + + public: alternative_token(alternative_token const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(alternative_token const&) -> void = delete; + + +#line 1340 "reflect_impl.h2" +}; + +class alternative_token_gen +: public regex_token { + +#line 1346 "reflect_impl.h2" + private: token_vec alternatives; + + public: explicit alternative_token_gen(cpp2::impl::in a); + +#line 1353 "reflect_impl.h2" + public: auto generate_code(generation_context& ctx) const -> void override; + +#line 1370 "reflect_impl.h2" + public: auto add_groups(std::set& groups) const -> void override; + +#line 1377 "reflect_impl.h2" + public: [[nodiscard]] static auto gen_string(cpp2::impl::in a) -> std::string; + public: virtual ~alternative_token_gen() noexcept; + + public: alternative_token_gen(alternative_token_gen const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(alternative_token_gen const&) -> void = delete; + + +#line 1389 "reflect_impl.h2" +}; + +#line 1392 "reflect_impl.h2" +// Regex syntax: . +// +class any_token +: public regex_token_check { + +#line 1398 "reflect_impl.h2" + public: explicit any_token(cpp2::impl::in single_line); + +#line 1402 "reflect_impl.h2" + public: [[nodiscard]] static auto parse(parse_context& ctx) -> token_ptr; + public: virtual ~any_token() noexcept; + + public: any_token(any_token const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(any_token const&) -> void = delete; + + +#line 1407 "reflect_impl.h2" +}; + +#line 1410 "reflect_impl.h2" +// Regex syntax: a +// +class char_token +: public regex_token { + +#line 1416 "reflect_impl.h2" + private: std::string token; + private: bool ignore_case; + + public: explicit char_token(cpp2::impl::in t, cpp2::impl::in ignore_case_); + +#line 1425 "reflect_impl.h2" + public: [[nodiscard]] static auto parse(parse_context& ctx) -> token_ptr; + +#line 1429 "reflect_impl.h2" + public: auto generate_code(generation_context& ctx) const -> void override; + +#line 1452 "reflect_impl.h2" + public: auto gen_case_insensitive(cpp2::impl::in lower, cpp2::impl::in upper, generation_context& ctx) const& -> void; + +#line 1473 "reflect_impl.h2" + public: auto gen_case_sensitive(generation_context& ctx) const& -> void; + +#line 1491 "reflect_impl.h2" + public: [[nodiscard]] auto add_escapes(std::string str) const& -> std::string; + +#line 1506 "reflect_impl.h2" + public: auto append(char_token const& that) & -> void; + public: virtual ~char_token() noexcept; + + public: char_token(char_token const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(char_token const&) -> void = delete; + + +#line 1510 "reflect_impl.h2" +}; + +#line 1513 "reflect_impl.h2" +// Regex syntax: [] Example: [abcx-y[:digits:]] +// +class class_token +: public regex_token { + +#line 1519 "reflect_impl.h2" + private: bool negate; + private: bool case_insensitive; + private: std::string class_str; + + public: explicit class_token(cpp2::impl::in negate_, cpp2::impl::in case_insensitive_, cpp2::impl::in class_str_, cpp2::impl::in str); + +#line 1531 "reflect_impl.h2" + // TODO: Rework class generation: Generate check functions for classes. + public: [[nodiscard]] static auto parse(parse_context& ctx) -> token_ptr; + +#line 1657 "reflect_impl.h2" + public: auto generate_code(generation_context& ctx) const -> void override; + +#line 1662 "reflect_impl.h2" + private: [[nodiscard]] static auto create_matcher(cpp2::impl::in name, cpp2::impl::in template_arguments) -> std::string; + public: virtual ~class_token() noexcept; + + public: class_token(class_token const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(class_token const&) -> void = delete; + + +#line 1669 "reflect_impl.h2" +}; + +#line 1672 "reflect_impl.h2" +// Regex syntax: \a or \n or \[ +// +[[nodiscard]] auto escape_token_parse(parse_context& ctx) -> token_ptr; + +#line 1713 "reflect_impl.h2" +// Regex syntax: \K Example: ab\Kcd +// +[[nodiscard]] auto global_group_reset_token_parse(parse_context& ctx) -> token_ptr; + +#line 1724 "reflect_impl.h2" +// Regex syntax: \ Example: \1 +// \g{name_or_number} +// \k{name_or_number} +// \k +// \k'name_or_number' +// +class group_ref_token +: public regex_token { + +#line 1734 "reflect_impl.h2" + private: int id; + private: bool case_insensitive; + + public: explicit group_ref_token(cpp2::impl::in id_, cpp2::impl::in case_insensitive_, cpp2::impl::in str); + +#line 1744 "reflect_impl.h2" + public: [[nodiscard]] static auto parse(parse_context& ctx) -> token_ptr; + +#line 1845 "reflect_impl.h2" + public: auto generate_code(generation_context& ctx) const -> void override; + public: virtual ~group_ref_token() noexcept; + + public: group_ref_token(group_ref_token const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(group_ref_token const&) -> void = delete; + + +#line 1848 "reflect_impl.h2" +}; + +#line 1851 "reflect_impl.h2" +// Regex syntax: () Example: (abc) +// (?:) (?i:abc) +// (?<>:) (?:abc) +// (?#) (#Step 1 finished) +// (?|) (?|(abc)|(cde)) +// (?=) (?=abc) +// (?!) (?!abc) +// (*: syntax, cpp2::impl::in positive) -> token_ptr; + +#line 1882 "reflect_impl.h2" + public: [[nodiscard]] static auto parse(parse_context& ctx) -> token_ptr; + +#line 2019 "reflect_impl.h2" + public: [[nodiscard]] static auto gen_string(cpp2::impl::in name, cpp2::impl::in name_brackets, cpp2::impl::in has_modifier, cpp2::impl::in modifiers, cpp2::impl::in inner_) -> std::string; + +#line 2037 "reflect_impl.h2" + public: auto generate_code(generation_context& ctx) const -> void override; + +#line 2057 "reflect_impl.h2" + public: auto add_groups(std::set& groups) const -> void override; + public: virtual ~group_token() noexcept; + + public: group_token() = default; + public: group_token(group_token const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(group_token const&) -> void = delete; + + +#line 2064 "reflect_impl.h2" +}; + +#line 2067 "reflect_impl.h2" +// Regex syntax: \x or \x{} Example: \x{62} +// +[[nodiscard]] auto hexadecimal_token_parse(parse_context& ctx) -> token_ptr; + +#line 2108 "reflect_impl.h2" +// Regex syntax: $ Example: aa$ +// +[[nodiscard]] auto line_end_token_parse(parse_context& ctx) -> token_ptr; + +#line 2128 "reflect_impl.h2" +// Regex syntax: ^ Example: ^aa +// +[[nodiscard]] auto line_start_token_parse(parse_context& ctx) -> token_ptr; + +#line 2144 "reflect_impl.h2" +// Regex syntax: (?=) or (?!) or (*pla), etc. Example: (?=AA) +// +// Parsed in group_token. +// +class lookahead_token +: public regex_token { + +#line 2152 "reflect_impl.h2" + protected: bool positive; + public: token_ptr inner {nullptr}; + + public: explicit lookahead_token(cpp2::impl::in positive_); + +#line 2159 "reflect_impl.h2" + public: auto generate_code(generation_context& ctx) const -> void override; + +#line 2165 "reflect_impl.h2" + public: auto add_groups(std::set& groups) const -> void override; + public: virtual ~lookahead_token() noexcept; + + public: lookahead_token(lookahead_token const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(lookahead_token const&) -> void = delete; + + +#line 2168 "reflect_impl.h2" +}; + +#line 2171 "reflect_impl.h2" +// Named character classes +// +[[nodiscard]] auto named_class_token_parse(parse_context& ctx) -> token_ptr; + +#line 2199 "reflect_impl.h2" +// Regex syntax: \o{} Example: \o{142} +// +[[nodiscard]] auto octal_token_parse(parse_context& ctx) -> token_ptr; + +#line 2227 "reflect_impl.h2" +// Regex syntax: {min, max} Example: a{2,4} +// +class range_token +: public regex_token { + +#line 2233 "reflect_impl.h2" + protected: int min_count {-1}; + protected: int max_count {-1}; + protected: int kind {range_flags::greedy}; + protected: token_ptr inner_token {nullptr}; + + public: explicit range_token(); + + public: [[nodiscard]] static auto parse(parse_context& ctx) -> token_ptr; + +#line 2313 "reflect_impl.h2" + public: auto parse_modifier(parse_context& ctx) & -> void; + +#line 2325 "reflect_impl.h2" + public: [[nodiscard]] auto gen_mod_string() const& -> std::string; + +#line 2338 "reflect_impl.h2" + public: [[nodiscard]] auto gen_range_string() const& -> std::string; + +#line 2357 "reflect_impl.h2" + public: auto generate_code(generation_context& ctx) const -> void override; + +#line 2368 "reflect_impl.h2" + public: auto add_groups(std::set& groups) const -> void override; + public: virtual ~range_token() noexcept; + + public: range_token(range_token const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(range_token const&) -> void = delete; + + +#line 2372 "reflect_impl.h2" +}; + +#line 2375 "reflect_impl.h2" +// Regex syntax: *, +, or ? Example: aa* +// +class special_range_token +: public range_token { + +#line 2381 "reflect_impl.h2" + public: [[nodiscard]] static auto parse(parse_context& ctx) -> token_ptr; + public: virtual ~special_range_token() noexcept; + + public: special_range_token() = default; + public: special_range_token(special_range_token const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(special_range_token const&) -> void = delete; + + +#line 2411 "reflect_impl.h2" +}; + +#line 2414 "reflect_impl.h2" +// Regex syntax: \b or \B Example: \bword\b +// +// Matches the start end end of word boundaries. +// +[[nodiscard]] auto word_boundary_token_parse(parse_context& ctx) -> token_ptr; + +#line 2436 "reflect_impl.h2" +//----------------------------------------------------------------------- +// +// Parser for regular expression. +// +//----------------------------------------------------------------------- +// + +// Parser and generator for regular expressions. +template class regex_generator + { + private: std::string_view regex; + private: std::string modifier {""}; + private: std::string modifier_escape {""}; + + private: Error_out error_out; + + private: std::string source {""}; + + public: explicit regex_generator(cpp2::impl::in r, Error_out const& e); + +#line 2459 "reflect_impl.h2" + public: [[nodiscard]] auto parse() & -> std::string; + +#line 2494 "reflect_impl.h2" + private: auto extract_modifiers() & -> void; + public: regex_generator(regex_generator const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(regex_generator const&) -> void = delete; + + +#line 2508 "reflect_impl.h2" +}; + +template [[nodiscard]] auto generate_regex(cpp2::impl::in regex, Err const& err) -> std::string; + +#line 2520 "reflect_impl.h2" +auto regex_gen(meta::type_declaration& t) -> void; + +#line 2575 "reflect_impl.h2" +//----------------------------------------------------------------------- +// +// apply_metafunctions +// +[[nodiscard]] auto apply_metafunctions( + declaration_node& n, + type_declaration& rtype, + auto const& error, + auto const& lookup + ) -> bool; + +#line 2704 "reflect_impl.h2" +[[nodiscard]] auto apply_metafunctions( + declaration_node& n, + function_declaration& rfunction, + auto const& error + ) -> bool; + +#line 2745 "reflect_impl.h2" +} + +} + + +#include "cpp2reflect.hpp" + + +//=== Cpp2 function definitions ================================================= + +#line 1 "reflect_impl.h2" + +#line 33 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 38 "reflect_impl.h2" +[[nodiscard]] auto to_type_metafunction_cast( + cpp2::impl::in name, + cpp2::impl::in is_const_metafunction + ) -> std::string + +{ + auto const_ {""}; + if (is_const_metafunction) { + const_ = " const"; + } + return "static_cast(" + cpp2::to_string(name) + ")"; +} + +#line 52 "reflect_impl.h2" +//----------------------------------------------------------------------- +// +// dll_symbol +// + +#line 59 "reflect_impl.h2" + inline CPP2_CONSTEXPR std::string_view dll_symbol::constant_prefix{ "cpp2_metafunction_" }; + inline CPP2_CONSTEXPR std::string_view dll_symbol::reachable_mangle{ "r" }; + inline CPP2_CONSTEXPR std::string_view dll_symbol::const_metafunction_mangle{ "c" }; + +#line 63 "reflect_impl.h2" + dll_symbol::dll_symbol(){} +#line 64 "reflect_impl.h2" + dll_symbol::dll_symbol(cpp2::impl::in n, cpp2::impl::in is_reachable_) + : value{ constant_prefix } + +#line 68 "reflect_impl.h2" + { + cpp2::finally_presuccess cpp2_finally_presuccess; + if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(is_metafunction)(n)) ) { cpp2::cpp2_default.report_violation(""); } + cpp2_finally_presuccess.add([&]{if (cpp2::cpp2_default.is_active() && !(is_reachable() == is_reachable_) ) { cpp2::cpp2_default.report_violation(""); }} ); + cpp2_finally_presuccess.add([&]{if (cpp2::cpp2_default.is_active() && !(is_const_metafunction() == CPP2_UFCS(is_const_metafunction)(n)) ) { cpp2::cpp2_default.report_violation(""); }} ); + +#line 70 "reflect_impl.h2" + if (is_reachable_) { + value += reachable_mangle; + } + if (CPP2_UFCS(is_const_metafunction)(n)) { + value += const_metafunction_mangle; + } + value += mangle(n); + } +#line 78 "reflect_impl.h2" + dll_symbol::dll_symbol(std::string_view s) + { + if (!(CPP2_UFCS(starts_with)(s, constant_prefix))) { + value += constant_prefix; + } + value += cpp2::move(s); + } +#line 78 "reflect_impl.h2" + auto dll_symbol::operator=(std::string_view s) -> dll_symbol& +#line 79 "reflect_impl.h2" + { + value = {}; +#line 80 "reflect_impl.h2" + if (!(CPP2_UFCS(starts_with)(s, constant_prefix))) { + value += constant_prefix; + } + value += cpp2::move(s); + return *this; +#line 84 "reflect_impl.h2" + } + +#line 86 "reflect_impl.h2" + [[nodiscard]] auto dll_symbol::c_str() const& -> char const* { return CPP2_UFCS(c_str)(value); } +#line 87 "reflect_impl.h2" + [[nodiscard]] auto dll_symbol::view() const& -> std::string_view { return value; } +#line 88 "reflect_impl.h2" + [[nodiscard]] auto dll_symbol::without_prefix() const& -> std::string_view + + { + auto res {view()}; + CPP2_UFCS(remove_prefix)(res, CPP2_UFCS(size)(constant_prefix)); + for ( + auto const& m : { reachable_mangle, const_metafunction_mangle } ) + if (CPP2_UFCS(starts_with)(res, m)) { + CPP2_UFCS(remove_prefix)(res, CPP2_UFCS(size)(m)); + } + return res; + } + +#line 101 "reflect_impl.h2" + [[nodiscard]] auto dll_symbol::is_reachable() const& -> bool { return CPP2_UFCS(starts_with)(CPP2_UFCS(substr)(view(), CPP2_UFCS(size)(constant_prefix)), reachable_mangle); } +#line 102 "reflect_impl.h2" + [[nodiscard]] auto dll_symbol::is_const_metafunction() const& -> bool { + return CPP2_UFCS(starts_with)(CPP2_UFCS(substr)(CPP2_UFCS(substr)(view(), CPP2_UFCS(size)(constant_prefix)) + , unsigned(is_reachable()) * CPP2_UFCS(size)(reachable_mangle)) + , const_metafunction_mangle); } + + + dll_symbol::dll_symbol(dll_symbol const& that) + : value{ that.value }{} + +auto dll_symbol::operator=(dll_symbol const& that) -> dll_symbol& { + value = that.value; + return *this;} +dll_symbol::dll_symbol(dll_symbol&& that) noexcept + : value{ std::move(that).value }{} +auto dll_symbol::operator=(dll_symbol&& that) noexcept -> dll_symbol& { + value = std::move(that).value; + return *this;} +#line 108 "reflect_impl.h2" +[[nodiscard]] auto symbols_accessor(cpp2::impl::in lib_path) -> dll_symbol { + + return dll_symbol("get_symbol_names_" + cpp2::to_string(to_lower_and_collapsed_underbar(lib_path, true, true)) + ""); } + +namespace this_execution + { + +// The environment variable 'CPPFRONT_METAFUNCTION_LIBRARY' +// is read and interpreted as the Cpp2 metafunction library path of this execution +#line 117 "reflect_impl.h2" +[[nodiscard]] auto symbols_accessor() -> dll_symbol + +{ + auto constexpr env_var{ "CPPFRONT_METAFUNCTION_LIBRARY" }; + auto lib_path {std::getenv(env_var)}; + if (cpp2::cpp2_default.is_active() && !(lib_path && CPP2_ASSERT_IN_BOUNDS_LITERAL(lib_path, 0) != '\0') ) { cpp2::cpp2_default.report_violation(CPP2_CONTRACT_MSG("" + cpp2::to_string(env_var) + " should be set for a Cpp2 source with metafunctions")); } + // FIXME Doesn't work for a library with more than one source providing a metafunction + // See https://github.com/hsutter/cppfront/pull/907#issuecomment-1872644205 + return meta::symbols_accessor(cpp2::move(lib_path)); +} + +} + + +diagnostic::diagnostic(auto&& value_) +requires (std::is_convertible_v&>) + : value{ CPP2_FORWARD(value_) }{} +auto diagnostic::operator=(auto&& value_) -> diagnostic& +requires (std::is_convertible_v&>) { + value = CPP2_FORWARD(value_); + return *this;} +#line 131 "reflect_impl.h2" +//----------------------------------------------------------------------- +// +// expected with diagnostic to return to apply_metafunctions +// + +#line 143 "reflect_impl.h2" + template expected::expected(T const& v) { set_value(v); } +#line 143 "reflect_impl.h2" + template auto expected::operator=(T const& v) -> expected& { + _storage = {}; + _discriminator = -1; set_value(v); + return *this; } +#line 144 "reflect_impl.h2" + template expected::expected(cpp2::impl::in u) { set_error(u); } +#line 144 "reflect_impl.h2" + template auto expected::operator=(cpp2::impl::in u) -> expected& { + _storage = {}; + _discriminator = -1; set_error(u); + return *this; } + +#line 146 "reflect_impl.h2" + template template [[nodiscard]] auto expected::and_then(F const& f) && -> std::remove_cvref_t>{ + if (is_value()) { + return f(cpp2::move(*this).value()); + } + else { + return { cpp2::move(*this).error() }; + } + } + + + + template [[nodiscard]] auto expected::is_value() const& -> bool { return _discriminator == 0; } +template [[nodiscard]] auto expected::value() const& -> T const& { + if (cpp2::cpp2_default.is_active() && !(is_value()) ) { cpp2::cpp2_default.report_violation(""); }return *cpp2::impl::assert_not_null(reinterpret_cast(&_storage)); } +template [[nodiscard]] auto expected::value() & -> T& { + if (cpp2::cpp2_default.is_active() && !(is_value()) ) { cpp2::cpp2_default.report_violation(""); }return *cpp2::impl::assert_not_null(reinterpret_cast(&_storage)); } +template auto expected::set_value(cpp2::impl::in _value) & -> void{if (!(is_value())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _value);}else {*cpp2::impl::assert_not_null(reinterpret_cast(&_storage)) = _value;}_discriminator = 0;} +template auto expected::set_value(auto&& ..._args) & -> void{if (!(is_value())) {_destroy();std::construct_at(reinterpret_cast(&_storage), CPP2_FORWARD(_args)...);}else {*cpp2::impl::assert_not_null(reinterpret_cast(&_storage)) = T{CPP2_FORWARD(_args)...};}_discriminator = 0;} +template [[nodiscard]] auto expected::is_error() const& -> bool { return _discriminator == 1; } +template [[nodiscard]] auto expected::error() const& -> diagnostic const& { + if (cpp2::cpp2_default.is_active() && !(is_error()) ) { cpp2::cpp2_default.report_violation(""); }return *cpp2::impl::assert_not_null(reinterpret_cast(&_storage)); } +template [[nodiscard]] auto expected::error() & -> diagnostic& { + if (cpp2::cpp2_default.is_active() && !(is_error()) ) { cpp2::cpp2_default.report_violation(""); }return *cpp2::impl::assert_not_null(reinterpret_cast(&_storage)); } +template auto expected::set_error(cpp2::impl::in _value) & -> void{if (!(is_error())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _value);}else {*cpp2::impl::assert_not_null(reinterpret_cast(&_storage)) = _value;}_discriminator = 1;} +template auto expected::set_error(auto&& ..._args) & -> void{if (!(is_error())) {_destroy();std::construct_at(reinterpret_cast(&_storage), CPP2_FORWARD(_args)...);}else {*cpp2::impl::assert_not_null(reinterpret_cast(&_storage)) = diagnostic{CPP2_FORWARD(_args)...};}_discriminator = 1;} +template auto expected::_destroy() & -> void{ + if (_discriminator == 0) {std::destroy_at(reinterpret_cast(&_storage));} + if (_discriminator == 1) {std::destroy_at(reinterpret_cast(&_storage));} + _discriminator = -1; + } + + template expected::~expected() noexcept{_destroy();static_cast(cpp2::move((*this)));} +template expected::expected(){} +template expected::expected(expected const& that) + : _storage{ } + , _discriminator{ -1 }{ + if (CPP2_UFCS(is_value)(that)) {set_value(CPP2_UFCS(value)(that));} + if (CPP2_UFCS(is_error)(that)) {set_error(CPP2_UFCS(error)(that));} + } + + + template expected::expected(expected&& that) noexcept + : _storage{ } + , _discriminator{ -1 }{ + if (CPP2_UFCS(is_value)(cpp2::move(that))) {set_value(CPP2_UFCS(value)(cpp2::move(that)));} + if (CPP2_UFCS(is_error)(cpp2::move(that))) {set_error(CPP2_UFCS(error)(cpp2::move(that)));} + } + + template auto expected::operator=(expected const& that) -> expected& { + if (CPP2_UFCS(is_value)(that)) {set_value(CPP2_UFCS(value)(that));} + if (CPP2_UFCS(is_error)(that)) {set_error(CPP2_UFCS(error)(that));} + return *this; + } + + template auto expected::operator=(expected&& that) noexcept -> expected& { + if (CPP2_UFCS(is_value)(cpp2::move(that))) {set_value(CPP2_UFCS(value)(cpp2::move(that)));} + if (CPP2_UFCS(is_error)(cpp2::move(that))) {set_error(CPP2_UFCS(error)(cpp2::move(that)));} + return *this; + } +#line 156 "reflect_impl.h2" +} + +} + +#line 412 "reflect_impl.h2" +namespace cpp2 { + +namespace meta { + +#line 417 "reflect_impl.h2" +//----------------------------------------------------------------------- +// +// Compiler services data +// +//----------------------------------------------------------------------- +// + +#line 426 "reflect_impl.h2" + // Common data members + // + +#line 437 "reflect_impl.h2" + // Make function + // +#line 439 "reflect_impl.h2" + [[nodiscard]] auto compiler_services_data::make( + std::vector* errors_, + std::set* includes_, + stable_vector* generated_tokens_, + cpp2::impl::in translation_unit_has_interface + ) -> compiler_services_data + + { + return { errors_, + includes_, + cpp2::unchecked_narrow(std::ssize(*cpp2::impl::assert_not_null(errors_))), + generated_tokens_, + cpp2::parser(*cpp2::impl::assert_not_null(errors_), *cpp2::impl::assert_not_null(includes_), translation_unit_has_interface) }; + } + + + constexpr expression_flags::expression_flags(cpp2::impl::in _val) + : _value{ cpp2::unchecked_narrow(_val) } { } + +constexpr auto expression_flags::operator=(cpp2::impl::in _val) -> expression_flags& { + _value = cpp2::unchecked_narrow(_val); + return *this; } +constexpr auto expression_flags::operator|=(expression_flags const& that) & -> decltype(auto) { return _value |= that._value; } +constexpr auto expression_flags::operator&=(expression_flags const& that) & -> decltype(auto) { return _value &= that._value; } +constexpr auto expression_flags::operator^=(expression_flags const& that) & -> decltype(auto) { return _value ^= that._value; } +[[nodiscard]] constexpr auto expression_flags::operator|(expression_flags const& that) const& -> expression_flags { return _value | that._value; } +[[nodiscard]] constexpr auto expression_flags::operator&(expression_flags const& that) const& -> expression_flags { return _value & that._value; } +[[nodiscard]] constexpr auto expression_flags::operator^(expression_flags const& that) const& -> expression_flags { return _value ^ that._value; } +[[nodiscard]] constexpr auto expression_flags::has(expression_flags const& that) const& -> bool { return _value & that._value; } +constexpr auto expression_flags::set(expression_flags const& that) & -> void{_value |= that._value;} +constexpr auto expression_flags::clear(expression_flags const& that) & -> void{_value &= ~that._value;} +inline CPP2_CONSTEXPR expression_flags expression_flags::case_insensitive{ 1 }; + +inline CPP2_CONSTEXPR expression_flags expression_flags::multiple_lines{ 2 }; + +inline CPP2_CONSTEXPR expression_flags expression_flags::single_line{ 4 }; + +inline CPP2_CONSTEXPR expression_flags expression_flags::no_group_captures{ 8 }; + +inline CPP2_CONSTEXPR expression_flags expression_flags::perl_code_syntax{ 16 }; + +inline CPP2_CONSTEXPR expression_flags expression_flags::perl_code_syntax_in_classes{ 32 }; + +inline CPP2_CONSTEXPR expression_flags expression_flags::none{ 0 }; + +[[nodiscard]] constexpr auto expression_flags::get_raw_value() const& -> cpp2::u8 { return _value; } +constexpr expression_flags::expression_flags() + : _value{ none._value }{} +constexpr expression_flags::expression_flags(expression_flags const& that) + : _value{ that._value }{} +constexpr auto expression_flags::operator=(expression_flags const& that) -> expression_flags& { + _value = that._value; + return *this;} +constexpr expression_flags::expression_flags(expression_flags&& that) noexcept + : _value{ std::move(that)._value }{} +constexpr auto expression_flags::operator=(expression_flags&& that) noexcept -> expression_flags& { + _value = std::move(that)._value; + return *this;} +[[nodiscard]] auto expression_flags::to_string_impl(cpp2::impl::in prefix, cpp2::impl::in separator) const& -> std::string{ + + std::string ret {"("}; + + std::string sep {}; + if ((*this) == none) {return "(none)"; } + + auto pref {cpp2::to_string(prefix)}; + if (((*this) & case_insensitive) == case_insensitive) {ret += sep + pref + "case_insensitive";sep = separator;} + if (((*this) & multiple_lines) == multiple_lines) {ret += sep + pref + "multiple_lines";sep = separator;} + if (((*this) & single_line) == single_line) {ret += sep + pref + "single_line";sep = separator;} + if (((*this) & no_group_captures) == no_group_captures) {ret += sep + pref + "no_group_captures";sep = separator;} + if (((*this) & perl_code_syntax) == perl_code_syntax) {ret += sep + pref + "perl_code_syntax";sep = separator;} + if (((*this) & perl_code_syntax_in_classes) == perl_code_syntax_in_classes) {ret += sep + cpp2::move(pref) + "perl_code_syntax_in_classes";sep = separator;} + return cpp2::move(ret) + ")"; + } + + [[nodiscard]] auto expression_flags::to_string() const& -> std::string { return to_string_impl("", ", "); } +[[nodiscard]] auto expression_flags::to_code() const& -> std::string { return to_string_impl("expression_flags::", " | "); } +[[nodiscard]] auto expression_flags::from_string(cpp2::impl::in s) -> expression_flags{ + + auto ret {none}; + do {{ + for ( auto const& x : cpp2::string_util::split_string_list(s) ) { + if ("case_insensitive" == x) {ret |= case_insensitive;} + else {if ("multiple_lines" == x) {ret |= multiple_lines;} + else {if ("single_line" == x) {ret |= single_line;} + else {if ("no_group_captures" == x) {ret |= no_group_captures;} + else {if ("perl_code_syntax" == x) {ret |= perl_code_syntax;} + else {if ("perl_code_syntax_in_classes" == x) {ret |= perl_code_syntax_in_classes;} + else {if ("none" == x) {ret |= none;} + else {goto BREAK_outer;} +#line 1 "reflect_impl.h2" +}}}}}} +} + +return ret; +} CPP2_CONTINUE_BREAK(outer) } + while ( +false +); +CPP2_UFCS(report_violation)(cpp2::type_safety, CPP2_UFCS(c_str)(("can't convert string '" + cpp2::to_string(s) + "' to flag_enum of type expression_flags"))); +return none; +} + +[[nodiscard]] auto expression_flags::from_code(cpp2::impl::in s) -> expression_flags{ +std::string str {s}; return from_string(cpp2::string_util::replace_all(cpp2::move(str), "expression_flags::", "")); } + +#line 456 "reflect_impl.h2" +//----------------------------------------------------------------------- +// +// regex - creates regular expressions from members +// +// Each member that starts with `regex` is replaced by a regular expression +// of the initializer string. E.g.: +// ``` +// regex := "ab"; +// ``` +// is replaced with +// ``` +// regex := ::cpp2::regex::regular_expression<...>; +// ``` +// + +#line 472 "reflect_impl.h2" +// Possible modifiers for a regular expression. +// + +#line 476 "reflect_impl.h2" + // mod: i + // mod: m + // mod: s + // mod: n + // mod: x + // mod: xx + +#line 485 "reflect_impl.h2" +// Tokens for regular expressions. +// + +// Basic class for a regex token. +// + +#line 494 "reflect_impl.h2" + regex_token::regex_token(cpp2::impl::in str) + : string_rep{ str }{ + +#line 496 "reflect_impl.h2" + } + +#line 498 "reflect_impl.h2" + regex_token::regex_token() + : string_rep{ "" }{ + +#line 500 "reflect_impl.h2" + } + + //parse: (inout ctx: parse_context) -> token_ptr; + // Generate the matching code. + +#line 505 "reflect_impl.h2" + auto regex_token::add_groups([[maybe_unused]] std::set& unnamed_param_2) const -> void{}// Adds all group indices to the set. +#line 506 "reflect_impl.h2" + [[nodiscard]] auto regex_token::to_string() const& -> std::string{return string_rep; }// Create a string representation. +#line 507 "reflect_impl.h2" + auto regex_token::set_string(cpp2::impl::in s) & -> void{string_rep = s; } + + regex_token::~regex_token() noexcept{}// Set the string representation. + +#line 522 "reflect_impl.h2" + regex_token_check::regex_token_check(cpp2::impl::in str, cpp2::impl::in check_) + : regex_token{ str } + , check{ check_ }{ + +#line 525 "reflect_impl.h2" + } + +#line 527 "reflect_impl.h2" + auto regex_token_check::generate_code(generation_context& ctx) const -> void{ + ctx.add_check(check + "(" + ctx.match_parameters() + ")"); + } + + regex_token_check::~regex_token_check() noexcept{} + +#line 541 "reflect_impl.h2" + regex_token_code::regex_token_code(cpp2::impl::in str, cpp2::impl::in code_) + : regex_token{ str } + , code{ code_ }{ + +#line 544 "reflect_impl.h2" + } + +#line 546 "reflect_impl.h2" + auto regex_token_code::generate_code(generation_context& ctx) const -> void{ + ctx.add(code); + } + + regex_token_code::~regex_token_code() noexcept{} + +#line 558 "reflect_impl.h2" + regex_token_empty::regex_token_empty(cpp2::impl::in str) + : regex_token{ str }{ + +#line 560 "reflect_impl.h2" + } + +#line 562 "reflect_impl.h2" + auto regex_token_empty::generate_code([[maybe_unused]] generation_context& unnamed_param_2) const -> void{ + // Nothing. + } + + regex_token_empty::~regex_token_empty() noexcept{} + +#line 576 "reflect_impl.h2" + regex_token_list::regex_token_list(cpp2::impl::in t) + : regex_token{ gen_string(t) } + , tokens{ t }{ + +#line 579 "reflect_impl.h2" + } + +#line 581 "reflect_impl.h2" + auto regex_token_list::generate_code(generation_context& ctx) const -> void{ + for ( auto const& token : tokens ) { + (*cpp2::impl::assert_not_null(token)).generate_code(ctx); + } + } + +#line 587 "reflect_impl.h2" + auto regex_token_list::add_groups(std::set& groups) const -> void{ + for ( auto const& token : tokens ) { + (*cpp2::impl::assert_not_null(token)).add_groups(groups); + } + } + +#line 593 "reflect_impl.h2" + [[nodiscard]] auto regex_token_list::gen_string(cpp2::impl::in vec) -> std::string{ + std::string r {""}; + for ( auto const& token : vec ) { + r += (*cpp2::impl::assert_not_null(token)).to_string(); + } + return r; + } + + regex_token_list::~regex_token_list() noexcept{} + +#line 616 "reflect_impl.h2" + auto parse_context_group_state::next_alternative() & -> void{ + token_vec new_list {}; + std::swap(new_list, cur_match_list); + post_process_list(new_list); + static_cast(alternate_match_lists.insert(alternate_match_lists.end(), CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, cpp2::move(new_list)))); + } + +#line 624 "reflect_impl.h2" + auto parse_context_group_state::swap(parse_context_group_state& t) & -> void{// NOLINT(performance-noexcept-swap) + std::swap(cur_match_list, t.cur_match_list); + std::swap(alternate_match_lists, t.alternate_match_lists); + std::swap(modifiers, t.modifiers); + } + +#line 631 "reflect_impl.h2" + [[nodiscard]] auto parse_context_group_state::get_as_token() & -> token_ptr{ + if (alternate_match_lists.empty()) { + post_process_list(cur_match_list); + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, cur_match_list); + } + else { + next_alternative(); + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, alternate_match_lists); + } + } + +#line 643 "reflect_impl.h2" + auto parse_context_group_state::add(cpp2::impl::in token) & -> void{ + cur_match_list.push_back(token); + } + +#line 648 "reflect_impl.h2" + [[nodiscard]] auto parse_context_group_state::empty() const& -> bool { return cur_match_list.empty(); } + +#line 652 "reflect_impl.h2" + auto parse_context_group_state::post_process_list(token_vec& list) -> void{ + // Merge all characters + auto merge_pos {list.begin()}; + for( ; merge_pos != list.end(); (++merge_pos) ) { + if (cpp2::impl::is(*cpp2::impl::assert_not_null(*cpp2::impl::assert_not_null(merge_pos)))) { + auto combine_pos {merge_pos + 1}; + while( combine_pos != list.end() && cpp2::impl::is(*cpp2::impl::assert_not_null(*cpp2::impl::assert_not_null(combine_pos))) ) {// The erase advances combine_pos + (cpp2::impl::as_(*cpp2::impl::assert_not_null(*cpp2::impl::assert_not_null(merge_pos)))).append(cpp2::impl::as_(*cpp2::impl::assert_not_null(*cpp2::impl::assert_not_null(combine_pos)))); + combine_pos = list.erase(combine_pos); + } + } + } + } + + parse_context_group_state::parse_context_group_state(auto const& cur_match_list_, auto const& alternate_match_lists_, auto const& modifiers_) + : cur_match_list{ cur_match_list_ } + , alternate_match_lists{ alternate_match_lists_ } + , modifiers{ modifiers_ }{} +parse_context_group_state::parse_context_group_state(){} + +#line 678 "reflect_impl.h2" + [[nodiscard]] auto parse_context_branch_reset_state::next() & -> int{ + auto g {cur_group}; + cur_group += 1; + max_group = max(max_group, cur_group); + + return g; + } + +#line 687 "reflect_impl.h2" + auto parse_context_branch_reset_state::set_next(cpp2::impl::in g) & -> void{ + cur_group = g; + max_group = max(max_group, g); + } + +#line 693 "reflect_impl.h2" + auto parse_context_branch_reset_state::next_alternative() & -> void{ + if (is_active) { + cur_group = from; + } + } + +#line 700 "reflect_impl.h2" + auto parse_context_branch_reset_state::set_active_reset(cpp2::impl::in restart) & -> void{ + is_active = true; + cur_group = restart; + from = restart; + max_group = restart; + } + + parse_context_branch_reset_state::parse_context_branch_reset_state(auto const& is_active_, auto const& cur_group_, auto const& max_group_, auto const& from_) + : is_active{ is_active_ } + , cur_group{ cur_group_ } + , max_group{ max_group_ } + , from{ from_ }{} +parse_context_branch_reset_state::parse_context_branch_reset_state(){} + +#line 728 "reflect_impl.h2" + parse_context::parse_context(cpp2::impl::in r, auto const& e) + : regex{ r } + , root{ CPP2_UFCS_TEMPLATE_NONLOCAL(cpp2_new)(cpp2::shared, "") } + , error_out{ e }{ + +#line 732 "reflect_impl.h2" + } + +#line 738 "reflect_impl.h2" + [[nodiscard]] auto parse_context::start_group() & -> parse_context_group_state + { + parse_context_group_state old_state {}; + old_state.swap(cur_group_state); + cur_group_state.modifiers = old_state.modifiers; + + return old_state; + } + +#line 748 "reflect_impl.h2" + [[nodiscard]] auto parse_context::end_group(cpp2::impl::in old_state) & -> token_ptr + { + auto inner {cur_group_state.get_as_token()}; + cur_group_state = old_state; + return inner; + } + +#line 755 "reflect_impl.h2" + [[nodiscard]] auto parse_context::get_modifiers() const& -> expression_flags{ + return cur_group_state.modifiers; + } + +#line 759 "reflect_impl.h2" + auto parse_context::set_modifiers(cpp2::impl::in mod) & -> void{ + cur_group_state.modifiers = mod; + } + +#line 766 "reflect_impl.h2" + [[nodiscard]] auto parse_context::branch_reset_new_state() & -> parse_context_branch_reset_state + { + parse_context_branch_reset_state old_state {}; + std::swap(old_state, cur_branch_reset_state); + + cur_branch_reset_state.set_active_reset(old_state.cur_group); + return old_state; + } + +#line 775 "reflect_impl.h2" + auto parse_context::branch_reset_restore_state(cpp2::impl::in old_state) & -> void + { + auto max_group {cur_branch_reset_state.max_group}; + cur_branch_reset_state = old_state; + cur_branch_reset_state.set_next(cpp2::move(max_group)); + } + +#line 782 "reflect_impl.h2" + auto parse_context::next_alternative() & -> void + { + cur_group_state.next_alternative(); + cur_branch_reset_state.next_alternative(); + } + +#line 790 "reflect_impl.h2" + auto parse_context::add_token(cpp2::impl::in token) & -> void{ + cur_group_state.add(token); + } + +#line 794 "reflect_impl.h2" + [[nodiscard]] auto parse_context::has_token() const& -> bool{ + return !(cur_group_state.empty()); + } + +#line 798 "reflect_impl.h2" + [[nodiscard]] auto parse_context::pop_token() & -> token_ptr + { + token_ptr r {nullptr}; + if (has_token()) { + r = cur_group_state.cur_match_list.back(); + cur_group_state.cur_match_list.pop_back(); + } + + return r; + } + +#line 809 "reflect_impl.h2" + [[nodiscard]] auto parse_context::get_as_token() & -> token_ptr{ + return root; + } + +#line 815 "reflect_impl.h2" + [[nodiscard]] auto parse_context::get_cur_group() const& -> int{ + return cur_branch_reset_state.cur_group; + } + +#line 819 "reflect_impl.h2" + [[nodiscard]] auto parse_context::next_group() & -> int{ + return cur_branch_reset_state.next(); + } + +#line 823 "reflect_impl.h2" + auto parse_context::set_named_group(cpp2::impl::in name, cpp2::impl::in id) & -> void + { + if (!(named_groups.contains(name))) {// Redefinition of group name is not an error. The left most one is retained. + CPP2_ASSERT_IN_BOUNDS(named_groups, name) = id; + } + } + +#line 830 "reflect_impl.h2" + [[nodiscard]] auto parse_context::get_named_group(cpp2::impl::in name) const& -> int + { + auto iter {named_groups.find(name)}; + if (iter == named_groups.end()) { + return -1; + } + else { + return (*cpp2::impl::assert_not_null(cpp2::move(iter))).second; + } + } + +#line 843 "reflect_impl.h2" + [[nodiscard]] auto parse_context::current() const& -> char{return CPP2_ASSERT_IN_BOUNDS(regex, pos); } + +#line 846 "reflect_impl.h2" + [[nodiscard]] auto parse_context::get_next_position(cpp2::impl::in in_class, cpp2::impl::in no_skip) const& -> size_t + { + auto perl_syntax {false}; + if (!(no_skip)) { + if (in_class) { + perl_syntax = get_modifiers().has(expression_flags::perl_code_syntax) && get_modifiers().has(expression_flags::perl_code_syntax_in_classes); + } + else { + perl_syntax = get_modifiers().has(expression_flags::perl_code_syntax); + } + } + auto cur {pos + 1}; + if (cpp2::move(perl_syntax)) { + for( ; cpp2::impl::cmp_less(cur,regex.size()); (cur += 1) ) { + auto n {CPP2_ASSERT_IN_BOUNDS(regex, cur)}; + + if (space_class::includes(n)) { + continue; + } + else {if (!(in_class) && '#' == cpp2::move(n)) { + cur = regex.find('\n', cur); + if (std::string::npos == cur) { + // No new line, comment runs until the end of the pattern + cur = regex.size(); + } + } + else { // None space none comment char + break; + }} + } + } + + // Check for end of file. + if (cpp2::impl::cmp_greater(cur,regex.size())) { + cur = regex.size(); + } + return cur; + } + +#line 886 "reflect_impl.h2" + [[nodiscard]] auto parse_context::next_impl(cpp2::impl::in in_class, cpp2::impl::in no_skip) & -> bool + { + pos = get_next_position(in_class, no_skip); + if (pos != regex.size()) { + return true; + } + else { + return false; + } + } + +#line 897 "reflect_impl.h2" + [[nodiscard]] auto parse_context::next() & -> decltype(auto) { return next_impl(false, false); } +#line 898 "reflect_impl.h2" + [[nodiscard]] auto parse_context::next_in_class() & -> decltype(auto) { return next_impl(true, false); } +#line 899 "reflect_impl.h2" + [[nodiscard]] auto parse_context::next_no_skip() & -> decltype(auto) { return next_impl(false, true); } + +#line 901 "reflect_impl.h2" + [[nodiscard]] auto parse_context::next_n(cpp2::impl::in n) & -> bool{ + auto r {true}; + auto cur {0}; + for( ; r && cpp2::impl::cmp_less(cur,n); (r = next()) ) { + cur += 1; + } + return r; + } + +#line 910 "reflect_impl.h2" + [[nodiscard]] auto parse_context::has_next() const& -> bool{return cpp2::impl::cmp_less(pos,regex.size()); } + +#line 912 "reflect_impl.h2" + [[nodiscard]] auto parse_context::grab_until_impl(cpp2::impl::in e, cpp2::impl::out r, cpp2::impl::in any) & -> bool + { + auto end {pos}; // NOLINT(clang-analyzer-deadcode.DeadStores) + if (any) { + end = regex.find_first_of(e, pos); + } + else { + end = regex.find(e, pos); + } + + if (end != std::string_view::npos) { + r.construct(regex.substr(pos, end - pos)); + pos = cpp2::move(end); + return true; + } + else { + r.construct(""); + return false; + } + } + +#line 933 "reflect_impl.h2" + [[nodiscard]] auto parse_context::grab_until(cpp2::impl::in e, cpp2::impl::out r) & -> decltype(auto) { return grab_until_impl(e, cpp2::impl::out(&r), false); } +#line 934 "reflect_impl.h2" + [[nodiscard]] auto parse_context::grab_until(cpp2::impl::in e, cpp2::impl::out r) & -> decltype(auto) { return grab_until_impl(std::string(1, e), cpp2::impl::out(&r), false); } +#line 935 "reflect_impl.h2" + [[nodiscard]] auto parse_context::grab_until_one_of(cpp2::impl::in e, cpp2::impl::out r) & -> decltype(auto) { return grab_until_impl(e, cpp2::impl::out(&r), true); } + +#line 937 "reflect_impl.h2" + [[nodiscard]] auto parse_context::grab_n(cpp2::impl::in n, cpp2::impl::out r) & -> bool + { + if (cpp2::impl::cmp_less_eq(pos + cpp2::impl::as_(n),regex.size())) { + r.construct(regex.substr(pos, cpp2::impl::as_(n))); + pos += (cpp2::impl::as_(n)) - 1; + return true; + } + else { + r.construct(""); + return false; + } + } + +#line 950 "reflect_impl.h2" + [[nodiscard]] auto parse_context::grab_number() & -> std::string + { + auto start {pos}; + auto start_search {pos}; + if (CPP2_ASSERT_IN_BOUNDS(regex, start_search) == '-') { + start_search += 1; + } + auto end {regex.find_first_not_of("1234567890", cpp2::move(start_search))}; + + cpp2::impl::deferred_init r; + if (end != std::string::npos) { + r.construct(regex.substr(start, end - start)); + pos = cpp2::move(end) - 1; + } + else { + r.construct(regex.substr(cpp2::move(start))); + pos = regex.size() - 1; + } + return cpp2::move(r.value()); + } + +#line 971 "reflect_impl.h2" + [[nodiscard]] auto parse_context::peek_impl(cpp2::impl::in in_class) const& -> char{ + auto next_pos {get_next_position(in_class, false)}; + if (cpp2::impl::cmp_less(next_pos,regex.size())) { + return CPP2_ASSERT_IN_BOUNDS(regex, cpp2::move(next_pos)); + } + else { + return '\0'; + } + } + +#line 981 "reflect_impl.h2" + [[nodiscard]] auto parse_context::peek() const& -> decltype(auto) { return peek_impl(false); } +#line 982 "reflect_impl.h2" + [[nodiscard]] auto parse_context::peek_in_class() const& -> decltype(auto) { return peek_impl(true); } + +#line 987 "reflect_impl.h2" + [[nodiscard]] auto parse_context::parser_group_modifiers(cpp2::impl::in change_str, expression_flags& parser_modifiers) & -> bool + { + auto is_negative {false}; + auto is_reset {false}; + + auto apply {[&, _1 = (&is_negative), _2 = (&parser_modifiers)](cpp2::impl::in flag) mutable -> void{ + if (*cpp2::impl::assert_not_null(_1)) { + (*cpp2::impl::assert_not_null(_2)).clear(flag); + } + else { + (*cpp2::impl::assert_not_null(_2)).set(flag); + } + }}; + + auto iter {change_str.begin()}; + for( ; iter != change_str.end(); (++iter) ) + { + auto cur {*cpp2::impl::assert_not_null(iter)}; + if (cur == '^') { + is_reset = true; + parser_modifiers = expression_flags::none; + } + else {if (cur == '-') { + if (is_reset) {static_cast(error("No negative modifier allowed.")); return false; } + is_negative = true; + } + else {if (cur == 'i') {apply(expression_flags::case_insensitive); } + else {if (cur == 'm') {apply(expression_flags::multiple_lines); } + else {if (cur == 's') {apply(expression_flags::single_line); } + else {if (cur == 'n') {apply(expression_flags::no_group_captures); } + else {if (cur == 'x') { + if ((iter + 1) == change_str.end() || *cpp2::impl::assert_not_null((iter + 1)) != 'x') { + // x modifier + apply(expression_flags::perl_code_syntax); + + // Just x unsets xx and remove x also removes xx + parser_modifiers.clear(expression_flags::perl_code_syntax_in_classes); + } + else { // xx modifier + // xx also sets or unsets x + apply(expression_flags::perl_code_syntax); + apply(expression_flags::perl_code_syntax_in_classes); + + ++iter; // Skip the second x + } + } + else { + static_cast(error("Unknown modifier: " + cpp2::to_string(cpp2::move(cur)) + "")); return false; + }}}}}}} + } + + return true; + } + +#line 1041 "reflect_impl.h2" + [[nodiscard]] auto parse_context::parse_until(cpp2::impl::in term) & -> bool{ + token_ptr cur_token {}; + + for( ; valid(); static_cast(next()) ) + { + if (term == current()) {break; } + + cur_token = nullptr; + + if (!(cur_token) && valid()) {cur_token = alternative_token::parse((*this)); } + if (!(cur_token) && valid()) {cur_token = any_token::parse((*this)); } + if (!(cur_token) && valid()) {cur_token = class_token::parse((*this)); } + if (!(cur_token) && valid()) {cur_token = escape_token_parse((*this)); } + if (!(cur_token) && valid()) {cur_token = global_group_reset_token_parse((*this)); } + if (!(cur_token) && valid()) {cur_token = group_ref_token::parse((*this)); } + if (!(cur_token) && valid()) {cur_token = group_token::parse((*this)); } + if (!(cur_token) && valid()) {cur_token = hexadecimal_token_parse((*this)); } + if (!(cur_token) && valid()) {cur_token = line_end_token_parse((*this)); } + if (!(cur_token) && valid()) {cur_token = line_start_token_parse((*this)); } + if (!(cur_token) && valid()) {cur_token = named_class_token_parse((*this)); } + if (!(cur_token) && valid()) {cur_token = octal_token_parse((*this)); } + if (!(cur_token) && valid()) {cur_token = range_token::parse((*this)); } + if (!(cur_token) && valid()) {cur_token = special_range_token::parse((*this)); } + if (!(cur_token) && valid()) {cur_token = word_boundary_token_parse((*this)); } + + // Everything else is matched as it is. + if (!(cur_token) && valid()) {cur_token = char_token::parse((*this)); } + + if (cur_token && valid()) { + add_token(cur_token); + }else { + return false; + } + } + + return true; + } + +#line 1079 "reflect_impl.h2" + [[nodiscard]] auto parse_context::parse(cpp2::impl::in modifiers) & -> bool + { + + expression_flags flags {}; + if (!(parser_group_modifiers(modifiers, flags))) {return false; } + set_modifiers(cpp2::move(flags)); + + auto r {parse_until('\0')}; + if (r) { + root = cur_group_state.get_as_token(); + } + + return r; + } + +#line 1096 "reflect_impl.h2" + [[nodiscard]] auto parse_context::get_pos() const& -> decltype(auto) { return pos; } +#line 1097 "reflect_impl.h2" + [[nodiscard]] auto parse_context::get_range(cpp2::impl::in start, cpp2::impl::in end) const& -> decltype(auto) { return std::string(regex.substr(start, end - start + 1)); } +#line 1098 "reflect_impl.h2" + [[nodiscard]] auto parse_context::valid() const& -> bool{return has_next() && !(has_error); } + +#line 1100 "reflect_impl.h2" + [[nodiscard]] auto parse_context::error(cpp2::impl::in err) & -> token_ptr{ + has_error = true; + error_out("Error during parsing of regex '" + cpp2::to_string(regex) + "' at position '" + cpp2::to_string(pos) + "': " + cpp2::to_string(err) + ""); + return nullptr; + } + +#line 1115 "reflect_impl.h2" + auto generation_function_context::add_tabs(cpp2::impl::in c) & -> void{ + int i {0}; + for( ; cpp2::impl::cmp_less(i,c); i += 1 ) { + tabs += " "; + } + } + +#line 1122 "reflect_impl.h2" + auto generation_function_context::remove_tabs(cpp2::impl::in c) & -> void{ + tabs = tabs.substr(0, (cpp2::impl::as_(c)) * 2); + } + + generation_function_context::generation_function_context(auto const& code_, auto const& tabs_) + : code{ code_ } + , tabs{ tabs_ }{} +generation_function_context::generation_function_context(){} + +#line 1140 "reflect_impl.h2" + [[nodiscard]] auto generation_context::match_parameters() const& -> std::string{return "r.pos, ctx"; } + +#line 1145 "reflect_impl.h2" + auto generation_context::add(cpp2::impl::in s) & -> void{ + auto cur {get_current()}; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + cpp2::to_string(s) + "\n"; + } + +#line 1151 "reflect_impl.h2" + auto generation_context::add_check(cpp2::impl::in check) & -> void{ + auto cur {get_current()}; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + "if !cpp2::regex::" + cpp2::to_string(check) + " { r.matched = false; break; }\n"; + } + +#line 1157 "reflect_impl.h2" + auto generation_context::add_statefull(cpp2::impl::in next_func, cpp2::impl::in check) & -> void + { + end_func_statefull(check); + + auto name {next_func.substr(0, next_func.size() - 2)}; + start_func_named(cpp2::move(name)); + } + +#line 1165 "reflect_impl.h2" + auto generation_context::start_func_named(cpp2::impl::in name) & -> void + { + auto cur {new_context()}; + + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + cpp2::to_string(name) + ": @struct type = {\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " operator(): (this, cur: Iter, inout ctx: context, other) -> cpp2::regex::match_return = {\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " r := ctx..pass(cur);\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " do {\n"; + (*cpp2::impl::assert_not_null(cpp2::move(cur))).add_tabs(3); + } + +#line 1176 "reflect_impl.h2" + [[nodiscard]] auto generation_context::start_func() & -> std::string + { + auto name {gen_func_name()}; + start_func_named(name); + return cpp2::move(name) + "()"; + } + +#line 1183 "reflect_impl.h2" + auto generation_context::end_func_statefull(cpp2::impl::in s) & -> void + { + auto cur {get_current()}; + (*cpp2::impl::assert_not_null(cur)).remove_tabs(3); + (*cpp2::impl::assert_not_null(cur)).code += "\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " } while false;\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " if r.matched {\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " r = " + cpp2::to_string(s) + ";\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " }\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " else {\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " r.pos = ctx.end;\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " }\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " return r;\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " }\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + "}\n"; + + finish_context(); + } + +#line 1203 "reflect_impl.h2" + [[nodiscard]] auto generation_context::generate_func(cpp2::impl::in token) & -> std::string + { + auto name {start_func()}; + (*cpp2::impl::assert_not_null(token)).generate_code((*this)); + end_func_statefull("other(" + cpp2::to_string(match_parameters()) + ")"); + + return name; + } + +#line 1213 "reflect_impl.h2" + [[nodiscard]] auto generation_context::generate_reset(cpp2::impl::in> groups) & -> std::string + { + if (groups.empty()) { + return "cpp2::regex::no_reset()"; + } + + auto name {gen_reset_func_name()}; + auto cur {new_context()}; + + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + cpp2::to_string(name) + ": @struct type = {\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " operator(): (this, inout ctx) = {\n"; + for ( auto const& g : groups ) { + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " ctx..set_group_invalid(" + cpp2::to_string(g) + ");\n"; + } + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + " }\n"; + (*cpp2::impl::assert_not_null(cur)).code += "" + cpp2::to_string((*cpp2::impl::assert_not_null(cur)).tabs) + "}\n"; + + finish_context(); + + return cpp2::move(name) + "()"; + } + +#line 1237 "reflect_impl.h2" + [[nodiscard]] auto generation_context::gen_func_name() & -> std::string{ + auto cur_id {matcher_func}; + matcher_func += 1; + return "func_" + cpp2::to_string(cpp2::move(cur_id)) + ""; + } + +#line 1243 "reflect_impl.h2" + [[nodiscard]] auto generation_context::next_func_name() & -> std::string{ + return gen_func_name() + "()"; + } + +#line 1247 "reflect_impl.h2" + [[nodiscard]] auto generation_context::gen_reset_func_name() & -> std::string{ + auto cur_id {reset_func}; + reset_func += 1; + return "reset_" + cpp2::to_string(cpp2::move(cur_id)) + ""; + } + +#line 1253 "reflect_impl.h2" + [[nodiscard]] auto generation_context::gen_temp() & -> std::string{ + auto cur_id {temp_name}; + temp_name += 1; + return "tmp_" + cpp2::to_string(cpp2::move(cur_id)) + ""; + } + +#line 1261 "reflect_impl.h2" + [[nodiscard]] auto generation_context::new_context() & -> generation_function_context*{ + gen_stack.push_back(generation_function_context()); + auto cur {get_current()}; + (*cpp2::impl::assert_not_null(cur)).tabs = " "; + + return cur; + } + +#line 1269 "reflect_impl.h2" + auto generation_context::finish_context() & -> void{ + auto cur {get_current()}; + auto base {get_base()}; + (*cpp2::impl::assert_not_null(base)).code += (*cpp2::impl::assert_not_null(cpp2::move(cur))).code; + + gen_stack.pop_back(); + } + +#line 1279 "reflect_impl.h2" + [[nodiscard]] auto generation_context::get_current() & -> generation_function_context*{ + return &gen_stack.back(); + } + +#line 1283 "reflect_impl.h2" + [[nodiscard]] auto generation_context::get_base() & -> generation_function_context*{ + return &CPP2_ASSERT_IN_BOUNDS_LITERAL(gen_stack, 0); + } + +#line 1287 "reflect_impl.h2" + [[nodiscard]] auto generation_context::get_entry_func() const& -> std::string{ + return entry_func; + } + +#line 1291 "reflect_impl.h2" + [[nodiscard]] auto generation_context::create_named_group_lookup(cpp2::impl::in> named_groups) const& -> std::string + { + std::string res {"get_named_group_index: (name) -> int = {\n"}; + + // Generate if selection. + std::string sep {""}; + for ( auto const& cur : named_groups ) { + res += "" + cpp2::to_string(sep) + "if name == \"" + cpp2::to_string(cur.first) + "\" { return " + cpp2::to_string(cur.second) + "; }"; + sep = "else "; + } + + // Generate else branch or return if list is empty. + if (named_groups.empty()) { + res += " _ = name;\n"; + res += " return -1;\n"; + } + else { + res += " else { return -1; }\n"; + } + res += "}\n"; + return res; + } + +#line 1316 "reflect_impl.h2" + [[nodiscard]] auto generation_context::run(cpp2::impl::in token) & -> std::string{ + entry_func = generate_func(token); + + return (*cpp2::impl::assert_not_null(get_base())).code; + } + +#line 1331 "reflect_impl.h2" + alternative_token::alternative_token() + : regex_token_empty{ "" }{} + +#line 1333 "reflect_impl.h2" + [[nodiscard]] auto alternative_token::parse(parse_context& ctx) -> token_ptr{ + if (ctx.current() != '|') {return nullptr; } + + if (!(ctx.has_token())) {return ctx.error("Alternative with no content."); } + ctx.next_alternative(); + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared); + } + + alternative_token::~alternative_token() noexcept{} + +#line 1348 "reflect_impl.h2" + alternative_token_gen::alternative_token_gen(cpp2::impl::in a) + : regex_token{ gen_string(a) } + , alternatives{ a }{ + +#line 1351 "reflect_impl.h2" + } + +#line 1353 "reflect_impl.h2" + auto alternative_token_gen::generate_code(generation_context& ctx) const -> void + { + std::string functions {""}; + + for ( auto const& cur : alternatives ) { + std::set groups {}; + (*cpp2::impl::assert_not_null(cur)).add_groups(groups); + + functions += ", " + ctx.generate_func(cur); + functions += ", " + ctx.generate_reset(cpp2::move(groups)); + } + + auto next_name {ctx.next_func_name()}; + + ctx.add_statefull(next_name, "cpp2::regex::alternative_token_matcher::match(" + cpp2::to_string(ctx.match_parameters()) + ", other, " + cpp2::to_string(next_name) + " " + cpp2::to_string(cpp2::move(functions)) + ")"); + } + +#line 1370 "reflect_impl.h2" + auto alternative_token_gen::add_groups(std::set& groups) const -> void + { + for ( auto const& cur : alternatives ) { + (*cpp2::impl::assert_not_null(cur)).add_groups(groups); + } + } + +#line 1377 "reflect_impl.h2" + [[nodiscard]] auto alternative_token_gen::gen_string(cpp2::impl::in a) -> std::string + { + std::string r {""}; + std::string sep {""}; + + for ( auto const& cur : a ) { + r += sep + (*cpp2::impl::assert_not_null(cur)).to_string(); + sep = "|"; + } + + return r; + } + + alternative_token_gen::~alternative_token_gen() noexcept{} + +#line 1398 "reflect_impl.h2" + any_token::any_token(cpp2::impl::in single_line) + : regex_token_check{ ".", "any_token_matcher" }{ + +#line 1400 "reflect_impl.h2" + } + +#line 1402 "reflect_impl.h2" + [[nodiscard]] auto any_token::parse(parse_context& ctx) -> token_ptr{ + if ('.' != ctx.current()) {return nullptr; } + + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, ctx.get_modifiers().has(expression_flags::single_line)); + } + + any_token::~any_token() noexcept{} + +#line 1419 "reflect_impl.h2" + char_token::char_token(cpp2::impl::in t, cpp2::impl::in ignore_case_) + : regex_token{ std::string(1, t) } + , token{ t } + , ignore_case{ ignore_case_ }{ + +#line 1423 "reflect_impl.h2" + } + +#line 1425 "reflect_impl.h2" + [[nodiscard]] auto char_token::parse(parse_context& ctx) -> token_ptr{ + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, ctx.current(), ctx.get_modifiers().has(expression_flags::case_insensitive)); + } + +#line 1429 "reflect_impl.h2" + auto char_token::generate_code(generation_context& ctx) const -> void + { + if (ignore_case) { + std::string upper {token}; + std::string lower {token}; +{ +size_t i{0}; + +#line 1435 "reflect_impl.h2" + for( ; cpp2::impl::cmp_less(i,token.size()); i += 1 ) { + CPP2_ASSERT_IN_BOUNDS(lower, i) = string_util::safe_tolower(CPP2_ASSERT_IN_BOUNDS(token, i)); + CPP2_ASSERT_IN_BOUNDS(upper, i) = string_util::safe_toupper(CPP2_ASSERT_IN_BOUNDS(token, i)); + } +} + +#line 1440 "reflect_impl.h2" + if (upper != lower) { + gen_case_insensitive(cpp2::move(lower), cpp2::move(upper), ctx); + } + else { + gen_case_sensitive(ctx); + } + } + else { + gen_case_sensitive(ctx); + } + } + +#line 1452 "reflect_impl.h2" + auto char_token::gen_case_insensitive(cpp2::impl::in lower, cpp2::impl::in upper, generation_context& ctx) const& -> void + { + std::string name {"str_" + cpp2::to_string(ctx.gen_temp()) + ""}; + std::string lower_name {"lower_" + cpp2::to_string(name) + ""}; + std::string upper_name {"upper_" + cpp2::to_string(cpp2::move(name)) + ""}; + auto size {token.size()}; + ctx.add("" + cpp2::to_string(lower_name) + " : std::array = \"" + cpp2::to_string(add_escapes(lower)) + "\";"); // TODO: Add constexpr when Issue https://github.com/hsutter/cppfront/issues/1104 is resolved. + ctx.add("" + cpp2::to_string(upper_name) + " : std::array = \"" + cpp2::to_string(add_escapes(upper)) + "\";"); // TODO: Add constexpr when Issue https://github.com/hsutter/cppfront/issues/1104 is resolved. + ctx.add("if std::distance(r.pos, ctx.end) < " + cpp2::to_string(size) + " {"); + ctx.add(" r.matched = false;"); + ctx.add(" break;"); + ctx.add("}"); + ctx.add(""); + ctx.add("(copy i : int = 0) while i < " + cpp2::to_string(size) + " next (i += 1) {"); + ctx.add(" if !(" + cpp2::to_string(cpp2::move(lower_name)) + "[i] == r.pos[i] || " + cpp2::to_string(cpp2::move(upper_name)) + "[i] == r.pos[i]) { r.matched = false; }"); + ctx.add("}"); + ctx.add(""); + ctx.add("if r.matched { r.pos += " + cpp2::to_string(cpp2::move(size)) + "; }"); + ctx.add("else { break; }"); + } + +#line 1473 "reflect_impl.h2" + auto char_token::gen_case_sensitive(generation_context& ctx) const& -> void + { + std::string name {"str_" + cpp2::to_string(ctx.gen_temp()) + ""}; + auto size {token.size()}; + ctx.add("" + cpp2::to_string(name) + " : std::array = \"" + cpp2::to_string(add_escapes(token)) + "\";"); // TODO: Add constexpr when Issue https://github.com/hsutter/cppfront/issues/1104 is resolved. + ctx.add("if std::distance(r.pos, ctx.end) < " + cpp2::to_string(size) + " {"); + ctx.add(" r.matched = false;"); + ctx.add(" break;"); + ctx.add("}"); + ctx.add(""); + ctx.add("(copy i : int = 0) while i < " + cpp2::to_string(size) + " next (i += 1) {"); + ctx.add(" if " + cpp2::to_string(cpp2::move(name)) + "[i] != r.pos[i] { r.matched = false; }"); + ctx.add("}"); + ctx.add(""); + ctx.add("if r.matched { r.pos += " + cpp2::to_string(cpp2::move(size)) + "; }"); + ctx.add("else { break; }"); + } + +#line 1491 "reflect_impl.h2" + [[nodiscard]] auto char_token::add_escapes(std::string str) const& -> std::string + { + str = string_util::replace_all(str, "\\", "\\\\"); + str = string_util::replace_all(str, "\a", "\\a"); + str = string_util::replace_all(str, "\f", "\\f"); + str = string_util::replace_all(str, "\x1b", "\" \"\\x1b\" \""); // Generate a separated string. This prevents + // situations like `\x1bblub` from generating + // wrong hex characters. + str = string_util::replace_all(str, "\n", "\\n"); + str = string_util::replace_all(str, "\r", "\\r"); + str = string_util::replace_all(str, "\t", "\\t"); + + return cpp2::move(str); + } + +#line 1506 "reflect_impl.h2" + auto char_token::append(char_token const& that) & -> void{ + (*this).token += that.token; + (*this).string_rep += that.string_rep; + } + + char_token::~char_token() noexcept{} + +#line 1523 "reflect_impl.h2" + class_token::class_token(cpp2::impl::in negate_, cpp2::impl::in case_insensitive_, cpp2::impl::in class_str_, cpp2::impl::in str) + : regex_token{ str } + , negate{ negate_ } + , case_insensitive{ case_insensitive_ } + , class_str{ class_str_ } +#line 1524 "reflect_impl.h2" + { + +#line 1529 "reflect_impl.h2" + } + +#line 1532 "reflect_impl.h2" + [[nodiscard]] auto class_token::parse(parse_context& ctx) -> token_ptr + { + if (ctx.current() != '[') {return nullptr; } + + auto start_pos {ctx.get_pos()}; + + std::vector supported_classes {"alnum", "alpha", "ascii", "blank", "cntrl", "digits", "graph", + "lower", "print", "punct", "space", "upper", "word", "xdigit"}; + + std::vector classes {}; + + // First step: parse until the end bracket and push single chars, ranges or groups on the class stack. + auto is_negate {false}; + auto first {true}; + auto range {false}; + while( ctx.next_in_class() && (ctx.current() != ']' || first) ) + { + if (ctx.current() == '^') + { + is_negate = true; + continue; // Skip rest of the loop. Also the first update. + } + + if (ctx.current() == '[' && ctx.peek_in_class() == ':') + { + // We have a character class. + static_cast(ctx.next_n(2));// Skip [: + + std::string name {""}; + if (!(ctx.grab_until(":]", cpp2::impl::out(&name)))) {return ctx.error("Could not find end of character class."); } + if (supported_classes.end() == std::find(supported_classes.begin(), supported_classes.end(), name)) { + return ctx.error("Unsupported character class. Supported ones are: " + cpp2::to_string(string_util::join(supported_classes)) + ""); + } + + classes.push_back("[:" + cpp2::to_string(cpp2::move(name)) + ":]"); + + static_cast(ctx.next());// Skip ':' pointing to the ending ']'. + } + else {if (ctx.current() == '\\') + { + if (ctx. next_no_skip() && (ctx. current() != ']')) + { + if ( ' ' == ctx. current() + && ctx.get_modifiers().has(expression_flags::perl_code_syntax) + && ctx.get_modifiers().has(expression_flags::perl_code_syntax_in_classes)) + { + classes.push_back(std::string(1, ctx.current())); + } + else { + auto name {""}; + if ( 'd' == ctx. current()) { name = "short_digits"; } + else {if ('D' == ctx.current()) {name = "short_not_digits"; } + else {if ('h' == ctx.current()) {name = "short_hor_space"; } + else {if ('H' == ctx.current()) {name = "short_not_hor_space"; } + else {if ('s' == ctx.current()) {name = "short_space"; } + else {if ('S' == ctx.current()) {name = "short_not_space"; } + else {if ('v' == ctx.current()) {name = "short_ver_space"; } + else {if ('V' == ctx.current()) {name = "short_not_ver_space"; } + else {if ('w' == ctx.current()) {name = "short_word"; } + else {if ('W' == ctx.current()) {name = "short_not_word"; } + else { + return ctx.error("Unknown group escape."); + }}}}}}}}}} + classes.push_back("[:" + cpp2::to_string(cpp2::move(name)) + ":]"); + } + }else { + return ctx.error("Escape without a following character."); + } + } + else {if (ctx.current() == '-') + { + if (first) {// Literal if first entry. + classes.push_back("" + cpp2::to_string(ctx.current()) + ""); + }else { + range = true; + } + } + else + { + if (range) {// Modify last element to be a range. + classes.back() += "-" + cpp2::to_string(ctx.current()) + ""; + range = false; + } + else { + classes.push_back("" + cpp2::to_string(ctx.current()) + ""); + } + }}} + + first = false; + } + + if (ctx.current() != ']') { + return ctx.error("Error end of character class definition before terminating ']'."); + } + auto end_pos {ctx.get_pos()}; + + if (cpp2::move(range)) {// If '-' is last entry treat it as a literal char. + classes.push_back("-"); + } + + // Second step: Wrap the item on the class stack with corresponding class implementation. + for ( auto& cur : classes ) + { + if (cur.starts_with("[:")) { + auto name {cur.substr(2, cur.size() - 4)}; + cur = create_matcher("" + cpp2::to_string(cpp2::move(name)) + "_class", ""); + } + else {if (1 != cur.size()) { + cur = create_matcher("range_class_entry", "'" + cpp2::to_string(CPP2_ASSERT_IN_BOUNDS_LITERAL(cur, 0)) + "', '" + cpp2::to_string(CPP2_ASSERT_IN_BOUNDS_LITERAL(cur, 2)) + "'"); + } + else { + cur = create_matcher("single_class_entry", "'" + cpp2::to_string(cur) + "'"); + }} + } + + auto inner {string_util::join(cpp2::move(classes))}; + auto string_rep {ctx.get_range(cpp2::move(start_pos), cpp2::move(end_pos))}; + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, + cpp2::move(is_negate), + ctx.get_modifiers().has(expression_flags::case_insensitive), + cpp2::move(inner), + cpp2::move(string_rep) + ); + } + +#line 1657 "reflect_impl.h2" + auto class_token::generate_code(generation_context& ctx) const -> void + { + ctx.add_check("class_token_matcher::match(" + cpp2::to_string(ctx.match_parameters()) + ")"); + } + +#line 1662 "reflect_impl.h2" + [[nodiscard]] auto class_token::create_matcher(cpp2::impl::in name, cpp2::impl::in template_arguments) -> std::string + { + auto sep {", "}; + if (template_arguments.empty()) {sep = ""; } + + return "::cpp2::regex::" + cpp2::to_string(name) + ""; + } + + class_token::~class_token() noexcept{} + +#line 1674 "reflect_impl.h2" +[[nodiscard]] auto escape_token_parse(parse_context& ctx) -> token_ptr +{ + if (ctx.current() != '\\') {return nullptr; } + +#line 1679 "reflect_impl.h2" + if (std::string::npos == std::string("afenrt^.[]()*{}?+|\\").find(ctx.peek())) { + return nullptr; + } + + static_cast(ctx.next());// Skip escape + + if (std::string::npos != std::string("afenrt\\").find(ctx.current())) + { + // Escape of string special char + char t {'\0'}; + if ( 'a' == ctx. current()) { t = '\a'; } + else {if ('f' == ctx.current()) {t = '\f'; } + else {if ('e' == ctx.current()) {t = '\x1b'; } + else {if ('n' == ctx.current()) {t = '\n'; } + else {if ('r' == ctx.current()) {t = '\r'; } + else {if ('t' == ctx.current()) {t = '\t'; } + else {if ('\\' == ctx.current()) {t = '\\'; } + else {return ctx.error("Internal: missing switch case for special escape."); }}}}}}} + + auto r {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, cpp2::move(t), false)}; + (*cpp2::impl::assert_not_null(r)).set_string("\\" + cpp2::to_string(ctx.current()) + ""); + return r; + } + else + { + // Escape of regex special char + auto r {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, ctx.current(), false)}; + (*cpp2::impl::assert_not_null(r)).set_string("\\" + cpp2::to_string(ctx.current()) + ""); + return r; + } + +} + +#line 1715 "reflect_impl.h2" +[[nodiscard]] auto global_group_reset_token_parse(parse_context& ctx) -> token_ptr +{ + if (!((ctx.current() == '\\' && ctx.peek() == 'K'))) {return nullptr; } + + static_cast(ctx.next());// Skip escape. + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "\\K", "ctx..set_group_start(0, r.pos);"); +} + +#line 1737 "reflect_impl.h2" + group_ref_token::group_ref_token(cpp2::impl::in id_, cpp2::impl::in case_insensitive_, cpp2::impl::in str) + : regex_token{ str } + , id{ id_ } + , case_insensitive{ case_insensitive_ } +#line 1738 "reflect_impl.h2" + { + +#line 1742 "reflect_impl.h2" + } + +#line 1744 "reflect_impl.h2" + [[nodiscard]] auto group_ref_token::parse(parse_context& ctx) -> token_ptr + { + if (ctx.current() != '\\') {return nullptr; } + + std::string str {"\\"}; + std::string group {""}; + + if ([_0 = '0', _1 = ctx.peek(), _2 = '9']{ return cpp2::impl::cmp_less_eq(_0,_1) && cpp2::impl::cmp_less_eq(_1,_2); }()) + { + static_cast(ctx.next());// Skip escape + group = ctx.grab_number(); + + if (cpp2::impl::cmp_greater_eq(group.size(),cpp2::impl::as_())) + { + // Octal syntax (\000) not a group ref matcher. + auto number {0}; + if (!(string_util::string_to_int(group, number, 8))) {return ctx.error("Could not convert octal to int."); } + + char number_as_char {cpp2::unchecked_narrow(cpp2::move(number))}; + + auto token {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, number_as_char, ctx.get_modifiers().has(expression_flags::case_insensitive))}; + (*cpp2::impl::assert_not_null(token)).set_string("\\" + cpp2::to_string(string_util::int_to_string<8>(cpp2::impl::as_(cpp2::move(number_as_char)))) + ""); + + return token; + } + + str += group; + // Regular group ref + } + else {if ('g' == ctx.peek()) + { + static_cast(ctx.next());// Skip escape + if (!(ctx.next())) {return ctx.error("Group escape without a following char."); }// Skip g + + str += "g"; + + if (ctx.current() == '{') { + str += "{"; + if (!((ctx.next() && ctx.grab_until('}', cpp2::impl::out(&group))))) {return ctx.error("No ending bracket."); } + + str += group + "}"; + } + else { + group = ctx.grab_number(); + str += group; + } + } + else {if ('k' == ctx.peek()) + { + static_cast(ctx.next());// Skip escape + if (!(ctx.next())) {return ctx.error("Group escape without a following char."); }// Skip k + + str += "k"; + + auto term_char {'\0'}; + if (ctx.current() == '{') {term_char = '}'; } + else {if (ctx.current() == '<') {term_char = '>'; } + else {if (ctx.current() == '\'') {term_char = '\''; } + else { + return ctx.error("Group escape has wrong operator."); + }}} + + str += ctx.current(); + + if (!((ctx.next() && ctx.grab_until(term_char, cpp2::impl::out(&group))))) {return ctx.error("No ending bracket."); } + + str += group + cpp2::move(term_char); + } + else + { + // No group ref matcher + return nullptr; + }}} + + // Parse the group + group = string_util::trim_copy(group); + int group_id {0}; + if (string_util::string_to_int(group, group_id)) + { + if (cpp2::impl::cmp_less(group_id,0)) { + group_id = ctx.get_cur_group() + group_id; + + if (cpp2::impl::cmp_less(group_id,1)) {// Negative and zero are no valid groups. + return ctx.error("Relative group reference does not reference a valid group. (Would be " + cpp2::to_string(group_id) + ".)"); + } + } + + if (cpp2::impl::cmp_greater_eq(group_id,ctx.get_cur_group())) { + return ctx.error("Group reference is used before the group is declared."); + } + } + else + { + // Named group + group_id = ctx.get_named_group(group); + if (-1 == group_id) {return ctx.error("Group names does not exist. (Name is: " + cpp2::to_string(cpp2::move(group)) + ")"); } + } + + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, cpp2::move(group_id), ctx.get_modifiers().has(expression_flags::case_insensitive), cpp2::move(str)); + } + +#line 1845 "reflect_impl.h2" + auto group_ref_token::generate_code(generation_context& ctx) const -> void{ + ctx.add_check("group_ref_token_matcher(" + cpp2::to_string(ctx.match_parameters()) + ")"); + } + + group_ref_token::~group_ref_token() noexcept{} + +#line 1868 "reflect_impl.h2" + [[nodiscard]] auto group_token::parse_lookahead(parse_context& ctx, cpp2::impl::in syntax, cpp2::impl::in positive) -> token_ptr + { + static_cast(ctx.next());// Skip last token defining the syntax + + auto r {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, positive)}; + + auto old_state {ctx.start_group()}; + if (!(ctx.parse_until(')'))) {return ctx.error("Lookahead without a closing bracket."); } + (*cpp2::impl::assert_not_null(r)).inner = ctx.end_group(cpp2::move(old_state)); + (*cpp2::impl::assert_not_null(r)).set_string("(" + cpp2::to_string(syntax) + cpp2::to_string((*cpp2::impl::assert_not_null((*cpp2::impl::assert_not_null(r)).inner)).to_string()) + ")"); + + return r; + } + +#line 1882 "reflect_impl.h2" + [[nodiscard]] auto group_token::parse(parse_context& ctx) -> token_ptr + { + if (ctx.current() != '(') {return nullptr; } + + auto has_id {!(ctx.get_modifiers().has(expression_flags::no_group_captures))}; + auto has_pattern {true}; + std::string group_name {""}; + auto group_name_brackets {true}; + std::string modifiers {""}; + auto modifiers_change_to {ctx.get_modifiers()}; + + // Skip the '(' + if (!(ctx.next())) {return ctx.error("Group without closing bracket."); } + + if (ctx.current() == '?') + { + // Special group + if (!(ctx.next_no_skip())) {return ctx.error("Missing character after group opening."); } + + if (ctx.current() == '<' || ctx.current() == '\'') + { + // Named group + auto end_char {ctx.current()}; + if (end_char == '<') { + end_char = '>'; + }else { + group_name_brackets = false; + } + has_id = true; // Force id for named groups. + if (!(ctx.next())) { return ctx. error("Missing ending bracket for named group."); }/* skip '<' */ + if (!(ctx.grab_until(cpp2::move(end_char), cpp2::impl::out(&group_name)))) {return ctx.error("Missing ending bracket for named group."); } + if (!(ctx.next())) {return ctx.error("Group without closing bracket."); } + } + else {if (ctx.current() == '#') + { + // Comment + std::string comment_str {""}; + static_cast(ctx.next());// Skip # + if (!(ctx.grab_until(")", cpp2::impl::out(&comment_str)))) {return ctx.error("Group without closing bracket."); } + // Do not add comment. Has problems with ranges. + + // Pop token and add a list. This fixes comments between a token and a range + if (ctx.has_token()) { + token_vec list {}; + list.push_back(ctx.pop_token()); + list.push_back(CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "(?#" + cpp2::to_string(cpp2::move(comment_str)) + ")")); + + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, cpp2::move(list)); + } + else { + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "(?#" + cpp2::to_string(cpp2::move(comment_str)) + ")"); + } + } + else {if (ctx.current() == '|') + { + // Branch reset group + + if (!(ctx.next())) { return ctx. error("Missing ending bracket for named group."); }/* skip '|' */ + + auto old_parser_state {ctx.start_group()}; + auto old_branch_state {ctx.branch_reset_new_state()}; + if (!(ctx.parse_until(')'))) {return nullptr; } + ctx.branch_reset_restore_state(cpp2::move(old_branch_state)); + auto inner_ {ctx.end_group(cpp2::move(old_parser_state))}; + + token_vec list {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "(?|"), cpp2::move(inner_), CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, ")")}; + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, cpp2::move(list)); + } + else {if (ctx.current() == '=' || ctx.current() == '!') + { + return parse_lookahead(ctx, "?" + cpp2::to_string(ctx.current()) + "", ctx.current() == '='); + } + else + { + // Simple modifier + has_id = false; + if (!(ctx.grab_until_one_of("):", cpp2::impl::out(&modifiers)))) {return ctx.error("Missing ending bracket for group."); } + if (!(ctx.parser_group_modifiers(modifiers, modifiers_change_to))) { + return nullptr; + } + + if (')' == ctx.current()) { + has_pattern = false; + } + else { + if (!(ctx.next())) { return ctx. error("Missing ending bracket for group."); }/* skip ':' */ + } + }}}} + } + else {if (ctx.current() == '*') + { + // Named pattern + static_cast(ctx.next());// Skip *. + std::string name {""}; + if (!(ctx.grab_until(':', cpp2::impl::out(&name)))) {return ctx.error("Missing colon for named pattern."); } + + if (name == "pla" || name == "positive_lookahead") { + return parse_lookahead(ctx, "*" + cpp2::to_string(cpp2::move(name)) + ":", true); + } + else {if (name == "nla" || name == "negative_lookahead") { + return parse_lookahead(ctx, "*" + cpp2::to_string(cpp2::move(name)) + ":", false); + } + else { + return ctx.error("Unknown named group pattern: '" + cpp2::to_string(cpp2::move(name)) + "'"); + }} + }} + + if (cpp2::move(has_pattern)) + { + // Regular group + + auto r {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared)}; + if (has_id) { + (*cpp2::impl::assert_not_null(r)).number = ctx.next_group(); + + if (0 != group_name.size()) { + ctx.set_named_group(group_name, (*cpp2::impl::assert_not_null(r)).number); + } + } + + auto old_state {ctx.start_group()}; + ctx.set_modifiers(cpp2::move(modifiers_change_to)); + if (!(ctx.parse_until(')'))) {return nullptr; } + (*cpp2::impl::assert_not_null(r)).inner = ctx.end_group(cpp2::move(old_state)); + (*cpp2::impl::assert_not_null(r)).set_string(gen_string(cpp2::move(group_name), cpp2::move(group_name_brackets), !(cpp2::move(has_id)), cpp2::move(modifiers), (*cpp2::impl::assert_not_null(r)).inner)); + + return r; + } + else + { + // Only a modifier + ctx.set_modifiers(cpp2::move(modifiers_change_to)); + + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "(?" + cpp2::to_string(cpp2::move(modifiers)) + ")"); + } + } + +#line 2019 "reflect_impl.h2" + [[nodiscard]] auto group_token::gen_string(cpp2::impl::in name, cpp2::impl::in name_brackets, cpp2::impl::in has_modifier, cpp2::impl::in modifiers, cpp2::impl::in inner_) -> std::string + { + std::string start {"("}; + if (0 != name.size()) { + if (name_brackets) { + start += "?<" + cpp2::to_string(name.data()) + ">"; + } + else { + start += "?'" + cpp2::to_string(name.data()) + "'"; + } + } + else {if (has_modifier) { + start += "?" + modifiers + ":"; + }} + + return cpp2::move(start) + (*cpp2::impl::assert_not_null(inner_)).to_string() + ")"; + } + +#line 2037 "reflect_impl.h2" + auto group_token::generate_code(generation_context& ctx) const -> void + { + if (-1 != number) { + ctx.add("ctx..set_group_start(" + cpp2::to_string(number) + ", r.pos);"); + } + + (*cpp2::impl::assert_not_null(inner)).generate_code(ctx); + if (-1 != number) { + ctx.add("ctx..set_group_end(" + cpp2::to_string(number) + ", r.pos);"); + auto tmp_name {ctx.gen_temp()}; + ctx.add("" + cpp2::to_string(tmp_name) + "_func := :() = {"); + ctx.add(" if !r&$*.matched {"); + ctx.add(" ctx&$*..set_group_invalid(" + cpp2::to_string(number) + ");"); + ctx.add(" }"); + ctx.add("};"); + ctx.add("" + cpp2::to_string(tmp_name) + " := cpp2::regex::make_on_return(" + cpp2::to_string(tmp_name) + "_func);"); + ctx.add("_ = " + cpp2::to_string(cpp2::move(tmp_name)) + ";");// Logic is done in the destructor. Same behavior as for guard objects. + } + } + +#line 2057 "reflect_impl.h2" + auto group_token::add_groups(std::set& groups) const -> void + { + (*cpp2::impl::assert_not_null(inner)).add_groups(groups); + if (-1 != number) { + static_cast(groups.insert(number)); + } + } + + group_token::~group_token() noexcept{} + +#line 2069 "reflect_impl.h2" +[[nodiscard]] auto hexadecimal_token_parse(parse_context& ctx) -> token_ptr +{ + if (!((ctx.current() == '\\' && ctx.peek() == 'x'))) {return nullptr; } + + static_cast(ctx.next());// Skip escape. + + if (!(ctx.next())) {return ctx.error("x escape without number."); } + + auto has_brackets {false}; + std::string number_str {""}; + if ('{' == ctx.current()) { + // Bracketed + has_brackets = true; + static_cast(ctx.next());// Skip '{' + if (!(ctx.grab_until('}', cpp2::impl::out(&number_str)))) {return ctx.error("No ending bracket for \\x"); } + } + else { + // Grab two chars + if (!(ctx.grab_n(2, cpp2::impl::out(&number_str)))) {return ctx.error("Missing hexadecimal digits after \\x."); } + } + + auto number {0}; + if (!(string_util::string_to_int(cpp2::move(number_str), number, 16))) {return ctx.error("Could not convert hexadecimal to int."); } + + // TODO: Change for unicode. + char number_as_char {cpp2::unchecked_narrow(cpp2::move(number))}; + + std::string syntax {string_util::int_to_string<16>(cpp2::impl::as_(number_as_char))}; + if (cpp2::move(has_brackets)) { + syntax = "{" + cpp2::to_string(syntax) + "}"; + } + syntax = "\\x" + cpp2::to_string(syntax) + ""; + + auto r {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, cpp2::move(number_as_char), ctx.get_modifiers().has(expression_flags::case_insensitive))}; + (*cpp2::impl::assert_not_null(r)).set_string(cpp2::move(syntax)); + return r; +} + +#line 2110 "reflect_impl.h2" +[[nodiscard]] auto line_end_token_parse(parse_context& ctx) -> token_ptr +{ + if (ctx.current() == '$' || (ctx.current() == '\\' && ctx.peek() == '$')) { + if ((ctx.current() == '\\')) {static_cast(ctx.next()); }// Skip escape + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "$", "line_end_token_matcher"); + } + else {if (ctx.current() == '\\' && (ctx.peek() == 'z' || ctx.peek() == 'Z')) { + static_cast(ctx.next());// Skip escape + + auto negate {ctx.current() == 'Z'}; + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "\\" + cpp2::to_string(ctx.current()) + "", "line_end_token_matcher"); + } + else { + return nullptr; + }} +} + +#line 2130 "reflect_impl.h2" +[[nodiscard]] auto line_start_token_parse(parse_context& ctx) -> token_ptr +{ + if (ctx.current() != '^' && !((ctx.current() == '\\' && ctx.peek() == 'A'))) {return nullptr; } + + if (ctx.current() == '\\') { + static_cast(ctx.next()); + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "\\A", "line_start_token_matcher"); + } + else { + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "^", "line_start_token_matcher"); + } +} + +#line 2155 "reflect_impl.h2" + lookahead_token::lookahead_token(cpp2::impl::in positive_) + : regex_token{ "" } + , positive{ positive_ }{ + +#line 2157 "reflect_impl.h2" + } + +#line 2159 "reflect_impl.h2" + auto lookahead_token::generate_code(generation_context& ctx) const -> void{ + auto inner_name {ctx.generate_func(inner)}; + + ctx.add_check("lookahead_token_matcher(" + cpp2::to_string(ctx.match_parameters()) + ", " + cpp2::to_string(cpp2::move(inner_name)) + ")"); + } + +#line 2165 "reflect_impl.h2" + auto lookahead_token::add_groups(std::set& groups) const -> void{ + (*cpp2::impl::assert_not_null(inner)).add_groups(groups); + } + + lookahead_token::~lookahead_token() noexcept{} + +#line 2173 "reflect_impl.h2" +[[nodiscard]] auto named_class_token_parse(parse_context& ctx) -> token_ptr +{ + if (ctx.current() != '\\') {return nullptr; } + + auto name {""}; + auto c_next {ctx.peek()}; + + if ( 'd' == c_next) { name = "named_class_digits"; } + else {if ('D' == c_next) {name = "named_class_not_digits"; } + else {if ('h' == c_next) {name = "named_class_hor_space"; } + else {if ('H' == c_next) {name = "named_class_not_hor_space"; } + else {if ('N' == c_next) {name = "named_class_no_new_line"; } + else {if ('s' == c_next) {name = "named_class_space"; } + else {if ('S' == c_next) {name = "named_class_not_space"; } + else {if ('v' == c_next) {name = "named_class_ver_space"; } + else {if ('V' == c_next) {name = "named_class_not_ver_space"; } + else {if ('w' == c_next) {name = "named_class_word"; } + else {if ('W' == cpp2::move(c_next)) {name = "named_class_not_word"; } + else { return nullptr; }}}}}}}}}}} + + static_cast(ctx.next());// Skip escape + + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "\\" + cpp2::to_string(ctx.current()) + "", "" + cpp2::to_string(cpp2::move(name)) + "::match"); +} + +#line 2201 "reflect_impl.h2" +[[nodiscard]] auto octal_token_parse(parse_context& ctx) -> token_ptr +{ + if (!((ctx.current() == '\\' && ctx.peek() == 'o'))) {return nullptr; } + + static_cast(ctx.next());// Skip escape. + + if (!(ctx.next())) { return ctx. error("o escape without number."); } + if (ctx.current() != '{') {return ctx.error("Missing opening bracket for \\o."); } + + std::string number_str {""}; + static_cast(ctx.next());// Skip '{' + if (!(ctx.grab_until('}', cpp2::impl::out(&number_str)))) {return ctx.error("No ending bracket for \\o"); } + + auto number {0}; + if (!(string_util::string_to_int(cpp2::move(number_str), number, 8))) {return ctx.error("Could not convert octal to int."); } + + // TODO: Change for unicode. + char number_as_char {cpp2::unchecked_narrow(cpp2::move(number))}; + + std::string syntax {"\\o{" + cpp2::to_string(string_util::int_to_string<8>(cpp2::impl::as_(number_as_char))) + "}"}; + auto r {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, cpp2::move(number_as_char), ctx.get_modifiers().has(expression_flags::case_insensitive))}; + (*cpp2::impl::assert_not_null(r)).set_string(cpp2::move(syntax)); + return r; +} + +#line 2238 "reflect_impl.h2" + range_token::range_token() + : regex_token{ "" }{} + +#line 2240 "reflect_impl.h2" + [[nodiscard]] auto range_token::parse(parse_context& ctx) -> token_ptr + { + auto r {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared)}; + if (ctx.current() == '{') + { + if (!(ctx.has_token())) {return ctx.error("'{' without previous element."); } + + std::string inner {""}; + if (!(ctx.grab_until('}', cpp2::impl::out(&inner)))) {return ctx.error("Missing closing bracket '}'."); } + + inner = string_util::trim_copy(inner.substr(1)); // Remove '{' and white spaces. + if (inner.empty()) {return ctx.error("Empty range specifier. Either '{n}', '{n,}', '{,m}' '{n,m}'"); } + + // Non-greedy or possessive + (*cpp2::impl::assert_not_null(r)).parse_modifier(ctx); + + // Get range arguments + std::string min_count_str {"-1"}; + std::string max_count_str {"-1"}; + + size_t sep {inner.find(',')}; + if (sep == std::string::npos) + { + min_count_str = inner; + max_count_str = inner; + if (!(string_util::string_to_int(cpp2::move(inner), (*cpp2::impl::assert_not_null(r)).min_count))) {return ctx.error("Could not convert range to number."); } + (*cpp2::impl::assert_not_null(r)).max_count = (*cpp2::impl::assert_not_null(r)).min_count; + } + else + { + std::string inner_first {string_util::trim_copy(inner.substr(0, sep))}; + std::string inner_last {string_util::trim_copy(cpp2::move(inner).substr(cpp2::move(sep) + 1))}; + + if ((inner_first.empty() && inner_last.empty())) { + return ctx.error("Empty range specifier. Either '{n}', '{n,}', '{,m}' '{n,m}'"); + } + + if (!(inner_first.empty())) { + min_count_str = inner_first; + if (!(string_util::string_to_int(cpp2::move(inner_first), (*cpp2::impl::assert_not_null(r)).min_count))) {return ctx.error("Could not convert range to number."); } + } + if (!(inner_last.empty())) { + max_count_str = inner_last; + if (!(string_util::string_to_int(cpp2::move(inner_last), (*cpp2::impl::assert_not_null(r)).max_count))) {return ctx.error("Could not convert range to number."); } + } + } + + // Check validity of the range. + if (-1 != (*cpp2::impl::assert_not_null(r)).min_count) { + if (!((cpp2::impl::cmp_less_eq(0,(*cpp2::impl::assert_not_null(r)).min_count)))) { + return ctx.error("Min value in range is negative. Have " + cpp2::to_string((*cpp2::impl::assert_not_null(r)).min_count) + ")"); + } + } + if (-1 != (*cpp2::impl::assert_not_null(r)).max_count) { + if (!((cpp2::impl::cmp_less_eq(0,(*cpp2::impl::assert_not_null(r)).max_count)))) { + return ctx.error("Max value in range is negative. Have " + cpp2::to_string((*cpp2::impl::assert_not_null(r)).max_count) + ")"); + } + if (-1 != (*cpp2::impl::assert_not_null(r)).min_count) { + if (!((cpp2::impl::cmp_less_eq((*cpp2::impl::assert_not_null(r)).min_count,(*cpp2::impl::assert_not_null(r)).max_count)))) { + return ctx.error("Min and max values in range are wrong it should hold 0 <= min <= max. Have 0 <= " + cpp2::to_string((*cpp2::impl::assert_not_null(r)).min_count) + " <= " + cpp2::to_string((*cpp2::impl::assert_not_null(r)).max_count) + ""); + } + } + } + + (*cpp2::impl::assert_not_null(r)).inner_token = ctx.pop_token(); + (*cpp2::impl::assert_not_null(r)).string_rep = (*cpp2::impl::assert_not_null((*cpp2::impl::assert_not_null(r)).inner_token)).to_string() + (*cpp2::impl::assert_not_null(r)).gen_range_string() + (*cpp2::impl::assert_not_null(r)).gen_mod_string(); + + return r; + } + + return nullptr; + } + +#line 2313 "reflect_impl.h2" + auto range_token::parse_modifier(parse_context& ctx) & -> void + { + if (ctx.peek() == '?') { + kind = range_flags::not_greedy; + static_cast(ctx.next()); + } + else {if (ctx.peek() == '+') { + kind = range_flags::possessive; + static_cast(ctx.next()); + }} + } + +#line 2325 "reflect_impl.h2" + [[nodiscard]] auto range_token::gen_mod_string() const& -> std::string + { + if (kind == range_flags::not_greedy) { + return "?"; + } + else {if (kind == range_flags::possessive) { + return "+"; + } + else { + return ""; + }} + } + +#line 2338 "reflect_impl.h2" + [[nodiscard]] auto range_token::gen_range_string() const& -> std::string + { + std::string r {""}; + if (min_count == max_count) { + r += "{" + cpp2::to_string(min_count) + "}"; + } + else {if (min_count == -1) { + r += "{," + cpp2::to_string(max_count) + "}"; + } + else {if (max_count == -1) { + r += "{" + cpp2::to_string(min_count) + ",}"; + } + else { + r += "{" + cpp2::to_string(min_count) + "," + cpp2::to_string(max_count) + "}"; + }}} + + return r; + } + +#line 2357 "reflect_impl.h2" + auto range_token::generate_code(generation_context& ctx) const -> void + { + auto inner_name {ctx.generate_func(inner_token)}; + std::set groups {}; + (*cpp2::impl::assert_not_null(inner_token)).add_groups(groups); + auto reset_name {ctx.generate_reset(cpp2::move(groups))}; + + auto next_name {ctx.next_func_name()}; + ctx.add_statefull(next_name, "cpp2::regex::range_token_matcher::match(" + cpp2::to_string(ctx.match_parameters()) + ", " + cpp2::to_string(cpp2::move(inner_name)) + ", " + cpp2::to_string(cpp2::move(reset_name)) + ", other, " + cpp2::to_string(next_name) + ")"); + } + +#line 2368 "reflect_impl.h2" + auto range_token::add_groups(std::set& groups) const -> void{ + (*cpp2::impl::assert_not_null(inner_token)).add_groups(groups); + } + + range_token::~range_token() noexcept{} + +#line 2381 "reflect_impl.h2" + [[nodiscard]] auto special_range_token::parse(parse_context& ctx) -> token_ptr + { + auto r {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared)}; + char symbol {'\0'}; + if (ctx.current() == '*') { + (*cpp2::impl::assert_not_null(r)).min_count = 0; + (*cpp2::impl::assert_not_null(r)).max_count = -1; + symbol = '*'; + } + else {if (ctx.current() == '+') { + (*cpp2::impl::assert_not_null(r)).min_count = 1; + (*cpp2::impl::assert_not_null(r)).max_count = -1; + symbol = '+'; + }else {if (ctx.current() == '?') { + (*cpp2::impl::assert_not_null(r)).min_count = 0; + (*cpp2::impl::assert_not_null(r)).max_count = 1; + symbol = '?'; + }else { + return nullptr; + }}} + + if (!(ctx.has_token())) {return ctx.error("'" + cpp2::to_string(ctx.current()) + "' without previous element."); } + +#line 2405 "reflect_impl.h2" + (*cpp2::impl::assert_not_null(r)).parse_modifier(ctx); + + (*cpp2::impl::assert_not_null(r)).inner_token = ctx.pop_token(); + (*cpp2::impl::assert_not_null(r)).string_rep = (*cpp2::impl::assert_not_null((*cpp2::impl::assert_not_null(r)).inner_token)).to_string() + cpp2::move(symbol) + (*cpp2::impl::assert_not_null(r)).gen_mod_string(); + return r; + } + + special_range_token::~special_range_token() noexcept{} + +#line 2418 "reflect_impl.h2" +[[nodiscard]] auto word_boundary_token_parse(parse_context& ctx) -> token_ptr +{ + if (ctx.current() != '\\') {return nullptr; } + + if (ctx.peek() == 'b') { + static_cast(ctx.next()); + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "\\b", "word_boundary_token_matcher"); + } + else {if (ctx.peek() == 'B') { + static_cast(ctx.next()); + return CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, "\\B", "word_boundary_token_matcher"); + } + else { + return nullptr; + }} +} + +#line 2454 "reflect_impl.h2" + template regex_generator::regex_generator(cpp2::impl::in r, Error_out const& e) + : regex{ r } + , error_out{ e }{ + +#line 2457 "reflect_impl.h2" + } + +#line 2459 "reflect_impl.h2" + template [[nodiscard]] auto regex_generator::parse() & -> std::string + { + // Extract modifiers and adapt regex. + extract_modifiers(); + + parse_context parse_ctx {regex, error_out}; + if (!(parse_ctx.parse(modifier))) { + return ""; + } + + source += "{\n"; + source += " wrap: type = {\n"; // TODO: Remove wrapper when template template parameters are available. + source += " context: type == cpp2::regex::match_context;"; + + generation_context gen_ctx {}; + source += gen_ctx.run(parse_ctx.get_as_token()); + source += " entry: (cur: Iter, inout ctx: context) -> cpp2::regex::match_return = {\n"; + source += " ctx..set_group_start(0, cur);\n"; + source += " r := " + cpp2::to_string(gen_ctx.get_entry_func()) + "(cur, ctx, cpp2::regex::true_end_func());\n"; + source += " if r.matched { ctx..set_group_end(0, r.pos); }\n"; + source += " return r;\n"; + source += " }\n"; + + source += cpp2::move(gen_ctx).create_named_group_lookup(parse_ctx.named_groups); + source += "}\n"; + + auto string {(*cpp2::impl::assert_not_null(parse_ctx.get_as_token())).to_string()}; + source += " to_string: () -> std::string = { return R\"(" + cpp2::to_string(modifier_escape) + cpp2::to_string(cpp2::move(string)) + cpp2::to_string(modifier_escape) + cpp2::to_string(modifier) + ")\"; }\n"; + source += "}\n"; + + static_cast(cpp2::move(parse_ctx)); + + return source; + } + +#line 2494 "reflect_impl.h2" + template auto regex_generator::extract_modifiers() & -> void + { + if (regex.find_first_of("'/") == 0) { + char mod_token {CPP2_ASSERT_IN_BOUNDS_LITERAL(regex, 0)}; + + auto end_pos {regex.rfind(mod_token)}; + if (end_pos != 0) { + // Found valid start end escape + modifier = regex.substr(end_pos + 1); + modifier_escape = cpp2::move(mod_token); + regex = regex.substr(1, cpp2::move(end_pos) - 1); + } + } + } + +#line 2510 "reflect_impl.h2" +template [[nodiscard]] auto generate_regex(cpp2::impl::in regex, Err const& err) -> std::string +{ + regex_generator parser {regex, err}; + auto r {parser.parse()}; + static_cast(cpp2::move(parser)); + return r; +} + +#line 2520 "reflect_impl.h2" +auto regex_gen(meta::type_declaration& t) -> void +{ + auto has_default {false}; + auto exact_name {"regex"}; + auto prefix {"regex_"}; + std::map expressions {}; + + for ( auto& m : CPP2_UFCS(get_member_objects)(t) ) + { + std::string name {CPP2_UFCS(name)(m)}; + + if (CPP2_UFCS(starts_with)(name, prefix) || name == exact_name) + { + if (!(CPP2_UFCS(has_initializer)(m))) { + CPP2_UFCS(error)(t, "Regular expression must have an initializer."); + } + CPP2_UFCS(mark_for_removal_from_enclosing_type)(m); + + if (name == exact_name) { + if (has_default) { + CPP2_UFCS(error)(t, "Type can only contain one default named regular expression."); + } + has_default = true; + } + + std::string expr {CPP2_UFCS(initializer)(m)}; + if (CPP2_UFCS(starts_with)(expr, "R\"(") && CPP2_UFCS(ends_with)(expr, ")\"")) { + expr = CPP2_UFCS(substr)(expr, 3, CPP2_UFCS(size)(expr) - 5); + } + else {if (string_util::is_escaped(expr)) { + expr = CPP2_UFCS(substr)(expr, 1, CPP2_UFCS(size)(expr) - 2); + } + else { + CPP2_UFCS(error)(t, "Unknown string format '" + cpp2::to_string(expr) + "'"); + }} + + CPP2_ASSERT_IN_BOUNDS(expressions, name) = cpp2::move(expr); + } + } + + CPP2_UFCS(remove_marked_members)(t); + + for ( auto const& expr : cpp2::move(expressions) ) { + auto regular_expression {generate_regex(expr.second, [_0 = t](auto const& message) mutable -> decltype(auto) { return CPP2_UFCS(error)(_0, message); })}; + + if (!(regular_expression.empty())) { + CPP2_UFCS(add_member)(t, "public " + cpp2::to_string(expr.first) + "_matcher: type = " + cpp2::to_string(cpp2::move(regular_expression)) + ""); + CPP2_UFCS(add_member)(t, "public " + cpp2::to_string(expr.first) + ": cpp2::regex::regular_expression = ();"); + } + } + + CPP2_UFCS(add_runtime_support_include)(t, "cpp2regex.h"); +} + +#line 2579 "reflect_impl.h2" +[[nodiscard]] auto apply_metafunctions( + declaration_node& n, + type_declaration& rtype, + auto const& error, + auto const& lookup + ) -> bool + +{ + if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(is_type)(n)) ) { cpp2::cpp2_default.report_violation(""); } + + // Check for _names reserved for the metafunction implementation + if (!(CPP2_UFCS(empty)(n.metafunctions))) + { + for ( + auto const& m : CPP2_UFCS(get_members)(rtype) ) + { + CPP2_UFCS(require)(m, + !(CPP2_UFCS(starts_with)(CPP2_UFCS(name)(m), "_")) || CPP2_UFCS(ssize)(CPP2_UFCS(name)(m)) == 1, + "a type that applies a metafunction cannot have a body that declares " + "a name that starts with '_' - those names are reserved for the " + "metafunction implementation" + ); + } + } + + // For each metafunction, apply it + for ( + auto const& meta : n.metafunctions ) + { + // Convert the name and any template arguments to strings + // and record that in rtype + auto name {CPP2_UFCS(to_string)((*cpp2::impl::assert_not_null(meta)))}; + name = CPP2_UFCS(substr)(name, 0, CPP2_UFCS(find)(name, '<')); + + std::vector args {}; + for ( + auto const& arg : CPP2_UFCS(template_arguments)((*cpp2::impl::assert_not_null(meta))) ) + CPP2_UFCS(push_back)(args, CPP2_UFCS(to_string)(arg)); + + CPP2_UFCS(set_metafunction_name)(rtype, name, cpp2::move(args)); + + // Dispatch + // + if (name == "interface") { + interface(rtype); + } + else {if (name == "polymorphic_base") { + polymorphic_base(rtype); + } + else {if (name == "ordered") { + ordered(rtype); + } + else {if (name == "weakly_ordered") { + weakly_ordered(rtype); + } + else {if (name == "partially_ordered") { + partially_ordered(rtype); + } + else {if (name == "copyable") { + copyable(rtype); + } + else {if (name == "hashable") { + hashable(rtype); + } + else {if (name == "basic_value") { + basic_value(rtype); + } + else {if (name == "value") { + value(rtype); + } + else {if (name == "weakly_ordered_value") { + weakly_ordered_value(rtype); + } + else {if (name == "partially_ordered_value") { + partially_ordered_value(rtype); + } + else {if (name == "cpp1_rule_of_zero") { + cpp1_rule_of_zero(rtype); + } + else {if (name == "struct") { + cpp2_struct(rtype); + } + else {if (name == "enum") { + cpp2_enum(rtype); + } + else {if (name == "flag_enum") { + flag_enum(rtype); + } + else {if (name == "union") { + cpp2_union(rtype); + } + else {if (name == "print") { + print(rtype); + } + else {if (name == "regex") { + regex_gen(rtype); + } + else {if (name == "dll_visible") { + dll_visible(rtype); + } + else { +{ +auto const& load{load_metafunction(name, lookup)}; + +#line 2681 "reflect_impl.h2" + if (CPP2_UFCS(is_value)(load)) { + CPP2_UFCS(value)(load)(rtype); + }else { + error("unrecognized metafunction name: " + name); + if (CPP2_UFCS(find)(name, "::") == name.npos) { + error( + "(temporary alpha limitation) currently the supported names are: " + "interface, polymorphic_base, ordered, weakly_ordered, partially_ordered, " + "copyable, basic_value, value, weakly_ordered_value, partially_ordered_value, " + "struct, enum, flag_enum, union, cpp1_rule_of_zero, regex, dll_visible, print" + ); + } + if (!(CPP2_UFCS(empty)(CPP2_UFCS(error)(load).value))) { + error(CPP2_UFCS(error)(load).value); + } + return false; + } +} +#line 2698 "reflect_impl.h2" + }}}}}}}}}}}}}}}}}}} + } + + return true; +} + +#line 2704 "reflect_impl.h2" +[[nodiscard]] auto apply_metafunctions( + declaration_node& n, + function_declaration& rfunction, + auto const& error + ) -> bool + +{ + if (cpp2::cpp2_default.is_active() && !(CPP2_UFCS(is_function)(n)) ) { cpp2::cpp2_default.report_violation(""); } + + // For each metafunction, apply it + for ( + auto const& meta : n.metafunctions ) + { + // Convert the name and any template arguments to strings + // and record that in rfunction + auto name {CPP2_UFCS(to_string)((*cpp2::impl::assert_not_null(meta)))}; + name = CPP2_UFCS(substr)(name, 0, CPP2_UFCS(find)(name, '<')); + + std::vector args {}; + for ( + auto const& arg : CPP2_UFCS(template_arguments)((*cpp2::impl::assert_not_null(meta))) ) + CPP2_UFCS(push_back)(args, CPP2_UFCS(to_string)(arg)); + + CPP2_UFCS(set_metafunction_name)(rfunction, name, cpp2::move(args)); + + // Dispatch + // + if (name == "dll_visible") { + dll_visible(rfunction); + } + else { + error("unrecognized metafunction name: " + cpp2::move(name)); + error("(temporary alpha limitation) currently the supported names are: dll_visible "); + return false; + } + } + + return true; +} + +#line 2745 "reflect_impl.h2" +} + +} + +#endif diff --git a/source/reflect_impl.h2 b/source/reflect_impl.h2 new file mode 100644 index 0000000000..9a4354521b --- /dev/null +++ b/source/reflect_impl.h2 @@ -0,0 +1,2750 @@ + +// Copyright (c) Herb Sutter +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +//=========================================================================== +// Reflection and meta +//=========================================================================== + +#include "parse.h" +#include "cpp2regex.h" +#include +#include +#include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#else +#include +#endif // _WIN32 + +using namespace cpp2::regex; + +cpp2: namespace = { + +meta: namespace = { + + +to_type_metafunction_cast: ( + name: std::string_view, + is_const_metafunction: bool + ) + -> std::string += { + const_ := ""; + if is_const_metafunction { + const_ = " const"; + } + return "static_cast((name)$)"; +} + + +//----------------------------------------------------------------------- +// +// dll_symbol +// +dll_symbol: @value type = { + value: std::string = (); + + constant_prefix: std::string_view == "cpp2_metafunction_"; + reachable_mangle: std::string_view == "r"; + const_metafunction_mangle: std::string_view == "c"; + + operator=: (implicit out this) = { } + operator=: (out this, n: declaration_node, is_reachable_: bool) + pre(n.is_metafunction()) + post(is_reachable() == is_reachable_) + post(is_const_metafunction() == n.is_const_metafunction()) + = { + value = constant_prefix; + if is_reachable_ { + value += reachable_mangle; + } + if n.is_const_metafunction() { + value += const_metafunction_mangle; + } + value += mangle(n); + } + operator=: (out this, copy s: std::string_view) + = { + if !s.starts_with(constant_prefix) { + value += constant_prefix; + } + value += s; + } + + c_str: (this) -> *const char = value.c_str(); + view: (this) -> std::string_view = value; + without_prefix: (this) + -> std::string_view + = { + res := view(); + res.remove_prefix(constant_prefix.size()); + for (reachable_mangle, const_metafunction_mangle) + do (m) + if res.starts_with(m) { + res.remove_prefix(m.size()); + } + return res; + } + + is_reachable: (this) -> bool = view().substr(constant_prefix.size()).starts_with(reachable_mangle); + is_const_metafunction: (this) -> bool = + view().substr(constant_prefix.size()) + .substr(unsigned(is_reachable()) * reachable_mangle.size()) + .starts_with(const_metafunction_mangle); +} + +symbols_accessor: (lib_path: std::string_view) + -> dll_symbol + = dll_symbol("get_symbol_names_(to_lower_and_collapsed_underbar(lib_path, true, true))$"); + +this_execution: namespace = +{ + +// The environment variable 'CPPFRONT_METAFUNCTION_LIBRARY' +// is read and interpreted as the Cpp2 metafunction library path of this execution +symbols_accessor: () + -> dll_symbol += { + env_var :== "CPPFRONT_METAFUNCTION_LIBRARY"; + lib_path := std::getenv(env_var); + assert(lib_path && lib_path[0] != '\0', "(env_var)$ should be set for a Cpp2 source with metafunctions"); + // FIXME Doesn't work for a library with more than one source providing a metafunction + // See https://github.com/hsutter/cppfront/pull/907#issuecomment-1872644205 + return meta::symbols_accessor(lib_path); +} + +} + + +//----------------------------------------------------------------------- +// +// expected with diagnostic to return to apply_metafunctions +// +diagnostic: @struct type = { + value: std::string; +} + +expected: @union type = { + value: T; + error: diagnostic; + + operator=: (implicit out this, v: T) = set_value(v); + operator=: (implicit out this, u: diagnostic) = set_error(u); + + and_then: (move this, f: F) -> std::remove_cvref_t> = { + if is_value() { + return f(value()); + } + else { + return (error()); + } + } +} + +} + +} + + +namespace cpp2::meta { + + +//----------------------------------------------------------------------- +// +// (de)mangling +// +auto mangle(std::string res) + -> std::string +{ + // Mangle (id length, id) pairs according to + // https://en.wikipedia.org/wiki/Name_mangling#Complex_example + // "... by the GNU GCC 3.x compilers, according to the IA-64 (Itanium) ABI" + auto xpos = res.size(); + auto prev_id_end = xpos; + while ((xpos = res.find_last_of(':', xpos)) != res.npos) + { + res.replace(xpos - 1, 2, std::to_string(prev_id_end - xpos - 1)); + prev_id_end = xpos - 1; + } + + return res; +} + +auto mangle(declaration_node const& n) + -> std::string +{ + assert(n.identifier); + assert(n.identifier->template_arguments().empty()); + assert(n.parent_is_namespace()); + + return mangle(n.fully_qualified_name()); +} + +auto demangle(std::string_view s) + -> std::string +{ + std::string res{}; + + // Demangle (id length, id) pairs according to + // https://en.wikipedia.org/wiki/Name_mangling#Complex_example + // "... by the GNU GCC 3.x compilers, according to the IA-64 (Itanium) ABI" + while (!s.empty()) + { + auto length = s.substr(0, s.find_first_not_of("0123456789")); + s.remove_prefix(length.size()); + auto id = s.substr(0, unchecked_narrow(std::stoi(std::string{length}))); + s.remove_prefix(id.size()); + + assert(!length.empty()); + assert(!id.empty()); + + res += id; + if (!s.empty()) { + res += "::"; + } + } + + return res; +} + + +//----------------------------------------------------------------------- +// +// dll Load DLL and its symbols with the OS API +// +class dll +{ +public: + dll(std::string const& path) + : handle_{ +#ifdef _WIN32 + static_cast(LoadLibraryA(path.c_str())) +#else + static_cast(dlopen(path.c_str(), RTLD_NOW|RTLD_LOCAL)) +#endif // _WIN32 + , +[](void* handle) + { +#ifdef _WIN32 + FreeLibrary(static_cast(handle)); +#else + dlclose(handle); +#endif // _WIN32 + } + } + { + if(!handle_) { + cpp2_default.report_violation(("failed to load DLL '" + path + "': " + get_last_error()).c_str()); + } + } + + template + auto get_alias(std::string const& name) noexcept -> T& + { +#ifdef _WIN32 + auto symbol = reinterpret_cast(GetProcAddress(static_cast(handle_.get()), name.c_str())); +#else + auto symbol = dlsym(handle_.get(), name.c_str()); + if(!symbol) + { + // Some platforms export with additional underscore, so try that + auto const us_name = "_" + name; + symbol = dlsym(handle_.get(), us_name.c_str()); + } +#endif // _WIN32 + if (!symbol) { + cpp2_default.report_violation(("failed to load DLL symbol '" + name+ "': " + get_last_error()).c_str()); + } + return **reinterpret_cast(symbol); + } +private: + std::shared_ptr handle_; + + static auto get_last_error() noexcept -> std::string + { +#ifdef _WIN32 + DWORD errorMessageID = GetLastError(); + if(errorMessageID == 0) { + return {}; // No error message has been recorded + } + LPSTR messageBuffer = nullptr; + auto size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + errorMessageID, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR)&messageBuffer, + 0, + nullptr + ); + std::string message(messageBuffer, unchecked_narrow(size)); + LocalFree(messageBuffer); + return message; +#else + return std::string{dlerror()}; +#endif // _WIN32 + } + +}; + + +//----------------------------------------------------------------------- +// +// get_reachable_metafunction_symbols +// +struct library +{ + std::string_view name; + std::vector symbols; +}; + +namespace this_execution { + +// Load Cpp2 libraries with metafunctions by opening DLL with the OS API +// +// The environment variable 'CPPFRONT_METAFUNCTION_LIBRARIES' +// is read and interpreted as ':'-separated Cpp2 metafunction library paths +std::span get_reachable_metafunction_symbols() +{ + static std::vector res = []{ + std::vector res; + + // FIXME: On Windows, using this approach with the system apis not set to utf8, will + // break if a metafunction library contains unicode codepoints in its name, a proper + // way to handle this would be to use _wgetenv and use wchar_t strings for the dll opening + // function + auto cpp1_libraries_cstr = std::getenv("CPPFRONT_METAFUNCTION_LIBRARIES"); + if ( + !cpp1_libraries_cstr + || cpp1_libraries_cstr[0] == '\0' + ) + { + return res; + } + + auto cpp1_libraries = std::string_view{cpp1_libraries_cstr}; + while (!cpp1_libraries.empty()) + { + auto colon = cpp1_libraries.find(':'); + auto lib_path = cpp1_libraries.substr(0, colon); + cpp1_libraries.remove_prefix(lib_path.size() + unsigned(colon != lib_path.npos)); + + auto lib = dll{std::string{lib_path}}; + + auto get_symbols = lib.get_alias(meta::symbols_accessor(lib_path).c_str()); + + res.push_back({lib_path, {}}); + auto c_strings = get_symbols(); + if (!c_strings || !*c_strings) { + cpp2_default.report_violation( + ("symbols accesor returns no symbols (in '" + std::string{lib_path} + "')").c_str() + ); + } + + for (; *c_strings; ++c_strings) { + auto symbol = res.back().symbols.emplace_back(*c_strings); + } + } + + return res; + }(); + + return res; +} + +} + + +//----------------------------------------------------------------------- +// +// load_metafunction +// +struct lookup_res { + std::string_view library; + dll_symbol const* symbol; +}; + +using load_metafunction_ret = std::function; + +// Load Cpp2 metafunction by opening DLL with the OS API +auto load_metafunction( + std::string const& name, + std::function(std::string const&)> lookup + ) + -> expected +{ + return lookup(name).and_then( + [](lookup_res res) + -> expected + { + auto [lib_path, cpp1_name] = res; + + auto lib = dll{std::string(lib_path)}; + return load_metafunction_ret{ + [ + fun = lib.get_alias(cpp1_name->c_str()), + lib = std::move(lib) + ] + (type_declaration& t) + -> void + { + fun(t); + } + }; + } + ); +} + +} + + +cpp2: namespace = { + +meta: namespace = { + + +//----------------------------------------------------------------------- +// +// Compiler services data +// +//----------------------------------------------------------------------- +// + +compiler_services_data: @cpp1_rule_of_zero type = +{ + // Common data members + // + public errors : *std::vector; + public includes : *std::set; + public errors_original_size : int; + public generated_tokens : *stable_vector; + public parser : cpp2::parser; + public metafunction_name : std::string = (); + public metafunction_args : std::vector = (); + public metafunctions_used : bool = false; + + // Make function + // + make: ( + errors_ : *std::vector, + includes_ : *std::set, + generated_tokens_ : *stable_vector, + translation_unit_has_interface: bool + ) + -> compiler_services_data + = { + return (errors_, + includes_, + unchecked_narrow(std::ssize(errors_*)), + generated_tokens_, + cpp2::parser(errors_*, includes_*, translation_unit_has_interface)); + } +} + + +//----------------------------------------------------------------------- +// +// regex - creates regular expressions from members +// +// Each member that starts with `regex` is replaced by a regular expression +// of the initializer string. E.g.: +// ``` +// regex := "ab"; +// ``` +// is replaced with +// ``` +// regex := ::cpp2::regex::regular_expression<...>; +// ``` +// +error_func: type == std::function< (x: std::string) -> void >; + +// Possible modifiers for a regular expression. +// +expression_flags: @flag_enum type = +{ + case_insensitive; // mod: i + multiple_lines; // mod: m + single_line; // mod: s + no_group_captures; // mod: n + perl_code_syntax; // mod: x + perl_code_syntax_in_classes; // mod: xx +} + + +// Tokens for regular expressions. +// + +// Basic class for a regex token. +// +regex_token: @polymorphic_base type = +{ + public string_rep: std::string; + + operator=:(out this, str: std::string) = { + string_rep = str; + } + + operator=:(out this) = { + string_rep = ""; + } + + //parse: (inout ctx: parse_context) -> token_ptr; + generate_code: (virtual this, inout _: generation_context); // Generate the matching code. + + add_groups: (virtual this, inout _: std::set) = {} // Adds all group indices to the set. + to_string: (this) -> std::string = { return string_rep; } // Create a string representation. + set_string: (inout this, s: std::string) = { string_rep = s; } // Set the string representation. +} + +token_ptr : type == std::shared_ptr; +token_vec: type == std::vector; + + +// Adds a check in code generation. +// +regex_token_check: @polymorphic_base type = +{ + this: regex_token; + + check: std::string; + + operator=:(out this, str: std::string, check_: std::string) = { + regex_token = (str); + check = check_; + } + + generate_code: (override this, inout ctx: generation_context) = { + ctx..add_check(check + "(" + ctx..match_parameters() + ")"); + } +} + + +// Adds code in code generation. +// +regex_token_code: @polymorphic_base type = +{ + this: regex_token; + + code: std::string; + + operator=:(out this, str: std::string, code_: std::string) = { + regex_token = (str); + code = code_; + } + + generate_code: (override this, inout ctx: generation_context) = { + ctx..add(code); + } +} + + +// Token that does not influence the matching. E.g. comment. +// +regex_token_empty: @polymorphic_base type = +{ + this: regex_token; + + operator=:(out this, str: std::string) = { + regex_token = (str); + } + + generate_code: (override this, inout _: generation_context) = { + // Nothing. + } +} + + +// Represents a list of regex tokens as one token. +// +regex_token_list: @polymorphic_base type = +{ + this: regex_token; + + public tokens: token_vec; + + operator=:(out this, t: token_vec) = { + regex_token = (gen_string(t)); + tokens = t; + } + + generate_code: (override this, inout ctx: generation_context) = { + for tokens do (token) { + token*..generate_code(ctx); + } + } + + add_groups: (override this, inout groups: std::set) = { + for tokens do (token) { + token*..add_groups(groups); + } + } + + gen_string: (vec: token_vec) -> std::string = { + r : std::string = ""; + for vec do (token) { + r += token*..to_string(); + } + return r; + } +} + + +// +// Parse and generation context. +// + +// State of the current capturing group. See '()' +// +parse_context_group_state: @struct type = +{ + cur_match_list: token_vec = (); // Current list of matchers. + alternate_match_lists: token_vec = (); // List of alternate matcher lists. E.g. ab|cd|xy. + modifiers : expression_flags = (); // Current modifiers for the group/regular expression. + + // Start a new alternative. + next_alternative: (inout this) = { + new_list: token_vec = (); + std::swap(new_list, cur_match_list); + post_process_list(new_list); + _ = alternate_match_lists..insert(alternate_match_lists..end(), shared.new(new_list)); + } + + // Swap this state with the other one. NOLINTNEXTLINE(performance-noexcept-swap) + swap: (inout this, inout t: parse_context_group_state) = { // NOLINT(performance-noexcept-swap) + std::swap(cur_match_list, t.cur_match_list); + std::swap(alternate_match_lists, t.alternate_match_lists); + std::swap(modifiers, t.modifiers); + } + + // Convert this state into a regex token. + get_as_token: (inout this) -> token_ptr = { + if alternate_match_lists..empty() { + post_process_list(cur_match_list); + return shared.new(cur_match_list); + } + else { + next_alternative(); + return shared.new(alternate_match_lists); + } + } + + // Add a token to the current matcher list. + add: (inout this, token: token_ptr) = { + cur_match_list..push_back(token); + } + + // True if current matcher list is empty. + empty: (this) -> bool = cur_match_list..empty(); + + + // Apply optimizations to the matcher list. + post_process_list: (inout list: token_vec) = { + // Merge all characters + merge_pos := list..begin(); + while merge_pos != list..end() next (merge_pos++) { + if merge_pos** is char_token { + combine_pos := merge_pos + 1; + while combine_pos != list..end() && combine_pos** is char_token { // The erase advances combine_pos + (merge_pos** as char_token)..append(combine_pos** as char_token); + combine_pos = list..erase(combine_pos); + } + } + } + } +} + + +// State for the branch reset. Takes care of the group numbering. See '(|)'. +// +parse_context_branch_reset_state: @struct type = +{ + is_active : bool = false; // If we have a branch reset group. + cur_group : int = 1; // Next group identifier. 0 == global capture group. + max_group : int = 1; // Maximum group identifier generated. + from : int = 1; // Starting identifier on new alternative branch. + + // Next group identifier. + next: (inout this) -> int = { + g := cur_group; + cur_group += 1; + max_group = max(max_group, cur_group); + + return g; + } + + // Set next group identifier. + set_next: (inout this, g: int) = { + cur_group = g; + max_group = max(max_group, g); + } + + // Start a new alternative branch. + next_alternative: (inout this) = { + if is_active { + cur_group = from; + } + } + + // Initialize for a branch reset group. + set_active_reset: (inout this, restart: int) = { + is_active = true; + cur_group = restart; + from = restart; + max_group = restart; + } +} + + +// Context during parsing of the regular expressions. +// +// Keeps track of the distributed group identifiers, current parsed group and branch resets. +// +parse_context: type = +{ + regex: std::string_view; // Regular expression string. + pos: size_t = 0; // Current parsing position. + root: token_ptr; // Token representing the regular expression. + + cur_group_state: parse_context_group_state = (); + cur_branch_reset_state: parse_context_branch_reset_state = (); + + + public named_groups: std::map = (); + + error_out: error_func; // TODO: Declaring std::function fails for cpp2. + has_error: bool = false; + + operator=:(out this, r: std::string_view, e) = { + regex = r; + root = shared.new(""); + error_out = e; + } + + // State management functions + // + + // Returned group state needs to be stored and provided in `end_group`. + start_group: (inout this) -> parse_context_group_state = + { + old_state: parse_context_group_state = (); + old_state..swap(cur_group_state); + cur_group_state.modifiers = old_state.modifiers; + + return old_state; + } + + // `old_state` argument needs to be from start group. + end_group: (inout this, old_state: parse_context_group_state) -> token_ptr = + { + inner := cur_group_state..get_as_token(); + cur_group_state = old_state; + return inner; + } + + get_modifiers: (this) -> expression_flags = { + return cur_group_state.modifiers; + } + + set_modifiers: (inout this, mod: expression_flags) = { + cur_group_state.modifiers = mod; + } + + // Branch reset management functions + // + + branch_reset_new_state: (inout this) -> parse_context_branch_reset_state = + { + old_state: parse_context_branch_reset_state = (); + std::swap(old_state, cur_branch_reset_state); + + cur_branch_reset_state..set_active_reset(old_state.cur_group); + return old_state; + } + + branch_reset_restore_state: (inout this, old_state: parse_context_branch_reset_state) = + { + max_group := cur_branch_reset_state.max_group; + cur_branch_reset_state = old_state; + cur_branch_reset_state..set_next(max_group); + } + + next_alternative: (inout this) = + { + cur_group_state..next_alternative(); + cur_branch_reset_state..next_alternative(); + } + + // Regex token management + // + add_token: (inout this, token: token_ptr) = { + cur_group_state..add(token); + } + + has_token: (this) -> bool = { + return !cur_group_state..empty(); + } + + pop_token: (inout this) -> token_ptr = + { + r : token_ptr = nullptr; + if has_token() { + r = cur_group_state.cur_match_list..back(); + cur_group_state.cur_match_list..pop_back(); + } + + return r; + } + + get_as_token: (inout this) -> token_ptr = { + return root; + } + + // Group management + // + get_cur_group: (this) -> int = { + return cur_branch_reset_state.cur_group; + } + + next_group: (inout this) -> int = { + return cur_branch_reset_state..next(); + } + + set_named_group: (inout this, name: std::string, id: int) = + { + if !named_groups..contains(name) { // Redefinition of group name is not an error. The left most one is retained. + named_groups[name] = id; + } + } + + get_named_group: (this, name: std::string) -> int = + { + iter := named_groups..find(name); + if iter == named_groups..end() { + return -1; + } + else { + return iter*.second; + } + } + + // Position management functions + // + current: (this) -> char = { return regex[pos]; } + + // Get the next token in the regex, skipping spaces according to the parameters. See `x` and `xx` modifiers. + private get_next_position: (in this, in_class: bool, no_skip: bool) -> size_t = + { + perl_syntax := false; + if !no_skip { + if in_class { + perl_syntax = get_modifiers()..has(expression_flags::perl_code_syntax) && get_modifiers()..has(expression_flags::perl_code_syntax_in_classes); + } + else { + perl_syntax = get_modifiers()..has(expression_flags::perl_code_syntax); + } + } + cur := pos + 1; + if perl_syntax { + while cur < regex..size() next (cur += 1) { + n: = regex[cur]; + + if space_class::includes(n) { + continue; + } + else if !in_class && '#' == n { + cur = regex..find('\n', cur); + if std::string::npos == cur { + // No new line, comment runs until the end of the pattern + cur = regex..size(); + } + } + else { // None space none comment char + break; + } + } + } + + // Check for end of file. + if cur > regex..size() { + cur = regex..size(); + } + return cur; + } + + // Return true if next token is available. + private next_impl: (inout this, in_class: bool, no_skip: bool) -> bool = + { + pos = get_next_position(in_class, no_skip); + if pos != regex..size() { + return true; + } + else { + return false; + } + } + + next : (inout this) = next_impl(false, false); + next_in_class: (inout this) = next_impl( true, false); + next_no_skip : (inout this) = next_impl(false, true); + + next_n: (inout this, n: int) -> bool = { + r := true; + cur := 0; + while r && cur < n next (r = next()) { + cur += 1; + } + return r; + } + + has_next: (this) -> bool = { return pos < regex..size(); } + + private grab_until_impl: (inout this, in e: std::string, out r: std::string, any: bool) -> bool = + { + end:= pos; // NOLINT(clang-analyzer-deadcode.DeadStores) + if any { + end = regex..find_first_of(e, pos); + } + else { + end = regex..find(e, pos); + } + + if end != std::string_view::npos { + r = regex..substr(pos, end - pos); + pos = end; + return true; + } + else { + r = ""; + return false; + } + } + + grab_until: (inout this, in e: std::string, out r: std::string) = grab_until_impl(e, out r, false); + grab_until: (inout this, in e: char, out r: std::string) = grab_until_impl(std::string(1, e), out r, false); + grab_until_one_of: (inout this, in e: std::string, out r: std::string) = grab_until_impl(e, out r, true); + + grab_n: (inout this, in n: int, out r: std::string) -> bool = + { + if pos + n as size_t <= regex..size() { + r = regex..substr(pos, n as size_t); + pos += (n as size_t) - 1; + return true; + } + else { + r = ""; + return false; + } + } + + grab_number: (inout this) -> std::string = + { + start := pos; + start_search := pos; + if regex[start_search] == '-' { + start_search += 1; + } + end := regex..find_first_not_of("1234567890", start_search); + + r : std::string; + if end != std::string::npos { + r = regex..substr(start, end - start); + pos = end - 1; + } + else { + r = regex..substr(start); + pos = regex..size() - 1; + } + return r; + } + + private peek_impl: (in this, in_class: bool) -> char = { + next_pos := get_next_position(in_class, false); + if next_pos < regex..size() { + return regex[next_pos]; + } + else { + return '\0'; + } + } + + peek : (in this) = peek_impl(false); + peek_in_class: (in this) = peek_impl( true); + + + // Parsing functions + // + parser_group_modifiers: (inout this, change_str: std::string, inout parser_modifiers: expression_flags) -> bool = + { + is_negative := false; + is_reset := false; + + apply := :(flag: expression_flags) = { + if is_negative&$* { + parser_modifiers&$*..clear(flag); + } + else { + parser_modifiers&$*..set(flag); + } + }; + + iter := change_str..begin(); + while iter != change_str..end() next (iter++) + { + cur := iter*; + if cur == '^' { + is_reset = true; + parser_modifiers = expression_flags::none; + } + else if cur == '-' { + if is_reset { _= error("No negative modifier allowed."); return false; } + is_negative = true; + } + else if cur == 'i' { apply(expression_flags::case_insensitive); } + else if cur == 'm' { apply(expression_flags::multiple_lines); } + else if cur == 's' { apply(expression_flags::single_line); } + else if cur == 'n' { apply(expression_flags::no_group_captures); } + else if cur == 'x' { + if (iter + 1) == change_str..end() || (iter + 1)* != 'x' { + // x modifier + apply(expression_flags::perl_code_syntax); + + // Just x unsets xx and remove x also removes xx + parser_modifiers..clear(expression_flags::perl_code_syntax_in_classes); + } + else { // xx modifier + // xx also sets or unsets x + apply(expression_flags::perl_code_syntax); + apply(expression_flags::perl_code_syntax_in_classes); + + iter++; // Skip the second x + } + } + else { + _= error("Unknown modifier: (cur)$"); return false; + } + } + + return true; + } + + parse_until:(inout this, term: char) -> bool = { + cur_token: token_ptr = (); + + while valid() next _ = next() + { + if term == current() { break; } + + cur_token = nullptr; + + if !cur_token && valid() { cur_token = alternative_token::parse(this); } + if !cur_token && valid() { cur_token = any_token::parse(this); } + if !cur_token && valid() { cur_token = class_token::parse(this); } + if !cur_token && valid() { cur_token = escape_token_parse(this); } + if !cur_token && valid() { cur_token = global_group_reset_token_parse(this); } + if !cur_token && valid() { cur_token = group_ref_token::parse(this); } + if !cur_token && valid() { cur_token = group_token::parse(this); } + if !cur_token && valid() { cur_token = hexadecimal_token_parse(this); } + if !cur_token && valid() { cur_token = line_end_token_parse(this); } + if !cur_token && valid() { cur_token = line_start_token_parse(this); } + if !cur_token && valid() { cur_token = named_class_token_parse(this); } + if !cur_token && valid() { cur_token = octal_token_parse(this); } + if !cur_token && valid() { cur_token = range_token::parse(this); } + if !cur_token && valid() { cur_token = special_range_token::parse(this); } + if !cur_token && valid() { cur_token = word_boundary_token_parse(this); } + + // Everything else is matched as it is. + if !cur_token && valid() { cur_token = char_token::parse(this); } + + if cur_token && valid() { + add_token(cur_token); + } else { + return false; + } + } + + return true; + } + + parse: (inout this, modifiers: std::string) -> bool = + { + + flags : expression_flags = (); + if !parser_group_modifiers(modifiers, flags) { return false; } + set_modifiers(flags); + + r := parse_until('\0'); + if r { + root = cur_group_state..get_as_token(); + } + + return r; + } + + // Misc functions + + get_pos: (this) = pos; + get_range: (this, start: size_t, end: size_t) = std::string(regex..substr(start, end - start + 1)); + valid: (this) -> bool = { return has_next() && !has_error; } + + error: (inout this, err: std::string) -> token_ptr = { + has_error = true; + error_out("Error during parsing of regex '(regex)$' at position '(pos)$': (err)$"); + return nullptr; + } +} + + +// Context for one function generation. Generation of functions can be interleaved, +// therefore we buffer the code for one function here. +// +generation_function_context: @struct type = { + code: std::string = ""; + tabs: std::string = ""; + + add_tabs: (inout this, c: int) = { + i: int = 0; + while i < c next i += 1 { + tabs += " "; + } + } + + remove_tabs: (inout this, c: int) = { + tabs = tabs..substr(0, (c as size_t) * 2); + } +} + + +// Context for generating the state machine. +generation_context: type = +{ + gen_stack: std::vector = (1); // Element 0 contains all the code. + + matcher_func: int = 0; + reset_func: int = 0; + temp_name: int = 0; + entry_func: std::string = ""; + + // Generation helpers + // + match_parameters: (this) -> std::string = { return "r.pos, ctx"; } + + // Code generation. + + // Add code line. + add: (inout this, s: std::string) = { + cur := get_current(); + cur*.code += "(cur*.tabs)$(s)$\n"; + } + + // Add check for token. The check needs to be a function call that returns a boolean. + add_check: (inout this, check: std::string) = { + cur := get_current(); + cur*.code += "(cur*.tabs)$if !cpp2::regex::(check)$ { r.matched = false; break; }\n"; + } + + // Add a stateful check. The check needs to return a `match_return`. + add_statefull: (inout this, next_func: std::string, check: std::string) = + { + end_func_statefull(check); + + name := next_func..substr(0, next_func..size() - 2); + start_func_named(name); + } + + protected start_func_named: (inout this, name: std::string) = + { + cur := new_context(); + + cur*.code += "(cur*.tabs)$(name)$: @struct type = {\n"; + cur*.code += "(cur*.tabs)$ operator(): (this, cur: Iter, inout ctx: context, other) -> cpp2::regex::match_return = {\n"; + cur*.code += "(cur*.tabs)$ r := ctx..pass(cur);\n"; + cur*.code += "(cur*.tabs)$ do {\n"; + cur*..add_tabs(3); + } + + protected start_func: (inout this) -> std::string = + { + name := gen_func_name(); + start_func_named(name); + return name + "()"; + } + + protected end_func_statefull: (inout this, s: std::string) = + { + cur := get_current(); + cur*..remove_tabs(3); + cur*.code += "\n"; + cur*.code += "(cur*.tabs)$ } while false;\n"; + cur*.code += "(cur*.tabs)$ if r.matched {\n"; + cur*.code += "(cur*.tabs)$ r = (s)$;\n"; + cur*.code += "(cur*.tabs)$ }\n"; + cur*.code += "(cur*.tabs)$ else {\n"; + cur*.code += "(cur*.tabs)$ r.pos = ctx.end;\n"; + cur*.code += "(cur*.tabs)$ }\n"; + cur*.code += "(cur*.tabs)$ return r;\n"; + cur*.code += "(cur*.tabs)$ }\n"; + cur*.code += "(cur*.tabs)$}\n"; + + finish_context(); + } + + // Generate the function for a token. + generate_func: (inout this, token: token_ptr) -> std::string = + { + name := start_func(); + token*..generate_code(this); + end_func_statefull("other((match_parameters())$)"); + + return name; + } + + // Generate the reset for a list of group identifiers. + generate_reset: (inout this, groups: std::set) -> std::string = + { + if groups..empty() { + return "cpp2::regex::no_reset()"; + } + + name := gen_reset_func_name(); + cur := new_context(); + + cur*.code += "(cur*.tabs)$(name)$: @struct type = {\n"; + cur*.code += "(cur*.tabs)$ operator(): (this, inout ctx) = {\n"; + for groups do (g) { + cur*.code += "(cur*.tabs)$ ctx..set_group_invalid((g)$);\n"; + } + cur*.code += "(cur*.tabs)$ }\n"; + cur*.code += "(cur*.tabs)$}\n"; + + finish_context(); + + return name + "()"; + } + + // Name generation + // + protected gen_func_name: (inout this) -> std::string = { + cur_id : = matcher_func; + matcher_func += 1; + return "func_(cur_id)$"; + } + + next_func_name: (inout this) -> std::string = { + return gen_func_name() + "()"; + } + + protected gen_reset_func_name: (inout this) -> std::string = { + cur_id : = reset_func; + reset_func += 1; + return "reset_(cur_id)$"; + } + + gen_temp: (inout this) -> std::string = { + cur_id := temp_name; + temp_name += 1; + return "tmp_(cur_id)$"; + } + + // Context management + // + new_context: (inout this) -> *generation_function_context = { + gen_stack..push_back(generation_function_context()); + cur := get_current(); + cur*.tabs = " "; + + return cur; + } + + finish_context: (inout this) = { + cur := get_current(); + base := get_base(); + base*.code += cur*.code; + + gen_stack..pop_back(); + } + + // Misc functions + // + private get_current: (inout this) -> *generation_function_context = { + return gen_stack..back()&; + } + + private get_base: (inout this) -> *generation_function_context = { + return gen_stack[0]&; + } + + get_entry_func: (this) -> std::string = { + return entry_func; + } + + create_named_group_lookup: (this, named_groups: std::map) -> std::string = + { + res: std::string = "get_named_group_index: (name) -> int = {\n"; + + // Generate if selection. + sep: std::string = ""; + for named_groups do (cur) { + res += "(sep)$if name == \"(cur.first)$\" { return (cur.second)$; }"; + sep = "else "; + } + + // Generate else branch or return if list is empty. + if named_groups..empty() { + res += " _ = name;\n"; + res += " return -1;\n"; + } + else { + res += " else { return -1; }\n"; + } + res += "}\n"; + return res; + } + + + // Run the generation for the token. + run: (inout this, token: token_ptr) -> std::string = { + entry_func = generate_func(token); + + return get_base()*.code; + } +} + +// Regex syntax: | Example: ab|ba +// +// Non greedy implementation. First alternative that matches is chosen. +// +alternative_token: @polymorphic_base type = +{ + this: regex_token_empty = (""); // No code gen here. alternative_token_gen is created in the parse_context + + operator=:(out this) = {} + + parse: (inout ctx: parse_context) -> token_ptr = { + if ctx..current() != '|' { return nullptr; } + + if !ctx..has_token() { return ctx..error("Alternative with no content."); } + ctx..next_alternative(); + return shared.new(); + } +} + +alternative_token_gen: @polymorphic_base type = +{ + this: regex_token; + + alternatives: token_vec; + + operator=: (out this, a: token_vec) = { + regex_token = gen_string(a); + alternatives = a; + } + + generate_code: (override this, inout ctx: generation_context) = + { + functions: std::string = ""; + + for alternatives do (cur) { + groups: std::set = (); + cur*..add_groups(groups); + + functions += ", " + ctx..generate_func(cur); + functions += ", " + ctx..generate_reset(groups); + } + + next_name := ctx..next_func_name(); + + ctx..add_statefull(next_name, "cpp2::regex::alternative_token_matcher::match((ctx..match_parameters())$, other, (next_name)$ (functions)$)"); + } + + add_groups: (override this, inout groups: std::set) = + { + for alternatives do (cur) { + cur*..add_groups(groups); + } + } + + gen_string: (a: token_vec) -> std::string = + { + r: std::string = ""; + sep: std::string = ""; + + for a do (cur) { + r += sep + cur*..to_string(); + sep = "|"; + } + + return r; + } +} + + +// Regex syntax: . +// +any_token: @polymorphic_base type = +{ + this: regex_token_check = ("."); + + operator=:(out this, single_line: bool) = { + regex_token_check = (".", "any_token_matcher"); + } + + parse: (inout ctx: parse_context) -> token_ptr = { + if '.' != ctx..current() { return nullptr;} + + return shared.new(ctx..get_modifiers()..has(expression_flags::single_line)); + } +} + + +// Regex syntax: a +// +char_token: @polymorphic_base type = +{ + this: regex_token; + + token : std::string; + ignore_case: bool; + + operator=: (out this, t: char, ignore_case_: bool) = { + regex_token = (std::string(1, t)); + token = t; + ignore_case = ignore_case_; + } + + parse: (inout ctx: parse_context) -> token_ptr = { + return shared.new(ctx..current(), ctx..get_modifiers()..has(expression_flags::case_insensitive)); + } + + generate_code: (override this, inout ctx: generation_context) = + { + if ignore_case { + upper: std::string = token; + lower: std::string = token; + + (copy i: size_t = 0) while i < token..size() next i += 1 { + lower[i] = string_util::safe_tolower(token[i]); + upper[i] = string_util::safe_toupper(token[i]); + } + + if upper != lower { + gen_case_insensitive(lower, upper, ctx); + } + else { + gen_case_sensitive(ctx); + } + } + else { + gen_case_sensitive(ctx); + } + } + + gen_case_insensitive: (this, lower: std::string, upper: std::string, inout ctx: generation_context) = + { + name: std::string = "str_(ctx..gen_temp())$"; + lower_name: std::string = "lower_(name)$"; + upper_name: std::string = "upper_(name)$"; + size := token..size(); + ctx..add("(lower_name)$ : std::array = \"(add_escapes(lower))$\";"); // TODO: Add constexpr when Issue https://github.com/hsutter/cppfront/issues/1104 is resolved. + ctx..add("(upper_name)$ : std::array = \"(add_escapes(upper))$\";"); // TODO: Add constexpr when Issue https://github.com/hsutter/cppfront/issues/1104 is resolved. + ctx..add("if std::distance(r.pos, ctx.end) < (size)$ {"); + ctx..add(" r.matched = false;"); + ctx..add(" break;"); + ctx..add("}"); + ctx..add(""); + ctx..add("(copy i : int = 0) while i < (size)$ next (i += 1) {"); + ctx..add(" if !((lower_name)$[i] == r.pos[i] || (upper_name)$[i] == r.pos[i]) { r.matched = false; }"); + ctx..add("}"); + ctx..add(""); + ctx..add("if r.matched { r.pos += (size)$; }"); + ctx..add("else { break; }"); + } + + gen_case_sensitive: (this, inout ctx: generation_context) = + { + name: std::string = "str_(ctx..gen_temp())$"; + size := token..size(); + ctx..add("(name)$ : std::array = \"(add_escapes(token))$\";"); // TODO: Add constexpr when Issue https://github.com/hsutter/cppfront/issues/1104 is resolved. + ctx..add("if std::distance(r.pos, ctx.end) < (size)$ {"); + ctx..add(" r.matched = false;"); + ctx..add(" break;"); + ctx..add("}"); + ctx..add(""); + ctx..add("(copy i : int = 0) while i < (size)$ next (i += 1) {"); + ctx..add(" if (name)$[i] != r.pos[i] { r.matched = false; }"); + ctx..add("}"); + ctx..add(""); + ctx..add("if r.matched { r.pos += (size)$; }"); + ctx..add("else { break; }"); + } + + add_escapes: (this, copy str: std::string) -> std::string = + { + str = string_util::replace_all(str, "\\", "\\\\"); + str = string_util::replace_all(str, "\a", "\\a"); + str = string_util::replace_all(str, "\f", "\\f"); + str = string_util::replace_all(str, "\x1b", "\" \"\\x1b\" \""); // Generate a separated string. This prevents + // situations like `\x1bblub` from generating + // wrong hex characters. + str = string_util::replace_all(str, "\n", "\\n"); + str = string_util::replace_all(str, "\r", "\\r"); + str = string_util::replace_all(str, "\t", "\\t"); + + return str; + } + + append: (inout this, that) = { + this.token += that.token; + this.string_rep += that.string_rep; + } +} + + +// Regex syntax: [] Example: [abcx-y[:digits:]] +// +class_token: @polymorphic_base type = +{ + this : regex_token = (); + + negate : bool; + case_insensitive: bool; + class_str : std::string; + + operator=: (out this, negate_: bool, case_insensitive_: bool, class_str_: std::string, str: std::string) = + { + regex_token = str; + negate = negate_; + case_insensitive = case_insensitive_; + class_str = class_str_; + } + + // TODO: Rework class generation: Generate check functions for classes. + parse: (inout ctx: parse_context) -> token_ptr = + { + if ctx..current() != '[' { return nullptr; } + + start_pos := ctx..get_pos(); + + supported_classes: std::vector = ("alnum", "alpha", "ascii", "blank", "cntrl", "digits", "graph", + "lower", "print", "punct", "space", "upper", "word", "xdigit"); + + classes: std::vector = (); + + // First step: parse until the end bracket and push single chars, ranges or groups on the class stack. + is_negate := false; + first := true; + range := false; + while ctx..next_in_class() && (ctx..current() != ']' || first) + { + if ctx..current() == '^' + { + is_negate = true; + continue; // Skip rest of the loop. Also the first update. + } + + if ctx..current() == '[' && ctx..peek_in_class() == ':' + { + // We have a character class. + _ = ctx..next_n(2); // Skip [: + + name: std::string = ""; + if !ctx..grab_until(":]", out name) { return ctx..error("Could not find end of character class."); } + if supported_classes..end() == std::find(supported_classes..begin(), supported_classes..end(), name) { + return ctx..error("Unsupported character class. Supported ones are: (string_util::join(supported_classes))$"); + } + + classes..push_back("[:(name)$:]"); + + _ = ctx..next(); // Skip ':' pointing to the ending ']'. + } + else if ctx..current() == '\\' + { + if ctx..next_no_skip() && (ctx..current() != ']') + { + if ' ' == ctx..current() + && ctx..get_modifiers()..has(expression_flags::perl_code_syntax) + && ctx..get_modifiers()..has(expression_flags::perl_code_syntax_in_classes) + { + classes..push_back(std::string(1, ctx..current())); + } + else { + name := ""; + if 'd' == ctx..current() { name = "short_digits"; } + else if 'D' == ctx..current() { name = "short_not_digits"; } + else if 'h' == ctx..current() { name = "short_hor_space"; } + else if 'H' == ctx..current() { name = "short_not_hor_space"; } + else if 's' == ctx..current() { name = "short_space"; } + else if 'S' == ctx..current() { name = "short_not_space"; } + else if 'v' == ctx..current() { name = "short_ver_space"; } + else if 'V' == ctx..current() { name = "short_not_ver_space"; } + else if 'w' == ctx..current() { name = "short_word"; } + else if 'W' == ctx..current() { name = "short_not_word"; } + else { + return ctx..error("Unknown group escape."); + } + classes..push_back("[:(name)$:]"); + } + } else { + return ctx..error("Escape without a following character."); + } + } + else if ctx..current() == '-' + { + if first { // Literal if first entry. + classes..push_back("(ctx..current())$"); + } else { + range = true; + } + } + else + { + if range { // Modify last element to be a range. + classes..back() += "-(ctx..current())$"; + range = false; + } + else { + classes..push_back("(ctx..current())$"); + } + } + + first = false; + } + + if ctx..current() != ']' { + return ctx..error("Error end of character class definition before terminating ']'."); + } + end_pos := ctx..get_pos(); + + if range { // If '-' is last entry treat it as a literal char. + classes..push_back("-"); + } + + // Second step: Wrap the item on the class stack with corresponding class implementation. + for classes do (inout cur) + { + if cur..starts_with("[:") { + name := cur..substr(2, cur..size() - 4); + cur = create_matcher("(name)$_class", ""); + } + else if 1 != cur..size() { + cur = create_matcher("range_class_entry", "'(cur[0])$', '(cur[2])$'"); + } + else { + cur = create_matcher("single_class_entry", "'(cur)$'"); + } + } + + inner := string_util::join(classes); + string_rep := ctx..get_range(start_pos, end_pos); + return shared.new( + is_negate, + ctx..get_modifiers()..has(expression_flags::case_insensitive), + inner, + string_rep + ); + } + + generate_code: (override this, inout ctx: generation_context) = + { + ctx..add_check("class_token_matcher::match((ctx..match_parameters())$)"); + } + + private create_matcher: (name: std::string, template_arguments: std::string) -> std::string = + { + sep := ", "; + if template_arguments..empty() { sep = ""; } + + return "::cpp2::regex::(name)$"; + } +} + + +// Regex syntax: \a or \n or \[ +// +escape_token_parse: (inout ctx: parse_context) -> token_ptr = +{ + if ctx..current() != '\\' { return nullptr; } + + + if std::string::npos == std::string("afenrt^.[]()*{}?+|\\")..find(ctx..peek()) { + return nullptr; + } + + _ = ctx..next(); // Skip escape + + if std::string::npos != std::string("afenrt\\")..find(ctx..current()) + { + // Escape of string special char + t : char = '\0'; + if 'a' == ctx..current() { t = '\a'; } + else if 'f' == ctx..current() { t = '\f'; } + else if 'e' == ctx..current() { t = '\x1b'; } + else if 'n' == ctx..current() { t = '\n'; } + else if 'r' == ctx..current() { t = '\r'; } + else if 't' == ctx..current() { t = '\t'; } + else if '\\' == ctx..current() { t = '\\'; } + else { return ctx..error("Internal: missing switch case for special escape."); } + + r: = shared.new(t, false); + r*..set_string("\\(ctx..current())$"); + return r; + } + else + { + // Escape of regex special char + r := shared.new(ctx..current(), false); + r*..set_string("\\(ctx..current())$"); + return r; + } + +} + + +// Regex syntax: \K Example: ab\Kcd +// +global_group_reset_token_parse: (inout ctx: parse_context) -> token_ptr = +{ + if !(ctx..current() == '\\' && ctx..peek() == 'K') { return nullptr; } + + _ = ctx..next(); // Skip escape. + return shared.new("\\K", "ctx..set_group_start(0, r.pos);"); +} + + +// Regex syntax: \ Example: \1 +// \g{name_or_number} +// \k{name_or_number} +// \k +// \k'name_or_number' +// +group_ref_token: @polymorphic_base type = +{ + this : regex_token = (); + + id : int; + case_insensitive: bool; + + operator=:(out this, id_: int, case_insensitive_: bool, str: std::string) = + { + regex_token = str; + id = id_; + case_insensitive = case_insensitive_; + } + + parse: (inout ctx: parse_context) -> token_ptr = + { + if ctx..current() != '\\' { return nullptr; } + + str : std::string = "\\"; + group : std::string = ""; + + if '0' <= ctx..peek() <= '9' + { + _ = ctx..next(); // Skip escape + group = ctx..grab_number(); + + if group..size() >= 3 as size_t + { + // Octal syntax (\000) not a group ref matcher. + number := 0; + if !string_util::string_to_int(group, number, 8) { return ctx..error("Could not convert octal to int."); } + + number_as_char : char = unchecked_narrow(number); + + token := shared.new(number_as_char, ctx..get_modifiers()..has(expression_flags::case_insensitive)); + token*..set_string("\\(string_util::int_to_string<8>(number_as_char as int))$"); + + return token; + } + + str += group; + // Regular group ref + } + else if 'g' == ctx..peek() + { + _ = ctx..next(); // Skip escape + if !ctx..next() { return ctx..error("Group escape without a following char."); } // Skip g + + str += "g"; + + if ctx..current() == '{' { + str += "{"; + if !(ctx..next() && ctx..grab_until('}', out group)) { return ctx..error("No ending bracket."); } + + str += group + "}"; + } + else { + group = ctx..grab_number(); + str += group; + } + } + else if 'k' == ctx..peek() + { + _ = ctx..next(); // Skip escape + if !ctx..next() { return ctx..error("Group escape without a following char."); } // Skip k + + str += "k"; + + term_char := '\0'; + if ctx..current() == '{' { term_char = '}'; } + else if ctx..current() == '<' { term_char = '>'; } + else if ctx..current() == '\'' { term_char = '\''; } + else { + return ctx..error("Group escape has wrong operator."); + } + + str += ctx..current(); + + if !(ctx..next() && ctx..grab_until(term_char, out group)) { return ctx..error("No ending bracket."); } + + str += group + term_char; + } + else + { + // No group ref matcher + return nullptr; + } + + // Parse the group + group = string_util::trim_copy(group); + group_id : int = 0; + if string_util::string_to_int(group, group_id) + { + if group_id < 0 { + group_id = ctx..get_cur_group() + group_id; + + if group_id < 1 { // Negative and zero are no valid groups. + return ctx..error("Relative group reference does not reference a valid group. (Would be (group_id)$.)"); + } + } + + if group_id >= ctx..get_cur_group() { + return ctx..error("Group reference is used before the group is declared."); + } + } + else + { + // Named group + group_id = ctx..get_named_group(group); + if -1 == group_id { return ctx..error("Group names does not exist. (Name is: (group)$)");} + } + + return shared.new(group_id, ctx..get_modifiers()..has(expression_flags::case_insensitive), str); + } + + generate_code: (override this, inout ctx: generation_context) = { + ctx..add_check("group_ref_token_matcher((ctx..match_parameters())$)"); + } +} + + +// Regex syntax: () Example: (abc) +// (?:) (?i:abc) +// (?<>:) (?:abc) +// (?#) (#Step 1 finished) +// (?|) (?|(abc)|(cde)) +// (?=) (?=abc) +// (?!) (?!abc) +// (*: token_ptr = + { + _ = ctx..next(); // Skip last token defining the syntax + + r := shared.new(positive); + + old_state := ctx..start_group(); + if !ctx..parse_until(')') { return ctx..error("Lookahead without a closing bracket."); } + r*.inner = ctx..end_group(old_state); + r*..set_string("((syntax)$(r*.inner*..to_string())$)"); + + return r; + } + + parse: (inout ctx: parse_context) -> token_ptr = + { + if ctx..current() != '(' { return nullptr; } + + has_id := !ctx..get_modifiers()..has(expression_flags::no_group_captures); + has_pattern := true; + group_name : std::string = ""; + group_name_brackets := true; + modifiers : std::string = ""; + modifiers_change_to : = ctx..get_modifiers(); + + // Skip the '(' + if !ctx..next() { return ctx..error("Group without closing bracket."); } + + if ctx..current() == '?' + { + // Special group + if !ctx..next_no_skip() { return ctx..error("Missing character after group opening."); } + + if ctx..current() == '<' || ctx..current() == '\'' + { + // Named group + end_char := ctx..current(); + if end_char == '<' { + end_char = '>'; + } else { + group_name_brackets = false; + } + has_id = true; // Force id for named groups. + if !ctx..next() /* skip '<' */ { return ctx..error("Missing ending bracket for named group."); } + if !ctx..grab_until(end_char, out group_name) { return ctx..error("Missing ending bracket for named group."); } + if !ctx..next() { return ctx..error("Group without closing bracket."); } + } + else if ctx..current() == '#' + { + // Comment + comment_str : std::string = ""; + _ = ctx..next(); // Skip # + if !ctx..grab_until(")", out comment_str) { return ctx..error("Group without closing bracket."); } + // Do not add comment. Has problems with ranges. + + // Pop token and add a list. This fixes comments between a token and a range + if ctx..has_token() { + list : token_vec = (); + list..push_back(ctx..pop_token()); + list..push_back(shared.new("(?#(comment_str)$)")); + + return shared.new(list); + } + else { + return shared.new("(?#(comment_str)$)"); + } + } + else if ctx..current() == '|' + { + // Branch reset group + + if !ctx..next() /* skip '|' */ { return ctx..error("Missing ending bracket for named group."); } + + old_parser_state := ctx..start_group(); + old_branch_state := ctx..branch_reset_new_state(); + if !ctx..parse_until(')') { return nullptr; } + ctx..branch_reset_restore_state(old_branch_state); + inner_ := ctx..end_group(old_parser_state); + + list: token_vec = (shared.new("(?|"), inner_, shared.new(")")); + return shared.new(list); + } + else if ctx..current() == '=' || ctx..current() == '!' + { + return parse_lookahead(ctx, "?(ctx..current())$", ctx..current() == '='); + } + else + { + // Simple modifier + has_id = false; + if !ctx..grab_until_one_of("):", out modifiers) { return ctx..error("Missing ending bracket for group."); } + if !ctx..parser_group_modifiers(modifiers, modifiers_change_to) { + return nullptr; + } + + if ')' == ctx..current() { + has_pattern = false; + } + else { + if !ctx..next() /* skip ':' */ { return ctx..error("Missing ending bracket for group."); } + } + } + } + else if ctx..current() == '*' + { + // Named pattern + _ = ctx..next(); // Skip *. + name: std::string = ""; + if !ctx..grab_until(':', out name) { return ctx..error("Missing colon for named pattern."); } + + if name == "pla" || name == "positive_lookahead" { + return parse_lookahead(ctx, "*(name)$:", true); + } + else if name == "nla" || name == "negative_lookahead" { + return parse_lookahead(ctx, "*(name)$:", false); + } + else { + return ctx..error("Unknown named group pattern: '(name)$'"); + } + } + + if has_pattern + { + // Regular group + + r := shared.new(); + if has_id { + r*.number = ctx..next_group(); + + if 0 != group_name..size() { + ctx..set_named_group(group_name, r*.number); + } + } + + old_state := ctx..start_group(); + ctx..set_modifiers(modifiers_change_to); + if !ctx..parse_until(')') { return nullptr; } + r*.inner = ctx..end_group(old_state); + r*..set_string(gen_string(group_name, group_name_brackets, !has_id, modifiers, r*.inner)); + + return r; + } + else + { + // Only a modifier + ctx..set_modifiers(modifiers_change_to); + + return shared.new("(?(modifiers)$)"); + } + } + + gen_string: (name: std::string, name_brackets: bool, has_modifier: bool, modifiers: std::string, inner_: token_ptr) -> std::string = + { + start : std::string = "("; + if 0 != name..size() { + if name_brackets { + start += "?<(name..data())$>"; + } + else { + start += "?'(name..data())$'"; + } + } + else if has_modifier { + start += "?" + modifiers + ":"; + } + + return start + inner_*..to_string() + ")"; + } + + generate_code: (override this, inout ctx: generation_context) = + { + if -1 != number { + ctx..add("ctx..set_group_start((number)$, r.pos);"); + } + + inner*..generate_code(ctx); + if -1 != number { + ctx..add("ctx..set_group_end((number)$, r.pos);"); + tmp_name := ctx..gen_temp(); + ctx..add("(tmp_name)$_func := :() = {"); + ctx..add(" if !r&$*.matched {"); + ctx..add(" ctx&$*..set_group_invalid((number)$);"); + ctx..add(" }"); + ctx..add("};"); + ctx..add("(tmp_name)$ := cpp2::regex::make_on_return((tmp_name)$_func);"); + ctx..add("_ = (tmp_name)$;"); // Logic is done in the destructor. Same behavior as for guard objects. + } + } + + add_groups: (override this, inout groups: std::set) = + { + inner*..add_groups(groups); + if -1 != number { + _ = groups..insert(number); + } + } +} + + +// Regex syntax: \x or \x{} Example: \x{62} +// +hexadecimal_token_parse: (inout ctx: parse_context) -> token_ptr = +{ + if !(ctx..current() == '\\' && ctx..peek() == 'x') { return nullptr; } + + _ = ctx..next(); // Skip escape. + + if !ctx..next() { return ctx..error("x escape without number.");} + + has_brackets := false; + number_str: std::string = ""; + if '{' == ctx..current() { + // Bracketed + has_brackets = true; + _ = ctx..next(); // Skip '{' + if !ctx..grab_until('}', out number_str) { return ctx..error("No ending bracket for \\x"); } + } + else { + // Grab two chars + if !ctx..grab_n(2, out number_str) { return ctx..error("Missing hexadecimal digits after \\x."); } + } + + number := 0; + if !string_util::string_to_int(number_str, number, 16) { return ctx..error("Could not convert hexadecimal to int."); } + + // TODO: Change for unicode. + number_as_char : char = unchecked_narrow(number); + + syntax: std::string = string_util::int_to_string<16>(number_as_char as int); + if has_brackets { + syntax = "{(syntax)$}"; + } + syntax = "\\x(syntax)$"; + + r := shared.new(number_as_char, ctx..get_modifiers()..has(expression_flags::case_insensitive)); + r*..set_string(syntax); + return r; +} + + +// Regex syntax: $ Example: aa$ +// +line_end_token_parse: (inout ctx: parse_context) -> token_ptr = +{ + if ctx..current() == '$' || (ctx..current() == '\\' && ctx..peek() == '$') { + if (ctx..current() == '\\') { _ = ctx..next(); } // Skip escape + return shared.new("$", "line_end_token_matcher"); + } + else if ctx..current() == '\\' && (ctx..peek() == 'z' || ctx..peek() == 'Z') { + _ = ctx..next(); // Skip escape + + negate := ctx..current() == 'Z'; + return shared.new("\\(ctx..current())$", "line_end_token_matcher"); + } + else { + return nullptr; + } +} + + +// Regex syntax: ^ Example: ^aa +// +line_start_token_parse: (inout ctx: parse_context) -> token_ptr = +{ + if ctx..current() != '^' && !(ctx..current() == '\\' && ctx..peek() == 'A') { return nullptr; } + + if ctx..current() == '\\' { + _ = ctx..next(); + return shared.new("\\A", "line_start_token_matcher"); + } + else { + return shared.new("^", "line_start_token_matcher"); + } +} + + +// Regex syntax: (?=) or (?!) or (*pla), etc. Example: (?=AA) +// +// Parsed in group_token. +// +lookahead_token: @polymorphic_base type = +{ + this: regex_token = (""); + + protected positive: bool; + public inner : token_ptr = nullptr; + + operator=: (out this, positive_: bool) = { + positive = positive_; + } + + generate_code: (override this, inout ctx: generation_context) = { + inner_name := ctx..generate_func(inner); + + ctx..add_check("lookahead_token_matcher((ctx..match_parameters())$, (inner_name)$)"); + } + + add_groups: (override this, inout groups: std::set) = { + inner*..add_groups(groups); + } +} + + +// Named character classes +// +named_class_token_parse: (inout ctx: parse_context) -> token_ptr = +{ + if ctx..current() != '\\' { return nullptr; } + + name := ""; + c_next := ctx..peek(); + + if 'd' == c_next { name = "named_class_digits"; } + else if 'D' == c_next { name = "named_class_not_digits"; } + else if 'h' == c_next { name = "named_class_hor_space"; } + else if 'H' == c_next { name = "named_class_not_hor_space"; } + else if 'N' == c_next { name = "named_class_no_new_line"; } + else if 's' == c_next { name = "named_class_space"; } + else if 'S' == c_next { name = "named_class_not_space"; } + else if 'v' == c_next { name = "named_class_ver_space"; } + else if 'V' == c_next { name = "named_class_not_ver_space"; } + else if 'w' == c_next { name = "named_class_word"; } + else if 'W' == c_next { name = "named_class_not_word"; } + else { return nullptr; } + + _ = ctx..next(); // Skip escape + + return shared.new("\\(ctx..current())$", "(name)$::match"); +} + + +// Regex syntax: \o{} Example: \o{142} +// +octal_token_parse: (inout ctx: parse_context) -> token_ptr = +{ + if !(ctx..current() == '\\' && ctx..peek() == 'o') { return nullptr; } + + _ = ctx..next(); // Skip escape. + + if !ctx..next() { return ctx..error("o escape without number.");} + if ctx..current() != '{' { return ctx..error("Missing opening bracket for \\o."); } + + number_str: std::string = ""; + _ = ctx..next(); // Skip '{' + if !ctx..grab_until('}', out number_str) { return ctx..error("No ending bracket for \\o"); } + + number := 0; + if !string_util::string_to_int(number_str, number, 8) { return ctx..error("Could not convert octal to int."); } + + // TODO: Change for unicode. + number_as_char : char = unchecked_narrow(number); + + syntax: std::string = "\\o{(string_util::int_to_string<8>(number_as_char as int))$}"; + r := shared.new(number_as_char, ctx..get_modifiers()..has(expression_flags::case_insensitive)); + r*..set_string(syntax); + return r; +} + + +// Regex syntax: {min, max} Example: a{2,4} +// +range_token: @polymorphic_base type = +{ + this : regex_token = (""); + + protected min_count : int = -1; + protected max_count : int = -1; + protected kind : int = range_flags::greedy; + protected inner_token: token_ptr = nullptr; + + operator=: (out this) = {} + + parse: (inout ctx: parse_context) -> token_ptr = + { + r := shared.new(); + if ctx..current() == '{' + { + if !ctx..has_token() { return ctx..error("'{' without previous element."); } + + inner: std::string = ""; + if !ctx..grab_until('}', out inner) { return ctx..error("Missing closing bracket '}'."); } + + inner = string_util::trim_copy(inner..substr(1)); // Remove '{' and white spaces. + if inner..empty() { return ctx..error("Empty range specifier. Either '{n}', '{n,}', '{,m}' '{n,m}'"); } + + // Non-greedy or possessive + r*..parse_modifier(ctx); + + // Get range arguments + min_count_str: std::string = "-1"; + max_count_str: std::string = "-1"; + + sep: size_t = inner..find(','); + if sep == std::string::npos + { + min_count_str = inner; + max_count_str = inner; + if !string_util::string_to_int(inner, r*.min_count) { return ctx..error("Could not convert range to number."); } + r*.max_count = r*.min_count; + } + else + { + inner_first: std::string = string_util::trim_copy(inner..substr(0, sep)); + inner_last: std::string = string_util::trim_copy(inner..substr(sep + 1)); + + if (inner_first..empty() && inner_last..empty()) { + return ctx..error("Empty range specifier. Either '{n}', '{n,}', '{,m}' '{n,m}'"); + } + + if !inner_first..empty() { + min_count_str = inner_first; + if !string_util::string_to_int(inner_first, r*.min_count) { return ctx..error("Could not convert range to number."); } + } + if !inner_last..empty() { + max_count_str = inner_last; + if !string_util::string_to_int(inner_last, r*.max_count) { return ctx..error("Could not convert range to number."); } + } + } + + // Check validity of the range. + if -1 != r*.min_count { + if !(0 <= r*.min_count) { + return ctx..error("Min value in range is negative. Have (r*.min_count)$)"); + } + } + if -1 != r*.max_count { + if !(0 <= r*.max_count) { + return ctx..error("Max value in range is negative. Have (r*.max_count)$)"); + } + if -1 != r*.min_count { + if !(r*.min_count <= r*.max_count) { + return ctx..error("Min and max values in range are wrong it should hold 0 <= min <= max. Have 0 <= (r*.min_count)$ <= (r*.max_count)$"); + } + } + } + + r*.inner_token = ctx..pop_token(); + r*.string_rep = r*.inner_token*..to_string() + r*..gen_range_string() + r*..gen_mod_string(); + + return r; + } + + return nullptr; + } + + parse_modifier: (inout this, inout ctx: parse_context) = + { + if ctx..peek() == '?' { + kind = range_flags::not_greedy; + _ = ctx..next(); + } + else if ctx..peek() == '+' { + kind = range_flags::possessive; + _ = ctx..next(); + } + } + + gen_mod_string: (this) -> std::string = + { + if kind == range_flags::not_greedy { + return "?"; + } + else if kind == range_flags::possessive { + return "+"; + } + else { + return ""; + } + } + + gen_range_string: (this) -> std::string = + { + r : std::string = ""; + if min_count == max_count { + r += "{(min_count)$}"; + } + else if min_count == -1 { + r += "{,(max_count)$}"; + } + else if max_count == -1 { + r += "{(min_count)$,}"; + } + else { + r += "{(min_count)$,(max_count)$}"; + } + + return r; + } + + generate_code: (override this, inout ctx: generation_context) = + { + inner_name := ctx..generate_func(inner_token); + groups: std::set = (); + inner_token*..add_groups(groups); + reset_name := ctx..generate_reset(groups); + + next_name := ctx..next_func_name(); + ctx..add_statefull(next_name, "cpp2::regex::range_token_matcher::match((ctx..match_parameters())$, (inner_name)$, (reset_name)$, other, (next_name)$)"); + } + + add_groups: (override this, inout groups: std::set) = { + inner_token*..add_groups(groups); + } + +} + + +// Regex syntax: *, +, or ? Example: aa* +// +special_range_token: @polymorphic_base type = +{ + this : range_token = (); + + parse: (inout ctx: parse_context) -> token_ptr = + { + r := shared.new(); + symbol: char = '\0'; + if ctx..current() == '*' { + r*.min_count = 0; + r*.max_count = -1; + symbol = '*'; + } + else if ctx..current() == '+' { + r*.min_count = 1; + r*.max_count = -1; + symbol = '+'; + } else if ctx..current() == '?' { + r*.min_count = 0; + r*.max_count = 1; + symbol = '?'; + } else { + return nullptr; + } + + if !ctx..has_token() { return ctx..error("'(ctx..current())$' without previous element."); } + + + r*..parse_modifier(ctx); + + r*.inner_token = ctx..pop_token(); + r*.string_rep = r*.inner_token*..to_string() + symbol + r*..gen_mod_string(); + return r; + } +} + + +// Regex syntax: \b or \B Example: \bword\b +// +// Matches the start end end of word boundaries. +// +word_boundary_token_parse: (inout ctx: parse_context) -> token_ptr = +{ + if ctx..current() != '\\' { return nullptr; } + + if ctx..peek() == 'b' { + _ = ctx..next(); + return shared.new("\\b", "word_boundary_token_matcher"); + } + else if ctx..peek() == 'B' { + _ = ctx..next(); + return shared.new("\\B", "word_boundary_token_matcher"); + } + else { + return nullptr; + } +} + + +//----------------------------------------------------------------------- +// +// Parser for regular expression. +// +//----------------------------------------------------------------------- +// + +// Parser and generator for regular expressions. +regex_generator: type = +{ + regex: std::string_view; + modifier: std::string = ""; + modifier_escape: std::string = ""; + + error_out: Error_out; + + source: std::string = ""; + + operator=: (out this, r: std::string_view, e: Error_out) = { + regex = r; + error_out = e; + } + + parse:(inout this) -> std::string = + { + // Extract modifiers and adapt regex. + extract_modifiers(); + + parse_ctx: parse_context = (regex, error_out); + if !parse_ctx..parse(modifier) { + return ""; + } + + source += "{\n"; + source += " wrap: type = {\n"; // TODO: Remove wrapper when template template parameters are available. + source += " context: type == cpp2::regex::match_context;"; + + gen_ctx: generation_context = (); + source += gen_ctx..run(parse_ctx..get_as_token()); + source += " entry: (cur: Iter, inout ctx: context) -> cpp2::regex::match_return = {\n"; + source += " ctx..set_group_start(0, cur);\n"; + source += " r := (gen_ctx..get_entry_func())$(cur, ctx, cpp2::regex::true_end_func());\n"; + source += " if r.matched { ctx..set_group_end(0, r.pos); }\n"; + source += " return r;\n"; + source += " }\n"; + + source += gen_ctx..create_named_group_lookup(parse_ctx.named_groups); + source += "}\n"; + + string := parse_ctx..get_as_token()*..to_string(); + source += " to_string: () -> std::string = { return R\"((modifier_escape)$(string)$(modifier_escape)$(modifier)$)\"; }\n"; + source += "}\n"; + + _ = parse_ctx; + + return source; + } + + private extract_modifiers: (inout this) = + { + if regex..find_first_of("'/") == 0 { + mod_token: char = regex[0]; + + end_pos := regex..rfind(mod_token); + if end_pos != 0 { + // Found valid start end escape + modifier = regex..substr(end_pos + 1); + modifier_escape = mod_token; + regex = regex..substr(1, end_pos - 1); + } + } + } +} + +generate_regex: (regex: std::string_view, err: Err) -> std::string = +{ + parser: regex_generator = (regex, err); + r := parser..parse(); + _ = parser; + return r; +} + + + +regex_gen: (inout t: meta::type_declaration) = +{ + has_default := false; + exact_name := "regex"; + prefix := "regex_"; + expressions : std::map = (); + + for t.get_member_objects() do (inout m) + { + name: std::string = m.name(); + + if name.starts_with(prefix) || name == exact_name + { + if !m.has_initializer() { + t.error("Regular expression must have an initializer."); + } + m.mark_for_removal_from_enclosing_type(); + + if name == exact_name { + if has_default { + t.error("Type can only contain one default named regular expression."); + } + has_default = true; + } + + expr: std::string = m.initializer(); + if expr.starts_with("R\"(") && expr.ends_with(")\"") { + expr = expr.substr(3, expr.size() - 5); + } + else if string_util::is_escaped(expr) { + expr = expr.substr(1, expr.size() - 2); + } + else { + t.error("Unknown string format '(expr)$'"); + } + + expressions[name] = expr; + } + } + + t.remove_marked_members(); + + for expressions do (expr) { + regular_expression := generate_regex(expr.second, :(message) = t$.error(message);); + + if !regular_expression..empty() { + t.add_member("public (expr.first)$_matcher: type = (regular_expression)$"); + t.add_member("public (expr.first)$: cpp2::regex::regular_expression = ();"); + } + } + + t.add_runtime_support_include( "cpp2regex.h" ); +} + + +//----------------------------------------------------------------------- +// +// apply_metafunctions +// +apply_metafunctions: ( + inout n : declaration_node, + inout rtype : type_declaration, + error, + lookup + ) + -> bool += { + assert( n.is_type() ); + + // Check for _names reserved for the metafunction implementation + if !n.metafunctions.empty() + { + for rtype.get_members() + do (m) + { + m.require( + !m.name().starts_with("_") || m.name().ssize() == 1, + "a type that applies a metafunction cannot have a body that declares " + "a name that starts with '_' - those names are reserved for the " + "metafunction implementation" + ); + } + } + + // For each metafunction, apply it + for n.metafunctions + do (meta) + { + // Convert the name and any template arguments to strings + // and record that in rtype + name := meta*.to_string(); + name = name.substr(0, name.find('<')); + + args: std::vector = (); + for meta*.template_arguments() + do (arg) + args.push_back( arg.to_string() ); + + rtype.set_metafunction_name( name, args ); + + // Dispatch + // + if name == "interface" { + interface( rtype ); + } + else if name == "polymorphic_base" { + polymorphic_base( rtype ); + } + else if name == "ordered" { + ordered( rtype ); + } + else if name == "weakly_ordered" { + weakly_ordered( rtype ); + } + else if name == "partially_ordered" { + partially_ordered( rtype ); + } + else if name == "copyable" { + copyable( rtype ); + } + else if name == "hashable" { + hashable( rtype ); + } + else if name == "basic_value" { + basic_value( rtype ); + } + else if name == "value" { + value( rtype ); + } + else if name == "weakly_ordered_value" { + weakly_ordered_value( rtype ); + } + else if name == "partially_ordered_value" { + partially_ordered_value( rtype ); + } + else if name == "cpp1_rule_of_zero" { + cpp1_rule_of_zero( rtype ); + } + else if name == "struct" { + cpp2_struct( rtype ); + } + else if name == "enum" { + cpp2_enum( rtype ); + } + else if name == "flag_enum" { + flag_enum( rtype ); + } + else if name == "union" { + cpp2_union( rtype ); + } + else if name == "print" { + print( rtype ); + } + else if name == "regex" { + regex_gen( rtype ); + } + else if name == "dll_visible" { + dll_visible( rtype ); + } + else { + (load := load_metafunction( name, lookup )) + if load.is_value() { + load.value()( rtype ); + } else { + error( "unrecognized metafunction name: " + name ); + if name.find("::") == name.npos { + error( + "(temporary alpha limitation) currently the supported names are: " + "interface, polymorphic_base, ordered, weakly_ordered, partially_ordered, " + "copyable, basic_value, value, weakly_ordered_value, partially_ordered_value, " + "struct, enum, flag_enum, union, cpp1_rule_of_zero, regex, dll_visible, print" + ); + } + if !load.error().value.empty() { + error( load.error().value ); + } + return false; + } + } + } + + return true; +} + +apply_metafunctions: ( + inout n : declaration_node, + inout rfunction : function_declaration, + error + ) + -> bool += { + assert( n.is_function() ); + + // For each metafunction, apply it + for n.metafunctions + do (meta) + { + // Convert the name and any template arguments to strings + // and record that in rfunction + name := meta*.to_string(); + name = name.substr(0, name.find('<')); + + args: std::vector = (); + for meta*.template_arguments() + do (arg) + args.push_back( arg.to_string() ); + + rfunction.set_metafunction_name( name, args ); + + // Dispatch + // + if name == "dll_visible" { + dll_visible( rfunction ); + } + else { + error( "unrecognized metafunction name: " + name ); + error( "(temporary alpha limitation) currently the supported names are: dll_visible " ); + return false; + } + } + + return true; +} + + +} + +} + + +#include "cpp2reflect.hpp" diff --git a/source/sema.h b/source/sema.h index f3d8ac63f1..eb3135aa8c 100644 --- a/source/sema.h +++ b/source/sema.h @@ -18,23 +18,163 @@ #ifndef CPP2_SEMA_H #define CPP2_SEMA_H -#include "reflect.h" +#include "reflect_impl.h" namespace cpp2 { +using current_declarations_span = std::span; + +auto lookup_metafunction( + std::string const& name, + current_declarations_span current_declarations, + current_names_span current_names + ) + -> meta::expected +{ + struct scope_t { + std::string fully_qualified_mangled_name; + current_names_span::pointer names_first; + current_names_span::pointer names_last = names_first; + + current_names_span names() const { return {names_first, names_last}; } + }; + std::vector scopes = { { {}, current_names.data() + current_names.size() } }; + + // Build up 'scopes' + assert(current_declarations.back()->is_type()); + for ( + auto first = current_declarations.data() + current_declarations.size() - 2, + last = current_declarations.data(); + first != last; + --first + ) + { + assert(*first); + if (!(*first)->is_namespace()) { + continue; + } + + // TODO Handle unnamed namespace '_' + auto id = (*first)->name(); + assert(id); + for (auto& scope : scopes) + { + scope.fully_qualified_mangled_name.insert(0u, *id); + scope.fully_qualified_mangled_name.insert(0u, "::"); + } + do { + --scopes.back().names_first; + } while ( + !get_if(scopes.back().names_first) + || get(*scopes.back().names_first) != *first + ); + scopes.push_back( { {}, scopes.back().names_first } ); + } + scopes.back().names_first = current_names.data(); + for (auto& scope : scopes) { + scope.fully_qualified_mangled_name = meta::mangle(std::move(scope.fully_qualified_mangled_name)); + } + + // Lookup the name + auto libraries = meta::this_execution::get_reachable_metafunction_symbols(); + auto mangled_name = meta::mangle((name.starts_with("::") ? "" : "::") + name); + for (auto const& scope : scopes) + { + auto expected_symbol = scope.fully_qualified_mangled_name + mangled_name; + // FIXME #470 or emit using statement only in + // Phase 2 "Cpp2 type definitions and function declarations" + if (auto lookup = source_order_name_lookup(scope.names(), name)) { + if (auto using_ = get_if(&*lookup)) { + // TODO Handle relative qualified-id + expected_symbol = meta::mangle((name.starts_with("::") ? "" : "::") + using_->to_string()); + } + } + + // Save a const overload + auto res = meta::lookup_res{}; + for (auto&& lib: libraries) + { + for (auto&& sym: lib.symbols) + { + if (sym.without_prefix() == expected_symbol) + { + res = {lib.name, &sym}; + // Immediately return a non-const overload + if (!sym.is_const_metafunction()) { + return res; + } + } + } + } + + // Return a const overload without a non-const overload + if (!res.library.empty()) { + return res; + } + } + + return meta::diagnostic{ + "metafunction '" + name + "' not found\n" + + "(temporary alpha limitation) lookup for a metafunction name is limited: " + + "it can be used unqualified from its declaring namespace or a nested namespace thereof, " + + "and otherwise requires full qualification" + }; +} + auto parser::apply_type_metafunctions( declaration_node& n ) -> bool { assert(n.is_type()); // Get the reflection state ready to pass to the function - auto cs = meta::compiler_services{ &errors, &includes, generated_tokens }; + auto cs = meta::compiler_services{ + meta::compiler_services_data::make( &errors, &includes, generated_tokens, translation_unit_has_interface() ) + }; auto rtype = meta::type_declaration{ &n, cs }; return apply_metafunctions( n, rtype, + [&](std::string const& msg) { error( msg, false ); }, + [&](std::string const& name) { + auto res = lookup_metafunction(name, current_declarations, current_names); + + // Save sanity check to ensure the Cpp1 lookup matches ours + if ( + res.is_value() + && res.value().symbol->is_reachable() + ) + { + auto check = std::string{}; + check += "static_assert("; + check += meta::to_type_metafunction_cast(name, res.value().symbol->is_const_metafunction()); + check += " == "; + check += res.value().symbol->view(); + check += ", "; + check += "\"the metafunction name '" + name + "' must be "; + check += "reachable and equal to the one evaluated by cppfront\""; + check += ");\n"; + n.metafunction_lookup_checks.push_back(check); + } + + return res; + } + ); +} + +auto parser::apply_function_metafunctions( declaration_node& n ) + -> bool +{ + assert(n.is_function()); + + // Get the reflection state ready to pass to the function + auto cs = meta::compiler_services{meta::compiler_services_data::make( &errors, &includes, generated_tokens, false )}; + auto rfunction = meta::function_declaration{ &n, cs }; + + return apply_metafunctions( + n, + rfunction, [&](std::string const& msg) { error( msg, false ); } ); } diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 4a891ad1dc..aadd73a4aa 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -1016,6 +1016,12 @@ class cppfront std::vector errors; std::set includes; + struct metafunction_symbol { + meta::dll_symbol name; + std::string definition; + }; + std::vector metafunction_symbols; + // For building // cpp2::source source; @@ -1044,19 +1050,6 @@ class cppfront }; std::vector current_args = { {} }; - struct active_using_declaration { - token const* identifier = {}; - - explicit active_using_declaration(using_statement_node const& n) { - if (auto id = get_if(&n.id->id)) { - identifier = (*id)->ids.back().id->identifier; - } - } - }; - - using source_order_name_lookup_res = - std::optional>; - // Stack of the currently active nested declarations we're inside std::vector current_declarations = { {} }; @@ -1167,7 +1160,7 @@ class cppfront : sourcefile{ filename } , source { errors } , tokens { errors } - , parser { errors, includes } + , parser { errors, includes, sourcefile.ends_with(".h2") } , sema { errors } { // "Constraints enable creativity in the right directions" @@ -1494,7 +1487,12 @@ class cppfront // If there is Cpp2 code, we have more to do... - // First, if this is a .h2 and in a -pure-cpp2 compilation, + // First, emit metafunction symbols in the global namespace + for (auto const& mf: metafunction_symbols) { + printer.print_extra(mf.definition); + } + + // Then, if this is a .h2 and in a -pure-cpp2 compilation, // we need to switch filenames if ( cpp1_filename.back() == 'h' @@ -1557,6 +1555,39 @@ class cppfront printer.finalize_phase( true ); + //--------------------------------------------------------------------- + // Emit the accessor for declared metafunctions + // + if (!metafunction_symbols.empty()) + { + assert(source.has_cpp2()); + + auto symbols_accessor = std::string{meta::this_execution::symbols_accessor().view()}; + auto decl = std::string{}; + decl += "std::type_identity_t " + + symbols_accessor + + "_() {\n"; + decl += " static char const* res[] = {\n"; + auto prefix = " \""; + auto suffix = std::string{"\"\n"}; + for (auto const& mf: metafunction_symbols) { + decl += prefix + (mf.name.view() + suffix); + prefix = " , \""; + } + decl += " , nullptr\n"; // Sentinel element + decl += " };\n"; + decl += " return res;\n"; + decl += "}\n"; + printer.print_extra(decl); + printer.print_extra( + "CPP2_C_API constexpr auto " + + symbols_accessor + + " = &" + + symbols_accessor + + "_;" + ); + } + // Finally, some debug checks assert( (!errors.empty() || tokens.num_unprinted_comments() == 0) @@ -2933,38 +2964,6 @@ class cppfront } - auto source_order_name_lookup(std::string_view identifier) - -> source_order_name_lookup_res - { - for ( - auto first = current_names.rbegin(), last = current_names.rend() - 1; - first != last; - ++first - ) - { - if ( - auto decl = get_if(&*first); - decl - && *decl - && (*decl)->has_name(identifier) - ) - { - return *decl; - } - else if ( - auto using_ = get_if(&*first); - using_ - && using_->identifier - && *using_->identifier == identifier - ) - { - return *using_; - } - } - - return {}; - } - auto lookup_finds_type_scope_function(id_expression_node const& n) -> bool { @@ -2974,7 +2973,7 @@ class cppfront } auto const& id = *get(n.id); - auto lookup = source_order_name_lookup(*id.identifier); + auto lookup = source_order_name_lookup(current_names, *id.identifier); if ( !lookup @@ -3007,7 +3006,7 @@ class cppfront } auto const& id = *get(n.id); - auto lookup = source_order_name_lookup(*id.identifier); + auto lookup = source_order_name_lookup(current_names, *id.identifier); if ( !lookup @@ -4249,51 +4248,6 @@ class cppfront } - // Consider moving these `stack` functions to `common.h` to enable more general use. - - template - auto stack_value( - T& var, - std::type_identity_t const& value - ) - -> auto - { - return finally([&var, old = std::exchange(var, value)]() { - var = old; - }); - } - - template - auto stack_element( - std::vector& cont, - std::type_identity_t const& value - ) - -> auto - { - cont.push_back(value); - return finally([&]{ cont.pop_back(); }); - } - - template - auto stack_size(std::vector& cont) - -> auto - { - return finally([&, size = cont.size()]{ cont.resize(size); }); - } - - template - auto stack_size_if( - std::vector& cont, - bool cond - ) - -> std::optional - { - if (cond) { - return stack_size(cont); - } - return {}; - } - //----------------------------------------------------------------------- // auto emit( @@ -4665,10 +4619,11 @@ class cppfront && !type_id.is_pointer_qualified() ) { - switch (n.pass) { - break;case passing_style::in : printer.print_cpp2( "cpp2::impl::in<", n.position() ); - break;case passing_style::out : printer.print_cpp2( "cpp2::impl::out<", n.position() ); - break;default: ; + if (n.pass == passing_style::in) { + printer.print_cpp2( "cpp2::impl::in<", n.position() ); + } + else if (n.pass == passing_style::out) { + printer.print_cpp2( "cpp2::impl::out<", n.position() ); } } @@ -4704,23 +4659,33 @@ class cppfront function_requires_conditions.push_back(req); } - switch (n.pass) { - break;case passing_style::in : - case passing_style::in_ref : printer.print_cpp2( name+" const&", n.position() ); - break;case passing_style::copy : printer.print_cpp2( name, n.position() ); - break;case passing_style::inout : printer.print_cpp2( name+"&", n.position() ); - + if ( + n.pass == passing_style::in + || n.pass == passing_style::in_ref + ) + { + printer.print_cpp2( name+" const&", n.position() ); + } + else if (n.pass == passing_style::copy) { + printer.print_cpp2( name, n.position() ); + } + else if (n.pass == passing_style::inout) { + printer.print_cpp2( name+"&", n.position() ); + } // For generic out parameters, we take a pointer to anything with paramater named "identifier_" // and then generate the out<> as a stack local with the expected name "identifier" - break;case passing_style::out : printer.print_cpp2( name, n.position() ); - current_functions.back().prolog.statements.push_back( - "auto " + identifier + " = cpp2::impl::out(" + identifier + "_); " - ); - identifier += "_"; - - break;case passing_style::move : printer.print_cpp2( name+"&&", n.position() ); - break;case passing_style::forward: printer.print_cpp2( name+"&&", n.position() ); - break;default: ; + else if (n.pass == passing_style::out) { + printer.print_cpp2( name, n.position() ); + current_functions.back().prolog.statements.push_back( + "auto " + identifier + " = cpp2::impl::out(" + identifier + "_); " + ); + identifier += "_"; + } + else if (n.pass == passing_style::move) { + printer.print_cpp2( name+"&&", n.position() ); + } + else if (n.pass == passing_style::forward) { + printer.print_cpp2( name+"&&", n.position() ); } if (type_id.constraint) { @@ -4764,15 +4729,26 @@ class cppfront && !n.declaration->is_variadic ) { - switch (n.pass) { - break;case passing_style::in : printer.print_cpp2( ">", n.position() ); - break;case passing_style::in_ref : printer.print_cpp2( " const&", n.position() ); - break;case passing_style::copy : printer.print_cpp2( "", n.position() ); - break;case passing_style::inout : printer.print_cpp2( "&", n.position() ); - break;case passing_style::out : printer.print_cpp2( ">", n.position() ); - break;case passing_style::move : printer.print_cpp2( "&&", n.position() ); - break;case passing_style::forward: printer.print_cpp2( "&&", n.position() ); - break;default: ; + if (n.pass == passing_style::in) { + printer.print_cpp2( ">", n.position() ); + } + else if (n.pass == passing_style::in_ref) { + printer.print_cpp2( " const&", n.position() ); + } + else if (n.pass == passing_style::copy) { + printer.print_cpp2( "", n.position() ); + } + else if (n.pass == passing_style::inout) { + printer.print_cpp2( "&", n.position() ); + } + else if (n.pass == passing_style::out) { + printer.print_cpp2( ">", n.position() ); + } + else if (n.pass == passing_style::move) { + printer.print_cpp2( "&&", n.position() ); + } + else if (n.pass == passing_style::forward) { + printer.print_cpp2( "&&", n.position() ); } } @@ -5983,6 +5959,14 @@ class cppfront } + // Emit sanity check to ensure the Cpp1 lookup matches ours + if (printer.get_phase() == printer.phase1_type_defs_func_decls) { + for (auto&& check : n.metafunction_lookup_checks) { + printer.print_extra(check); + } + } + + // If this is a function that has multiple return values, // first we need to emit the struct that contains the returns if ( @@ -6100,6 +6084,17 @@ class cppfront printer.print_extra(""); } + // If this is a function declaration visible in a DLL + // emit the portable macro that makes it visible + if ( + printer.get_phase() != printer.phase0_type_decls + && n.is_function() + && n.is_dll_visible() + ) + { + printer.print_cpp2("CPPFRONTAPI ", n.position()); + } + // If this is a function definition and the function is inside // type(s) that have template parameters and/or requires clauses, // emit those outer template parameters and requires clauses too @@ -6154,6 +6149,17 @@ class cppfront } printer.print_cpp2("class ", n.position()); + + // If this is a declaration visible in a DLL + // emit the portable macro that makes it visible + if ( + printer.get_phase() == printer.phase1_type_defs_func_decls + && n.is_dll_visible() + ) + { + printer.print_cpp2("CPPFRONTAPI ", n.position()); + } + emit(*n.identifier); // Type declaration @@ -6472,30 +6478,34 @@ class cppfront assert (is_in_type); auto& this_ = func->parameters->parameters[0]; - switch (this_->pass) { - break;case passing_style::in: + if (this_->pass == passing_style::in) { suffix1 += " const"; // Cpp1 ref-qualifiers don't belong on virtual functions if (!this_->is_polymorphic()) { suffix1 += "&"; } - break;case passing_style::inout: + } + else if (this_->pass == passing_style::inout) { // Cpp1 ref-qualifiers don't belong on virtual functions if (!this_->is_polymorphic()) { suffix1 += " &"; } - break;case passing_style::out: - ; // constructor is handled below - break;case passing_style::move: + } + else if (this_->pass == passing_style::out) { + // constructor is handled below + } + else if (this_->pass == passing_style::move) { suffix1 += " &&"; - + } // We shouldn't be able to get into a state where these values // exist here, if we did it's our compiler bug - break;case passing_style::copy: - case passing_style::in_ref: - case passing_style::forward: - case passing_style::forward_ref: - default: + else if (this_->pass == passing_style::copy + || this_->pass == passing_style::in_ref + || this_->pass == passing_style::forward + || this_->pass == passing_style::forward_ref + || this_->pass == passing_style::invalid + ) + { errors.emplace_back( n.position(), "ICE: invalid parameter passing style, should have been rejected", true); } @@ -6741,6 +6751,20 @@ class cppfront printer.print_cpp2( ";", n.position() ); } + // Save the symbol for a metafunction + // to be emitted in the global namespace and + // to be loaded by `cpp2::meta::load_metafunction` + if (n.is_metafunction()) + { + metafunction_symbols.push_back({meta::dll_symbol(n, parser.translation_unit_has_interface()), {}}); + metafunction_symbols.back().definition = + std::string{"\nCPP2_C_API constexpr auto "} + + metafunction_symbols.back().name.view() + + " = " + + meta::to_type_metafunction_cast(n.fully_qualified_name(), n.is_const_metafunction()) + + ";"; + } + // Note: Not just early "return;" here because we may need // to recurse to emit generated operator declarations too, // so all the definition work goes into a big 'else' branch