Text-Glob-DWIW
view release on metacpan or search on metacpan
lib/Text/Glob/DWIW.pm view on Meta::CPAN
sub _nth (&$@) { my ($f,$n)=splice@_,0,2; first { $f->($_)&&--$n<0 } @_ }
sub _addparens { $_[0]=~/^$ch{'('}*[|] | [|]$ch{')'}*$ | \).*\(/xs ? "(?:$_[0])" : $_[0] }
sub __funwa(;$) { my ($name,$wa)=(caller 1+($_[0]//0))[3,5];$name=~s/^.*:://;($name,$wa)}
#~~ element count & guess
sub __mlen ($$) { my@l=map length,@_; my$r= $l[0] ne $l[1] ? max @l :
length +("$_[0]$_[1]"=~/^(.*)(.*)(?=.{$l[0]}$)(?:\1).*$/s)[1];--$r if $r>0&&$r<4; }
# ^- mattered length, ignore same start, => 26**_mlen(...) = a roughly guess for range size
#~~ errors
sub _2wide ($$) { my($mx,$v)=@_;croak"Step size too wide (>$mx)." if$mx&&$mx>0&&$v&& $mx<$v}
sub _2much($$;$){ my($br,$v,$d)=@_;croak "Too much (>$br)." if$br&& $br<abs $v/max 1,$d//1 }
sub _2void () { my($n,$wa)=__funwa 1; $wa//croak "Useless call of '$n' in void context." }
sub _2opt (;$) { croak join ': ','Error in option setting',grep $_,@_ }
#~ option handling
sub _hinthash() { my($l,$f,$hh)=0; do{($f,$hh)=(caller++$l)[3,10]} while $f=~/^_|::_/; $hh }
sub _uuid () { int rand max ~0,2**32 };
sub _hval ($$) {exists$_[0]->{$_[1]}&&$_[0]->{$_[1]}} # (+$) not under 5.10
sub _opt_get { @_ ? (_reft($_[0])eq'HASH' ?shift : _reft($_[$#_])eq'HASH' ?pop : ()) : () }
sub _opt_def { +{qw'anchored 1 capture 0 tree 0 chunk 0 range 1 star 1 invert 0
anchors 0 pattern 0 case 1 minus 1 break 0 twin 1 quant 1
backslash 0 stepsize 0 last 1 greedy 0 mell 0 default 0',
(map {$_=>''} qw'unhead unchar charclass'), rewrite=>undef, tilde=>undef} }
# not planned: braces 1 parens/parentheses 0 sort 0 brackets 1?
sub _opt_chk ($;$$)
{ my ($opt,$adef,$fillup)=@_; $adef||=_opt_def; $fillup//=1;
eval { lock_keys %$opt, keys %$adef; %$opt=(%$adef,%$opt) if $fillup; $opt } or do
{ croak "Unknown option '$1'." if $@=~/^Hash\s*has\s*key\s*'(.*?)'/; _2opt $@ }
}
our (%_prv,%_opt); # inside-out, lexical
sub _opt_lex (;$)
{ my $hh=$_[0]||_hinthash; my $hv=_hval $hh,$keyH; my @ohist;
for (my$i=$hv; $i; $i=_hval\%_prv,$i) { unshift @ohist,$_opt{$i} if _hval \%_opt,$i }
my $lexcfg={ map %$_,_opt_def,@ohist };
wantarray ? %$lexcfg : $lexcfg; # X: silent fallback to _opt_def if lex-scope...
}
sub textglob_options
{ my $new=@_<2?shift:{@_}; my $hh=_hinthash; my $hv; _2void unless $new;
croak "Options must be given as hash(ref)." if $new && _reft($new) ne 'HASH';
_2opt 'Scope of use declaration not found.' unless $hv=_hval $hh,$keyH;
my $lexcfg=_opt_lex $hh if defined wantarray;
$_opt{$hv}={$_opt{$hv}?%{$_opt{$hv}}:(),%$new} if $new; # ensure copy of $new
wantarray ? %$lexcfg : $lexcfg;
}
sub _opt # use: my %o=&_opt; => shift/pop; _opt @_ => don't shift/pop
{ my $opt=&_opt_get//{};
if (keys %$opt==keys %{+_opt_def} || $opt->{default}) { _opt_chk $opt }
else { _opt_chk($opt,_opt_lex) }
for ($opt->{greedy}) { $_||=0; $_=1 if /[^0-9]/; $_=2 if $_>2 } # only 0,1,2
for ($opt->{stepsize}) { $_=0 if $_ && /[^-+0-9]/ }
for (qw'range{[ anchored^$az anchors^$az twin***** star******?','quant#,##')
{ my($k,$v)=split/\b/,$_,2; $_= $_&&!/[\Q$v\E]|^[0,\s]*$/ ? $v : $_//'' for$opt->{$k} }
for (@$opt{qw'unchar unhead backslash'})
{ $_//=''; $_=join'',textglob_expand $_,{default=>1} if /.../&&/^$charclass$/ }
wantarray ? %$opt : $opt;
}
sub _opt_fmt # use: my %o=&_opt; => shift/pop; _opt @_ => don't shift/pop
{ my $opt=&_opt_get//{}; _opt_chk $opt,{qw'paired 0'}; wantarray ? %$opt : $opt }
#~~ import-export # v- every 'use' hold a option hash forever
{ sub import { $INC{'TGDWIW.pm'}=__FILE__ if first {/^:use$/} @_; # use-abbr
splice @_,0,1,$pkg if $_[0] eq 'TGDWIW'; my $opt=&_opt_get;
my $p=$^H{$keyH}; $^H{$keyH}=my$dk=_uuid; $_prv{$dk}=$p if $p;
$_opt{$dk}=_opt_chk $opt,0,0 if $opt; goto &Exporter::import }
sub unimport { undef $^H{$keyH} } # at compile-time: so we can't free any data;
*TGDWIW::import=\&import; *TGDWIW::unimport=\&unimport; #\ also not called often anyway
} # i: refaddr \%^H is same for same scope
#~~ inner of range
sub _succ ($;$) # wrapping magic++ => # 1a..12g, 1a1..2b2 works (test: {9y-10b})
{ my($val,$to)=@_; $^R=[]; my $carry=1; #use re 'eval';
return ++$val if $val!~/^(?:([a-z]+[0-9]*|[0-9]+)(?{ [@{$^R},$^N] }))*$/xis; #<< fallback
my $r=join '',reverse # ^- no ...|| @{$^R}<=1 optimization,because of carry
map { my $p=$_;++$p if $carry; $carry=substr $p,0,my$d=length($p)-length; substr$p,$d }
reverse @{$^R}; return "$carry$r" unless $to && $carry;
no warnings 'substr'; my $t=substr $to,-1-length$r,1; $carry=__toA($t,$carry) if $t;
"$carry$r"
}
sub __le ($$) { (length($_[0])<=>length($_[1])||$_[0]cmp$_[1])<=0 }
sub __toA { (local$_,my$q)=@_;"$_$q"=~/$pure/?$q:/[0-9]/?1:/[a-z]/?'a':/[A-Z]/?'A':$q}
sub __toZ ($$) { (local$_,my$q)=@_;"$_$q"=~/$pure/?$q:/[0-9]/?9:/[a-z]/?'z':/[A-Z]/?'Z':$q}
sub _prep ($$) { my($f,$t)=@_;for my $p (1..length$f) #\ a-9 aa-b9 0-a => a-z aa-bz 0-9
{ $_=__toZ substr($f,-$p,1),$_ for substr$t,-$p,1 }; $t }
sub __formnumber{ $_[0]=~/^[+-]?(0\d*)/ ? '%0'.length($1).'s' : '%s' }
sub __formpat ($) # so ranges can have punctation in it...
{ my ($inp)=@_; my @def=($inp,sub{$_[0]}); my $pu='[[:punct:]]';
return @def if $inp=~/\\/ || $inp=~/^.$/s; # don't know how to handle '\' reliable
my $beg= $inp=~s/^($pu++)//sg ? $1 : ''; # $3-$5000, (0)-(1000) ...
(my $val=$inp)=~s/$pu++//sg; my $pat=$inp; my $wrap=0;
$pat=~s{([^[:punct:]]++)$pu*}{++$wrap; my$l=length $1; my$l2=$l<3?'':'{1,'.($l-1).'}';
!pos $pat ? '(.+))?' : "(.{$l})|(^.$l2))?" }seg;
$pat=('(?:' x $wrap).$pat; my $re=qr/^(?:\Q$beg\E)?$pat\z/s;
(my $frm=$inp)=~s/(%|[^[:punct:]]++)/$1 eq'%' ? '%%' : '%s'/seg; $beg=~s/%/%%/sg;
return $val eq $def[0] || $val eq '' ? @def :
($val,sub{ my$c=my@parts=grep defined$_, $_[0]=~/$re/; # 1_1_1-1
(my $f=$frm)=~s/.*( (?:$pu*? (?<!%)(?:%%)*?%s $pu*?){$c} )$/$1/xsg if $c;
$c ? sprintf $beg.$f,@parts : undef })
}
sub _updwn ($$$$;$)
{ my ($br,$ssz, $f,$t,$step)=@_; $step||=1; my @r; return $f if $f eq $t;
if ("$f$t"=~/^([[:punct:]])\1*\z/)
{ if (($f=length$f)<=($t=length$t)) # *****-*
{while ($f<=$t) { push @r,$1 x$f; $f+=$step; _2much$br,@r } }
else {while ($t<=$f) { push @r,$1 x$f; $f-=$step; _2much$br,@r } }
}
elsif ($f=~/$int/ && $t=~/$int/)
{ my $x=__formnumber abs($f)<abs($t)?$f:$t; # 01..10 vs. 1..10
if ($f<=$t)
{while ($f<=$t) { push @r,sprintf$x,$f;$f+=$step; _2much$br,@r } }
else {while ($t<=$f) { push @r,sprintf$x,$f;$f-=$step; _2much$br,@r } }
} else
{ my ($f1,$pf)=__formpat($f); my ($t1,$pt)=__formpat($t);
return $step>1 ? $f : ($f,$t) if ($f1 eq $t1 && $f ne $t); # 1#1,1'1
my $mod=0; my $last; my $fnext;
my ($f2,$t2,$do)=(__le $f1,$t1)
?($f1,$t1, sub{ push @r,$fnext->($_[0],$pf,$pt) if !($mod%=$step)++ })
:($t1,$f1, sub{ unshift @r,$fnext->($_[0],$pt,$pf) if !($mod%=$step)++ });
my $chlen=length($f2) - (length($f2)==length$t2);
$fnext=sub{ my($v,$f,$g)=@_; _2much$br,@r,$ssz; # only here, doesn't matter 4 int
$last=(defined$last && length($v)>$chlen ? $g->($v):()) // $f->($v) };
my $nm=$f2=~/$int/; my $t3=_prep $f2, $t2; $t3=$t2 if $t3 eq $f2; # aa-z9
while (__le$f2,$t3) # v- ..,undef$last,.. vs. ..,--$mod,..
{$do->($f2);$f2=_succ($f2,$t3); $do->($t2),undef$last,last if !$nm&&$f2=~/$int/; }
#$do->($t2) if defined$last&& !__le($t2,$last)&&!__le($f2,$t2) && ($t2 eq $t3 ||@r<2);
( run in 1.881 second using v1.01-cache-2.11-cpan-5b529ec07f3 )