File Coverage

lib/Perl6/Str.pm
Criterion Covered Total %
statement 133 145 91.7
branch 24 28 85.7
condition 2 2 100.0
subroutine 30 34 88.2
pod 13 19 68.4
total 202 228 88.6


line stmt bran cond sub pod time code
1             package Perl6::Str;
2              
3             # for documentation see end of file
4             # TODO: normalize, index, rindex, pack/unpack (?), quotemeta
5             # split, comb, sprintf
6              
7 8     8   460653 use strict;
  8         17  
  8         268  
8 8     8   43 use warnings;
  8         16  
  8         348  
9             our $VERSION = '0.0.5';
10 8     8   12631 use Encode qw(encode_utf8);
  8         88323  
  8         770  
11 8     8   6890 use Unicode::Normalize qw();
  8         18524  
  8         423  
12              
13             use overload
14 8         74 '""' => \&Str,
15             'cmp' => \&compare,
16 8     8   13926 ;
  8         9164  
17              
18              
19             sub new {
20 353     353 1 24287 my ($class, $str) = @_;
21 353 100       737 $class = ref $class ? ref $class : $class;
22 353         1071 utf8::upgrade($str);
23 353         2483 return bless \$str, $class;
24             }
25              
26             sub codes {
27 3     3 1 7117 return length(${$_[0]});
  3         26  
28             }
29              
30             sub bytes {
31 3     3 1 12 return length(encode_utf8(Unicode::Normalize::NFKC(${$_[0]})));
  3         111  
32             }
33              
34             sub graphs {
35 5     5 1 11 my $str = shift;
36 5         54 return scalar(()= $$str =~ m/\X/g);
37             }
38              
39             {
40 8     8   1720 no warnings 'once';
  8         17  
  8         1071  
41             *chars = \&graphs;
42             }
43              
44             sub Str {
45 261     261 0 271 return ${$_[0]};
  261         3073  
46             }
47              
48             sub compare {
49 245 100   245 0 2130 return $_[2] ?
50             Unicode::Normalize::NFKC($_[1]) cmp Unicode::Normalize::NFKC($_[0])
51             : Unicode::Normalize::NFKC($_[0]) cmp Unicode::Normalize::NFKC($_[1]) ;
52             }
53              
54 8     8   40 no warnings 'redefine';
  8         14  
  8         8081  
