File Coverage

blib/lib/Switcheroo.pm
Criterion Covered Total %
statement 142 151 94.0
branch 14 50 28.0
condition 1 2 50.0
subroutine 17 17 100.0
pod 1 1 100.0
total 175 221 79.1


line stmt bran cond sub pod time code
1 11     6   258222 use 5.014;
  6         24  
  6         246  
2 6     6   37 use strict;
  6         12  
  6         218  
3 6     6   42 use warnings;
  6         9  
  6         750  
4              
5             package Switcheroo;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.008';
9             our @EXPORT = qw( switch );
10             our @EXPORT_OK = qw( match );
11             our @ISA = qw( Exporter::Tiny );
12              
13 6     6   5631 use Exporter::Tiny qw( );
  6         12249  
  6         150  
14 6     6   5667 use match::simple qw( match );
  6         27772  
  6         118  
15 6     6   6855 use PadWalker qw( peek_my set_closed_over );
  6         5693  
  6         535  
16 6     6   5289 use Parse::Keyword { switch => \&_parse_switch };
  6         66807  
  6         87  
17              
18             sub import
19             {
20 6     6   64 my $pkg = caller;
21 6         696 eval qq[ package $pkg; our \$a; our \$b; ];
22 6         54 goto \&Exporter::Tiny::import;
23             }
24              
25             sub switch
26             {
27 77     77 1 309 my ($pkg, $expr, $comparator, $cases, $default) = @_;
28            
29 77         105 my @args = @_ = do {
30             package # replaces Devel::Caller::caller_args(1)
31 77         908 DB; my @x = caller(1); our @args;
  77         345  
32             };
33            
34 77         491 my $pad = peek_my(1);
35             my $var = defined($expr)
36 77 100       171 ? do {
37 69         161 set_closed_over($expr, $pad);
38 69         218 $expr->(@args);
39             }
40             : $_;
41 77         154 Internals::SvREADONLY($var, 1);
42 77         132 local *_ = \$var;
43            
44 77         132 my $match = \&match::simple::match;
45 77 100       305 if ($comparator)
46             {
47             $match = sub {
48 6     6   8742 no strict 'refs';
  6         14  
  6         11278  
49 31     31   35 local *{"$pkg\::a"} = \ $_[0];
  31         72  
50 31         198 local *{"$pkg\::b"} = \ $_[1];
  31         60  
51 31         165 $comparator->(@args);
52 10         44 };
53             }
54            
55 77         139 CASE: for my $case ( @$cases )
56             {
57 131         297 my ($type, $condition, $block) = @$case;
58            
59 131         153 my $matched = 0;
60 131 100       222 if ($type eq 'block')
61             {
62 8         15 set_closed_over($condition, $pad);
63 8         19 $matched = !!$condition->(@args);
64             }
65             else
66             {
67 123         177 TERM: for my $termexpr (@$condition)
68             {
69 157         291 set_closed_over($termexpr, $pad);
70 157         495 my $term = $termexpr->(@args);
71 157 100 50     1216 $match->($var, $term) ? (++$matched && last TERM) : next TERM;
72             }
73             }
74            
75 131         5114 set_closed_over($block, $pad);
76 131 100       537 goto $block if $matched;
77             }
78            
79 40 100       93 if ($default)
80             {
81 38         73 set_closed_over($default, $pad);
82 38         162 goto $default;
83             }
84 2         19 return;
85             }
86              
87             sub _parse_switch
88             {
89 13     13   6192 my ($expr, $comparator, @cases, $default);
90 13         26 my $is_statement = 1;
91            
92 13         30 lex_read_space;
93            
94 13 0       54 if (lex_peek eq '(')
95             {
96 12         43 lex_read(1);
97 12         24 lex_read_space;
98 12         260 $expr = parse_fullexpr;
99 12         32 lex_read_space;
100 12 0       34 die "syntax error; expected close parenthesis" unless lex_peek eq ')';
101 12         44 lex_read(1);
102 12         25 lex_read_space;
103             }
104            
105 13 0       48 if (lex_peek(4) eq 'mode')
106             {
107 2         9 lex_read(4);
108 2         5 lex_read_space;
109 2 0       7 die "syntax error; expected open parenthesis" unless lex_peek eq '(';
110 2         9 lex_read(1);
111 2         4 lex_read_space;
112 2         55 $comparator = parse_fullexpr;
113 2         8 lex_read_space;
114 2 0       11 die "syntax error; expected close parenthesis" unless lex_peek eq ')';
115 2         10 lex_read(1);
116 2         4 lex_read_space;
117             }
118            
119 13 0       50 if (lex_peek(2) eq 'do')
120             {
121 2         8 lex_read(2);
122 2         5 lex_read_space;
123 2         3 $is_statement = 0;
124             }
125            
126 13 0       46 die "syntax error; expected block" unless lex_peek eq '{';
127 13         43 lex_read(1);
128 13         35 lex_read_space;
129            
130 13         39 while ( lex_peek(4) eq 'case' )
131             {
132 29         87 lex_read(4);
133 29         77 push @cases, _parse_case();
134 29         102 lex_read_space;
135             }
136            
137 13 0       54 if ( lex_peek(7) eq 'default' )
138             {
139 12         43 lex_read(7);
140 12         17 lex_read_space;
141 12 0       40 if (lex_peek eq ':')
142             {
143 12         35 lex_read(1);
144 12         51 lex_read_space;
145             }
146 12         31 $default = _parse_consequence();
147 12         27 lex_read_space;
148             }
149            
150 13 0       44 die "syntax error; expected end of switch block" unless lex_peek eq '}';
151 13         44 lex_read(1);
152            
153 13         56 my $pkg = compiling_package;
154            
155             return (
156 72     77   44822 sub { ($pkg, $expr, $comparator, \@cases, $default) },
157 13         12954 $is_statement,
158             );
159             }
160              
161             sub _munge_term
162             {
163 33 0   33   91 if (lex_peek(1) eq '/')
    0          
164             {
165 2         9 lex_stuff('qr');
166             }
167             elsif (lex_peek(2) =~ /m\W/)
168             {
169 0         0 lex_read(1);
170 0         0 lex_stuff('qr');
171             }
172             }
173              
174             sub _parse_case
175             {
176 29     29   31 my ($expr, $type);
177 29         56 lex_read_space;
178            
179 29 0       71 if (lex_peek eq '(')
    0          
180             {
181 0         0 lex_read(1);
182 0         0 $type = 'term';
183 0         0 $expr = _parse_list_of_terms(\&_munge_term);
184 0         0 lex_read_space;
185 0 0       0 die "syntax error; expected close parenthesis" unless lex_peek eq ')';
186 0         0 lex_read(1);
187 0         0 lex_read_space;
188             }
189            
190             elsif (lex_peek eq '{')
191             {
192 2         8 $type = 'block';
193 2         47 $expr = parse_block;
194 2         6 lex_read_space;
195             }
196            
197             else
198             {
199 27         98 $type = 'simple-term';
200 27         61 $expr = _parse_list_of_terms(\&_munge_term);
201 27         58 lex_read_space;
202             }
203            
204 29 0       79 die "syntax error; expected colon" unless lex_peek eq ':';
205 29         91 lex_read(1);
206 29         47 lex_read_space;
207            
208 29         44 my $block = _parse_consequence();
209 29         84 return [ $type, $expr, $block ];
210             }
211              
212             sub _parse_list_of_terms
213             {
214 27     27   33 my $munge = shift;
215            
216 27         32 my @expr;
217 27         53 lex_read_space;
218 27 50       85 $munge->() if $munge;
219 27         615 push @expr, parse_termexpr;
220 27         61 lex_read_space;
221            
222 27         68 while (lex_peek eq ',')
223             {
224 6         21 lex_read(1);
225 6         11 lex_read_space;
226 6 50       19 $munge->() if $munge;
227 6         121 push @expr, parse_termexpr;
228 6         25 lex_read_space;
229             }
230            
231 27         104 return \@expr;
232             }
233              
234             sub _parse_consequence
235             {
236 41     41   45 my ($expr, $type);
237 41         64 lex_read_space;
238            
239 41 0       92 my $block = (lex_peek eq '{') ? parse_block() : parse_fullstmt();
240 41         787 lex_read_space;
241 41         118 (lex_read(1), lex_read_space) while lex_peek eq ';';
242            
243 41         168 return $block;
244             }
245              
246              
247             1;
248              
249             __END__