use strict; use warnings; use Data::Diver qw#DiveVal DiveError#; use Data::Dumper qw#Dumper#; $/ = qq#\r\n#; my $state = { }; my $ref = undef; while ( my $line = ) { next if ( $line =~ qr#^(\#|\s*$)# ); # ignore line if comment or blank. chomp $line; # remove newline. my ( $selector, $value ) = split qr#\s*=\s*#, $line; # (selector)=(value). todo: unless escaped. next if ( not defined $selector ); # ignore line if no selector. my @selector = split qr#\.#, $selector; # (one).(two).(three)... todo: unless escaped. $ref //= ( $selector[0] =~ qr#^([><]|\d+)$# ) ? [ ] : { } ; _dive( $ref, \@selector, $value ); } print Dumper $state, $ref; sub _dive { my ( $ref, $selector, $value ) = @_; return if ( not defined $ref or not defined $selector or not scalar @$selector ); # return if no ref or no selectees. my @selector_b = qw##; for my $selectee ( @$selector ) { if ( $selectee =~ qr#^([><])$# ) # if incognito selectee. todo: unless escaped. { my $selector_b = join q#.#, @selector_b; if ( $1 eq q#># ) # incognito selectee is of increment type. { if ( defined $state->{$selector_b} ) # we have seen this state before. { push @selector_b, $state->{$selector_b} += 1; # push current index + 1. } else { push @selector_b, $state->{$selector_b} = 0; # push 0 (first) index. } } elsif ( $1 eq q#<# ) # incognito selectee is of maintain type. { push @selector_b, $state->{$selector_b} //= 0; # push current index or 0 (first) index. } } else # else non inconito selectee. { push @selector_b, $selectee; # push selectee. } } DiveVal( $ref, @selector_b ) = $value; my ( $error ) = DiveError( ); $error and warn $error; return 1; } __DATA__ >.name = john <.location = uk <.interests.> = programming <.interests.> = cycling >.name = laura <.location = <.interests.> = knitting <.interests.> = tennis <.interests.> = dancing >.name <.location = canada <.interests.>.> = dogs <.interests.<.> = horses <.interests.> = cars # test.error = blah