55              
56             sub substr {
57 106     106 1 587 my ($self, @args) = @_;
58 106         146 my $start = shift @args;
59 106         221 my $graph_start = $self->_graph_index($start);
60 106         126 my $res;
61 106 100       174 if (@args == 0) {
62 30         76 $res = CORE::substr $$self, $graph_start;
63             } else {
64 76         168 my $end = $self->_graph_index(shift @args, $graph_start);
65 76 100       144 if (@args == 0) {
66 73         219 $res = substr $$self, $graph_start, $end - $graph_start;
67             } else {
68             # replacement
69 3         15 $res = substr $$self, $graph_start, $end - $graph_start, $args[0];
70             }
71             }
72 106 50       166 if (defined $res) {
73 106         269 return $self->new($res);
74             } else {
75 0         0 return;
76             }
77             }
78              
79             sub _graph_index {
80             # turn a grapheme index into a codepoint index
81             # $offest is optional, and ignored if $idx < 0
82 182     182   234 my ($self, $idx, $offset) = @_;
83 182   100     612 $offset ||= 0;
84             # warn "Offset: $offset\n" if $offset;
85 182         229 $idx = int (0 + $idx);
86 182         313 my $old_pos = pos $$self;
87 182         180 my $result;
88             my $re;
89 182 100       359 if ( $idx >= 0) {
90 132         129 $idx += $offset;
91 132         1509 $re = qr{\A\X{$idx}};
92             } else {
93 50         106 $idx = abs($idx);
94 50         585 $re = qr{(?=\X{$idx}\z)} ;
95             }
96 182 50       1084 if ($$self =~ m/$re/g) {
97 182         2075 $result = pos $$self;
98             } else {
99 0         0 warn "substr outside of string";
100 0         0 $result = undef;
101             }
102 182         313 pos $$self = $old_pos;
103 182         486 return $result;
104             }
105              
106             sub chop {
107 1     1 1 3 my $self = shift;
108 1         3 my $copy = $$self;
109 1         11 $copy =~ s/\X\z//;
110 1         5 return $self->new($copy);
111             }
112              
113             sub chomp {
114             # XXX should we check for $/ or \n?
115 1     1 1 3 my $self = shift;
116 1         5 my $delim = $self->new($/);
117 1         5 my $dl = $delim->graphs;
118 1         4 my $sl = $self->graphs;
119 1 50       8 return $self->new('') if $sl < $dl;
120              
121 1 50       5 if ($self->substr(-$sl) eq $delim){
122 0         0 return $self->substr(0, $sl - $dl);
123             } else {
124             # return a copy
125 1         4 return $self->new($self);
126             }
127             }
128              
129             sub reverse {
130 5     5 1 37 my $self = shift;
131 5         9 my $copy = '';
132 5         9 my $self_pos = pos $self;
133 5         84 pos $$self = 0;
134 5         42 while ($$self =~ m/(\X)/g){
135 19         4118 $copy = $1 . $copy;
136             }
137 5         12 pos $$self = $self_pos;
138 5         16 return $self->new($copy);
139             }
140              
141             sub _same_stuff {
142 16     16   31 my $func = shift;
143             return sub {
144 29     29   70 my ($self, $pattern) = @_;
145 29         233 my $old_self_pos = pos $$self;
146 29         36 my $old_pattern_pos = pos $pattern;
147 29 100       124 return $self unless length $pattern;
148 27         74 pos $$self = 0;
149 27         58 pos $pattern = 0;
150 27         43 my $copy = '';
151 27         28 my $last_pattern;
152 27         96 while ($pattern =~ m/(\X)/g){
153 57         86 $last_pattern = $1;
154 57 100       214 last unless $$self =~ m/(\X)/g;
155 55         4187 my $s = $1;
156 55         102 $copy .= $func->($s, $last_pattern);
157             }
158 27 100       5253 if (pos($$self)){
159             # $$self longer than $pattern
160 25         79 while ($$self =~ m/(\X)/g){
161 36         67 $copy .= $func->($1, $last_pattern);
162             }
163             }
164 27         58 pos $$self = $old_self_pos;
165 27         48 pos $pattern = $old_pattern_pos;
166 27         69 return $self->new($copy);
167             }
168 16         121 }
169              
170             BEGIN {
171              
172 8     8   39 *samecase = _same_stuff(\&_copy_case);
173 8         46 *sameaccent = _same_stuff(\&_copy_markings);
174              
175 8         24 for (qw(uc lc ucfirst lcfirst)) {
176 32     8 1 2018 eval qq{
  8     8 1 18  
  8     8 1 47  
  8     8 1 16  
  8         39  
  8         784  
  8         139  
  8         19  
  8         42  
177             sub $_ {
178             return \$_[0]->new(CORE::$_ \${\$_[0]});
179             }
180             };
181             }
182              
183 8         101 for (qw(NFD NFC NFKD NFKC)) {
184 32     0 0 2667 eval qq{
  0     0 0 0  
  0     0 0 0  
  0     0 0 0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
185             sub $_ {
186             return \$_[0]->new(Unicode::Normalize::$_ \${\$_[0]});
187             }
188             };
189             }
190             }
191              
192             sub capitalize {
193 8     8 1 14 my $self = shift;
194 8         22 my $copy = CORE::lc $$self;
195 8         47 $copy =~ s/(\w+)/CORE::ucfirst $1/eg;
  12         40  
196 8         21 return $self->new($copy);
197             }
198              
199             sub _copy_case {
200 62     62   91 my ($chr, $pattern) = @_;
201 62 100   1   248 if ($pattern =~ m/\p{IsTitle}|\p{IsUpper}/){
  1 100       1305  
  1         10  
  1         12  
202 17         78 return CORE::uc $chr;
203             } elsif ($pattern =~ m/\p{IsLower}/){
204 32         250 return CORE::lc $chr;
205             } else {
206 13         57 return $chr;
207             }
208             }
209              
210             sub _split_markings {
211 58     58   191 my $char = Unicode::Normalize::NFKD(shift);
212 58         145 return split m//, $char, 2;
213             }
214              
215             sub _copy_markings {
216 29     29   47 my ($source, $pattern) = @_;
217 29         52 my (undef, $accents) = _split_markings($pattern);
218 29         49 my ($base, undef) = _split_markings($source);
219 29         123 return $base . $accents;
220             }
221              
222             1;
223              
224             __END__