File Coverage

blib/lib/Attribute/Overload/Match.pm
Criterion Covered Total %
statement 38 54 70.3
branch 14 32 43.7
condition 0 3 0.0
subroutine 9 13 69.2
pod 0 3 0.0
total 61 105 58.1


line stmt bran cond sub pod time code
1             # $Id: Match.pm,v 1.1.1.1 2007/02/28 11:49:47 dk Exp $
2            
3             package Attribute::Overload::Match;
4            
5 1     1   807 use strict;
  1         2  
  1         33  
6 1     1   5 use warnings;
  1         3  
  1         25  
7 1     1   258531 use Attribute::Handlers;
  1         9421  
  1         7  
8             our ( %ops, $VERSION);
9             $VERSION = '0.01';
10            
11             sub handle
12             {
13 441     441 0 747 my ( $pkg, $op) = ( shift, shift );
14 441         543 NEXTARG: for my $arg ( @{$ops{$pkg}{$op}}) {
  441         1001  
15 477         946 my $sym = $$arg[0];
16 477 50       1050 next if $#$arg > @_;
17 477         1142 for ( my $x = 1; $x < @$arg; $x++) {
18 71 100       2189 next NEXTARG unless $arg-> [$x]->( $_[$x - 1]);
19             }
20 441         1597 goto $sym;
21            
22             }
23 0         0 die "Nothing matches $op in $pkg";
24            
25             }
26            
27             sub parse
28             {
29 11     11 0 13 my @r;
30 11         17 for my $v ( @_) {
31 2         11 $v =~ s/^\s*//;
32 2         10 $v =~ s/\s*$//;
33 2 50       23 if ( $v eq '') {
    100          
    50          
    50          
    50          
    50          
    0          
34 0     0   0 push @r, sub { 1 };
  0         0  
35             } elsif ( $v =~ /^\d/) {
36 1 50   4   6 push @r, sub { defined $_[0] and $_[0] == $v };
  4         17  
37             } elsif ( $v =~ /^'(.*)'$/ ) {
38 0         0 $v = $1;
39 0 0   0   0 push @r, sub { defined $_[0] and $_[0] eq $v };
  0         0  
40             } elsif ( $v =~ /^[A-Z]/) {
41 0 0 0 0   0 push @r, sub { defined $_[0] and ref($_[0]) and $_[0]->isa($v) };
  0         0  
42             } elsif ( $v eq '//') {
43 0     0   0 push @r, sub { defined $_[0] };
  0         0  
44             } elsif ( $v =~ /^(<|>|lt|gt|eq|==)\s*(.*)$/) {
45 1         113 $v = eval "sub { defined \$_[0] and \$_[0] $v ;}";
46 1 50       4 die $@ if $@;
47 1         2 push @r, $v;
48             } elsif ( $v =~ /^(ne|!=)\s*(.*)$/) {
49 0         0 $v = eval "sub { not defined \$_[0] or \$_[0] $v ;}";
50 0 0       0 die $@ if $@;
51 0         0 push @r, $v;
52             } else {
53 0         0 $v = eval "sub { $v }";
54 0 0       0 die $@ if $@;
55 0         0 push @r, $v;
56             }
57             }
58 11         52 @r;
59             }
60            
61             sub UNIVERSAL::op : ATTR(CODE,RAWDATA) {
62 11     11 0 33737 my ($pkg, $sub, $data) = @_[0,2,4];
63 11         47 require overload;
64 11         31 my ($op, @arg) = split( ',', $data);
65 441     441   5172 overload::OVERLOAD( $pkg, $op, sub { handle( $pkg, $op, @_ ) } )
66 11 100       66 unless exists $ops{$pkg}{$op};
67 11         165 push @{$ops{$pkg}{$op}}, [ $sub, parse @arg ];
  11         32  
68 1     1   751 }
  1         2  
  1         4  
69            
70             1;
71            
72             =pod
73            
74             =head1 NAME
75            
76             Attribute::Overload::Match - argument-dependent handlers for overloaded operators
77            
78             =head1 DESCRIPTION
79            
80             The module is a wrapper for L, that provides a simple syntax for
81             calling different operator handlers for different passed arguments. The idea is
82             a curious ( but probably not a very practical ) mix of L
83             and L .
84            
85             =head1 SYNOPSIS
86            
87             use Attribute::Overload::Match;
88            
89             Suppose we declare a class that overloads operations on integers:
90            
91             sub new($) { my $x = $_[0]; bless \$x, __PACKAGE__ }
92             sub val($) { ${$_[0]} }
93             sub eq : op(==) { val(shift) == shift }
94             sub subtract : op(-) { new val(shift) - shift }
95             sub mul : op(*) { new val(shift) * shift }
96             sub add : op(+) { new val(shift) + shift }
97             sub qq : op("") { val(shift) }
98             sub le : op(<) { val(shift) < shift }
99             ...
100            
101             then we can change meaning of some operators with a touch of functional style:
102            
103             no warnings 'redefine';
104             sub fac : op(!,1) { new 1 }
105             sub fac : op(!) { !($_[0] - 1) * $_[0] }
106            
107             or
108            
109             sub fib : op(~,<2) { new 1 }
110             sub fib : op(~) { ~( $_[0] - 1) + ~($_[0] - 2) }
111            
112             (if you don't like C, just use different sub names for C etc)
113             thus
114            
115             my $x = !new(10);
116             print "$x\n";
117             3628800
118            
119             and
120            
121             my $x = ~new(10);
122             print "$x\n";
123             89
124            
125             =head1 SYNTAX
126            
127             The only syntax available here is syntax that is passed to C attributes,
128             which is in general C, where
129             C belongs to strings defined in L ( such as C<+>, C<[]>,
130             C<""> etc), and C strings are perl code, matching a parameter. However,
131             for the sake of readability, C can be also one of the following
132             signatures:
133            
134             =over
135            
136             =item Empty string
137            
138             Parameter is never checked
139            
140             =item String starting with a digit
141            
142             Pataremeter must be defined and be equal (C<==>) to the value if the string
143            
144             =item Single-quoted string
145            
146             Parameter must be defined and be equal (C) to the value if the string
147            
148             =item Non-quoted string beginning with a capital letter
149            
150             The string defined as a class name. Parameter must be defined and be an instance
151             of the class (or its descendant).
152            
153             =item C
154            
155             Parameter must be defined.
156            
157             =item One of C<< <,>,lt,gt,eq,==,ne,!= >> followed by an expression
158            
159             Parameter must be defined and return true when compared with the expression
160             using given comparison operator
161            
162             =item Anything else
163            
164             Anything else is passed directly to C and is treated in a boolean context
165             thereafter.
166            
167             =back
168            
169             =head1 ACKNOWLEDGEMENTS
170            
171             Thanks to Anton Berezin for ideas on L .
172             Thanks to H. Merijn Brandt for C.
173            
174             =head1 SEE ALSO
175            
176             L, L, L.
177            
178             =head1 COPYRIGHT
179            
180             This library is free software; you can redistribute it and/or modify it
181             under the same terms as Perl itself.
182            
183            
184             =head1 AUTHOR
185            
186             Dmitry Karasik
187            
188             =cut