diff --git a/include/tao/config/contrib/rot13.hpp b/include/tao/config/contrib/rot13.hpp index e78d3e7..eb2a44c 100644 --- a/include/tao/config/contrib/rot13.hpp +++ b/include/tao/config/contrib/rot13.hpp @@ -6,13 +6,16 @@ #include +#include "../internal/atom.hpp" +#include "../internal/pegtl.hpp" + namespace tao::config { - [[nodiscard]] std::string rot13( const std::string& in ) + [[nodiscard]] inline internal::string_t rot13( const pegtl::position& pos, const std::string& in ) { - std::string out( in ); + internal::string_t out( in, pos ); - for( char& c : out ) { + for( char& c : out.value ) { if( ( ( 'a' <= c ) && ( c <= 'm' ) ) || ( ( 'A' <= c ) && ( c <= 'M' ) ) ) { c += 13; } diff --git a/include/tao/config/internal/array.hpp b/include/tao/config/internal/array.hpp index 80bbb28..c5fae54 100644 --- a/include/tao/config/internal/array.hpp +++ b/include/tao/config/internal/array.hpp @@ -36,6 +36,11 @@ namespace tao::config::internal basic_array& operator=( basic_array&& ) = default; basic_array& operator=( const basic_array& ) = default; + [[nodiscard]] const pegtl::position& get_position() const noexcept + { + return position; + } + std::string function; std::list< C > array; pegtl::position position; diff --git a/include/tao/config/internal/atom.hpp b/include/tao/config/internal/atom.hpp new file mode 100644 index 0000000..8b4a74b --- /dev/null +++ b/include/tao/config/internal/atom.hpp @@ -0,0 +1,58 @@ +// Copyright (c) 2024 Dr. Colin Hirsch and Daniel Frey +// Please see LICENSE for license or visit https://github.com/taocpp/config/ + +#ifndef TAO_CONFIG_INTERNAL_ATOM_HPP +#define TAO_CONFIG_INTERNAL_ATOM_HPP + +#include +#include +#include +#include + +#include "pegtl.hpp" + +namespace tao::config::internal +{ + struct null + { + explicit null( const pegtl::position& pos ) + : position( pos ) + {} + + [[nodiscard]] const pegtl::position& get_position() const noexcept + { + return position; + } + + pegtl::position position; + }; + + template< typename T > + struct atom + { + template< typename V > + atom( V&& v, const pegtl::position& pos ) + : value( std::forward< V >( v ) ), + position( pos ) + {} + + [[nodiscard]] const pegtl::position& get_position() const noexcept + { + return position; + } + + T value; + pegtl::position position; + }; + + using boolean = atom< bool >; + using string_t = atom< std::string >; + using binary_t = atom< std::vector< std::byte > >; + + using signed_t = atom< std::int64_t >; + using unsigned_t = atom< std::uint64_t >; + using double_t = atom< double >; + +} // namespace tao::config::internal + +#endif diff --git a/include/tao/config/internal/concat.hpp b/include/tao/config/internal/concat.hpp index 7001770..7feda3c 100644 --- a/include/tao/config/internal/concat.hpp +++ b/include/tao/config/internal/concat.hpp @@ -12,6 +12,7 @@ #include #include +#include "constants.hpp" #include "entry_kind.hpp" #include "forward.hpp" #include "json.hpp" @@ -45,48 +46,28 @@ namespace tao::config::internal return implicit || temporary || concat.empty(); } - [[nodiscard]] const json_t* get_value() const - { - const json_t* result = nullptr; - - for( const auto& e : concat ) { - switch( e.kind() ) { - case entry_kind::value: - if( result != nullptr ) { - return nullptr; - } - result = &e.get_value(); - continue; - case entry_kind::reference: - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - case entry_kind::array: - throw pegtl::parse_error( "array as referenced reference part", e.get_array().position ); - case entry_kind::object: - throw pegtl::parse_error( "object as referenced reference part", e.get_object().position ); - case entry_kind::concat: - throw pegtl::parse_error( "concat as referenced reference part", e.get_concat().position ); - } - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - } - return result; - } - - void back_ensure_kind( const entry_kind k, const pegtl::position& p ) + template< typename T > + void back_ensure_init( const T k, const pegtl::position& p ) { if( concat.empty() ) { concat.emplace_back( k, p ); } - else if( concat.back().kind() != k ) { + else if( concat.back().kind() != T::kind ) { concat.emplace_back( k, p ); } } void back_emplace_func( const std::string& name, const pegtl::position& p ) { - back_ensure_kind( entry_kind::array, p ); + back_ensure_init( array_init, p ); concat.back().get_array().function = name; } + [[nodiscard]] const pegtl::position& get_position() const noexcept + { + return position; + } + bool remove = false; // Whether generated by += (false) or by = (true), because in the latter case everything that comes before must be removed. bool implicit = false; // Whether implicitly generated by a delete, e.g. a.b.c = delete when a.b doesn't even exist, so it's implicitly generated to set the remove flag on a.b.c. bool temporary = false; // Whether flagged as temporary by the user, i.e. this is not to be included in the final result and will be omitted by phase3. diff --git a/include/tao/config/internal/config_action.hpp b/include/tao/config/internal/config_action.hpp index d322042..358a4fd 100644 --- a/include/tao/config/internal/config_action.hpp +++ b/include/tao/config/internal/config_action.hpp @@ -48,7 +48,7 @@ namespace tao::config::internal static void apply( Input& in, State& st, const function_map& /*unused*/ ) { const auto p = in.position(); - const auto f = [ & ]( concat& c ) { c.back_ensure_kind( entry_kind::array, p ); }; + const auto f = [ & ]( concat& c ) { c.back_ensure_init( array_init, p ); }; phase1_append( st.root, st.prefix + st.suffix, f, phase1_mode::manifest ); } }; @@ -60,7 +60,7 @@ namespace tao::config::internal static void apply( Input& in, State& st, const function_map& /*unused*/ ) { const auto p = in.position(); - const auto f = [ & ]( concat& c ) { c.back_ensure_kind( entry_kind::object, p ); }; + const auto f = [ & ]( concat& c ) { c.back_ensure_init( object_init, p ); }; phase1_append( st.root, st.prefix + st.suffix, f, phase1_mode::manifest ); } }; diff --git a/include/tao/config/internal/config_grammar.hpp b/include/tao/config/internal/config_grammar.hpp index 62afd2a..c7104d3 100644 --- a/include/tao/config/internal/config_grammar.hpp +++ b/include/tao/config/internal/config_grammar.hpp @@ -5,6 +5,8 @@ #define TAO_CONFIG_INTERNAL_CONFIG_GRAMMAR_HPP #include "forward.hpp" +#include "jaxn_action.hpp" +#include "jaxn_to_entry.hpp" #include "json.hpp" #include "key1_grammar.hpp" #include "key1_guard.hpp" @@ -29,8 +31,10 @@ namespace tao::config::internal::rules typename State > [[nodiscard]] static bool match( pegtl_input_t& in, State& st, const function_map& /*unused*/ ) { - const auto j = parse_jaxn( in ); - const auto f = [ & ]( concat& c ) { c.concat.emplace_back( j ); }; + jaxn_to_entry consumer; + pegtl::parse< pegtl::must< json::jaxn::internal::rules::sor_single_value >, jaxn_action, json::jaxn::internal::errors >( in, consumer ); + // assert( consumer.value.has_value() ); + const auto f = [ &e = *consumer.value ]( concat& c ) { c.concat.emplace_back( std::move( e ) ); }; phase1_append( st.root, st.prefix + st.suffix, f, phase1_mode::manifest ); return true; } diff --git a/include/tao/config/internal/config_parser.hpp b/include/tao/config/internal/config_parser.hpp index 606f878..146d8d6 100644 --- a/include/tao/config/internal/config_parser.hpp +++ b/include/tao/config/internal/config_parser.hpp @@ -16,7 +16,6 @@ #include "function_implementations.hpp" #include "function_wrapper.hpp" #include "json.hpp" -#include "json_traits.hpp" #include "pegtl.hpp" #include "phase2_everything.hpp" #include "phase3_remove.hpp" @@ -29,18 +28,18 @@ namespace tao::config::internal { config_parser() : fm( { { "binary", wrap( binary_function ) }, - { "cbor", wrap( cbor_function ) }, + // { "cbor", wrap( cbor_function ) }, { "default", wrap( default_function ) }, { "env", wrap( env_function ) }, { "env?", wrap( env_if_function ) }, { "jaxn", wrap( jaxn_function ) }, - { "json", wrap( json_function ) }, - { "msgpack", wrap( msgpack_function ) }, + // { "json", wrap( json_function ) }, + // { "msgpack", wrap( msgpack_function ) }, { "read", wrap( read_function ) }, { "shell", wrap( shell_function ) }, { "split", wrap( split_function ) }, - { "string", wrap( string_function ) }, - { "ubjson", wrap( ubjson_function ) } } ) + { "string", wrap( string_function ) } } ) + //{ "ubjson", wrap( ubjson_function ) } } ) {} config_parser( config_parser&& ) = delete; diff --git a/include/tao/config/internal/constants.hpp b/include/tao/config/internal/constants.hpp index 4088521..fa2d7a2 100644 --- a/include/tao/config/internal/constants.hpp +++ b/include/tao/config/internal/constants.hpp @@ -4,11 +4,13 @@ #ifndef TAO_CONFIG_INTERNAL_CONSTANTS_HPP #define TAO_CONFIG_INTERNAL_CONSTANTS_HPP +#include "entry_kind.hpp" + namespace tao::config::internal { - struct part_star_t + struct part_asterisk_t { - explicit constexpr part_star_t( int /*unused*/ ) {} + explicit constexpr part_asterisk_t( int /*unused*/ ) {} }; struct part_vector_t @@ -16,14 +18,39 @@ namespace tao::config::internal explicit constexpr part_vector_t( int /*unused*/ ) {} }; - constexpr part_star_t part_star{ 0 }; + constexpr part_asterisk_t part_asterisk{ 0 }; constexpr part_vector_t part_vector{ 0 }; - constexpr bool operator<( const part_star_t, const part_star_t ) noexcept + [[nodiscard]] constexpr bool operator<( const part_asterisk_t, const part_asterisk_t ) noexcept { return false; } + struct array_init_t + { + explicit constexpr array_init_t( int /*unused*/ ) {} + + static constexpr entry_kind kind = entry_kind::ARRAY; + }; + + struct object_init_t + { + explicit constexpr object_init_t( int /*unused*/ ) {} + + static constexpr entry_kind kind = entry_kind::OBJECT; + }; + + struct asterisk_init_t + { + explicit constexpr asterisk_init_t( int /*unused*/ ) {} + + static constexpr entry_kind kind = entry_kind::ASTERISK; + }; + + constexpr array_init_t array_init{ 0 }; + constexpr object_init_t object_init{ 0 }; + constexpr asterisk_init_t asterisk_init{ 0 }; + } // namespace tao::config::internal #endif diff --git a/include/tao/config/internal/debug_traits.hpp b/include/tao/config/internal/debug_traits.hpp index 99b3faf..6fc8c49 100644 --- a/include/tao/config/internal/debug_traits.hpp +++ b/include/tao/config/internal/debug_traits.hpp @@ -14,7 +14,6 @@ #include "concat.hpp" #include "entry.hpp" #include "json.hpp" -#include "json_traits.hpp" #include "key1.hpp" #include "object.hpp" #include "pegtl.hpp" @@ -114,10 +113,10 @@ namespace tao::config::internal }; template<> - struct debug_traits< part_star_t > + struct debug_traits< part_asterisk_t > { template< template< typename... > class Traits, typename Consumer > - static void produce( Consumer& c, const part_star_t /*unused*/ ) + static void produce( Consumer& c, const part_asterisk_t /*unused*/ ) { c.string( "star" ); } @@ -138,7 +137,7 @@ namespace tao::config::internal case key1_kind::index: c.string( "index" ); return; - case key1_kind::star: + case key1_kind::asterisk: c.string( "star" ); return; case key1_kind::append: @@ -239,36 +238,44 @@ namespace tao::config::internal static void produce( Consumer& c, const entry_kind k ) { switch( k ) { - case entry_kind::value: - c.string( "value" ); + case entry_kind::NULL_: + c.string( "null" ); return; - case entry_kind::reference: - c.string( "reference" ); + case entry_kind::BOOLEAN: + c.string( "boolean" ); + return; + case entry_kind::STRING: + c.string( "string" ); + return; + case entry_kind::BINARY: + c.string( "binary" ); + return; + case entry_kind::SIGNED: + c.string( "signed" ); + return; + case entry_kind::UNSIGNED: + c.string( "unsigned" ); + return; + case entry_kind::DOUBLE: + c.string( "double" ); return; - case entry_kind::array: + case entry_kind::ARRAY: c.string( "array" ); return; - case entry_kind::object: + case entry_kind::OBJECT: c.string( "object" ); return; - case entry_kind::concat: + case entry_kind::ASTERISK: c.string( "concat" ); return; + case entry_kind::REFERENCE: + c.string( "reference" ); + return; } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } }; - template<> - struct debug_traits< json::basic_value< json_traits > > - { - template< template< typename... > class, typename Consumer > - static void produce( Consumer& c, const json::basic_value< json_traits >& v ) - { - json::events::from_value( c, v ); - } - }; - template<> struct debug_traits< entry > { @@ -276,29 +283,52 @@ namespace tao::config::internal static void produce( Consumer& c, const entry& v ) { c.begin_object(); - // c.key( "type" ); - // json::events::produce< Traits >( c, v.type() ); - // c.member(); + c.key( "type" ); + json::events::produce< Traits >( c, v.kind() ); + c.member(); switch( v.kind() ) { - case entry_kind::value: - c.key( "value" ); - json::events::produce< Traits >( c, v.get_value() ); + case entry_kind::NULL_: + c.end_object(); + return; + case entry_kind::BOOLEAN: + c.key( "data" ); + json::events::produce< Traits >( c, v.get_boolean() ); break; - case entry_kind::reference: - c.key( "reference" ); - json::events::produce< Traits >( c, v.get_reference() ); + case entry_kind::STRING: + c.key( "data" ); + json::events::produce< Traits >( c, v.get_string() ); + break; + case entry_kind::BINARY: + c.key( "data" ); + json::events::produce< Traits >( c, v.get_binary() ); break; - case entry_kind::array: - c.key( "array" ); + case entry_kind::SIGNED: + c.key( "data" ); + json::events::produce< Traits >( c, v.get_signed() ); + break; + case entry_kind::UNSIGNED: + c.key( "data" ); + json::events::produce< Traits >( c, v.get_unsigned() ); + break; + case entry_kind::DOUBLE: + c.key( "data" ); + json::events::produce< Traits >( c, v.get_double() ); + break; + case entry_kind::ARRAY: + c.key( "data" ); json::events::produce< Traits >( c, v.get_array() ); break; - case entry_kind::object: - c.key( "object" ); + case entry_kind::OBJECT: + c.key( "data" ); json::events::produce< Traits >( c, v.get_object() ); break; - case entry_kind::concat: - c.key( "concat" ); - json::events::produce< Traits >( c, v.get_concat() ); + case entry_kind::ASTERISK: + c.key( "data" ); + json::events::produce< Traits >( c, v.get_asterisk() ); + break; + case entry_kind::REFERENCE: + c.key( "data" ); + json::events::produce< Traits >( c, v.get_reference() ); break; } c.member(); diff --git a/include/tao/config/internal/entry.hpp b/include/tao/config/internal/entry.hpp index e548f2d..b1b35af 100644 --- a/include/tao/config/internal/entry.hpp +++ b/include/tao/config/internal/entry.hpp @@ -12,12 +12,11 @@ #include #include "array.hpp" +#include "atom.hpp" #include "concat.hpp" #include "constants.hpp" #include "entry_kind.hpp" #include "forward.hpp" -#include "json.hpp" -#include "json_traits.hpp" #include "object.hpp" #include "pegtl.hpp" #include "reference2.hpp" @@ -26,13 +25,35 @@ namespace tao::config::internal { struct entry { - using data_t = std::variant< json_t, reference2, array, object, concat >; + using data_t = std::variant< null, boolean, string_t, binary_t, signed_t, unsigned_t, double_t, array, object, concat, reference2 >; - explicit entry( const json_t& j ) - : m_data( j ) - { - expand(); // Recursively convert JSON Value class arrays and objects into config::internal arrays and objects with alternating concat/entry structure. - } + explicit entry( const null& n ) + : m_data( n ) + {} + + explicit entry( const boolean& b ) + : m_data( b ) + {} + + explicit entry( const string_t& s ) + : m_data( s ) + {} + + explicit entry( const binary_t& b ) + : m_data( b ) + {} + + explicit entry( const signed_t& s ) + : m_data( s ) + {} + + explicit entry( const unsigned_t& u ) + : m_data( u ) + {} + + explicit entry( const double_t& d ) + : m_data( d ) + {} explicit entry( const reference2& r ) : m_data( r ) @@ -44,46 +65,44 @@ namespace tao::config::internal : m_data( std::in_place_type_t< array >(), n, p ) {} - entry( const entry_kind k, const pegtl::position& p ) + entry( const array_init_t /*unused*/, const pegtl::position& p ) + : m_data( std::in_place_type_t< array >(), p ) + {} + + entry( const object_init_t /*unused*/, const pegtl::position& p ) : m_data( std::in_place_type_t< object >(), p ) - { - switch( k ) { - case entry_kind::value: - case entry_kind::reference: - throw std::string( "this should never happen" ); - case entry_kind::array: - set_array( p ); - return; - case entry_kind::object: - return; - case entry_kind::concat: - set_concat( p ); - return; - } - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - } - - entry( entry&& ) = default; + {} + + entry( const asterisk_init_t /*unused*/, const pegtl::position& p ) + : m_data( std::in_place_type_t< concat >(), p ) + {} + + entry( entry&& ) noexcept = default; entry( const entry& ) = default; ~entry() = default; - entry& operator=( entry&& ) = default; + entry& operator=( entry&& ) noexcept = default; entry& operator=( const entry& ) = default; - entry_kind kind() const noexcept + [[nodiscard]] entry_kind kind() const noexcept { return entry_kind( m_data.index() ); } - [[nodiscard]] bool is_value() const noexcept + [[nodiscard]] bool is_null() const noexcept { - return std::holds_alternative< json_t >( m_data ); + return std::holds_alternative< null >( m_data ); } - [[nodiscard]] bool is_reference() const noexcept + [[nodiscard]] bool is_string() const noexcept { - return std::holds_alternative< reference2 >( m_data ); + return std::holds_alternative< string_t >( m_data ); + } + + [[nodiscard]] bool is_binary() const noexcept + { + return std::holds_alternative< binary_t >( m_data ); } [[nodiscard]] bool is_array() const noexcept @@ -96,134 +115,179 @@ namespace tao::config::internal return std::holds_alternative< object >( m_data ); } - [[nodiscard]] bool is_concat() const noexcept + [[nodiscard]] bool is_asterisk() const noexcept { return std::holds_alternative< concat >( m_data ); } - void set_value( json_t&& t ) + [[nodiscard]] bool is_reference() const noexcept { - m_data = std::move( t ); - expand(); + return std::holds_alternative< reference2 >( m_data ); } - void set_value( const json_t& t ) + template< typename T > + void set_value( atom< T >&& t ) { - m_data = t; - expand(); + m_data.emplace< atom< T > >( std::move( t ) ); } void set_array( pegtl::position p ) { - m_data.emplace< std::size_t( entry_kind::array ) >( std::move( p ) ); + m_data.emplace< std::size_t( entry_kind::ARRAY ) >( std::move( p ) ); } void set_object( pegtl::position p ) { - m_data.emplace< std::size_t( entry_kind::object ) >( std::move( p ) ); + m_data.emplace< std::size_t( entry_kind::OBJECT ) >( std::move( p ) ); } - void set_concat( pegtl::position p ) + [[nodiscard]] bool get_boolean() const noexcept { - m_data.emplace< std::size_t( entry_kind::concat ) >( std::move( p ) ); + return std::get< boolean >( m_data ).value; } - [[nodiscard]] json_t& get_value() noexcept + [[nodiscard]] boolean& get_boolean_atom() noexcept { - auto* s = std::get_if< json_t >( &m_data ); - assert( s ); - return *s; + return std::get< boolean >( m_data ); } - [[nodiscard]] reference2& get_reference() noexcept + [[nodiscard]] const boolean& get_boolean_atom() const noexcept { - auto* s = std::get_if< reference2 >( &m_data ); - assert( s ); - return *s; + return std::get< boolean >( m_data ); } - [[nodiscard]] array& get_array() noexcept + [[nodiscard]] std::string& get_string() noexcept { - auto* s = std::get_if< array >( &m_data ); - assert( s ); - return *s; + return std::get< string_t >( m_data ).value; } - [[nodiscard]] object& get_object() noexcept + [[nodiscard]] const std::string& get_string() const noexcept { - auto* s = std::get_if< object >( &m_data ); - assert( s ); - return *s; + return std::get< string_t >( m_data ).value; } - [[nodiscard]] concat& get_concat() noexcept + [[nodiscard]] string_t& get_string_atom() noexcept { - auto* s = std::get_if< concat >( &m_data ); - assert( s ); - return *s; + return std::get< string_t >( m_data ); } - [[nodiscard]] const json_t& get_value() const noexcept + [[nodiscard]] const string_t& get_string_atom() const noexcept { - const auto* s = std::get_if< json_t >( &m_data ); - assert( s ); - return *s; + return std::get< string_t >( m_data ); } - [[nodiscard]] const reference2& get_reference() const noexcept + [[nodiscard]] std::vector< std::byte >& get_binary() noexcept + { + return std::get< binary_t >( m_data ).value; + } + + [[nodiscard]] const std::vector< std::byte >& get_binary() const noexcept { - const auto* s = std::get_if< reference2 >( &m_data ); - assert( s ); - return *s; + return std::get< binary_t >( m_data ).value; + } + + [[nodiscard]] binary_t& get_binary_atom() noexcept + { + return std::get< binary_t >( m_data ); + } + + [[nodiscard]] const binary_t& get_binary_atom() const noexcept + { + return std::get< binary_t >( m_data ); + } + + [[nodiscard]] std::int64_t get_signed() const noexcept + { + return std::get< signed_t >( m_data ).value; + } + + [[nodiscard]] signed_t& get_signed_atom() noexcept + { + return std::get< signed_t >( m_data ); + } + + [[nodiscard]] const signed_t& get_signed_atom() const noexcept + { + return std::get< signed_t >( m_data ); + } + + [[nodiscard]] std::uint64_t get_unsigned() const noexcept + { + return std::get< unsigned_t >( m_data ).value; + } + + [[nodiscard]] unsigned_t& get_unsigned_atom() noexcept + { + return std::get< unsigned_t >( m_data ); + } + + [[nodiscard]] const unsigned_t& get_unsigned_atom() const noexcept + { + return std::get< unsigned_t >( m_data ); + } + + [[nodiscard]] double get_double() const noexcept + { + return std::get< double_t >( m_data ).value; + } + + [[nodiscard]] double_t& get_double_atom() noexcept + { + return std::get< double_t >( m_data ); + } + + [[nodiscard]] const double_t& get_double_atom() const noexcept + { + return std::get< double_t >( m_data ); + } + + [[nodiscard]] array& get_array() noexcept + { + return std::get< array >( m_data ); } [[nodiscard]] const array& get_array() const noexcept { - const auto* s = std::get_if< array >( &m_data ); - assert( s ); - return *s; + return std::get< array >( m_data ); + } + + [[nodiscard]] object& get_object() noexcept + { + return std::get< object >( m_data ); } [[nodiscard]] const object& get_object() const noexcept { - const auto* s = std::get_if< object >( &m_data ); - assert( s ); - return *s; + return std::get< object >( m_data ); } - [[nodiscard]] const concat& get_concat() const noexcept + [[nodiscard]] concat& get_asterisk() noexcept { - const auto* s = std::get_if< concat >( &m_data ); - assert( s ); - return *s; + return std::get< concat >( m_data ); } - private: - data_t m_data; + [[nodiscard]] const concat& get_asterisk() const noexcept + { + return std::get< concat >( m_data ); + } - void expand() - { - if( get_value().is_array() ) { - json_t::array_t a = std::move( get_value().get_array() ); - set_array( get_value().position ); - for( json_t& j : a ) { - concat& d = get_array().array.emplace_back( j.position ); - d.concat.emplace_back( std::move( j ) ); - d.remove = true; // TODO: Make configurable? - } - return; - } - if( get_value().is_object() ) { - json_t::object_t o = std::move( get_value().get_object() ); - set_object( get_value().position ); - for( auto& [ k, j ] : o ) { - const auto p = get_object().object.try_emplace( std::move( k ), j.position ); - p.first->second.concat.emplace_back( std::move( j ) ); - p.first->second.remove = true; // TODO: Make configurable? - } - return; - } + [[nodiscard]] reference2& get_reference() noexcept + { + return std::get< reference2 >( m_data ); + } + + [[nodiscard]] const reference2& get_reference() const noexcept + { + return std::get< reference2 >( m_data ); } + + [[nodiscard]] const pegtl::position& get_position() const noexcept + { + return std::visit( []( const auto& v ) -> const pegtl::position& { return v.get_position(); }, m_data ); + } + + private: + data_t m_data; }; } // namespace tao::config::internal diff --git a/include/tao/config/internal/entry_kind.hpp b/include/tao/config/internal/entry_kind.hpp index 815d9e2..a0a9fb4 100644 --- a/include/tao/config/internal/entry_kind.hpp +++ b/include/tao/config/internal/entry_kind.hpp @@ -4,17 +4,62 @@ #ifndef TAO_CONFIG_INTERNAL_ENTRY_KIND_HPP #define TAO_CONFIG_INTERNAL_ENTRY_KIND_HPP +#include +#include +#include +#include + namespace tao::config::internal { enum class entry_kind : char { - value = 0, - reference = 1, - array = 2, - object = 3, - concat = 4 + NULL_ = 0, + BOOLEAN = 1, + STRING = 2, + BINARY = 3, + SIGNED = 4, + UNSIGNED = 5, + DOUBLE = 6, + ARRAY = 7, + OBJECT = 8, + ASTERISK = 9, + REFERENCE = 10 }; + [[nodiscard]] constexpr std::string_view to_string( const entry_kind k ) noexcept + { + switch( k ) { + case entry_kind::NULL_: + return "null"; + case entry_kind::BOOLEAN: + return "boolean"; + case entry_kind::STRING: + return "string"; + case entry_kind::BINARY: + return "binary"; + case entry_kind::SIGNED: + return "signed"; + case entry_kind::UNSIGNED: + return "unsigned"; + case entry_kind::DOUBLE: + return "double"; + case entry_kind::ARRAY: + return "array"; + case entry_kind::OBJECT: + return "object"; + case entry_kind::ASTERISK: + return "asterisk"; + case entry_kind::REFERENCE: + return "reference"; + } + std::abort(); + } + + inline std::ostream& operator<<( std::ostream& o, const entry_kind k ) + { + return o << to_string( k ); + } + } // namespace tao::config::internal #endif diff --git a/include/tao/config/internal/function_implementations.hpp b/include/tao/config/internal/function_implementations.hpp index ab64bc1..78c19c2 100644 --- a/include/tao/config/internal/function_implementations.hpp +++ b/include/tao/config/internal/function_implementations.hpp @@ -9,25 +9,26 @@ #include #include +#include "entry.hpp" #include "forward.hpp" #include "jaxn_action.hpp" +#include "jaxn_to_entry.hpp" #include "json.hpp" -#include "json_traits.hpp" #include "pegtl.hpp" #include "system_utility.hpp" namespace tao::config::internal { - [[nodiscard]] inline std::vector< std::byte > binary_function( const std::string& s ) + [[nodiscard]] inline binary_t binary_function( const pegtl::position& p, const std::string& s ) { - const auto* const p = reinterpret_cast< const std::byte* >( s.data() ); - return std::vector< std::byte >( p, p + s.size() ); + const auto* const d = reinterpret_cast< const std::byte* >( s.data() ); + return binary_t( std::vector< std::byte >( d, d + s.size() ), p ); } - [[nodiscard]] inline json_t cbor_function( const tao::binary_view bv ) - { - return json::cbor::basic_from_binary< json_traits >( bv ); // TODO: Positions. - } + // [[nodiscard]] inline json_t cbor_function( const std::vector< std::byte >& bv ) + // { + // return json::cbor::basic_from_binary< json_traits >( bv ); // TODO: Positions. + // } [[nodiscard]] inline bool default_function( entry& e ) { @@ -42,21 +43,25 @@ namespace tao::config::internal entry& f = c.concat.front(); switch( f.kind() ) { - case entry_kind::value: - if( f.get_value().is_null() ) { - continue; - } + case entry_kind::NULL_: + continue; + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::BOOLEAN: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: break; - case entry_kind::reference: - return false; - case entry_kind::array: + case entry_kind::ARRAY: if( !f.get_array().function.empty() ) { return false; } break; - case entry_kind::object: + case entry_kind::OBJECT: break; - case entry_kind::concat: + case entry_kind::ASTERISK: + return false; + case entry_kind::REFERENCE: return false; } // Both f and a are sub-objects of e. @@ -67,48 +72,48 @@ namespace tao::config::internal throw pegtl::parse_error( "default function requires at least one non-null argument", a.position ); } - [[nodiscard]] inline std::string env_function( const pegtl::position& p, const std::string& s ) + [[nodiscard]] inline string_t env_function( const pegtl::position& p, const std::string& s ) { - return getenv_throws( p, s ); + return string_t( getenv_throws( p, s ), p ); } - [[nodiscard]] inline std::string env_if_function( const std::string& s, const std::string& d ) + [[nodiscard]] inline string_t env_if_function( const pegtl::position& p, const std::string& s, const std::string& d ) { const auto r = getenv_nothrow( s ); - return r ? ( *r ) : d; + return string_t( r ? ( *r ) : d, p ); } - [[nodiscard]] inline json_t jaxn_function( const pegtl::position& p, const std::string& s ) + [[nodiscard]] inline entry jaxn_function( const pegtl::position& /*unused*/, const std::string& s ) { - json_to_value consumer( p ); + jaxn_to_entry consumer; pegtl::memory_input in( s, "TODO" ); pegtl::parse< json::jaxn::internal::grammar, jaxn_action, json::jaxn::internal::errors >( static_cast< pegtl_input_t& >( in ), consumer ); - return std::move( consumer.value ); + return std::move( consumer.value ).value(); } - [[nodiscard]] inline json_t json_function( const std::string& s ) - { - return json::basic_from_string< json_traits >( s ); // TODO: Positions. - } + // [[nodiscard]] inline json_t json_function( const std::string& s ) + // { + // return json::basic_from_string< json_traits >( s ); // TODO: Positions. + // } - [[nodiscard]] inline json_t msgpack_function( const tao::binary_view bv ) - { - return json::msgpack::basic_from_binary< json_traits >( bv ); // TODO: Positions. - } + // [[nodiscard]] inline json_t msgpack_function( const std::vector< std::byte >& bv ) + // { + // return json::msgpack::basic_from_binary< json_traits >( bv ); // TODO: Positions. + // } - [[nodiscard]] inline std::vector< std::byte > read_function( const std::string& filename ) + [[nodiscard]] inline binary_t read_function( const pegtl::position& p, const std::string& filename ) { const std::string d = read_file_throws( filename ); - const std::byte* p = reinterpret_cast< const std::byte* >( d.data() ); - return std::vector< std::byte >( p, p + d.size() ); + const std::byte* x = reinterpret_cast< const std::byte* >( d.data() ); + return binary_t( std::vector< std::byte >( x, x + d.size() ), p ); } - [[nodiscard]] inline std::string shell_function( const pegtl::position& p, [[maybe_unused]] const std::string& script ) + [[nodiscard]] inline string_t shell_function( const pegtl::position& p, [[maybe_unused]] const std::string& script ) { #if defined( _MSC_VER ) throw pegtl::parse_error( "shell extension not supported on this platform", p ); #else - return shell_popen_throws( p, script ); + return string_t( shell_popen_throws( p, script ), p ); #endif } @@ -127,32 +132,33 @@ namespace tao::config::internal struct split_action< split_string > { template< typename Input > - static void apply( const Input& in, std::vector< std::string >& result ) + static void apply( const Input& in, entry& result, const pegtl::position& p ) { - result.emplace_back( in.string() ); + concat& c = result.get_array().array.emplace_back( p ); + c.concat.emplace_back( string_t( in.string(), in.position() ) ); } }; - [[nodiscard]] inline std::vector< std::string > split_function( const pegtl::position& p, const std::string& s ) + [[nodiscard]] inline entry split_function( const pegtl::position& p, const std::string& s ) { - std::vector< std::string > result; + entry result( array_init, p ); pegtl::memory_input< pegtl::tracking_mode::lazy, pegtl_input_t::eol_t, const char* > in( s, __FUNCTION__ ); - pegtl::parse_nested< split_rule, split_action >( p, in, result ); + pegtl::parse_nested< split_rule, split_action >( p, in, result, p ); return result; } - [[nodiscard]] inline std::string string_function( const pegtl::position& p, const tao::binary_view bv ) + [[nodiscard]] inline string_t string_function( const pegtl::position& p, const std::vector< std::byte >& bv ) { if( !json::internal::validate_utf8_nothrow( std::string_view( reinterpret_cast< const char* >( bv.data() ), bv.size() ) ) ) { throw pegtl::parse_error( "invalid utf8 in binary data", p ); } - return std::string( reinterpret_cast< const char* >( bv.data() ), bv.size() ); + return string_t( std::string( reinterpret_cast< const char* >( bv.data() ), bv.size() ), p ); } - [[nodiscard]] inline json_t ubjson_function( const tao::binary_view bv ) - { - return json::ubjson::basic_from_binary< json_traits >( bv ); // TODO: Positions. - } + // [[nodiscard]] inline json_t ubjson_function( const std::vector< std::byte >& bv ) + // { + // return json::ubjson::basic_from_binary< json_traits >( bv ); // TODO: Positions. + // } } // namespace tao::config::internal diff --git a/include/tao/config/internal/function_traits.hpp b/include/tao/config/internal/function_traits.hpp index e3442ef..56f7dac 100644 --- a/include/tao/config/internal/function_traits.hpp +++ b/include/tao/config/internal/function_traits.hpp @@ -15,7 +15,6 @@ #include "entry.hpp" #include "forward.hpp" #include "json.hpp" -#include "json_traits.hpp" #include "key1.hpp" #include "parse_utility.hpp" #include "pegtl.hpp" @@ -28,93 +27,82 @@ namespace tao::config::internal template< typename, typename = void > struct function_traits; - [[nodiscard]] inline decltype( auto ) function_traits_value( array& f, const std::size_t i ) + template<> + struct function_traits< entry > { - assert( f.array.size() > i ); - auto p = f.array.begin(); - std::advance( p, i ); - if( p->concat.size() > 1 ) { - throw arguments_unready(); - } - const entry& e = p->concat.front(); - if( e.kind() != entry_kind::value ) { - throw arguments_unready(); + static void put( entry& e, entry&& f ) + { + e = std::move( f ); } - return p->concat.front().get_value(); - } - template<> - struct function_traits< json_t > - { - static void put( entry& e, const json_t& t ) + static void put( entry& e, const entry& f ) { - e.set_value( t ); + e = f; } }; - template<> - struct function_traits< std::string > + template< typename T > + struct function_traits< atom< T > > { - [[nodiscard]] static std::string get( array& f, std::size_t& i ) + static void put( entry& e, atom< T >&& v ) { - const json_t& j = function_traits_value( f, i++ ); - if( j.type() == json::type::STRING ) { - return j.get_string(); - } - if( j.type() == json::type::BINARY ) { - const auto bv = j.as< tao::binary_view >(); - std::string s( reinterpret_cast< const char* >( bv.data() ), bv.size() ); - if( !json::internal::validate_utf8_nothrow( s ) ) { - throw pegtl::parse_error( "invalid utf-8 in binary data used as string argument", f.position ); - } - return s; - } - throw pegtl::parse_error( "invalid type for string argument", f.position ); + e.set_value( std::move( v ) ); } - static void put( entry& e, const std::string& s ) + static void put( entry& e, const atom< T >& v ) { - e.set_value( json_t( s ) ); + e.set_value( v ); } }; - template<> - struct function_traits< tao::binary_view > + [[nodiscard]] inline const entry& function_traits_value( array& f, const std::size_t i ) { - [[nodiscard]] static tao::binary_view get( array& f, std::size_t& i ) - { - const json_t& j = function_traits_value( f, i++ ); - if( j.is_binary_type() ) { - return j.get_binary_type(); - } - throw pegtl::parse_error( "invalid type for binary argument", f.position ); - } - }; + assert( f.array.size() > i ); - template<> - struct function_traits< std::vector< std::byte > > - { - static void put( entry& e, const std::vector< std::byte >& b ) - { - e.set_value( json_t( b ) ); + auto p = f.array.begin(); + std::advance( p, i ); + + assert( !p->concat.empty() ); + + if( p->concat.size() != 1 ) { + throw arguments_unready(); } - }; + return p->concat.front(); + } template<> - struct function_traits< std::vector< std::string > > + struct function_traits< std::string > { - static void put( entry& e, const std::vector< std::string >& v ) + [[nodiscard]] static std::string get( array& f, const std::size_t i ) { - e.set_value( json_t( v ) ); + const entry& e = function_traits_value( f, i ); + + if( e.is_string() ) { + return e.get_string(); + } + if( e.is_binary() ) { + const std::vector< std::byte >& b = e.get_binary(); + const std::string s( reinterpret_cast< const char* >( b.data() ), b.size() ); + if( !json::internal::validate_utf8_nothrow( s ) ) { + throw pegtl::parse_error( "invalid utf-8 in binary data used as string argument", e.get_position() ); + } + return s; + } + throw pegtl::parse_error( "invalid type for string argument", e.get_position() ); } }; template<> - struct function_traits< pegtl::position > + struct function_traits< std::vector< std::byte > > { - [[nodiscard]] static pegtl::position get( array& f, const std::size_t /*unused*/ ) + [[nodiscard]] static std::vector< std::byte > get( array& f, const std::size_t i ) { - return f.position; + const entry& e = function_traits_value( f, i ); + + if( e.is_binary() ) { + return e.get_binary(); + } + throw pegtl::parse_error( "invalid type for binary argument", e.get_position() ); } }; diff --git a/include/tao/config/internal/function_wrapper.hpp b/include/tao/config/internal/function_wrapper.hpp index 069111f..10401fc 100644 --- a/include/tao/config/internal/function_wrapper.hpp +++ b/include/tao/config/internal/function_wrapper.hpp @@ -11,7 +11,6 @@ #include "forward.hpp" #include "function_traits.hpp" #include "json.hpp" -#include "json_traits.hpp" #include "pegtl.hpp" namespace tao::config::internal @@ -29,7 +28,7 @@ namespace tao::config::internal } template< typename R, typename A > - [[nodiscard]] function wrap( R ( *x )( A ) ) + [[nodiscard]] function wrap( R ( *x )( const pegtl::position&, A ) ) { static_assert( !std::is_pointer_v< R > ); static_assert( !std::is_reference_v< R > ); @@ -38,9 +37,8 @@ namespace tao::config::internal return function( [ x ]( entry& e ) { array& f = e.get_array(); try { - std::size_t i = 0; - decltype( auto ) a = function_traits< std::decay_t< A > >::get( f, i ); - function_traits< std::decay_t< R > >::put( e, x( a ) ); + decltype( auto ) a = function_traits< std::decay_t< A > >::get( f, 0 ); + function_traits< std::decay_t< R > >::put( e, x( f.position, a ) ); } catch( const arguments_unready& ) { return false; @@ -50,7 +48,7 @@ namespace tao::config::internal } template< typename R, typename A, typename B > - [[nodiscard]] function wrap( R ( *x )( A, B ) ) + [[nodiscard]] function wrap( R ( *x )( const pegtl::position&, A, B ) ) { static_assert( !std::is_pointer_v< R > ); static_assert( !std::is_reference_v< R > ); @@ -59,10 +57,9 @@ namespace tao::config::internal return function( [ x ]( entry& e ) { array& f = e.get_array(); try { - std::size_t i = 0; - decltype( auto ) a = function_traits< std::decay_t< A > >::get( f, i ); - decltype( auto ) b = function_traits< std::decay_t< B > >::get( f, i ); - function_traits< std::decay_t< R > >::put( e, x( a, b ) ); + decltype( auto ) a = function_traits< std::decay_t< A > >::get( f, 0 ); + decltype( auto ) b = function_traits< std::decay_t< B > >::get( f, 1 ); + function_traits< std::decay_t< R > >::put( e, x( f.position, a, b ) ); } catch( const arguments_unready& ) { return false; diff --git a/include/tao/config/internal/json_to_value.hpp b/include/tao/config/internal/jaxn_to_entry.hpp similarity index 58% rename from include/tao/config/internal/json_to_value.hpp rename to include/tao/config/internal/jaxn_to_entry.hpp index eb2e091..0be2eab 100644 --- a/include/tao/config/internal/json_to_value.hpp +++ b/include/tao/config/internal/jaxn_to_entry.hpp @@ -1,106 +1,100 @@ // Copyright (c) 2016-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config -#ifndef TAO_CONFIG_INTERNAL_TO_VALUE_HPP -#define TAO_CONFIG_INTERNAL_TO_VALUE_HPP +#ifndef TAO_CONFIG_INTERNAL_JAXN_TO_ENTRY_HPP +#define TAO_CONFIG_INTERNAL_JAXN_TO_ENTRY_HPP +#include #include #include +#include #include #include #include #include -#include "json.hpp" -#include "json_traits.hpp" +#include "array.hpp" +#include "concat.hpp" +#include "constants.hpp" +#include "entry.hpp" +#include "forward.hpp" +#include "object.hpp" namespace tao::config::internal { - template< template< typename... > class Traits > - struct json_to_basic_value + struct jaxn_to_entry { - explicit json_to_basic_value( const pegtl::position& p ) - : value( json::uninitialized, p ) - {} + jaxn_to_entry() noexcept = default; - std::vector< json::basic_value< Traits > > stack_; + std::vector< entry > stack_; std::vector< std::string > keys_; - json::basic_value< Traits > value; + std::optional< entry > value; void null( const pegtl::position& p ) { - value.set_null(); - value.public_base().position = p; + value.emplace( internal::null( p ) ); } void boolean( const bool v, const pegtl::position& p ) { - value.set_boolean( v ); - value.public_base().position = p; + value.emplace( internal::boolean( v, p ) ); } void number( const std::int64_t v, const pegtl::position& p ) { - value.set_signed( v ); - value.public_base().position = p; + value.emplace( internal::signed_t( v, p ) ); } void number( const std::uint64_t v, const pegtl::position& p ) { - value.set_unsigned( v ); - value.public_base().position = p; + value.emplace( internal::unsigned_t( v, p ) ); } void number( const double v, const pegtl::position& p ) { - value.set_double( v ); - value.public_base().position = p; + value.emplace( internal::double_t( v, p ) ); } void string( const std::string_view v, const pegtl::position& p ) { - value.emplace_string( v ); - value.public_base().position = p; + value.emplace( internal::string_t( v, p ) ); } void string( const char* v, const pegtl::position& p ) { - value.emplace_string( v ); - value.public_base().position = p; + value.emplace( internal::string_t( v, p ) ); } void string( std::string&& v, const pegtl::position& p ) { - value.emplace_string( std::move( v ) ); - value.public_base().position = p; + value.emplace( internal::string_t( std::move( v ), p ) ); } void binary( const tao::binary_view v, const pegtl::position& p ) { - value.emplace_binary( v.begin(), v.end() ); - value.public_base().position = p; + binary( std::vector< std::byte >( v.data(), v.data() + v.size() ), p ); } void binary( std::vector< std::byte >&& v, const pegtl::position& p ) { - value.emplace_binary( std::move( v ) ); - value.public_base().position = p; + value.emplace( internal::binary_t( std::move( v ), p ) ); } void begin_array( const pegtl::position& p ) { - stack_.emplace_back( json::empty_array, p ); + stack_.emplace_back( array_init, p ); } - void begin_array( const std::size_t size, const pegtl::position& p ) + void begin_array( const std::size_t /*unused*/, const pegtl::position& p ) { begin_array( p ); - stack_.back().get_array().reserve( size ); } - void element( const pegtl::position& /*unused*/ ) + void element( const pegtl::position& p ) { - stack_.back().emplace_back( std::move( value ) ); + concat& c = stack_.back().get_array().array.emplace_back( p ); + c.remove = true; + c.concat.emplace_back( std::move( value ).value() ); } void end_array( const pegtl::position& /*unused*/ ) @@ -116,7 +110,7 @@ namespace tao::config::internal void begin_object( const pegtl::position& p ) { - stack_.emplace_back( json::empty_object, p ); + stack_.emplace_back( object_init, p ); } void begin_object( const std::size_t /*unused*/, const pegtl::position& p ) @@ -139,10 +133,13 @@ namespace tao::config::internal keys_.emplace_back( std::move( v ) ); } - void member( const pegtl::position& /*unused*/ ) + void member( const pegtl::position& p ) { - stack_.back().try_emplace( std::move( keys_.back() ), std::move( value ) ); + auto [ i, b ] = stack_.back().get_object().object.try_emplace( std::move( keys_.back() ), p ); + (void)b; keys_.pop_back(); + i->second.remove = true; + i->second.concat.emplace_back( std::move( value ).value() ); } void end_object( const pegtl::position& /*unused*/ ) @@ -157,8 +154,6 @@ namespace tao::config::internal } }; - using json_to_value = json_to_basic_value< json_traits >; - } // namespace tao::config::internal #endif diff --git a/include/tao/config/internal/json.hpp b/include/tao/config/internal/json.hpp index 287227b..3eeb554 100644 --- a/include/tao/config/internal/json.hpp +++ b/include/tao/config/internal/json.hpp @@ -11,15 +11,6 @@ #include "pegtl.hpp" -namespace tao::config::internal -{ - template< typename T > - struct json_traits; - - using json_t = tao::json::basic_value< json_traits >; - -} // namespace tao::config::internal - namespace tao::config::internal::rules { namespace jaxn = tao::json::jaxn::internal::rules; diff --git a/include/tao/config/internal/json_base.hpp b/include/tao/config/internal/json_base.hpp deleted file mode 100644 index 1dd14dd..0000000 --- a/include/tao/config/internal/json_base.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_JSON_BASE_HPP -#define TAO_CONFIG_INTERNAL_JSON_BASE_HPP - -#include -#include - -#include "pegtl.hpp" - -namespace tao::config::internal -{ - struct json_base - { - pegtl::position position; - - json_base() - : position( pegtl::internal::inputerator{ nullptr, 1, 1, 1 }, "TODO: Delete the default c'tor!" ) - {} - // json_base() = delete; - - json_base( json_base&& ) = default; - json_base( const json_base& ) = default; - - json_base& operator=( json_base&& ) = default; - json_base& operator=( const json_base& ) = default; - - json_base( pegtl::position&& p ) noexcept - : position( std::move( p ) ) - {} - - json_base( const pegtl::position& p ) - : position( p ) - {} - - void append_message_extension( std::ostream& o ) const - { - o << '(' << position.source << ':' << position.line << ':' << position.column << ')'; - } - }; - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/json_traits.hpp b/include/tao/config/internal/json_traits.hpp deleted file mode 100644 index 6f9b5b1..0000000 --- a/include/tao/config/internal/json_traits.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_JSON_TRAITS_HPP -#define TAO_CONFIG_INTERNAL_JSON_TRAITS_HPP - -#include "json.hpp" -#include "json_base.hpp" - -namespace tao::config::internal -{ - template< typename T > - struct json_traits - : json::traits< T > - {}; - - template<> - struct json_traits< void > - : json::traits< void > - { - static constexpr const bool enable_implicit_constructor = false; - - template< typename Value > - using public_base = json_base; - }; - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/key1_action.hpp b/include/tao/config/internal/key1_action.hpp index ed7d237..85a2e06 100644 --- a/include/tao/config/internal/key1_action.hpp +++ b/include/tao/config/internal/key1_action.hpp @@ -47,7 +47,7 @@ namespace tao::config::internal template< typename Input > static void apply( const Input& in, std::vector< key1_part >& st ) { - st.emplace_back( part_star, in.position() ); + st.emplace_back( part_asterisk, in.position() ); } }; diff --git a/include/tao/config/internal/key1_kind.hpp b/include/tao/config/internal/key1_kind.hpp index 6498f26..7edcf92 100644 --- a/include/tao/config/internal/key1_kind.hpp +++ b/include/tao/config/internal/key1_kind.hpp @@ -10,7 +10,7 @@ namespace tao::config::internal { name = 0, index = 1, - star = 2, + asterisk = 2, append = 3 }; diff --git a/include/tao/config/internal/key1_part.hpp b/include/tao/config/internal/key1_part.hpp index 9457d15..f297633 100644 --- a/include/tao/config/internal/key1_part.hpp +++ b/include/tao/config/internal/key1_part.hpp @@ -19,9 +19,9 @@ namespace tao::config::internal { struct key1_part { - using data_t = std::variant< std::string, std::size_t, part_star_t, std::shared_ptr< std::uint64_t > >; + using data_t = std::variant< std::string, std::size_t, part_asterisk_t, std::shared_ptr< std::uint64_t > >; - key1_part( const part_star_t t, const pegtl::position& p ) + key1_part( const part_asterisk_t t, const pegtl::position& p ) : position( p ), data( t ) {} diff --git a/include/tao/config/internal/object.hpp b/include/tao/config/internal/object.hpp index a868be6..bcbc0cb 100644 --- a/include/tao/config/internal/object.hpp +++ b/include/tao/config/internal/object.hpp @@ -44,6 +44,11 @@ namespace tao::config::internal return ( i == object.end() ) ? nullptr : ( &*i ); } + [[nodiscard]] const pegtl::position& get_position() const noexcept + { + return position; + } + std::map< std::string, C > object; pegtl::position position; }; diff --git a/include/tao/config/internal/parse_utility.hpp b/include/tao/config/internal/parse_utility.hpp index 278fc9f..493d3e4 100644 --- a/include/tao/config/internal/parse_utility.hpp +++ b/include/tao/config/internal/parse_utility.hpp @@ -7,10 +7,6 @@ #include #include "forward.hpp" -#include "jaxn_action.hpp" -#include "json.hpp" -#include "json_to_value.hpp" -#include "json_traits.hpp" #include "key1.hpp" #include "key1_action.hpp" #include "key1_grammar.hpp" @@ -35,13 +31,6 @@ namespace tao::config::internal return result; } - [[nodiscard]] inline json_t parse_jaxn( pegtl_input_t& in ) - { - json_to_value consumer( in.position() ); - pegtl::parse< pegtl::must< json::jaxn::internal::rules::sor_single_value >, jaxn_action, json::jaxn::internal::errors >( in, consumer ); - return std::move( consumer.value ); - } - } // namespace tao::config::internal #endif diff --git a/include/tao/config/internal/phase1_append.hpp b/include/tao/config/internal/phase1_append.hpp index 9c52e66..dac11f1 100644 --- a/include/tao/config/internal/phase1_append.hpp +++ b/include/tao/config/internal/phase1_append.hpp @@ -18,7 +18,6 @@ #include "constants.hpp" #include "entry.hpp" #include "json.hpp" -#include "json_traits.hpp" #include "key1.hpp" #include "limits.hpp" #include "object.hpp" @@ -31,16 +30,16 @@ namespace tao::config::internal void phase1_append( concat& c, const key1& path, const T& thing, const phase1_mode mode ); template< typename T > - void phase1_append_star( concat& c, const pegtl::position& p, const key1& path, const T& thing, const phase1_mode mode ) + void phase1_append_asterisk( concat& c, const pegtl::position& p, const key1& path, const T& thing, const phase1_mode mode ) { - c.back_ensure_kind( entry_kind::concat, p ); - phase1_append( c.concat.back().get_concat(), path, thing, mode ); + c.back_ensure_init( asterisk_init, p ); + phase1_append( c.concat.back().get_asterisk(), path, thing, mode ); } template< typename T > void phase1_append_name( concat& c, const pegtl::position& p, const std::string& name, const key1& path, const T& thing, const phase1_mode mode ) { - c.back_ensure_kind( entry_kind::object, p ); + c.back_ensure_init( object_init, p ); const auto pair = c.concat.back().get_object().object.try_emplace( name, p ); pair.first->second.implicit = ( mode == phase1_mode::implicit ) && ( pair.second || pair.first->second.implicit ); phase1_append( pair.first->second, path, thing, mode ); @@ -53,21 +52,27 @@ namespace tao::config::internal for( auto& e : c.concat ) { switch( e.kind() ) { - case entry_kind::value: + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: throw pegtl::parse_error( "cannot index (across) value", p ); - case entry_kind::reference: - throw pegtl::parse_error( "cannot index (across) reference", p ); - case entry_kind::array: + case entry_kind::ARRAY: if( e.get_array().array.size() > n ) { phase1_append( *std::next( e.get_array().array.begin(), n ), path, thing, mode ); return; } n -= e.get_array().array.size(); continue; - case entry_kind::object: + case entry_kind::OBJECT: throw pegtl::parse_error( "cannot index (across) object", p ); - case entry_kind::concat: + case entry_kind::ASTERISK: throw pegtl::parse_error( "cannot index (across) star", p ); + case entry_kind::REFERENCE: + throw pegtl::parse_error( "cannot index (across) reference", p ); } } throw pegtl::parse_error( "index out of range", p ); @@ -76,7 +81,7 @@ namespace tao::config::internal template< typename T > void phase1_append_append( concat& c, const pegtl::position& p, const std::uint64_t g, const key1& path, const T& thing, const phase1_mode mode ) { - c.back_ensure_kind( entry_kind::array, p ); + c.back_ensure_init( array_init, p ); auto& a = c.concat.back().get_array(); if( g > c.generation ) { c.generation = g; @@ -99,8 +104,8 @@ namespace tao::config::internal const auto& part = path.at( 0 ); switch( part.kind() ) { - case key1_kind::star: - phase1_append_star( c, part.position, pop_front( path ), thing, mode ); + case key1_kind::asterisk: + phase1_append_asterisk( c, part.position, pop_front( path ), thing, mode ); return; case key1_kind::name: phase1_append_name( c, part.position, part.get_name(), pop_front( path ), thing, mode ); diff --git a/include/tao/config/internal/phase2_access.hpp b/include/tao/config/internal/phase2_access.hpp index 0e5ae87..7dab194 100644 --- a/include/tao/config/internal/phase2_access.hpp +++ b/include/tao/config/internal/phase2_access.hpp @@ -38,16 +38,20 @@ namespace tao::config::internal } const auto& e = c.concat.front(); switch( e.kind() ) { - case entry_kind::value: + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: throw pegtl::parse_error( "access name in value", p ); // TODO: Add c.position to the exception, too? - case entry_kind::reference: - throw phase2_access_return(); - case entry_kind::array: + case entry_kind::ARRAY: if( down >= 0 ) { return nullptr; } throw pegtl::parse_error( "access name in array", p ); - case entry_kind::object: + case entry_kind::OBJECT: if( const auto i = e.get_object().object.find( name ); i != e.get_object().object.end() ) { return phase2_access( i->second, suffix, down - 1 ); } @@ -55,11 +59,13 @@ namespace tao::config::internal return nullptr; } throw pegtl::parse_error( "name not found", p ); - case entry_kind::concat: + case entry_kind::ASTERISK: if( down >= 0 ) { return nullptr; } throw pegtl::parse_error( "name not found", p ); + case entry_kind::REFERENCE: + throw phase2_access_return(); } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } @@ -77,11 +83,15 @@ namespace tao::config::internal } const auto& e = c.concat.front(); switch( e.kind() ) { - case entry_kind::value: + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: throw pegtl::parse_error( "cannot index (across) value", p ); - case entry_kind::reference: - throw pegtl::parse_error( "cannot index (across) reference", p ); - case entry_kind::array: + case entry_kind::ARRAY: if( e.get_array().array.size() > index ) { return phase2_access( *std::next( e.get_array().array.begin(), index ), suffix, down - 1 ); } @@ -89,13 +99,15 @@ namespace tao::config::internal return nullptr; } throw pegtl::parse_error( "index out of range", p ); - case entry_kind::object: + case entry_kind::OBJECT: throw pegtl::parse_error( "cannot index (across) object", p ); - case entry_kind::concat: + case entry_kind::ASTERISK: if( down >= 0 ) { return nullptr; } throw pegtl::parse_error( "cannot index (across) star", p ); + case entry_kind::REFERENCE: + throw pegtl::parse_error( "cannot index (across) reference", p ); } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } @@ -103,7 +115,7 @@ namespace tao::config::internal [[nodiscard]] inline const concat* phase2_access( const concat& c, const key1_part& p, const key1& suffix, const int down ) { switch( p.kind() ) { - case key1_kind::star: + case key1_kind::asterisk: throw pegtl::parse_error( "unable to access star", p.position ); case key1_kind::name: return phase2_access_name( c, p.position, p.get_name(), suffix, down ); diff --git a/include/tao/config/internal/phase2_additions.hpp b/include/tao/config/internal/phase2_additions.hpp index af52b96..18e0c40 100644 --- a/include/tao/config/internal/phase2_additions.hpp +++ b/include/tao/config/internal/phase2_additions.hpp @@ -1,129 +1,213 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey +// Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config/ -#ifndef TAO_CONFIG_INTERNAL_PHASE2_ADDITIONS_HH -#define TAO_CONFIG_INTERNAL_PHASE2_ADDITIONS_HH +#ifndef TAO_CONFIG_INTERNAL_PHASE2_ADDITIONS_HPP +#define TAO_CONFIG_INTERNAL_PHASE2_ADDITIONS_HPP -#include +#include +#include +#include #include -#include #include "array.hpp" #include "concat.hpp" #include "entry.hpp" #include "forward.hpp" #include "json.hpp" -#include "json_traits.hpp" #include "object.hpp" -#include "pegtl.hpp" #include "string_utility.hpp" namespace tao::config::internal { - struct phase2_additions_error - {}; - - // For numbers we currently use the following rules: - // 1. Integers and floating-point can't be mixed, and: - // 2. Mixed integers result in a signed value. - - inline void phase2_signed_add( const json_t& l, json_t& r ) + struct phase2_additions_impl { - if( r.is_signed() ) { - r.assign( l.get_signed() + r.get_signed() ); - return; - } - if( r.is_unsigned() ) { - r.assign( l.get_signed() + std::int64_t( r.get_unsigned() ) ); - return; + explicit phase2_additions_impl( object& root ) + : m_root( root ) + {} + + [[nodiscard]] std::size_t process() + { + for( auto& p : m_root.object ) { + process_concat( p.second ); + } + return m_changes; } - throw phase2_additions_error(); - } - inline void phase2_unsigned_add( const json_t& l, json_t& r ) - { - if( r.is_signed() ) { - r.assign( std::int64_t( l.get_unsigned() ) + r.get_signed() ); - return; + private: + object& m_root; + std::size_t m_changes = 0; + + void process_concat( concat& c ) + { + for( entry& e : c.concat ) { + process_entry( e ); + } + if( c.concat.size() < 2 ) { + return; + } + for( auto r = ++c.concat.begin(); r != c.concat.end(); ++r ) { + const auto l = std::prev( r ); + switch( r->kind() ) { + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + throw_type_error( *l, *r ); + + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + if( ignore_entry( *l ) || ignore_entry( *r ) ) { + continue; + } + if( ( r->kind() == entry_kind::SIGNED ) ) { + if( l->kind() == entry_kind::SIGNED ) { + r->get_signed_atom().value += l->get_signed(); + break; + } + else if( l->kind() == entry_kind::UNSIGNED ) { + r->get_signed_atom().value += std::int64_t( l->get_unsigned() ); + break; + } + } + else { // r->kind() == entry_kind::UNSIGNED + if( l->kind() == entry_kind::UNSIGNED ) { + r->get_unsigned_atom().value += l->get_unsigned(); + break; + } + else if( l->kind() == entry_kind::SIGNED ) { + l->get_signed_atom().value += std::int64_t( r->get_unsigned() ); + ( *r ) = ( *l ); + break; + } + } + throw_type_error( *l, *r ); + + case entry_kind::DOUBLE: + if( !throw_type_error_if( *l, *r, entry_kind::DOUBLE ) ) { + continue; + } + r->get_double_atom().value += l->get_double(); + break; + + case entry_kind::STRING: + if( !throw_type_error_if( *l, *r, entry_kind::STRING ) ) { + continue; + } + r->get_string_atom().value = l->get_string() + r->get_string(); + break; + + case entry_kind::BINARY: + if( !throw_type_error_if( *l, *r, entry_kind::BINARY ) ) { + continue; + } + r->get_binary_atom().value.insert( r->get_binary().begin(), l->get_binary().begin(), l->get_binary().end() ); + break; + + case entry_kind::ARRAY: + if( !throw_type_error_if( *l, *r, entry_kind::ARRAY ) ) { + continue; + } + r->get_array().array.splice( r->get_array().array.begin(), l->get_array().array ); // throw_type_error_if returns false if l and/or r are functions. + break; + + case entry_kind::OBJECT: + if( !throw_type_error_if( *l, *r, entry_kind::OBJECT ) ) { + continue; + } + process_object( std::move( l->get_object() ), r->get_object() ); + break; + + case entry_kind::ASTERISK: + case entry_kind::REFERENCE: + continue; + } + [[maybe_unused]] const auto t = c.concat.erase( l ); + assert( t == r ); + ++m_changes; + } } - if( r.is_unsigned() ) { - r.assign( l.get_unsigned() + r.get_unsigned() ); - return; + + void process_entry( entry& e ) + { + switch( e.kind() ) { + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: + return; + case entry_kind::ARRAY: + for( concat& c : e.get_array().array ) { + process_concat( c ); + } + return; + case entry_kind::OBJECT: + for( auto& p : e.get_object().object ) { + process_concat( p.second ); + } + return; + case entry_kind::ASTERISK: + process_concat( e.get_asterisk() ); + return; + case entry_kind::REFERENCE: + return; + } + throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } - throw phase2_additions_error(); - } - inline void phase2_double_add( const json_t& l, json_t& r ) - { - if( r.is_double() ) { - r.assign( l.get_double() + r.get_double() ); - return; + [[nodiscard]] static bool ignore_entry( const entry& e ) noexcept + { + if( ( e.kind() == entry_kind::ASTERISK ) || ( e.kind() == entry_kind::REFERENCE ) ) { + return true; + } + if( ( e.kind() == entry_kind::ARRAY ) && ( !e.get_array().function.empty() ) ) { + return true; + } + return false; } - throw phase2_additions_error(); - } - inline void phase2_string_add( const json_t& l, json_t& r ) - { - if( r.type() == json::type::STRING ) { - r.get_string() = l.get_string() + r.get_string(); - return; + [[nodiscard]] static bool throw_type_error_if( const entry& l, const entry& r, const entry_kind k ) + { + if( ignore_entry( l ) ) { + return false; + } + if( ignore_entry( r ) ) { + return false; + } + if( l.kind() == k ) { + return true; + } + throw_type_error( l, r ); } - throw phase2_additions_error(); - } - inline void phase2_binary_add( const json_t& l, json_t& r ) - { - if( r.type() == json::type::BINARY ) { - r.get_binary().insert( r.get_binary().begin(), l.get_binary().begin(), l.get_binary().end() ); - return; + [[noreturn]] static void throw_type_error( const entry& l, const entry& r ) + { + throw pegtl::parse_error( strcat( "incompatible or invalid type(s) in addition", l.kind(), "@", l.get_position(), " and ", r.kind(), "@", r.get_position() ), pegtl::position( 0, 0, 0, "(todo) location of '+'" ) ); } - throw phase2_additions_error(); - } - inline void phase2_value_add( json_t&& l, json_t& r ) - { - switch( l.type() ) { - case json::type::NULL_: - case json::type::BOOLEAN: - throw phase2_additions_error(); - case json::type::SIGNED: - phase2_signed_add( l, r ); - return; - case json::type::UNSIGNED: - phase2_unsigned_add( l, r ); - return; - case json::type::DOUBLE: - phase2_double_add( l, r ); - return; - case json::type::STRING: - phase2_string_add( l, r ); - return; - case json::type::BINARY: - phase2_binary_add( l, r ); - return; - case json::type::STRING_VIEW: - case json::type::BINARY_VIEW: - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - case json::type::UNINITIALIZED: - case json::type::ARRAY: - case json::type::OBJECT: - case json::type::VALUE_PTR: - case json::type::OPAQUE_PTR: - case json::type::VALUELESS_BY_EXCEPTION: - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE + static void process_object( object&& l, object& r ) + { + for( std::pair< const std::string, concat >& m : l.object ) { + const auto pair = r.object.try_emplace( m.first, m.second ); + if( !pair.second ) { + if( pair.first->second.remove ) { + continue; + } + if( m.second.remove ) { + pair.first->second.remove = true; + } + if( m.second.temporary ) { + pair.first->second.temporary = true; + } + pair.first->second.concat.splice( pair.first->second.concat.begin(), m.second.concat ); + } + } } - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - } + }; - inline void phase2_additions( json_t&& l, json_t& r ) + [[nodiscard]] inline std::size_t phase2_additions( object& root ) { - try { - phase2_value_add( std::move( l ), r ); // l is unchanged when a phase2_additions_error is thrown. - } - catch( const phase2_additions_error& /*unused*/ ) { - pegtl::parse_error p( strcat( "incompatible types ", l.type(), "@", l.position, " and ", r.type(), "@", r.position ), pegtl::position( 0, 1, 1, "(todo) location of '+'" ) ); - throw p; - } + return phase2_additions_impl( root ).process(); } } // namespace tao::config::internal diff --git a/include/tao/config/internal/phase2_asterisks.hpp b/include/tao/config/internal/phase2_asterisks.hpp index 991b9e8..42606e5 100644 --- a/include/tao/config/internal/phase2_asterisks.hpp +++ b/include/tao/config/internal/phase2_asterisks.hpp @@ -15,7 +15,6 @@ #include "entry.hpp" #include "forward.hpp" #include "json.hpp" -#include "json_traits.hpp" #include "object.hpp" namespace tao::config::internal @@ -50,13 +49,16 @@ namespace tao::config::internal void process_entry( concat& c, std::list< entry >::iterator& i ) { switch( i->kind() ) { - case entry_kind::value: + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::DOUBLE: + case entry_kind::UNSIGNED: ++i; return; - case entry_kind::reference: - ++i; - return; - case entry_kind::array: + case entry_kind::ARRAY: if( c.concat.size() == 1 ) { for( concat& d : i->get_array().array ) { process_concat( d ); @@ -64,7 +66,7 @@ namespace tao::config::internal } ++i; return; - case entry_kind::object: + case entry_kind::OBJECT: if( c.concat.size() == 1 ) { for( auto& p : i->get_object().object ) { process_concat( p.second ); @@ -72,58 +74,71 @@ namespace tao::config::internal } ++i; return; - case entry_kind::concat: + case entry_kind::ASTERISK: process_concat_entry( c, i ); return; + case entry_kind::REFERENCE: + ++i; + return; } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } void process_concat_entry( concat& c, std::list< entry >::iterator& i ) { - const concat star = std::move( i->get_concat() ); + const concat star = std::move( i->get_asterisk() ); std::set< std::string > names; for( auto j = c.concat.begin(); j != i; ++j ) { switch( j->kind() ) { - case entry_kind::value: - continue; - case entry_kind::reference: + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: continue; - case entry_kind::array: + case entry_kind::ARRAY: if( !j->get_array().function.empty() ) { throw pegtl::parse_error( "please do not use a star inside of a function", j->get_array().position ); } process_array_concat_entry( j->get_array(), star ); continue; - case entry_kind::object: + case entry_kind::OBJECT: for( const auto& p : j->get_object().object ) { names.emplace( p.first ); } continue; - case entry_kind::concat: + case entry_kind::ASTERISK: + case entry_kind::REFERENCE: continue; } } for( auto j = i; j != c.concat.end(); ++j ) { switch( j->kind() ) { - case entry_kind::value: - continue; - case entry_kind::reference: + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: continue; - case entry_kind::array: + case entry_kind::ARRAY: if( !j->get_array().function.empty() ) { throw pegtl::parse_error( "please do not use a star inside of a function", j->get_array().position ); } process_concat_entry_array( star, j->get_array() ); continue; - case entry_kind::object: + case entry_kind::OBJECT: for( const auto& p : j->get_object().object ) { names.emplace( p.first ); } continue; - case entry_kind::concat: + case entry_kind::ASTERISK: + case entry_kind::REFERENCE: continue; } } diff --git a/include/tao/config/internal/phase2_combinations.hpp b/include/tao/config/internal/phase2_combinations.hpp deleted file mode 100644 index 3f9232e..0000000 --- a/include/tao/config/internal/phase2_combinations.hpp +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_PHASE2_COMBINATIONS_HPP -#define TAO_CONFIG_INTERNAL_PHASE2_COMBINATIONS_HPP - -#include -#include -#include -#include - -#include "array.hpp" -#include "concat.hpp" -#include "entry.hpp" -#include "forward.hpp" -#include "json.hpp" -#include "json_traits.hpp" -#include "object.hpp" -#include "phase2_additions.hpp" - -namespace tao::config::internal -{ - struct phase2_combinations_impl - { - explicit phase2_combinations_impl( object& root ) - : m_root( root ) - {} - - [[nodiscard]] std::size_t process() - { - for( auto& p : m_root.object ) { - process_concat( p.second ); - } - return m_changes; - } - - private: - object& m_root; - std::size_t m_changes = 0; - - void process_concat( concat& c ) - { - if( c.concat.empty() ) { - return; - } - for( entry& e : c.concat ) { - process_entry( e ); - } - if( c.concat.size() == 1 ) { - return; - } - // assert( c.concat.size() >= 2 ); - - for( auto r = ++c.concat.begin(); r != c.concat.end(); ++r ) { - const auto l = std::prev( r ); - switch( r->kind() ) { - case entry_kind::value: - if( l->kind() == entry_kind::value ) { - phase2_additions( std::move( l->get_value() ), r->get_value() ); - [[maybe_unused]] const auto t = c.concat.erase( l ); - assert( t == r ); - ++m_changes; - } - continue; - case entry_kind::reference: - continue; - case entry_kind::array: - if( ( l->kind() == entry_kind::array ) && ( l->get_array().function.empty() ) && ( r->get_array().function.empty() ) ) { - process_array( std::move( l->get_array() ), r->get_array() ); - [[maybe_unused]] const auto t = c.concat.erase( l ); - assert( t == r ); - ++m_changes; - } - continue; - case entry_kind::object: - if( l->kind() == entry_kind::object ) { - process_object( std::move( l->get_object() ), r->get_object() ); - [[maybe_unused]] const auto t = c.concat.erase( l ); - assert( t == r ); - ++m_changes; - } - continue; - case entry_kind::concat: - continue; - } - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - } - } - - void process_entry( entry& e ) - { - switch( e.kind() ) { - case entry_kind::value: - return; - case entry_kind::reference: - return; - case entry_kind::array: - for( concat& c : e.get_array().array ) { - process_concat( c ); - } - return; - case entry_kind::object: - for( auto& p : e.get_object().object ) { - process_concat( p.second ); - } - return; - case entry_kind::concat: - process_concat( e.get_concat() ); - return; - } - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - } - - static void process_array( array&& l, array& r ) - { - r.array.splice( r.array.begin(), l.array ); - } - - static void process_object( object&& l, object& r ) - { - for( std::pair< const std::string, concat >& m : l.object ) { - const auto pair = r.object.try_emplace( m.first, m.second ); - if( !pair.second ) { - if( pair.first->second.remove ) { - continue; - } - if( m.second.remove ) { - pair.first->second.remove = true; - } - if( m.second.temporary ) { - pair.first->second.temporary = true; - } - pair.first->second.concat.splice( pair.first->second.concat.begin(), m.second.concat ); - } - } - } - }; - - [[nodiscard]] inline std::size_t phase2_combinations( object& root ) - { - return phase2_combinations_impl( root ).process(); - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/phase2_everything.hpp b/include/tao/config/internal/phase2_everything.hpp index 0821e50..4a89724 100644 --- a/include/tao/config/internal/phase2_everything.hpp +++ b/include/tao/config/internal/phase2_everything.hpp @@ -6,7 +6,7 @@ #include "forward.hpp" #include "phase2_asterisks.hpp" -#include "phase2_combinations.hpp" +#include "phase2_additions.hpp" #include "phase2_functions.hpp" #include "phase2_references.hpp" #include "state.hpp" @@ -15,7 +15,7 @@ namespace tao::config::internal { [[nodiscard]] inline bool phase2_iteration( state& st, const function_map& fm ) { - return ( phase2_functions( st, fm ) + phase2_combinations( st.root ) + phase2_references( st.root ) + phase2_asterisks( st.root ) ) > 0; + return ( phase2_functions( st, fm ) + phase2_additions( st.root ) + phase2_references( st.root ) + phase2_asterisks( st.root ) ) > 0; } inline void phase2_everything( state& st, const function_map& fm ) diff --git a/include/tao/config/internal/phase2_functions.hpp b/include/tao/config/internal/phase2_functions.hpp index 473f680..eb0c45a 100644 --- a/include/tao/config/internal/phase2_functions.hpp +++ b/include/tao/config/internal/phase2_functions.hpp @@ -51,11 +51,15 @@ namespace tao::config::internal void process_entry( entry& e ) { switch( e.kind() ) { - case entry_kind::value: + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: return; - case entry_kind::reference: - return; - case entry_kind::array: + case entry_kind::ARRAY: for( auto& c : e.get_array().array ) { process_concat( c ); } @@ -63,12 +67,13 @@ namespace tao::config::internal process_function( e ); } return; - case entry_kind::object: + case entry_kind::OBJECT: for( auto& p : e.get_object().object ) { process_concat( p.second ); } return; - case entry_kind::concat: + case entry_kind::ASTERISK: + case entry_kind::REFERENCE: return; } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE diff --git a/include/tao/config/internal/phase2_references.hpp b/include/tao/config/internal/phase2_references.hpp index 230e61e..cbecfc4 100644 --- a/include/tao/config/internal/phase2_references.hpp +++ b/include/tao/config/internal/phase2_references.hpp @@ -16,7 +16,6 @@ #include "entry.hpp" #include "forward.hpp" #include "json.hpp" -#include "json_traits.hpp" #include "object.hpp" #include "phase2_access.hpp" #include "phase2_guard.hpp" @@ -58,7 +57,7 @@ namespace tao::config::internal assert( d->concat.size() == 1 ); e = d->concat.front(); - // TODO: Call phase2_combinations( c ) to get things done quicker? + // TODO: Call phase2_additions( c ) to get things done quicker? ++m_changes; } } @@ -97,16 +96,23 @@ namespace tao::config::internal [[nodiscard]] std::optional< key1_part > process_inner_reference( const key1& prefix, const std::vector< reference2_part >& reference ) { if( const concat* c = process_reference_parts( prefix, reference ) ) { - if( const json_t* j = c->get_value() ) { - switch( j->type() ) { - case json::type::STRING: - case json::type::STRING_VIEW: - return key1_part( j->as< std::string >(), j->position ); - case json::type::SIGNED: - case json::type::UNSIGNED: - return key1_part( j->as< std::size_t >(), j->position ); - default: - throw pegtl::parse_error( strcat( "invalid json type '", j->type(), "' for reference part" ), j->position ); + if( c->concat.size() == 1 ) { + const entry& e = c->concat.back(); + switch( e.kind() ) { + case entry_kind::STRING: + return key1_part( e.get_string(), e.get_string_atom().position ); + case entry_kind::UNSIGNED: + return key1_part( e.get_unsigned(), e.get_unsigned_atom().position ); + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::DOUBLE: + case entry_kind::ARRAY: + case entry_kind::OBJECT: + case entry_kind::ASTERISK: + case entry_kind::REFERENCE: + throw pegtl::parse_error( strcat( "invalid type '", e.kind(), "' for reference part" ), e.get_position() ); } } } @@ -116,25 +122,31 @@ namespace tao::config::internal [[nodiscard]] const concat* process_entry( const key1& prefix, entry& e ) { switch( e.kind() ) { - case entry_kind::value: + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: return nullptr; - case entry_kind::reference: - return process_reference_parts( prefix, e.get_reference() ); - case entry_kind::array: { + case entry_kind::ARRAY: { std::size_t i = 0; for( auto& c : e.get_array().array ) { process_concat( prefix + key1_part( i++, m_root.position ), c ); } return nullptr; } - case entry_kind::object: + case entry_kind::OBJECT: for( auto& p : e.get_object().object ) { process_concat( prefix + key1_part( p.first, m_root.position ), p.second ); } return nullptr; - case entry_kind::concat: - // process_concat( prefix + key1_part( part_star, m_root.position ), e.get_concat() ); + case entry_kind::ASTERISK: + // process_concat( prefix + key1_part( part_asterisk, m_root.position ), e.get_concat() ); return nullptr; + case entry_kind::REFERENCE: + return process_reference_parts( prefix, e.get_reference() ); } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } diff --git a/include/tao/config/internal/phase3_remove.hpp b/include/tao/config/internal/phase3_remove.hpp index 3947507..a6687d8 100644 --- a/include/tao/config/internal/phase3_remove.hpp +++ b/include/tao/config/internal/phase3_remove.hpp @@ -23,15 +23,27 @@ namespace tao::config::internal [[nodiscard]] inline std::string phase3_partial_type( const entry& e ) { switch( e.kind() ) { - case entry_kind::value: - return std::string( json::to_string( e.get_value().type() ) ); - case entry_kind::reference: - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - case entry_kind::array: + case entry_kind::NULL_: + return "null"; + case entry_kind::BOOLEAN: + return "boolean"; + case entry_kind::STRING: + return "string"; + case entry_kind::BINARY: + return "binary"; + case entry_kind::SIGNED: + return "signed"; + case entry_kind::UNSIGNED: + return "unsigned"; + case entry_kind::DOUBLE: + return "double"; + case entry_kind::ARRAY: return "array"; - case entry_kind::object: + case entry_kind::OBJECT: return "object"; - case entry_kind::concat: + case entry_kind::ASTERISK: + throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE + case entry_kind::REFERENCE: throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE @@ -40,15 +52,19 @@ namespace tao::config::internal [[nodiscard]] inline pegtl::position phase3_partial_position( const entry& e ) { switch( e.kind() ) { - case entry_kind::value: - return e.get_value().position; - case entry_kind::reference: + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: + case entry_kind::ARRAY: + case entry_kind::OBJECT: + return e.get_position(); + case entry_kind::ASTERISK: throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - case entry_kind::array: - return e.get_array().position; - case entry_kind::object: - return e.get_object().position; - case entry_kind::concat: + case entry_kind::REFERENCE: throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE @@ -58,21 +74,27 @@ namespace tao::config::internal { for( auto& e : c.concat ) { switch( e.kind() ) { - case entry_kind::value: + case entry_kind::NULL_: + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: continue; - case entry_kind::reference: - throw pegtl::parse_error( "unresolved reference '" + e.get_reference().to_string() + '\'', e.get_reference().at( 0 ).position ); - case entry_kind::array: + case entry_kind::ARRAY: if( !e.get_array().function.empty() ) { throw pegtl::parse_error( "unresolved function '" + e.get_array().function + '\'', e.get_array().position ); } phase3_remove( e.get_array() ); continue; - case entry_kind::object: + case entry_kind::OBJECT: phase3_remove( e.get_object() ); continue; - case entry_kind::concat: - throw pegtl::parse_error( "unresolved star", e.get_concat().position ); // Can happen when there are also unresolved references. + case entry_kind::ASTERISK: + throw pegtl::parse_error( "unresolved star", e.get_asterisk().position ); // Can happen when there are also unresolved references. + case entry_kind::REFERENCE: + throw pegtl::parse_error( "unresolved reference '" + e.get_reference().to_string() + '\'', e.get_reference().at( 0 ).position ); } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } diff --git a/include/tao/config/internal/phase5_repack.hpp b/include/tao/config/internal/phase5_repack.hpp index 4b6596c..aa723bc 100644 --- a/include/tao/config/internal/phase5_repack.hpp +++ b/include/tao/config/internal/phase5_repack.hpp @@ -13,8 +13,6 @@ #include "concat.hpp" #include "entry.hpp" #include "forward.hpp" -#include "json.hpp" -#include "json_traits.hpp" #include "object.hpp" #include "repack_traits.hpp" @@ -62,56 +60,49 @@ namespace tao::config::internal consumer.end_object( o.object.size() ); } - template< template< typename... > class Traits > - void phase5_repack( const key& k, json::events::to_basic_value< Traits >& consumer, const json_t& j ) - { - switch( j.type() ) { - case json::type::NULL_: - consumer.null(); - break; - case json::type::BOOLEAN: - consumer.boolean( j.get_boolean() ); - break; - case json::type::SIGNED: - consumer.number( j.get_signed() ); - break; - case json::type::UNSIGNED: - consumer.number( j.get_unsigned() ); - break; - case json::type::DOUBLE: - consumer.number( j.get_double() ); - break; - case json::type::STRING: - consumer.string( j.get_string() ); - break; - case json::type::BINARY: - consumer.binary( j.get_binary() ); - break; - default: - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - } - set_key_and_position( consumer.value, k, j.position ); - } - template< template< typename... > class Traits > void phase5_repack( const key& k, json::events::to_basic_value< Traits >& consumer, const entry& e ) { switch( e.kind() ) { - case entry_kind::value: - phase5_repack( k, consumer, e.get_value() ); + case entry_kind::NULL_: + consumer.null(); + set_key_and_position( consumer.value, k, e.get_position() ); return; - case entry_kind::reference: - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE -- must have been either eliminated or flagged as error earlier. - case entry_kind::array: + case entry_kind::BOOLEAN: + consumer.boolean( e.get_boolean() ); + set_key_and_position( consumer.value, k, e.get_position() ); + return; + case entry_kind::SIGNED: + consumer.number( e.get_signed() ); + set_key_and_position( consumer.value, k, e.get_position() ); + return; + case entry_kind::UNSIGNED: + consumer.number( e.get_unsigned() ); + set_key_and_position( consumer.value, k, e.get_position() ); + return; + case entry_kind::DOUBLE: + consumer.number( e.get_double() ); + set_key_and_position( consumer.value, k, e.get_position() ); + return; + case entry_kind::STRING: + consumer.string( e.get_string() ); + set_key_and_position( consumer.value, k, e.get_position() ); + return; + case entry_kind::BINARY: + consumer.binary( e.get_binary() ); + set_key_and_position( consumer.value, k, e.get_position() ); + return; + case entry_kind::ARRAY: if( !e.get_array().function.empty() ) { throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE -- must have been either eliminated or flagged as error earlier. } phase5_repack( k, consumer, e.get_array() ); return; - case entry_kind::object: + case entry_kind::OBJECT: phase5_repack( k, consumer, e.get_object() ); return; - case entry_kind::concat: + case entry_kind::ASTERISK: + case entry_kind::REFERENCE: throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE -- must have been either eliminated or flagged as error earlier. } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE diff --git a/include/tao/config/internal/reference2.hpp b/include/tao/config/internal/reference2.hpp index fdd5121..058b884 100644 --- a/include/tao/config/internal/reference2.hpp +++ b/include/tao/config/internal/reference2.hpp @@ -4,6 +4,7 @@ #ifndef TAO_CONFIG_INTERNAL_REFERENCE2_HPP #define TAO_CONFIG_INTERNAL_REFERENCE2_HPP +#include #include #include #include @@ -74,6 +75,12 @@ namespace tao::config::internal pegtl::memory_input< pegtl::tracking_mode::lazy, pegtl::eol::lf_crlf, const char* > in( s, __FUNCTION__ ); pegtl::parse< grammar, reference2_action >( in, vector() ); } + + [[nodiscard]] const pegtl::position& get_position() const noexcept + { + assert( !vector().empty() ); + return vector()[ 0 ].position; + } }; } // namespace tao::config::internal diff --git a/include/tao/config/internal/statistics.hpp b/include/tao/config/internal/statistics.hpp index 94966da..1c0215c 100644 --- a/include/tao/config/internal/statistics.hpp +++ b/include/tao/config/internal/statistics.hpp @@ -31,21 +31,29 @@ namespace tao::config::internal void count( const entry& e ) { switch( e.kind() ) { - case entry_kind::value: - ++m_atoms; + case entry_kind::NULL_: + ++m_nulls; return; - case entry_kind::reference: - ++m_references; + case entry_kind::BOOLEAN: + case entry_kind::STRING: + case entry_kind::BINARY: + case entry_kind::SIGNED: + case entry_kind::UNSIGNED: + case entry_kind::DOUBLE: + ++m_atoms; return; - case entry_kind::array: + case entry_kind::ARRAY: count( e.get_array() ); return; - case entry_kind::object: + case entry_kind::OBJECT: count( e.get_object() ); return; - case entry_kind::concat: + case entry_kind::ASTERISK: ++m_asterisks; - count( e.get_concat() ); + count( e.get_asterisk() ); + return; + case entry_kind::REFERENCE: + ++m_references; return; } std::abort(); // LCOV_EXCL_LINE diff --git a/src/test/config/enumerations.cpp b/src/test/config/enumerations.cpp index c5361aa..5fa5a33 100644 --- a/src/test/config/enumerations.cpp +++ b/src/test/config/enumerations.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -23,14 +22,13 @@ namespace tao::config static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( key_kind::name ), key_part::data_t >, std::string > ); static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( key_kind::index ), key_part::data_t >, std::size_t > ); - static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::entry_kind::value ), internal::entry::data_t >, internal::json_t > ); - static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::entry_kind::reference ), internal::entry::data_t >, internal::reference2 > ); - static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::entry_kind::array ), internal::entry::data_t >, internal::array > ); - static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::entry_kind::object ), internal::entry::data_t >, internal::object > ); + static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::entry_kind::ARRAY ), internal::entry::data_t >, internal::array > ); + static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::entry_kind::OBJECT ), internal::entry::data_t >, internal::object > ); + static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::entry_kind::REFERENCE ), internal::entry::data_t >, internal::reference2 > ); static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::key1_kind::name ), internal::key1_part::data_t >, std::string > ); static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::key1_kind::index ), internal::key1_part::data_t >, std::size_t > ); - static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::key1_kind::star ), internal::key1_part::data_t >, internal::part_star_t > ); + static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::key1_kind::asterisk ), internal::key1_part::data_t >, internal::part_asterisk_t > ); static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::reference2_kind::name ), internal::reference2_part::data_t >, std::string > ); static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::reference2_kind::index ), internal::reference2_part::data_t >, std::size_t > ); diff --git a/src/test/config/parse_key1.cpp b/src/test/config/parse_key1.cpp index 7447e42..a9808c0 100644 --- a/src/test/config/parse_key1.cpp +++ b/src/test/config/parse_key1.cpp @@ -17,7 +17,7 @@ namespace tao::config TAO_CONFIG_TEST_ASSERT( k[ 1 ].get_name() == "bar" ); TAO_CONFIG_TEST_ASSERT( k[ 2 ].kind() == internal::key1_kind::index ); TAO_CONFIG_TEST_ASSERT( k[ 2 ].get_index() == 42 ); - TAO_CONFIG_TEST_ASSERT( k[ 3 ].kind() == internal::key1_kind::star ); + TAO_CONFIG_TEST_ASSERT( k[ 3 ].kind() == internal::key1_kind::asterisk ); TAO_CONFIG_TEST_THROWS( internal::key1( "foo.-" ) ); } diff --git a/tests/formats.jaxn b/tests/formats.jaxn index 081b1e2..2babdc8 100644 --- a/tests/formats.jaxn +++ b/tests/formats.jaxn @@ -1,8 +1,4 @@ { - cbor: "hallo", jaxn: "hallo", - json: "hallo", - msgpack: "hallo", txt: "hallo", - ubjson: "hallo" } diff --git a/tests/formats.success b/tests/formats.success index 7224b54..003a4c8 100644 --- a/tests/formats.success +++ b/tests/formats.success @@ -1,6 +1,6 @@ -cbor = (cbor (read "tests/formats.cbor")) +// cbor = (cbor (read "tests/formats.cbor")) jaxn = (jaxn (read "tests/formats.json")) -json = (json (read "tests/formats.json")) -msgpack = (msgpack (read "tests/formats.msgpack")) +// json = (json (read "tests/formats.json")) +// msgpack = (msgpack (read "tests/formats.msgpack")) txt = (string (read "tests/formats.txt")) -ubjson = (ubjson (read "tests/formats.ubjson")) +// ubjson = (ubjson (read "tests/formats.ubjson"))