File Coverage

blib/lib/Sidef/Object/Object.pm
Criterion Covered Total %
statement 24 228 10.5
branch 0 144 0.0
condition 0 53 0.0
subroutine 8 44 18.1
pod 19 21 90.4
total 51 490 10.4


line stmt bran cond sub pod time code
1             package Sidef::Object::Object {
2              
3 1     1   326 use 5.016;
  1         3  
4 1     1   5 use Scalar::Util qw();
  1         1  
  1         25  
5              
6 1     1   4 use Sidef;
  1         2  
  1         20  
7 1     1   4 use Sidef::Types::Bool::Bool;
  1         1  
  1         20  
8              
9 1     1   4 use parent qw(Sidef::Object::Convert);
  1         1  
  1         5  
10              
11             use overload
12 1         19 q{~~} => \&{__PACKAGE__ . '::' . '~~'},
13             q{bool} => sub {
14 0 0   0   0 if (defined(my $sub = UNIVERSAL::can($_[0], 'to_b'))) {
15 0         0 @_ = ($_[0]);
16 0         0 goto $sub;
17             }
18 0         0 $_[0];
19             },
20             q{0+} => sub {
21 0 0   0   0 if (defined(my $sub = UNIVERSAL::can($_[0], 'to_n'))) {
22 0         0 @_ = ($_[0]);
23 0         0 goto $sub;
24             }
25 0         0 $_[0];
26             },
27             q{""} => sub {
28 0 0   0   0 if (defined(my $sub = UNIVERSAL::can($_[0], 'to_s'))) {
29 0         0 @_ = ($_[0]);
30 0         0 goto $sub;
31             }
32 0         0 $_[0];
33             },
34             q{cmp} => sub {
35 0     0   0 my ($obj1, $obj2, $swapped) = @_;
36              
37 0 0 0     0 if ( ref($obj1) eq ref($obj2)
      0        
38             and ref($obj1) ne 'Sidef::Types::Number::Number'
39             and Scalar::Util::refaddr($obj1) == Scalar::Util::refaddr($obj2)) {
40 0         0 return 0;
41             }
42              
43 0 0       0 if ($swapped) {
44 0         0 ($obj1, $obj2) = ($obj2, $obj1);
45             }
46              
47 0 0 0     0 if ( CORE::ref($obj1) && UNIVERSAL::isa($obj1, CORE::ref($obj2))
      0        
      0        
48             or CORE::ref($obj2) && UNIVERSAL::isa($obj2, CORE::ref($obj1))) {
49 0 0       0 if (defined(my $sub = UNIVERSAL::can($obj1, '<=>'))) {
50 0         0 @_ = ($obj1, $obj2);
51 0         0 goto $sub;
52             }
53             }
54              
55             #<<<
56 0 0       0 (CORE::ref($obj1) ? Scalar::Util::refaddr($obj1) : ('-inf' + 0)) <=>
    0          
57             (CORE::ref($obj2) ? Scalar::Util::refaddr($obj2) : ('-inf' + 0));
58             #>>>
59             },
60             q{eq} => sub {
61 0     0   0 my ($obj1, $obj2) = @_;
62              
63 0 0 0     0 if ( ref($obj1) eq ref($obj2)
      0        
64             and ref($obj1) ne 'Sidef::Types::Number::Number'
65             and Scalar::Util::refaddr($obj1) == Scalar::Util::refaddr($obj2)) {
66 0         0 return 1;
67             }
68              
69             #<<<
70             (
71 0 0 0     0 UNIVERSAL::isa($obj1, CORE::ref($obj2) || return 0) ||
      0        
      0        
72             UNIVERSAL::isa($obj2, CORE::ref($obj1) || return 0)
73             ) || return 0;
74             #>>>
75              
76 0 0       0 if (defined(my $sub = UNIVERSAL::can($obj1, '=='))) {
77 0         0 @_ = ($obj1, $obj2);
78 0         0 goto $sub;
79             }
80              
81 0         0 !CORE::int($obj1 cmp $obj2);
82 1     1   680 };
  1         3  
  1         3  
83              
84             sub new {
85 0     0 1   bless {}, __PACKAGE__;
86             }
87              
88             sub say {
89 0 0   0 1   (CORE::say @_)
90             ? (Sidef::Types::Bool::Bool::TRUE)
91             : (Sidef::Types::Bool::Bool::FALSE);
92             }
93              
94             *println = \&say;
95              
96             sub print {
97 0 0   0 1   (CORE::print @_)
98             ? (Sidef::Types::Bool::Bool::TRUE)
99             : (Sidef::Types::Bool::Bool::FALSE);
100             }
101              
102             sub lazy {
103 0     0 1   my ($self) = @_;
104 0           Sidef::Object::Lazy->new(obj => $self);
105             }
106              
107             sub method {
108 0     0 1   my ($self, $method, @args) = @_;
109 0           Sidef::Object::LazyMethod->new({obj => $self, method => "$method", args => \@args});
110             }
111              
112             sub object_id {
113 0     0 0   my ($self) = @_;
114 0           Sidef::Types::Number::Number->new(Scalar::Util::refaddr($self));
115             }
116              
117             *refaddr = \&object_id;
118              
119             sub object_type {
120 0     0 0   my ($self) = @_;
121 0           Sidef::Types::String::String->new(Scalar::Util::reftype($self));
122             }
123              
124             *reftype = \&object_type;
125              
126             sub class {
127 0     0 1   my ($obj) = @_;
128 0   0       my $ref = CORE::ref($obj) || $obj;
129              
130 0           my $rindex = rindex($ref, '::');
131 0 0         Sidef::Types::String::String->new($rindex == -1 ? $ref : substr($ref, $rindex + 2));
132             }
133              
134             sub ref {
135 0     0 1   my ($obj) = @_;
136 0   0       Sidef::Types::String::String->new(CORE::ref($obj) || $obj);
137             }
138              
139             sub bless {
140 0     0 1   my ($obj, $arg) = @_;
141 0   0       CORE::bless($arg, (CORE::ref($obj) || $obj));
142             }
143              
144             sub clone {
145 0     0 1   my ($obj) = @_;
146              
147 0           my $class = CORE::ref($obj);
148 0           my $reftype = Scalar::Util::reftype($obj);
149              
150 0 0         if ($reftype eq 'HASH') {
    0          
151 0           CORE::bless {%$obj}, $class;
152             }
153             elsif ($reftype eq 'ARRAY') {
154 0           CORE::bless [@$obj], $class;
155             }
156             else {
157 0           $obj;
158             }
159             }
160              
161             sub dclone {
162 0     0 1   my %addr; # keeps track of cloned objects
163              
164             sub {
165 0     0     my ($obj, $reftype) = @_;
166              
167 0           my $refaddr = Scalar::Util::refaddr($obj);
168              
169             exists($addr{$refaddr})
170 0 0         and return $addr{$refaddr};
171              
172 0           my $class = Scalar::Util::blessed($obj);
173              
174 0 0 0       if (defined($class) and not UNIVERSAL::isa($class, 'Sidef::Object::Object')) {
175 0           $addr{$refaddr} = $obj;
176 0           return $obj;
177             }
178              
179 0 0         if ($reftype eq 'HASH') {
    0          
180 0 0         my $o = defined($class) ? CORE::bless({}, $class) : {};
181 0           $addr{$refaddr} = $o;
182             %$o = (
183             map {
184 0           my $v = $obj->{$_};
185 0           my $r = Scalar::Util::reftype($v);
186 0 0 0       ($_ => ($r eq 'HASH' || $r eq 'ARRAY' ? __SUB__->($v, $r) : $v))
187 0           } CORE::keys(%{$obj})
  0            
188             );
189 0           $o;
190             }
191             elsif ($reftype eq 'ARRAY') {
192 0 0         my $o = defined($class) ? CORE::bless([], $class) : [];
193 0           $addr{$refaddr} = $o;
194             @$o = (
195             map {
196 0           my $r = Scalar::Util::reftype($_);
197 0 0 0       $r eq 'ARRAY' || $r eq 'HASH' ? __SUB__->($_, $r) : $_
198 0           } @{$obj}
  0            
199             );
200 0           $o;
201             }
202             else {
203 0           $obj;
204             }
205             }
206 0           ->($_[0], Scalar::Util::reftype($_[0]));
207             }
208              
209             sub respond_to {
210 0     0 1   my ($self, $method) = @_;
211 0 0         UNIVERSAL::can($self, "$method")
212             ? (Sidef::Types::Bool::Bool::TRUE)
213             : (Sidef::Types::Bool::Bool::FALSE);
214             }
215              
216             sub is_a {
217 0     0 1   my ($self, $obj) = @_;
218 0 0         UNIVERSAL::isa($self, "$obj")
219             ? (Sidef::Types::Bool::Bool::TRUE)
220             : (Sidef::Types::Bool::Bool::FALSE);
221             }
222              
223             *is_an = \&is_a;
224             *kind_of = \&is_a;
225              
226             sub parent_classes {
227 0     0 1   my ($obj) = @_;
228              
229 1     1   1373 no strict 'refs';
  1         3  
  1         644  
230              
231 0           my %seen;
232             my $extract_parents;
233             $extract_parents = sub {
234 0     0     my ($ref) = @_;
235              
236 0           my @parents = @{${$ref . '::'}{ISA}};
  0            
  0            
237              
238 0 0         if (@parents) {
239 0           foreach my $parent (@parents) {
240 0 0         next if $seen{$parent}++;
241 0           push @parents, $extract_parents->($parent);
242             }
243             }
244              
245 0           @parents;
246 0           };
247              
248 0           Sidef::Types::Array::Array->new([map { Sidef::Types::String::String->new($_) } $extract_parents->(CORE::ref($obj))]);
  0            
249             }
250              
251             sub interpolate {
252 0     0 1   my $self = shift(@_);
253 0           $self->new(CORE::join('', @_));
254             }
255              
256             sub dump {
257 0     0 1   my %addr; # keep track of dumped objects
258              
259             my $sub = sub {
260 0     0     my ($obj) = @_;
261              
262 0           my $refaddr = Scalar::Util::refaddr($obj);
263              
264             exists($addr{$refaddr})
265 0 0         and return $addr{$refaddr};
266              
267 0 0         my $type = Sidef::normalize_type(CORE::ref($obj) ? CORE::ref($obj) : $obj);
268 0 0         Scalar::Util::reftype($obj) eq 'HASH' or return $type;
269 0           my @keys = CORE::sort CORE::keys(%{$obj});
  0            
270              
271 0           my $str = Sidef::Types::String::String->new($type . "(#`($refaddr)...)");
272 0           $addr{$refaddr} = $str;
273              
274             $$str = (
275             "$type(" . CORE::join(
276             ', ',
277             map {
278 0 0         my $str = UNIVERSAL::can($obj->{$_}, 'dump') ? $obj->{$_}->dump : "$obj->{$_}";
  0            
279 0           "$_: $str";
280             } @keys
281             )
282             . ')'
283             );
284              
285 0           $str;
286 0           };
287              
288 0           local *Sidef::Object::Object::dump = $sub;
289 0           $sub->($_[0]);
290             }
291              
292             {
293 1     1   10 no strict 'refs';
  1         3  
  1         1497  
294              
295             sub def_method {
296 0     0 1   my ($self, $name, $block) = @_;
297 0 0         *{(CORE::ref($self) ? CORE::ref($self) : $self) . '::' . $name} = sub {
298 0     0     $block->call(@_);
299 0           };
300 0           $self;
301             }
302              
303             sub undef_method {
304 0     0 1   my ($self, $name) = @_;
305 0 0         delete ${(CORE::ref($self) ? CORE::ref($self) : $self) . '::'}{$name};
  0            
306 0           $self;
307             }
308              
309             sub alias_method {
310 0     0 1   my ($self, $old, $new) = @_;
311              
312 0 0         my $ref = (CORE::ref($self) ? CORE::ref($self) : $self);
313 0           my $to = \&{$ref . '::' . $old};
  0            
314              
315 0 0         if (not defined &$to) {
316 0           die "[ERROR] Can't alias the nonexistent method '$old' as '$new'!";
317             }
318              
319 0           *{$ref . '::' . $new} = $to;
  0            
320             }
321              
322             sub methods {
323 0     0 1   my ($self) = @_;
324              
325 0           my %alias;
326             my %methods;
327 0           my $ref = CORE::ref($self);
328 0 0         foreach my $method (grep { $_ !~ /^[(_]/ and defined(&{$ref . '::' . $_}) } keys %{$ref . '::'}) {
  0            
  0            
  0            
329             $methods{$method} = (
330 0   0       $alias{\&{$ref . '::' . $method}} //=
  0            
331             Sidef::Object::LazyMethod->new(
332             {
333             obj => $self,
334             method => $method,
335             }
336             )
337             );
338             }
339              
340 0           Sidef::Types::Hash::Hash->new(%methods);
341             }
342              
343             # Logical AND
344             *{__PACKAGE__ . '::' . '&&'} = sub {
345 0 0   0     $_[0] ? $_[1] : $_[0];
346             };
347              
348             # Logical OR
349             *{__PACKAGE__ . '::' . '||'} = sub {
350 0 0   0     $_[0] ? $_[0] : $_[1];
351             };
352              
353             # Logical XOR
354             *{__PACKAGE__ . '::' . '^'} = sub {
355 0 0 0 0     ($_[0] xor $_[1])
356             ? (Sidef::Types::Bool::Bool::TRUE)
357             : (Sidef::Types::Bool::Bool::FALSE);
358             };
359              
360             # Defined-OR
361             *{__PACKAGE__ . '::' . '\\\\'} = sub {
362 0 0   0     defined($_[0]) ? $_[1] : $_[0];
363             };
364              
365             # Smart match operator
366             *{__PACKAGE__ . '::' . '~~'} = sub {
367 0     0     my ($first, $second, $swapped) = @_;
368              
369 0 0         if ($swapped) {
370 0           ($first, $second) = ($second, $first);
371             }
372              
373             # First is String
374 0 0         if (UNIVERSAL::isa($first, 'Sidef::Types::String::String')) {
375              
376             # String ~~ RangeString
377 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Range::RangeString')) {
378 0           return $second->contains($first);
379             }
380              
381             # String ~~ String
382 0 0         if (CORE::ref($first) eq CORE::ref($second)) {
383 0           return $second->eq($first);
384             }
385             }
386              
387             # First is Number
388 0 0         if (UNIVERSAL::isa($first, 'Sidef::Types::Number::Number')) {
389              
390             # Number ~~ RangeNumber
391 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Range::RangeNumber')) {
392 0           return $second->contains($first);
393             }
394             }
395              
396             # First is RangeNumber
397 0 0         if (UNIVERSAL::isa($first, 'Sidef::Types::Range::RangeNumber')) {
398              
399             # RangeNumber ~~ Number
400 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Number::Number')) {
401 0           return $first->contains($second);
402             }
403             }
404              
405             # First is RangeString
406 0 0         if (UNIVERSAL::isa($first, 'Sidef::Types::Range::RangeString')) {
407              
408             # RangeString ~~ String
409 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::String::String')) {
410 0           return $first->contains($second);
411             }
412             }
413              
414             # First is Array
415 0 0         if (UNIVERSAL::isa($first, 'Sidef::Types::Array::Array')) {
416              
417             # Array ~~ Array
418 0 0         if (CORE::ref($first) eq CORE::ref($second)) {
419 0           return $first->eq($second);
420             }
421              
422             # Array ~~ Regex
423 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Regex::Regex')) {
424 0           return $first->match($second);
425             }
426              
427             # Array ~~ Hash
428 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Hash::Hash')) {
429 0           return $second->keys->contains_all($first);
430             }
431              
432             # Array ~~ Any
433 0 0         if (!UNIVERSAL::isa($second, 'Sidef::Types::Array::Array')) {
434 0           return $first->contains($second);
435             }
436             }
437              
438             # First is Hash
439 0 0         if (UNIVERSAL::isa($first, 'Sidef::Types::Hash::Hash')) {
440              
441             # Hash ~~ Array
442 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Array::Array')) {
443 0           return $second->contains_all($first->keys);
444             }
445              
446             # Hash ~~ Hash
447 0 0         if (CORE::ref($first) eq CORE::ref($second)) {
448 0           return $second->eq($first->keys);
449             }
450              
451             # Hash ~~ Regex
452 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Regex::Regex')) {
453 0           return $first->keys->match($second);
454             }
455              
456             # Hash ~~ Any
457 0 0         if (!UNIVERSAL::isa($second, 'Sidef::Types::Hash::Hash')) {
458 0           return $first->exists($second);
459             }
460             }
461              
462             # First is Regex
463 0 0         if (UNIVERSAL::isa($first, 'Sidef::Types::Regex::Regex')) {
464              
465             # Regex ~~ Regex
466 0 0         if (CORE::ref($first) eq CORE::ref($second)) {
467 0           return $first->eq($second);
468             }
469              
470             # Regex ~~ Array
471 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Array::Array')) {
472 0           return $second->match($first);
473             }
474              
475             # Regex ~~ Hash
476 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Hash::Hash')) {
477 0           return $second->keys->match($first);
478             }
479              
480             # Regex ~~ Any
481 0 0         if (!UNIVERSAL::isa($second, 'Sidef::Types::Regex::Regex')) {
482 0           return $first->match($second)->is_successful;
483             }
484             }
485              
486             # Second is Array
487 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Array::Array')) {
488              
489             # Any ~~ Array
490 0           return $second->contains($first);
491             }
492              
493             # Second is Hash
494 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Hash::Hash')) {
495              
496             # Any ~~ Hash
497 0           return $second->exists($first);
498             }
499              
500             # Second is Regex
501 0 0         if (UNIVERSAL::isa($second, 'Sidef::Types::Regex::Regex')) {
502 0           return $second->match($first)->is_successful;
503             }
504              
505 0           my $bool = $first eq $second;
506             #<<<
507 0 0         CORE::ref($bool) ? $bool : (
    0          
508             $bool ? Sidef::Types::Bool::Bool::TRUE
509             : Sidef::Types::Bool::Bool::FALSE
510             );
511             #>>>
512             };
513              
514             # Negation of smart match
515             *{__PACKAGE__ . '::' . '!~'} = sub {
516 0     0     state $method = '~~';
517 0           $_[0]->$method($_[1])->neg;
518             };
519             }
520             }
521              
522             1;