diff --git a/include/tao/config/internal/array.hpp b/include/tao/config/internal/array.hpp index 9bf2a49..faf43ab 100644 --- a/include/tao/config/internal/array.hpp +++ b/include/tao/config/internal/array.hpp @@ -4,7 +4,7 @@ #ifndef TAO_CONFIG_INTERNAL_ARRAY_HPP #define TAO_CONFIG_INTERNAL_ARRAY_HPP -#include +#include #include #include "forward.hpp" diff --git a/include/tao/config/internal/concat.hpp b/include/tao/config/internal/concat.hpp index 0603034..e3d737a 100644 --- a/include/tao/config/internal/concat.hpp +++ b/include/tao/config/internal/concat.hpp @@ -4,6 +4,8 @@ #ifndef TAO_CONFIG_INTERNAL_CONCAT_HPP #define TAO_CONFIG_INTERNAL_CONCAT_HPP +#include +#include #include #include #include @@ -64,6 +66,8 @@ namespace tao::config::internal } result = &e.get_value(); continue; + case entry_kind::function: + throw pegtl::parse_error( "function as referenced reference part", e.get_function().position ); case entry_kind::reference: throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE case entry_kind::array: diff --git a/include/tao/config/internal/config_grammar.hpp b/include/tao/config/internal/config_grammar.hpp index 7bf1bff..24ec01a 100644 --- a/include/tao/config/internal/config_grammar.hpp +++ b/include/tao/config/internal/config_grammar.hpp @@ -19,10 +19,10 @@ namespace tao::config::internal::rules { - struct reference_value + struct jaxn_value { - using rule_t = reference_value; - using subs_t = pegtl::type_list< reference2_rest >; + using rule_t = jaxn_value; + using subs_t = pegtl::type_list< json::jaxn::internal::rules::sor_single_value >; template< pegtl::apply_mode A, pegtl::rewind_mode M, @@ -33,17 +33,17 @@ namespace tao::config::internal::rules typename State > [[nodiscard]] static bool match( pegtl_input_t& in, State& st, const extension_maps& ) { - const auto r = parse_reference2( in ); - const auto f = [ & ]( concat& c ) { c.concat.emplace_back( r ); }; + const auto j = parse_jaxn( in ); + const auto f = [ & ]( concat& c ) { c.concat.emplace_back( j ); }; phase1_append( st.root, st.prefix + st.suffix, f, phase1_mode::manifest ); return true; } }; - struct jaxn_value + struct reference_value { - using rule_t = jaxn_value; - using subs_t = pegtl::type_list< json::jaxn::internal::rules::sor_single_value >; + using rule_t = reference_value; + using subs_t = pegtl::type_list< reference2_rest >; template< pegtl::apply_mode A, pegtl::rewind_mode M, @@ -54,8 +54,8 @@ namespace tao::config::internal::rules typename State > [[nodiscard]] static bool match( pegtl_input_t& in, State& st, const extension_maps& ) { - const auto j = parse_jaxn( in ); - const auto f = [ & ]( concat& c ) { c.concat.emplace_back( j ); }; + const auto r = parse_reference2( in ); + const auto f = [ & ]( concat& c ) { c.concat.emplace_back( r ); }; phase1_append( st.root, st.prefix + st.suffix, f, phase1_mode::manifest ); return true; } @@ -107,8 +107,9 @@ namespace tao::config::internal::rules struct opt_comma : pegtl::opt< pegtl::one< ',' >, wss > {}; template< typename U > struct member_list_impl : pegtl::until< U, member, wss, opt_comma > {}; + template< typename U > struct element_list_impl : pegtl::until< U, value_list, wss, opt_comma > {}; - struct element_list : pegtl::until< jaxn::end_array, value_list, wss, opt_comma > {}; + struct element_list : element_list_impl< jaxn::end_array > {}; struct array : pegtl::if_must< jaxn::begin_array, element_list > {}; struct member_list : member_list_impl< jaxn::end_object > {}; struct object : pegtl::if_must< jaxn::begin_object, member_list > {}; diff --git a/include/tao/config/internal/config_parser.hpp b/include/tao/config/internal/config_parser.hpp index d7db676..cab0129 100644 --- a/include/tao/config/internal/config_parser.hpp +++ b/include/tao/config/internal/config_parser.hpp @@ -18,6 +18,7 @@ #include "json_traits.hpp" #include "member_functions.hpp" #include "pegtl.hpp" +#include "phase2_call.hpp" #include "phase2_combine.hpp" #include "phase2_replace.hpp" #include "phase2_resolve.hpp" @@ -89,12 +90,14 @@ namespace tao::config::internal [[nodiscard]] bool phase2_iteration() { - return ( phase2_combine( st.root ) + phase2_resolve( st.root ) + phase2_replace( st.root ) ) > 0; + return ( phase2_call( st.root, em ) + phase2_combine( st.root ) + phase2_resolve( st.root ) + phase2_replace( st.root ) ) > 0; } void phase2_loop() { while( phase2_iteration() ) { + // This loop could do with some major optimisations, though they would only be worth it + // if somebody reads some really large config files or reads one 10^7 times per second. } } diff --git a/include/tao/config/internal/debug_traits.hpp b/include/tao/config/internal/debug_traits.hpp index 3410c59..fd9862b 100644 --- a/include/tao/config/internal/debug_traits.hpp +++ b/include/tao/config/internal/debug_traits.hpp @@ -242,6 +242,9 @@ namespace tao::config::internal case entry_kind::value: c.string( "value" ); return; + case entry_kind::function: + c.string( "function" ); + return; case entry_kind::reference: c.string( "reference" ); return; @@ -284,6 +287,10 @@ namespace tao::config::internal c.key( "value" ); json::events::produce< Traits >( c, v.get_value() ); break; + case entry_kind::function: + c.key( "function" ); + json::events::produce< Traits >( c, v.get_function() ); + break; case entry_kind::reference: c.key( "reference" ); json::events::produce< Traits >( c, v.get_reference() ); @@ -327,6 +334,13 @@ namespace tao::config::internal TAO_JSON_BIND_REQUIRED( "object_data", &object::object ) > {}; + template<> + struct debug_traits< function > + : json::binding::object< TAO_JSON_BIND_REQUIRED( "position", &function::position ), + TAO_JSON_BIND_REQUIRED( "function", &function::name ), + TAO_JSON_BIND_REQUIRED( "function_args", &function::array ) > + {}; + template< typename T > struct debug_traits< const T* > { diff --git a/include/tao/config/internal/entry.hpp b/include/tao/config/internal/entry.hpp index aa6b0ba..d06efdf 100644 --- a/include/tao/config/internal/entry.hpp +++ b/include/tao/config/internal/entry.hpp @@ -16,6 +16,7 @@ #include "constants.hpp" #include "entry_kind.hpp" #include "forward.hpp" +#include "function.hpp" #include "json.hpp" #include "json_traits.hpp" #include "object.hpp" @@ -26,7 +27,7 @@ namespace tao::config::internal { struct entry { - using data_t = std::variant< json_t, reference2, array, object, concat >; + using data_t = std::variant< json_t, function, reference2, array, object, concat >; explicit entry( const json_t& j ) : m_data( j ) @@ -40,11 +41,16 @@ namespace tao::config::internal assert( !r.empty() ); } + entry( const std::string& n, const pegtl::position& p ) + : m_data( std::in_place_type_t< function >(), n, p ) + {} + entry( const entry_kind k, const pegtl::position& p ) : m_data( std::in_place_type_t< object >(), p ) { switch( k ) { case entry_kind::value: + case entry_kind::function: case entry_kind::reference: throw std::string( "this should never happen" ); case entry_kind::array: @@ -59,12 +65,12 @@ namespace tao::config::internal throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } - entry( entry&& ) = delete; + entry( entry&& ) = default; entry( const entry& ) = default; ~entry() = default; - entry& operator=( entry&& ) = delete; + entry& operator=( entry&& ) = default; entry& operator=( const entry& ) = default; entry_kind kind() const noexcept @@ -77,6 +83,11 @@ namespace tao::config::internal return std::holds_alternative< json_t >( m_data ); } + [[nodiscard]] bool is_function() const noexcept + { + return std::holds_alternative< function >( m_data ); + } + [[nodiscard]] bool is_reference() const noexcept { return std::holds_alternative< reference2 >( m_data ); @@ -119,6 +130,13 @@ namespace tao::config::internal return *s; } + [[nodiscard]] function& get_function() noexcept + { + auto* s = std::get_if< function >( &m_data ); + assert( s ); + return *s; + } + [[nodiscard]] reference2& get_reference() noexcept { auto* s = std::get_if< reference2 >( &m_data ); @@ -154,6 +172,13 @@ namespace tao::config::internal return *s; } + [[nodiscard]] const function& get_function() const noexcept + { + const auto* s = std::get_if< function >( &m_data ); + assert( s ); + return *s; + } + [[nodiscard]] const reference2& get_reference() const noexcept { const auto* s = std::get_if< reference2 >( &m_data ); @@ -187,6 +212,8 @@ namespace tao::config::internal switch( kind() ) { case entry_kind::value: return 0; + case entry_kind::function: + return get_function().count_references_recursive(); case entry_kind::reference: return 1; case entry_kind::array: diff --git a/include/tao/config/internal/entry_kind.hpp b/include/tao/config/internal/entry_kind.hpp index 815d9e2..a9bf418 100644 --- a/include/tao/config/internal/entry_kind.hpp +++ b/include/tao/config/internal/entry_kind.hpp @@ -9,10 +9,11 @@ namespace tao::config::internal enum class entry_kind : char { value = 0, - reference = 1, - array = 2, - object = 3, - concat = 4 + function = 1, + reference = 2, + array = 3, + object = 4, + concat = 5 }; } // namespace tao::config::internal diff --git a/include/tao/config/internal/extension_maps.hpp b/include/tao/config/internal/extension_maps.hpp index 20e1977..ef0ef39 100644 --- a/include/tao/config/internal/extension_maps.hpp +++ b/include/tao/config/internal/extension_maps.hpp @@ -31,6 +31,8 @@ namespace tao::config::internal inner_extension_map inner; outer_extension_map member; outer_extension_map value; + + inner_function_map functions; }; } // namespace tao::config::internal diff --git a/include/tao/config/internal/extension_types.hpp b/include/tao/config/internal/extension_types.hpp index b669dc9..6e31e04 100644 --- a/include/tao/config/internal/extension_types.hpp +++ b/include/tao/config/internal/extension_types.hpp @@ -15,6 +15,9 @@ namespace tao::config::internal { + using inner_function = std::function< bool( entry& ) >; + using inner_function_map = std::map< std::string, inner_function >; + using inner_extension = std::function< json_t( pegtl_input_t&, state&, const extension_maps& ) >; using inner_extension_map = std::map< std::string, inner_extension >; diff --git a/include/tao/config/internal/forward.hpp b/include/tao/config/internal/forward.hpp index 086dacf..30ed0df 100644 --- a/include/tao/config/internal/forward.hpp +++ b/include/tao/config/internal/forward.hpp @@ -18,9 +18,12 @@ namespace tao::config::internal struct basic_array; template< typename C > struct basic_object; + template< typename C > + struct basic_function; using array = basic_array< concat >; using object = basic_object< concat >; + using function = basic_function< concat >; template< typename T > struct argument_traits; diff --git a/include/tao/config/internal/function.hpp b/include/tao/config/internal/function.hpp new file mode 100644 index 0000000..4944bd5 --- /dev/null +++ b/include/tao/config/internal/function.hpp @@ -0,0 +1,53 @@ +// 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_FUNCTION_HPP +#define TAO_CONFIG_INTERNAL_FUNCTION_HPP + +#include +#include +#include + +#include "forward.hpp" +#include "pegtl.hpp" + +namespace tao::config::internal +{ + template< typename C > + struct basic_function + { + using data_t = std::list< C >; + + basic_function() = delete; + + basic_function( const std::string& n, const pegtl::position& p ) + : name( n ), + position( p ) + {} + + basic_function( basic_function&& ) = default; + basic_function( const basic_function& ) = default; + + ~basic_function() = default; + + basic_function& operator=( basic_function&& ) = default; + basic_function& operator=( const basic_function& ) = default; + + [[nodiscard]] std::size_t count_references_recursive() const noexcept + { + std::size_t result = 0; + + for( const auto& c : array ) { + result += c.count_references_recursive(); + } + return result; + } + + std::string name; + std::list< C > array; + pegtl::position position; + }; + +} // namespace tao::config::internal + +#endif diff --git a/include/tao/config/internal/inner_functions.hpp b/include/tao/config/internal/inner_functions.hpp index 885080d..51e4f8a 100644 --- a/include/tao/config/internal/inner_functions.hpp +++ b/include/tao/config/internal/inner_functions.hpp @@ -4,6 +4,9 @@ #ifndef TAO_CONFIG_INTERNAL_INNER_FUNCTIONS_HPP #define TAO_CONFIG_INTERNAL_INNER_FUNCTIONS_HPP +#include +#include + #include "jaxn_action.hpp" #include "json.hpp" #include "json_traits.hpp" @@ -29,6 +32,42 @@ namespace tao::config::internal return ( !l.is_null() ) ? l : r; } + [[nodiscard]] inline bool new_default_function( entry& e, function& f ) + { + if( f.array.size() < 2 ) { + throw pegtl::parse_error( "default function requires at least two arguments", f.position ); + } + for( concat& c : f.array ) { + if( c.concat.size() != 1 ) { + return false; + } + entry& a = c.concat.front(); + + switch( a.kind() ) { + case entry_kind::value: + if( a.get_value().is_null() ) { + continue; + } + break; + case entry_kind::function: + return false; + case entry_kind::reference: + return false; + case entry_kind::array: + break; + case entry_kind::object: + break; + case entry_kind::concat: + return false; + } + // Both a and f are sub-objects of e. + entry t = std::move( a ); + e = std::move( t ); + return true; + } + throw pegtl::parse_error( "default function requires at least one non-null argument", f.position ); + } + [[nodiscard]] inline std::string env_function( const pegtl::position& p, const std::string& s ) { return get_env_throws( p, s ); diff --git a/include/tao/config/internal/phase1_append.hpp b/include/tao/config/internal/phase1_append.hpp index 9adfe43..ad3b0c6 100644 --- a/include/tao/config/internal/phase1_append.hpp +++ b/include/tao/config/internal/phase1_append.hpp @@ -55,6 +55,12 @@ namespace tao::config::internal switch( e.kind() ) { case entry_kind::value: throw pegtl::parse_error( "cannot index (across) value", p ); + case entry_kind::function: + if( e.get_function().array.size() > n ) { + return phase1_append( *std::next( e.get_function().array.begin(), n ), path, thing, mode ); + } + n -= e.get_function().array.size(); + continue; case entry_kind::reference: throw pegtl::parse_error( "cannot index (across) reference", p ); case entry_kind::array: diff --git a/include/tao/config/internal/phase2_access.hpp b/include/tao/config/internal/phase2_access.hpp index 708ada6..fd92b98 100644 --- a/include/tao/config/internal/phase2_access.hpp +++ b/include/tao/config/internal/phase2_access.hpp @@ -38,6 +38,11 @@ namespace tao::config::internal switch( e.kind() ) { case entry_kind::value: throw pegtl::parse_error( "access name in value", p ); // TODO: Add c.position to the exception, too? + case entry_kind::function: + if( down >= 0 ) { + return nullptr; + } + throw pegtl::parse_error( "access name in function", p ); case entry_kind::reference: throw phase2_access_return(); case entry_kind::array: @@ -77,6 +82,14 @@ namespace tao::config::internal switch( e.kind() ) { case entry_kind::value: throw pegtl::parse_error( "cannot index (across) value", p ); + case entry_kind::function: + if( e.get_function().array.size() > index ) { + return phase2_access( *std::next( e.get_function().array.begin(), index ), suffix, down - 1 ); + } + if( down >= 0 ) { + return nullptr; + } + throw pegtl::parse_error( "function index out of range", p ); case entry_kind::reference: throw pegtl::parse_error( "cannot index (across) reference", p ); case entry_kind::array: diff --git a/include/tao/config/internal/phase2_append.hpp b/include/tao/config/internal/phase2_append.hpp deleted file mode 100644 index 5943d7f..0000000 --- a/include/tao/config/internal/phase2_append.hpp +++ /dev/null @@ -1,19 +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_PHASE2_APPEND_HH -#define TAO_CONFIG_INTERNAL_PHASE2_APPEND_HH - -#include "array.hpp" -#include "forward.hpp" - -namespace tao::config::internal -{ - inline void phase2_append( array&& l, array& r ) - { - r.array.splice( r.array.begin(), l.array ); - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/phase2_call.hpp b/include/tao/config/internal/phase2_call.hpp new file mode 100644 index 0000000..19d7a20 --- /dev/null +++ b/include/tao/config/internal/phase2_call.hpp @@ -0,0 +1,96 @@ +// 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_PHASE2_CALL_HPP +#define TAO_CONFIG_INTERNAL_PHASE2_CALL_HPP + +#include +#include + +#include "array.hpp" +#include "concat.hpp" +#include "entry.hpp" +#include "extension_maps.hpp" +#include "object.hpp" + +namespace tao::config::internal +{ + struct phase2_call_impl + { + phase2_call_impl( object& root, const extension_maps& em ) + : m_root( root ), + m_maps( em ) + {} + + [[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; + const extension_maps& m_maps; + + void process_concat( concat& c ) + { + for( auto& e : c.concat ) { + process_entry( e ); + } + } + + void process_entry( entry& e ) + { + switch( e.kind() ) { + case entry_kind::value: + return; + case entry_kind::function: + process_function( e ); + return; + case entry_kind::reference: + return; + case entry_kind::array: + for( auto& 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: + return; + } + throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE + } + + void process_function( entry& e ) + { + function& f = e.get_function(); + + for( auto& c : f.array ) { + process_concat( c ); + } + const auto i = m_maps.functions.find( f.name ); + + if( i == m_maps.functions.end() ) { + throw pegtl::parse_error( "unknown function name " + f.name, f.position ); + } + if( i->second( e ) ) { + ++m_changes; + } + } + }; + + [[nodiscard]] inline std::size_t phase2_call( object& root, const extension_maps& em ) + { + return phase2_call_impl( root, em ).process(); + } + +} // namespace tao::config::internal + +#endif diff --git a/include/tao/config/internal/phase2_combine.hpp b/include/tao/config/internal/phase2_combine.hpp index c7b47a7..6386be7 100644 --- a/include/tao/config/internal/phase2_combine.hpp +++ b/include/tao/config/internal/phase2_combine.hpp @@ -15,16 +15,13 @@ #include "json_traits.hpp" #include "object.hpp" #include "phase2_add.hpp" -#include "phase2_append.hpp" -#include "phase2_insert.hpp" namespace tao::config::internal { struct phase2_combine_impl { explicit phase2_combine_impl( object& root ) - : m_root( root ), - m_changes( 0 ) + : m_root( root ) {} [[nodiscard]] std::size_t process() @@ -37,7 +34,7 @@ namespace tao::config::internal private: object& m_root; - std::size_t m_changes; + std::size_t m_changes = 0; void process_concat( concat& c ) { @@ -50,7 +47,7 @@ namespace tao::config::internal if( c.concat.size() == 1 ) { return; } - assert( c.concat.size() >= 2 ); + // assert( c.concat.size() >= 2 ); for( auto r = ++c.concat.begin(); r != c.concat.end(); ++r ) { const auto l = std::prev( r ); @@ -63,11 +60,13 @@ namespace tao::config::internal ++m_changes; } continue; + case entry_kind::function: + continue; case entry_kind::reference: continue; case entry_kind::array: if( l->kind() == entry_kind::array ) { - phase2_append( std::move( l->get_array() ), r->get_array() ); + process_array( std::move( l->get_array() ), r->get_array() ); [[maybe_unused]] const auto t = c.concat.erase( l ); assert( t == r ); ++m_changes; @@ -75,7 +74,7 @@ namespace tao::config::internal continue; case entry_kind::object: if( l->kind() == entry_kind::object ) { - phase2_insert( std::move( l->get_object() ), r->get_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; @@ -93,6 +92,11 @@ namespace tao::config::internal switch( e.kind() ) { case entry_kind::value: return; + case entry_kind::function: + for( concat& c : e.get_function().array ) { + process_concat( c ); + } + return; case entry_kind::reference: return; case entry_kind::array: @@ -111,6 +115,30 @@ namespace tao::config::internal } 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_combine( object& root ) diff --git a/include/tao/config/internal/phase2_insert.hpp b/include/tao/config/internal/phase2_insert.hpp deleted file mode 100644 index 6d95bdc..0000000 --- a/include/tao/config/internal/phase2_insert.hpp +++ /dev/null @@ -1,36 +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_PHASE2_INSERT_HH -#define TAO_CONFIG_INTERNAL_PHASE2_INSERT_HH - -#include - -#include "concat.hpp" -#include "forward.hpp" -#include "object.hpp" - -namespace tao::config::internal -{ - inline void phase2_insert( 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 ); - } - } - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/phase2_replace.hpp b/include/tao/config/internal/phase2_replace.hpp index 0ed226e..1d5cc16 100644 --- a/include/tao/config/internal/phase2_replace.hpp +++ b/include/tao/config/internal/phase2_replace.hpp @@ -4,8 +4,11 @@ #ifndef TAO_CONFIG_INTERNAL_PHASE2_REPLACE_HPP #define TAO_CONFIG_INTERNAL_PHASE2_REPLACE_HPP +#include +#include #include #include +#include #include "array.hpp" #include "concat.hpp" @@ -20,8 +23,7 @@ namespace tao::config::internal struct phase2_replace_impl { explicit phase2_replace_impl( object& root ) - : m_root( root ), - m_changes( 0 ) + : m_root( root ) {} [[nodiscard]] std::size_t process() @@ -34,7 +36,7 @@ namespace tao::config::internal private: object& m_root; - std::size_t m_changes; + std::size_t m_changes = 0; void process_concat( concat& c ) { @@ -51,6 +53,14 @@ namespace tao::config::internal case entry_kind::value: ++i; return; + case entry_kind::function: + if( c.concat.size() == 1 ) { + for( concat& d : i->get_function().array ) { + process_concat( d ); + } + } + ++i; + return; case entry_kind::reference: ++i; return; @@ -87,6 +97,8 @@ namespace tao::config::internal switch( j->kind() ) { case entry_kind::value: continue; + case entry_kind::function: + throw pegtl::parse_error( "please do not use a star inside of a function", j->get_function().position ); case entry_kind::reference: continue; case entry_kind::array: @@ -105,6 +117,8 @@ namespace tao::config::internal switch( j->kind() ) { case entry_kind::value: continue; + case entry_kind::function: + throw pegtl::parse_error( "please do not use a star inside of a function", j->get_function().position ); case entry_kind::reference: continue; case entry_kind::array: diff --git a/include/tao/config/internal/phase2_resolve.hpp b/include/tao/config/internal/phase2_resolve.hpp index 84d4473..8cde9eb 100644 --- a/include/tao/config/internal/phase2_resolve.hpp +++ b/include/tao/config/internal/phase2_resolve.hpp @@ -24,8 +24,7 @@ namespace tao::config::internal struct phase2_resolve_impl { explicit phase2_resolve_impl( object& root ) - : m_root( root ), - m_changes( 0 ) + : m_root( root ) {} [[nodiscard]] std::size_t process() @@ -41,7 +40,7 @@ namespace tao::config::internal private: object& m_root; - std::size_t m_changes; + std::size_t m_changes = 0; std::set< const void* > m_stack; void process_concat( const key1& prefix, concat& c ) @@ -116,6 +115,13 @@ namespace tao::config::internal switch( e.kind() ) { case entry_kind::value: return nullptr; + case entry_kind::function: { + std::size_t i = 0; + for( auto& c : e.get_function().array ) { + process_concat( prefix + key1_part( i++, m_root.position ), c ); + } + return nullptr; + } case entry_kind::reference: return process_reference_parts( prefix, e.get_reference() ); case entry_kind::array: { diff --git a/include/tao/config/internal/phase3_remove.hpp b/include/tao/config/internal/phase3_remove.hpp index e6c3db0..4cbf2f3 100644 --- a/include/tao/config/internal/phase3_remove.hpp +++ b/include/tao/config/internal/phase3_remove.hpp @@ -24,6 +24,8 @@ namespace tao::config::internal switch( e.kind() ) { case entry_kind::value: return std::string( json::to_string( e.get_value().type() ) ); + case entry_kind::function: + 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 case entry_kind::array: @@ -41,6 +43,8 @@ namespace tao::config::internal switch( e.kind() ) { case entry_kind::value: return e.get_value().position; + case entry_kind::function: + 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 case entry_kind::array: @@ -59,6 +63,8 @@ namespace tao::config::internal switch( e.kind() ) { case entry_kind::value: continue; + case entry_kind::function: + throw pegtl::parse_error( "unresolved function '" + e.get_function().name + '\'', e.get_function().position ); 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: diff --git a/include/tao/config/internal/phase4_schema.hpp b/include/tao/config/internal/phase4_schema.hpp index e8f9110..63729a6 100644 --- a/include/tao/config/internal/phase4_schema.hpp +++ b/include/tao/config/internal/phase4_schema.hpp @@ -41,6 +41,8 @@ namespace tao::config::internal switch( e.kind() ) { case entry_kind::value: return; + case entry_kind::function: + 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 case entry_kind::array: diff --git a/include/tao/config/internal/phase5_repack.hpp b/include/tao/config/internal/phase5_repack.hpp index f9ec71e..95345eb 100644 --- a/include/tao/config/internal/phase5_repack.hpp +++ b/include/tao/config/internal/phase5_repack.hpp @@ -99,6 +99,8 @@ namespace tao::config::internal case entry_kind::value: phase5_repack( k, consumer, e.get_value() ); return; + case entry_kind::function: + throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE -- must have been either eliminated or flagged as error earlier. 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: