xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/Switch.pm (revision 0:68f95e015346)
1*0Sstevel@tonic-gatepackage Switch;
2*0Sstevel@tonic-gate
3*0Sstevel@tonic-gateuse strict;
4*0Sstevel@tonic-gateuse vars qw($VERSION);
5*0Sstevel@tonic-gateuse Carp;
6*0Sstevel@tonic-gate
7*0Sstevel@tonic-gate$VERSION = '2.10';
8*0Sstevel@tonic-gate
9*0Sstevel@tonic-gate
10*0Sstevel@tonic-gate# LOAD FILTERING MODULE...
11*0Sstevel@tonic-gateuse Filter::Util::Call;
12*0Sstevel@tonic-gate
13*0Sstevel@tonic-gatesub __();
14*0Sstevel@tonic-gate
15*0Sstevel@tonic-gate# CATCH ATTEMPTS TO CALL case OUTSIDE THE SCOPE OF ANY switch
16*0Sstevel@tonic-gate
17*0Sstevel@tonic-gate$::_S_W_I_T_C_H = sub { croak "case/when statement not in switch/given block" };
18*0Sstevel@tonic-gate
19*0Sstevel@tonic-gatemy $offset;
20*0Sstevel@tonic-gatemy $fallthrough;
21*0Sstevel@tonic-gatemy ($Perl5, $Perl6) = (0,0);
22*0Sstevel@tonic-gate
23*0Sstevel@tonic-gatesub import
24*0Sstevel@tonic-gate{
25*0Sstevel@tonic-gate	$fallthrough = grep /\bfallthrough\b/, @_;
26*0Sstevel@tonic-gate	$offset = (caller)[2]+1;
27*0Sstevel@tonic-gate	filter_add({}) unless @_>1 && $_[1] eq 'noimport';
28*0Sstevel@tonic-gate	my $pkg = caller;
29*0Sstevel@tonic-gate	no strict 'refs';
30*0Sstevel@tonic-gate	for ( qw( on_defined on_exists ) )
31*0Sstevel@tonic-gate	{
32*0Sstevel@tonic-gate		*{"${pkg}::$_"} = \&$_;
33*0Sstevel@tonic-gate	}
34*0Sstevel@tonic-gate	*{"${pkg}::__"} = \&__ if grep /__/, @_;
35*0Sstevel@tonic-gate	$Perl6 = 1 if grep(/Perl\s*6/i, @_);
36*0Sstevel@tonic-gate	$Perl5 = 1 if grep(/Perl\s*5/i, @_) || !grep(/Perl\s*6/i, @_);
37*0Sstevel@tonic-gate	1;
38*0Sstevel@tonic-gate}
39*0Sstevel@tonic-gate
40*0Sstevel@tonic-gatesub unimport
41*0Sstevel@tonic-gate{
42*0Sstevel@tonic-gate	filter_del()
43*0Sstevel@tonic-gate}
44*0Sstevel@tonic-gate
45*0Sstevel@tonic-gatesub filter
46*0Sstevel@tonic-gate{
47*0Sstevel@tonic-gate	my($self) = @_ ;
48*0Sstevel@tonic-gate	local $Switch::file = (caller)[1];
49*0Sstevel@tonic-gate
50*0Sstevel@tonic-gate	my $status = 1;
51*0Sstevel@tonic-gate	$status = filter_read(1_000_000);
52*0Sstevel@tonic-gate	return $status if $status<0;
53*0Sstevel@tonic-gate    	$_ = filter_blocks($_,$offset);
54*0Sstevel@tonic-gate	$_ = "# line $offset\n" . $_ if $offset; undef $offset;
55*0Sstevel@tonic-gate	return $status;
56*0Sstevel@tonic-gate}
57*0Sstevel@tonic-gate
58*0Sstevel@tonic-gateuse Text::Balanced ':ALL';
59*0Sstevel@tonic-gate
60*0Sstevel@tonic-gatesub line
61*0Sstevel@tonic-gate{
62*0Sstevel@tonic-gate	my ($pretext,$offset) = @_;
63*0Sstevel@tonic-gate	($pretext=~tr/\n/\n/)+($offset||0);
64*0Sstevel@tonic-gate}
65*0Sstevel@tonic-gate
66*0Sstevel@tonic-gatesub is_block
67*0Sstevel@tonic-gate{
68*0Sstevel@tonic-gate	local $SIG{__WARN__}=sub{die$@};
69*0Sstevel@tonic-gate	local $^W=1;
70*0Sstevel@tonic-gate	my $ishash = defined  eval 'my $hr='.$_[0];
71*0Sstevel@tonic-gate	undef $@;
72*0Sstevel@tonic-gate	return !$ishash;
73*0Sstevel@tonic-gate}
74*0Sstevel@tonic-gate
75*0Sstevel@tonic-gate
76*0Sstevel@tonic-gatemy $EOP = qr/\n\n|\Z/;
77*0Sstevel@tonic-gatemy $CUT = qr/\n=cut.*$EOP/;
78*0Sstevel@tonic-gatemy $pod_or_DATA = qr/ ^=(?:head[1-4]|item) .*? $CUT
79*0Sstevel@tonic-gate                    | ^=pod .*? $CUT
80*0Sstevel@tonic-gate                    | ^=for .*? $EOP
81*0Sstevel@tonic-gate                    | ^=begin \s* (\S+) .*? \n=end \s* \1 .*? $EOP
82*0Sstevel@tonic-gate                    | ^__(DATA|END)__\n.*
83*0Sstevel@tonic-gate                    /smx;
84*0Sstevel@tonic-gate
85*0Sstevel@tonic-gatemy $casecounter = 1;
86*0Sstevel@tonic-gatesub filter_blocks
87*0Sstevel@tonic-gate{
88*0Sstevel@tonic-gate	my ($source, $line) = @_;
89*0Sstevel@tonic-gate	return $source unless $Perl5 && $source =~ /case|switch/
90*0Sstevel@tonic-gate			   || $Perl6 && $source =~ /when|given|default/;
91*0Sstevel@tonic-gate	pos $source = 0;
92*0Sstevel@tonic-gate	my $text = "";
93*0Sstevel@tonic-gate	component: while (pos $source < length $source)
94*0Sstevel@tonic-gate	{
95*0Sstevel@tonic-gate		if ($source =~ m/(\G\s*use\s+Switch\b)/gc)
96*0Sstevel@tonic-gate		{
97*0Sstevel@tonic-gate			$text .= q{use Switch 'noimport'};
98*0Sstevel@tonic-gate			next component;
99*0Sstevel@tonic-gate		}
100*0Sstevel@tonic-gate		my @pos = Text::Balanced::_match_quotelike(\$source,qr/\s*/,1,0);
101*0Sstevel@tonic-gate		if (defined $pos[0])
102*0Sstevel@tonic-gate		{
103*0Sstevel@tonic-gate			my $pre = substr($source,$pos[0],$pos[1]); # matched prefix
104*0Sstevel@tonic-gate			$text .= $pre . substr($source,$pos[2],$pos[18]-$pos[2]);
105*0Sstevel@tonic-gate			next component;
106*0Sstevel@tonic-gate		}
107*0Sstevel@tonic-gate		if ($source =~ m/\G\s*($pod_or_DATA)/gc) {
108*0Sstevel@tonic-gate			next component;
109*0Sstevel@tonic-gate		}
110*0Sstevel@tonic-gate		@pos = Text::Balanced::_match_variable(\$source,qr/\s*/);
111*0Sstevel@tonic-gate		if (defined $pos[0])
112*0Sstevel@tonic-gate		{
113*0Sstevel@tonic-gate			$text .= " " if $pos[0] < $pos[2];
114*0Sstevel@tonic-gate			$text .= substr($source,$pos[0],$pos[4]-$pos[0]);
115*0Sstevel@tonic-gate			next component;
116*0Sstevel@tonic-gate		}
117*0Sstevel@tonic-gate
118*0Sstevel@tonic-gate		if ($Perl5 && $source =~ m/\G(\n*)(\s*)(switch)\b(?=\s*[(])/gc
119*0Sstevel@tonic-gate		 || $Perl6 && $source =~ m/\G(\n*)(\s*)(given)\b(?=\s*[(])/gc
120*0Sstevel@tonic-gate		 || $Perl6 && $source =~ m/\G(\n*)(\s*)(given)\b(.*)(?=\{)/gc)
121*0Sstevel@tonic-gate		{
122*0Sstevel@tonic-gate			my $keyword = $3;
123*0Sstevel@tonic-gate			my $arg = $4;
124*0Sstevel@tonic-gate			$text .= $1.$2.'S_W_I_T_C_H: while (1) ';
125*0Sstevel@tonic-gate			unless ($arg) {
126*0Sstevel@tonic-gate				@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\(/,qr/\)/,qr/[[{(<]/,qr/[]})>]/,undef)
127*0Sstevel@tonic-gate				or do {
128*0Sstevel@tonic-gate					die "Bad $keyword statement (problem in the parentheses?) near $Switch::file line ", line(substr($source,0,pos $source),$line), "\n";
129*0Sstevel@tonic-gate				};
130*0Sstevel@tonic-gate				$arg = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line));
131*0Sstevel@tonic-gate			}
132*0Sstevel@tonic-gate			$arg =~ s {^\s*[(]\s*%}   { ( \\\%}	||
133*0Sstevel@tonic-gate			$arg =~ s {^\s*[(]\s*m\b} { ( qr}	||
134*0Sstevel@tonic-gate			$arg =~ s {^\s*[(]\s*/}   { ( qr/}	||
135*0Sstevel@tonic-gate			$arg =~ s {^\s*[(]\s*qw}  { ( \\qw};
136*0Sstevel@tonic-gate			@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\{/,qr/\}/,qr/\{/,qr/\}/,undef)
137*0Sstevel@tonic-gate			or do {
138*0Sstevel@tonic-gate				die "Bad $keyword statement (problem in the code block?) near $Switch::file line ", line(substr($source,0, pos $source), $line), "\n";
139*0Sstevel@tonic-gate			};
140*0Sstevel@tonic-gate			my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line));
141*0Sstevel@tonic-gate			$code =~ s/{/{ local \$::_S_W_I_T_C_H; Switch::switch $arg;/;
142*0Sstevel@tonic-gate			$text .= $code . 'continue {last}';
143*0Sstevel@tonic-gate			next component;
144*0Sstevel@tonic-gate		}
145*0Sstevel@tonic-gate		elsif ($Perl5 && $source =~ m/\G(\s*)(case\b)(?!\s*=>)/gc
146*0Sstevel@tonic-gate		    || $Perl6 && $source =~ m/\G(\s*)(when\b)(?!\s*=>)/gc
147*0Sstevel@tonic-gate		    || $Perl6 && $source =~ m/\G(\s*)(default\b)(?=\s*\{)/gc)
148*0Sstevel@tonic-gate		{
149*0Sstevel@tonic-gate			my $keyword = $2;
150*0Sstevel@tonic-gate			$text .= $1 . ($keyword eq "default"
151*0Sstevel@tonic-gate					? "if (1)"
152*0Sstevel@tonic-gate					: "if (Switch::case");
153*0Sstevel@tonic-gate
154*0Sstevel@tonic-gate			if ($keyword eq "default") {
155*0Sstevel@tonic-gate				# Nothing to do
156*0Sstevel@tonic-gate			}
157*0Sstevel@tonic-gate			elsif (@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\{/,qr/\}/,qr/\{/,qr/\}/,undef)) {
158*0Sstevel@tonic-gate				my $code = substr($source,$pos[0],$pos[4]-$pos[0]);
159*0Sstevel@tonic-gate				$text .= " " if $pos[0] < $pos[2];
160*0Sstevel@tonic-gate				$text .= "sub " if is_block $code;
161*0Sstevel@tonic-gate				$text .= filter_blocks($code,line(substr($source,0,$pos[0]),$line)) . ")";
162*0Sstevel@tonic-gate			}
163*0Sstevel@tonic-gate			elsif (@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/[[(]/,qr/[])]/,qr/[[({]/,qr/[])}]/,undef)) {
164*0Sstevel@tonic-gate				my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line));
165*0Sstevel@tonic-gate				$code =~ s {^\s*[(]\s*%}   { ( \\\%}	||
166*0Sstevel@tonic-gate				$code =~ s {^\s*[(]\s*m\b} { ( qr}	||
167*0Sstevel@tonic-gate				$code =~ s {^\s*[(]\s*/}   { ( qr/}	||
168*0Sstevel@tonic-gate				$code =~ s {^\s*[(]\s*qw}  { ( \\qw};
169*0Sstevel@tonic-gate				$text .= " " if $pos[0] < $pos[2];
170*0Sstevel@tonic-gate				$text .= "$code)";
171*0Sstevel@tonic-gate			}
172*0Sstevel@tonic-gate			elsif ($Perl6 && do{@pos = Text::Balanced::_match_variable(\$source,qr/\s*/)}) {
173*0Sstevel@tonic-gate				my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line));
174*0Sstevel@tonic-gate				$code =~ s {^\s*%}  { \%}	||
175*0Sstevel@tonic-gate				$code =~ s {^\s*@}  { \@};
176*0Sstevel@tonic-gate				$text .= " " if $pos[0] < $pos[2];
177*0Sstevel@tonic-gate				$text .= "$code)";
178*0Sstevel@tonic-gate			}
179*0Sstevel@tonic-gate			elsif ( @pos = Text::Balanced::_match_quotelike(\$source,qr/\s*/,1,0)) {
180*0Sstevel@tonic-gate				my $code = substr($source,$pos[2],$pos[18]-$pos[2]);
181*0Sstevel@tonic-gate				$code = filter_blocks($code,line(substr($source,0,$pos[2]),$line));
182*0Sstevel@tonic-gate				$code =~ s {^\s*m}  { qr}	||
183*0Sstevel@tonic-gate				$code =~ s {^\s*/}  { qr/}	||
184*0Sstevel@tonic-gate				$code =~ s {^\s*qw} { \\qw};
185*0Sstevel@tonic-gate				$text .= " " if $pos[0] < $pos[2];
186*0Sstevel@tonic-gate				$text .= "$code)";
187*0Sstevel@tonic-gate			}
188*0Sstevel@tonic-gate			elsif ($Perl5 && $source =~ m/\G\s*(([^\$\@{])[^\$\@{]*)(?=\s*{)/gc
189*0Sstevel@tonic-gate			   ||  $Perl6 && $source =~ m/\G\s*([^;{]*)()/gc) {
190*0Sstevel@tonic-gate				my $code = filter_blocks($1,line(substr($source,0,pos $source),$line));
191*0Sstevel@tonic-gate				$text .= ' \\' if $2 eq '%';
192*0Sstevel@tonic-gate				$text .= " $code)";
193*0Sstevel@tonic-gate			}
194*0Sstevel@tonic-gate			else {
195*0Sstevel@tonic-gate				die "Bad $keyword statement (invalid $keyword value?) near $Switch::file line ", line(substr($source,0,pos $source), $line), "\n";
196*0Sstevel@tonic-gate			}
197*0Sstevel@tonic-gate
198*0Sstevel@tonic-gate		        die "Missing opening brace or semi-colon after 'when' value near $Switch::file line ", line(substr($source,0,pos $source), $line), "\n"
199*0Sstevel@tonic-gate				unless !$Perl6 || $source =~ m/\G(\s*)(?=;|\{)/gc;
200*0Sstevel@tonic-gate
201*0Sstevel@tonic-gate			do{@pos = Text::Balanced::_match_codeblock(\$source,qr/\s*/,qr/\{/,qr/\}/,qr/\{/,qr/\}/,undef)}
202*0Sstevel@tonic-gate			or do {
203*0Sstevel@tonic-gate				if ($source =~ m/\G\s*(?=([};]|\Z))/gc) {
204*0Sstevel@tonic-gate					$casecounter++;
205*0Sstevel@tonic-gate					next component;
206*0Sstevel@tonic-gate				}
207*0Sstevel@tonic-gate				die "Bad $keyword statement (problem in the code block?) near $Switch::file line ", line(substr($source,0,pos $source),$line), "\n";
208*0Sstevel@tonic-gate			};
209*0Sstevel@tonic-gate			my $code = filter_blocks(substr($source,$pos[0],$pos[4]-$pos[0]),line(substr($source,0,$pos[0]),$line));
210*0Sstevel@tonic-gate			$code =~ s/}(?=\s*\Z)/;last S_W_I_T_C_H }/
211*0Sstevel@tonic-gate				unless $fallthrough;
212*0Sstevel@tonic-gate			$text .= "{ while (1) $code continue { goto C_A_S_E_$casecounter } last S_W_I_T_C_H; C_A_S_E_$casecounter: }";
213*0Sstevel@tonic-gate			$casecounter++;
214*0Sstevel@tonic-gate			next component;
215*0Sstevel@tonic-gate		}
216*0Sstevel@tonic-gate
217*0Sstevel@tonic-gate		$source =~ m/\G(\s*(-[sm]\s+|\w+|#.*\n|\W))/gc;
218*0Sstevel@tonic-gate		$text .= $1;
219*0Sstevel@tonic-gate	}
220*0Sstevel@tonic-gate	$text;
221*0Sstevel@tonic-gate}
222*0Sstevel@tonic-gate
223*0Sstevel@tonic-gate
224*0Sstevel@tonic-gate
225*0Sstevel@tonic-gatesub in
226*0Sstevel@tonic-gate{
227*0Sstevel@tonic-gate	my ($x,$y) = @_;
228*0Sstevel@tonic-gate	my @numy;
229*0Sstevel@tonic-gate	for my $nextx ( @$x )
230*0Sstevel@tonic-gate	{
231*0Sstevel@tonic-gate		my $numx = ref($nextx) || defined $nextx && (~$nextx&$nextx) eq 0;
232*0Sstevel@tonic-gate		for my $j ( 0..$#$y )
233*0Sstevel@tonic-gate		{
234*0Sstevel@tonic-gate			my $nexty = $y->[$j];
235*0Sstevel@tonic-gate			push @numy, ref($nexty) || defined $nexty && (~$nexty&$nexty) eq 0
236*0Sstevel@tonic-gate				if @numy <= $j;
237*0Sstevel@tonic-gate			return 1 if $numx && $numy[$j] && $nextx==$nexty
238*0Sstevel@tonic-gate			         || $nextx eq $nexty;
239*0Sstevel@tonic-gate
240*0Sstevel@tonic-gate		}
241*0Sstevel@tonic-gate	}
242*0Sstevel@tonic-gate	return "";
243*0Sstevel@tonic-gate}
244*0Sstevel@tonic-gate
245*0Sstevel@tonic-gatesub on_exists
246*0Sstevel@tonic-gate{
247*0Sstevel@tonic-gate	my $ref = @_==1 && ref($_[0]) eq 'HASH' ? $_[0] : { @_ };
248*0Sstevel@tonic-gate	[ keys %$ref ]
249*0Sstevel@tonic-gate}
250*0Sstevel@tonic-gate
251*0Sstevel@tonic-gatesub on_defined
252*0Sstevel@tonic-gate{
253*0Sstevel@tonic-gate	my $ref = @_==1 && ref($_[0]) eq 'HASH' ? $_[0] : { @_ };
254*0Sstevel@tonic-gate	[ grep { defined $ref->{$_} } keys %$ref ]
255*0Sstevel@tonic-gate}
256*0Sstevel@tonic-gate
257*0Sstevel@tonic-gatesub switch(;$)
258*0Sstevel@tonic-gate{
259*0Sstevel@tonic-gate	my ($s_val) = @_ ? $_[0] : $_;
260*0Sstevel@tonic-gate	my $s_ref = ref $s_val;
261*0Sstevel@tonic-gate
262*0Sstevel@tonic-gate	if ($s_ref eq 'CODE')
263*0Sstevel@tonic-gate	{
264*0Sstevel@tonic-gate		$::_S_W_I_T_C_H =
265*0Sstevel@tonic-gate		      sub { my $c_val = $_[0];
266*0Sstevel@tonic-gate			    return $s_val == $c_val  if ref $c_val eq 'CODE';
267*0Sstevel@tonic-gate			    return $s_val->(@$c_val) if ref $c_val eq 'ARRAY';
268*0Sstevel@tonic-gate			    return $s_val->($c_val);
269*0Sstevel@tonic-gate			  };
270*0Sstevel@tonic-gate	}
271*0Sstevel@tonic-gate	elsif ($s_ref eq "" && defined $s_val && (~$s_val&$s_val) eq 0)	# NUMERIC SCALAR
272*0Sstevel@tonic-gate	{
273*0Sstevel@tonic-gate		$::_S_W_I_T_C_H =
274*0Sstevel@tonic-gate		      sub { my $c_val = $_[0];
275*0Sstevel@tonic-gate			    my $c_ref = ref $c_val;
276*0Sstevel@tonic-gate			    return $s_val == $c_val 	if $c_ref eq ""
277*0Sstevel@tonic-gate							&& defined $c_val
278*0Sstevel@tonic-gate							&& (~$c_val&$c_val) eq 0;
279*0Sstevel@tonic-gate			    return $s_val eq $c_val 	if $c_ref eq "";
280*0Sstevel@tonic-gate			    return in([$s_val],$c_val)	if $c_ref eq 'ARRAY';
281*0Sstevel@tonic-gate			    return $c_val->($s_val)	if $c_ref eq 'CODE';
282*0Sstevel@tonic-gate			    return $c_val->call($s_val)	if $c_ref eq 'Switch';
283*0Sstevel@tonic-gate			    return scalar $s_val=~/$c_val/
284*0Sstevel@tonic-gate							if $c_ref eq 'Regexp';
285*0Sstevel@tonic-gate			    return scalar $c_val->{$s_val}
286*0Sstevel@tonic-gate							if $c_ref eq 'HASH';
287*0Sstevel@tonic-gate		            return;
288*0Sstevel@tonic-gate			  };
289*0Sstevel@tonic-gate	}
290*0Sstevel@tonic-gate	elsif ($s_ref eq "")				# STRING SCALAR
291*0Sstevel@tonic-gate	{
292*0Sstevel@tonic-gate		$::_S_W_I_T_C_H =
293*0Sstevel@tonic-gate		      sub { my $c_val = $_[0];
294*0Sstevel@tonic-gate			    my $c_ref = ref $c_val;
295*0Sstevel@tonic-gate			    return $s_val eq $c_val 	if $c_ref eq "";
296*0Sstevel@tonic-gate			    return in([$s_val],$c_val)	if $c_ref eq 'ARRAY';
297*0Sstevel@tonic-gate			    return $c_val->($s_val)	if $c_ref eq 'CODE';
298*0Sstevel@tonic-gate			    return $c_val->call($s_val)	if $c_ref eq 'Switch';
299*0Sstevel@tonic-gate			    return scalar $s_val=~/$c_val/
300*0Sstevel@tonic-gate							if $c_ref eq 'Regexp';
301*0Sstevel@tonic-gate			    return scalar $c_val->{$s_val}
302*0Sstevel@tonic-gate							if $c_ref eq 'HASH';
303*0Sstevel@tonic-gate		            return;
304*0Sstevel@tonic-gate			  };
305*0Sstevel@tonic-gate	}
306*0Sstevel@tonic-gate	elsif ($s_ref eq 'ARRAY')
307*0Sstevel@tonic-gate	{
308*0Sstevel@tonic-gate		$::_S_W_I_T_C_H =
309*0Sstevel@tonic-gate		      sub { my $c_val = $_[0];
310*0Sstevel@tonic-gate			    my $c_ref = ref $c_val;
311*0Sstevel@tonic-gate			    return in($s_val,[$c_val]) 	if $c_ref eq "";
312*0Sstevel@tonic-gate			    return in($s_val,$c_val)	if $c_ref eq 'ARRAY';
313*0Sstevel@tonic-gate			    return $c_val->(@$s_val)	if $c_ref eq 'CODE';
314*0Sstevel@tonic-gate			    return $c_val->call(@$s_val)
315*0Sstevel@tonic-gate							if $c_ref eq 'Switch';
316*0Sstevel@tonic-gate			    return scalar grep {$_=~/$c_val/} @$s_val
317*0Sstevel@tonic-gate							if $c_ref eq 'Regexp';
318*0Sstevel@tonic-gate			    return scalar grep {$c_val->{$_}} @$s_val
319*0Sstevel@tonic-gate							if $c_ref eq 'HASH';
320*0Sstevel@tonic-gate		            return;
321*0Sstevel@tonic-gate			  };
322*0Sstevel@tonic-gate	}
323*0Sstevel@tonic-gate	elsif ($s_ref eq 'Regexp')
324*0Sstevel@tonic-gate	{
325*0Sstevel@tonic-gate		$::_S_W_I_T_C_H =
326*0Sstevel@tonic-gate		      sub { my $c_val = $_[0];
327*0Sstevel@tonic-gate			    my $c_ref = ref $c_val;
328*0Sstevel@tonic-gate			    return $c_val=~/s_val/ 	if $c_ref eq "";
329*0Sstevel@tonic-gate			    return scalar grep {$_=~/s_val/} @$c_val
330*0Sstevel@tonic-gate							if $c_ref eq 'ARRAY';
331*0Sstevel@tonic-gate			    return $c_val->($s_val)	if $c_ref eq 'CODE';
332*0Sstevel@tonic-gate			    return $c_val->call($s_val)	if $c_ref eq 'Switch';
333*0Sstevel@tonic-gate			    return $s_val eq $c_val	if $c_ref eq 'Regexp';
334*0Sstevel@tonic-gate			    return grep {$_=~/$s_val/ && $c_val->{$_}} keys %$c_val
335*0Sstevel@tonic-gate							if $c_ref eq 'HASH';
336*0Sstevel@tonic-gate		            return;
337*0Sstevel@tonic-gate			  };
338*0Sstevel@tonic-gate	}
339*0Sstevel@tonic-gate	elsif ($s_ref eq 'HASH')
340*0Sstevel@tonic-gate	{
341*0Sstevel@tonic-gate		$::_S_W_I_T_C_H =
342*0Sstevel@tonic-gate		      sub { my $c_val = $_[0];
343*0Sstevel@tonic-gate			    my $c_ref = ref $c_val;
344*0Sstevel@tonic-gate			    return $s_val->{$c_val} 	if $c_ref eq "";
345*0Sstevel@tonic-gate			    return scalar grep {$s_val->{$_}} @$c_val
346*0Sstevel@tonic-gate							if $c_ref eq 'ARRAY';
347*0Sstevel@tonic-gate			    return $c_val->($s_val)	if $c_ref eq 'CODE';
348*0Sstevel@tonic-gate			    return $c_val->call($s_val)	if $c_ref eq 'Switch';
349*0Sstevel@tonic-gate			    return grep {$_=~/$c_val/ && $s_val->{"$_"}} keys %$s_val
350*0Sstevel@tonic-gate							if $c_ref eq 'Regexp';
351*0Sstevel@tonic-gate			    return $s_val==$c_val	if $c_ref eq 'HASH';
352*0Sstevel@tonic-gate		            return;
353*0Sstevel@tonic-gate			  };
354*0Sstevel@tonic-gate	}
355*0Sstevel@tonic-gate	elsif ($s_ref eq 'Switch')
356*0Sstevel@tonic-gate	{
357*0Sstevel@tonic-gate		$::_S_W_I_T_C_H =
358*0Sstevel@tonic-gate		      sub { my $c_val = $_[0];
359*0Sstevel@tonic-gate			    return $s_val == $c_val  if ref $c_val eq 'Switch';
360*0Sstevel@tonic-gate			    return $s_val->call(@$c_val)
361*0Sstevel@tonic-gate						     if ref $c_val eq 'ARRAY';
362*0Sstevel@tonic-gate			    return $s_val->call($c_val);
363*0Sstevel@tonic-gate			  };
364*0Sstevel@tonic-gate	}
365*0Sstevel@tonic-gate	else
366*0Sstevel@tonic-gate	{
367*0Sstevel@tonic-gate		croak "Cannot switch on $s_ref";
368*0Sstevel@tonic-gate	}
369*0Sstevel@tonic-gate	return 1;
370*0Sstevel@tonic-gate}
371*0Sstevel@tonic-gate
372*0Sstevel@tonic-gatesub case($) { local $SIG{__WARN__} = \&carp;
373*0Sstevel@tonic-gate	      $::_S_W_I_T_C_H->(@_); }
374*0Sstevel@tonic-gate
375*0Sstevel@tonic-gate# IMPLEMENT __
376*0Sstevel@tonic-gate
377*0Sstevel@tonic-gatemy $placeholder = bless { arity=>1, impl=>sub{$_[1+$_[0]]} };
378*0Sstevel@tonic-gate
379*0Sstevel@tonic-gatesub __() { $placeholder }
380*0Sstevel@tonic-gate
381*0Sstevel@tonic-gatesub __arg($)
382*0Sstevel@tonic-gate{
383*0Sstevel@tonic-gate	my $index = $_[0]+1;
384*0Sstevel@tonic-gate	bless { arity=>0, impl=>sub{$_[$index]} };
385*0Sstevel@tonic-gate}
386*0Sstevel@tonic-gate
387*0Sstevel@tonic-gatesub hosub(&@)
388*0Sstevel@tonic-gate{
389*0Sstevel@tonic-gate	# WRITE THIS
390*0Sstevel@tonic-gate}
391*0Sstevel@tonic-gate
392*0Sstevel@tonic-gatesub call
393*0Sstevel@tonic-gate{
394*0Sstevel@tonic-gate	my ($self,@args) = @_;
395*0Sstevel@tonic-gate	return $self->{impl}->(0,@args);
396*0Sstevel@tonic-gate}
397*0Sstevel@tonic-gate
398*0Sstevel@tonic-gatesub meta_bop(&)
399*0Sstevel@tonic-gate{
400*0Sstevel@tonic-gate	my ($op) = @_;
401*0Sstevel@tonic-gate	sub
402*0Sstevel@tonic-gate	{
403*0Sstevel@tonic-gate		my ($left, $right, $reversed) = @_;
404*0Sstevel@tonic-gate		($right,$left) = @_ if $reversed;
405*0Sstevel@tonic-gate
406*0Sstevel@tonic-gate		my $rop = ref $right eq 'Switch'
407*0Sstevel@tonic-gate			? $right
408*0Sstevel@tonic-gate			: bless { arity=>0, impl=>sub{$right} };
409*0Sstevel@tonic-gate
410*0Sstevel@tonic-gate		my $lop = ref $left eq 'Switch'
411*0Sstevel@tonic-gate			? $left
412*0Sstevel@tonic-gate			: bless { arity=>0, impl=>sub{$left} };
413*0Sstevel@tonic-gate
414*0Sstevel@tonic-gate		my $arity = $lop->{arity} + $rop->{arity};
415*0Sstevel@tonic-gate
416*0Sstevel@tonic-gate		return bless {
417*0Sstevel@tonic-gate				arity => $arity,
418*0Sstevel@tonic-gate				impl  => sub { my $start = shift;
419*0Sstevel@tonic-gate					       return $op->($lop->{impl}->($start,@_),
420*0Sstevel@tonic-gate						            $rop->{impl}->($start+$lop->{arity},@_));
421*0Sstevel@tonic-gate					     }
422*0Sstevel@tonic-gate			     };
423*0Sstevel@tonic-gate	};
424*0Sstevel@tonic-gate}
425*0Sstevel@tonic-gate
426*0Sstevel@tonic-gatesub meta_uop(&)
427*0Sstevel@tonic-gate{
428*0Sstevel@tonic-gate	my ($op) = @_;
429*0Sstevel@tonic-gate	sub
430*0Sstevel@tonic-gate	{
431*0Sstevel@tonic-gate		my ($left) = @_;
432*0Sstevel@tonic-gate
433*0Sstevel@tonic-gate		my $lop = ref $left eq 'Switch'
434*0Sstevel@tonic-gate			? $left
435*0Sstevel@tonic-gate			: bless { arity=>0, impl=>sub{$left} };
436*0Sstevel@tonic-gate
437*0Sstevel@tonic-gate		my $arity = $lop->{arity};
438*0Sstevel@tonic-gate
439*0Sstevel@tonic-gate		return bless {
440*0Sstevel@tonic-gate				arity => $arity,
441*0Sstevel@tonic-gate				impl  => sub { $op->($lop->{impl}->(@_)) }
442*0Sstevel@tonic-gate			     };
443*0Sstevel@tonic-gate	};
444*0Sstevel@tonic-gate}
445*0Sstevel@tonic-gate
446*0Sstevel@tonic-gate
447*0Sstevel@tonic-gateuse overload
448*0Sstevel@tonic-gate	"+"	=> 	meta_bop {$_[0] + $_[1]},
449*0Sstevel@tonic-gate	"-"	=> 	meta_bop {$_[0] - $_[1]},
450*0Sstevel@tonic-gate	"*"	=>  	meta_bop {$_[0] * $_[1]},
451*0Sstevel@tonic-gate	"/"	=>  	meta_bop {$_[0] / $_[1]},
452*0Sstevel@tonic-gate	"%"	=>  	meta_bop {$_[0] % $_[1]},
453*0Sstevel@tonic-gate	"**"	=>  	meta_bop {$_[0] ** $_[1]},
454*0Sstevel@tonic-gate	"<<"	=>  	meta_bop {$_[0] << $_[1]},
455*0Sstevel@tonic-gate	">>"	=>  	meta_bop {$_[0] >> $_[1]},
456*0Sstevel@tonic-gate	"x"	=>  	meta_bop {$_[0] x $_[1]},
457*0Sstevel@tonic-gate	"."	=>  	meta_bop {$_[0] . $_[1]},
458*0Sstevel@tonic-gate	"<"	=>  	meta_bop {$_[0] < $_[1]},
459*0Sstevel@tonic-gate	"<="	=>  	meta_bop {$_[0] <= $_[1]},
460*0Sstevel@tonic-gate	">"	=>  	meta_bop {$_[0] > $_[1]},
461*0Sstevel@tonic-gate	">="	=>  	meta_bop {$_[0] >= $_[1]},
462*0Sstevel@tonic-gate	"=="	=>  	meta_bop {$_[0] == $_[1]},
463*0Sstevel@tonic-gate	"!="	=>  	meta_bop {$_[0] != $_[1]},
464*0Sstevel@tonic-gate	"<=>"	=>  	meta_bop {$_[0] <=> $_[1]},
465*0Sstevel@tonic-gate	"lt"	=>  	meta_bop {$_[0] lt $_[1]},
466*0Sstevel@tonic-gate	"le"	=> 	meta_bop {$_[0] le $_[1]},
467*0Sstevel@tonic-gate	"gt"	=> 	meta_bop {$_[0] gt $_[1]},
468*0Sstevel@tonic-gate	"ge"	=> 	meta_bop {$_[0] ge $_[1]},
469*0Sstevel@tonic-gate	"eq"	=> 	meta_bop {$_[0] eq $_[1]},
470*0Sstevel@tonic-gate	"ne"	=> 	meta_bop {$_[0] ne $_[1]},
471*0Sstevel@tonic-gate	"cmp"	=> 	meta_bop {$_[0] cmp $_[1]},
472*0Sstevel@tonic-gate	"\&"	=> 	meta_bop {$_[0] & $_[1]},
473*0Sstevel@tonic-gate	"^"	=> 	meta_bop {$_[0] ^ $_[1]},
474*0Sstevel@tonic-gate	"|"	=>	meta_bop {$_[0] | $_[1]},
475*0Sstevel@tonic-gate	"atan2"	=>	meta_bop {atan2 $_[0], $_[1]},
476*0Sstevel@tonic-gate
477*0Sstevel@tonic-gate	"neg"	=>	meta_uop {-$_[0]},
478*0Sstevel@tonic-gate	"!"	=>	meta_uop {!$_[0]},
479*0Sstevel@tonic-gate	"~"	=>	meta_uop {~$_[0]},
480*0Sstevel@tonic-gate	"cos"	=>	meta_uop {cos $_[0]},
481*0Sstevel@tonic-gate	"sin"	=>	meta_uop {sin $_[0]},
482*0Sstevel@tonic-gate	"exp"	=>	meta_uop {exp $_[0]},
483*0Sstevel@tonic-gate	"abs"	=>	meta_uop {abs $_[0]},
484*0Sstevel@tonic-gate	"log"	=>	meta_uop {log $_[0]},
485*0Sstevel@tonic-gate	"sqrt"  =>	meta_uop {sqrt $_[0]},
486*0Sstevel@tonic-gate	"bool"  =>	sub { croak "Can't use && or || in expression containing __" },
487*0Sstevel@tonic-gate
488*0Sstevel@tonic-gate	#	"&()"	=>	sub { $_[0]->{impl} },
489*0Sstevel@tonic-gate
490*0Sstevel@tonic-gate	#	"||"	=>	meta_bop {$_[0] || $_[1]},
491*0Sstevel@tonic-gate	#	"&&"	=>	meta_bop {$_[0] && $_[1]},
492*0Sstevel@tonic-gate	# fallback => 1,
493*0Sstevel@tonic-gate	;
494*0Sstevel@tonic-gate1;
495*0Sstevel@tonic-gate
496*0Sstevel@tonic-gate__END__
497*0Sstevel@tonic-gate
498*0Sstevel@tonic-gate
499*0Sstevel@tonic-gate=head1 NAME
500*0Sstevel@tonic-gate
501*0Sstevel@tonic-gateSwitch - A switch statement for Perl
502*0Sstevel@tonic-gate
503*0Sstevel@tonic-gate=head1 VERSION
504*0Sstevel@tonic-gate
505*0Sstevel@tonic-gateThis document describes version 2.10 of Switch,
506*0Sstevel@tonic-gatereleased Dec 29, 2003.
507*0Sstevel@tonic-gate
508*0Sstevel@tonic-gate=head1 SYNOPSIS
509*0Sstevel@tonic-gate
510*0Sstevel@tonic-gate	use Switch;
511*0Sstevel@tonic-gate
512*0Sstevel@tonic-gate	switch ($val) {
513*0Sstevel@tonic-gate
514*0Sstevel@tonic-gate		case 1		{ print "number 1" }
515*0Sstevel@tonic-gate		case "a"	{ print "string a" }
516*0Sstevel@tonic-gate		case [1..10,42]	{ print "number in list" }
517*0Sstevel@tonic-gate		case (@array)	{ print "number in list" }
518*0Sstevel@tonic-gate		case /\w+/	{ print "pattern" }
519*0Sstevel@tonic-gate		case qr/\w+/	{ print "pattern" }
520*0Sstevel@tonic-gate		case (%hash)	{ print "entry in hash" }
521*0Sstevel@tonic-gate		case (\%hash)	{ print "entry in hash" }
522*0Sstevel@tonic-gate		case (\&sub)	{ print "arg to subroutine" }
523*0Sstevel@tonic-gate		else		{ print "previous case not true" }
524*0Sstevel@tonic-gate	}
525*0Sstevel@tonic-gate
526*0Sstevel@tonic-gate=head1 BACKGROUND
527*0Sstevel@tonic-gate
528*0Sstevel@tonic-gate[Skip ahead to L<"DESCRIPTION"> if you don't care about the whys
529*0Sstevel@tonic-gateand wherefores of this control structure]
530*0Sstevel@tonic-gate
531*0Sstevel@tonic-gateIn seeking to devise a "Swiss Army" case mechanism suitable for Perl,
532*0Sstevel@tonic-gateit is useful to generalize this notion of distributed conditional
533*0Sstevel@tonic-gatetesting as far as possible. Specifically, the concept of "matching"
534*0Sstevel@tonic-gatebetween the switch value and the various case values need not be
535*0Sstevel@tonic-gaterestricted to numeric (or string or referential) equality, as it is in other
536*0Sstevel@tonic-gatelanguages. Indeed, as Table 1 illustrates, Perl
537*0Sstevel@tonic-gateoffers at least eighteen different ways in which two values could
538*0Sstevel@tonic-gategenerate a match.
539*0Sstevel@tonic-gate
540*0Sstevel@tonic-gate	Table 1: Matching a switch value ($s) with a case value ($c)
541*0Sstevel@tonic-gate
542*0Sstevel@tonic-gate        Switch  Case    Type of Match Implied   Matching Code
543*0Sstevel@tonic-gate        Value   Value
544*0Sstevel@tonic-gate        ======  =====   =====================   =============
545*0Sstevel@tonic-gate
546*0Sstevel@tonic-gate        number  same    numeric or referential  match if $s == $c;
547*0Sstevel@tonic-gate        or ref          equality
548*0Sstevel@tonic-gate
549*0Sstevel@tonic-gate	object  method	result of method call   match if $s->$c();
550*0Sstevel@tonic-gate	ref     name 				match if defined $s->$c();
551*0Sstevel@tonic-gate		or ref
552*0Sstevel@tonic-gate
553*0Sstevel@tonic-gate        other   other   string equality         match if $s eq $c;
554*0Sstevel@tonic-gate        non-ref non-ref
555*0Sstevel@tonic-gate        scalar  scalar
556*0Sstevel@tonic-gate
557*0Sstevel@tonic-gate        string  regexp  pattern match           match if $s =~ /$c/;
558*0Sstevel@tonic-gate
559*0Sstevel@tonic-gate        array   scalar  array entry existence   match if 0<=$c && $c<@$s;
560*0Sstevel@tonic-gate        ref             array entry definition  match if defined $s->[$c];
561*0Sstevel@tonic-gate                        array entry truth       match if $s->[$c];
562*0Sstevel@tonic-gate
563*0Sstevel@tonic-gate        array   array   array intersection      match if intersects(@$s, @$c);
564*0Sstevel@tonic-gate        ref     ref     (apply this table to
565*0Sstevel@tonic-gate                         all pairs of elements
566*0Sstevel@tonic-gate                         $s->[$i] and
567*0Sstevel@tonic-gate                         $c->[$j])
568*0Sstevel@tonic-gate
569*0Sstevel@tonic-gate        array   regexp  array grep              match if grep /$c/, @$s;
570*0Sstevel@tonic-gate        ref
571*0Sstevel@tonic-gate
572*0Sstevel@tonic-gate        hash    scalar  hash entry existence    match if exists $s->{$c};
573*0Sstevel@tonic-gate        ref             hash entry definition   match if defined $s->{$c};
574*0Sstevel@tonic-gate                        hash entry truth        match if $s->{$c};
575*0Sstevel@tonic-gate
576*0Sstevel@tonic-gate        hash    regexp  hash grep               match if grep /$c/, keys %$s;
577*0Sstevel@tonic-gate        ref
578*0Sstevel@tonic-gate
579*0Sstevel@tonic-gate        sub     scalar  return value defn       match if defined $s->($c);
580*0Sstevel@tonic-gate        ref             return value truth      match if $s->($c);
581*0Sstevel@tonic-gate
582*0Sstevel@tonic-gate        sub     array   return value defn       match if defined $s->(@$c);
583*0Sstevel@tonic-gate        ref     ref     return value truth      match if $s->(@$c);
584*0Sstevel@tonic-gate
585*0Sstevel@tonic-gate
586*0Sstevel@tonic-gateIn reality, Table 1 covers 31 alternatives, because only the equality and
587*0Sstevel@tonic-gateintersection tests are commutative; in all other cases, the roles of
588*0Sstevel@tonic-gatethe C<$s> and C<$c> variables could be reversed to produce a
589*0Sstevel@tonic-gatedifferent test. For example, instead of testing a single hash for
590*0Sstevel@tonic-gatethe existence of a series of keys (C<match if exists $s-E<gt>{$c}>),
591*0Sstevel@tonic-gateone could test for the existence of a single key in a series of hashes
592*0Sstevel@tonic-gate(C<match if exists $c-E<gt>{$s}>).
593*0Sstevel@tonic-gate
594*0Sstevel@tonic-gateAs L<perltodo> observes, a Perl case mechanism must support all these
595*0Sstevel@tonic-gate"ways to do it".
596*0Sstevel@tonic-gate
597*0Sstevel@tonic-gate
598*0Sstevel@tonic-gate=head1 DESCRIPTION
599*0Sstevel@tonic-gate
600*0Sstevel@tonic-gateThe Switch.pm module implements a generalized case mechanism that covers
601*0Sstevel@tonic-gatethe numerous possible combinations of switch and case values described above.
602*0Sstevel@tonic-gate
603*0Sstevel@tonic-gateThe module augments the standard Perl syntax with two new control
604*0Sstevel@tonic-gatestatements: C<switch> and C<case>. The C<switch> statement takes a
605*0Sstevel@tonic-gatesingle scalar argument of any type, specified in parentheses.
606*0Sstevel@tonic-gateC<switch> stores this value as the
607*0Sstevel@tonic-gatecurrent switch value in a (localized) control variable.
608*0Sstevel@tonic-gateThe value is followed by a block which may contain one or more
609*0Sstevel@tonic-gatePerl statements (including the C<case> statement described below).
610*0Sstevel@tonic-gateThe block is unconditionally executed once the switch value has
611*0Sstevel@tonic-gatebeen cached.
612*0Sstevel@tonic-gate
613*0Sstevel@tonic-gateA C<case> statement takes a single scalar argument (in mandatory
614*0Sstevel@tonic-gateparentheses if it's a variable; otherwise the parens are optional) and
615*0Sstevel@tonic-gateselects the appropriate type of matching between that argument and the
616*0Sstevel@tonic-gatecurrent switch value. The type of matching used is determined by the
617*0Sstevel@tonic-gaterespective types of the switch value and the C<case> argument, as
618*0Sstevel@tonic-gatespecified in Table 1. If the match is successful, the mandatory
619*0Sstevel@tonic-gateblock associated with the C<case> statement is executed.
620*0Sstevel@tonic-gate
621*0Sstevel@tonic-gateIn most other respects, the C<case> statement is semantically identical
622*0Sstevel@tonic-gateto an C<if> statement. For example, it can be followed by an C<else>
623*0Sstevel@tonic-gateclause, and can be used as a postfix statement qualifier.
624*0Sstevel@tonic-gate
625*0Sstevel@tonic-gateHowever, when a C<case> block has been executed control is automatically
626*0Sstevel@tonic-gatetransferred to the statement after the immediately enclosing C<switch>
627*0Sstevel@tonic-gateblock, rather than to the next statement within the block. In other
628*0Sstevel@tonic-gatewords, the success of any C<case> statement prevents other cases in the
629*0Sstevel@tonic-gatesame scope from executing. But see L<"Allowing fall-through"> below.
630*0Sstevel@tonic-gate
631*0Sstevel@tonic-gateTogether these two new statements provide a fully generalized case
632*0Sstevel@tonic-gatemechanism:
633*0Sstevel@tonic-gate
634*0Sstevel@tonic-gate        use Switch;
635*0Sstevel@tonic-gate
636*0Sstevel@tonic-gate        # AND LATER...
637*0Sstevel@tonic-gate
638*0Sstevel@tonic-gate        %special = ( woohoo => 1,  d'oh => 1 );
639*0Sstevel@tonic-gate
640*0Sstevel@tonic-gate        while (<>) {
641*0Sstevel@tonic-gate            switch ($_) {
642*0Sstevel@tonic-gate
643*0Sstevel@tonic-gate                case (%special) { print "homer\n"; }      # if $special{$_}
644*0Sstevel@tonic-gate                case /a-z/i     { print "alpha\n"; }      # if $_ =~ /a-z/i
645*0Sstevel@tonic-gate                case [1..9]     { print "small num\n"; }  # if $_ in [1..9]
646*0Sstevel@tonic-gate
647*0Sstevel@tonic-gate                case { $_[0] >= 10 } {                    # if $_ >= 10
648*0Sstevel@tonic-gate                    my $age = <>;
649*0Sstevel@tonic-gate                    switch (sub{ $_[0] < $age } ) {
650*0Sstevel@tonic-gate
651*0Sstevel@tonic-gate                        case 20  { print "teens\n"; }     # if 20 < $age
652*0Sstevel@tonic-gate                        case 30  { print "twenties\n"; }  # if 30 < $age
653*0Sstevel@tonic-gate                        else     { print "history\n"; }
654*0Sstevel@tonic-gate                    }
655*0Sstevel@tonic-gate                }
656*0Sstevel@tonic-gate
657*0Sstevel@tonic-gate                print "must be punctuation\n" case /\W/;  # if $_ ~= /\W/
658*0Sstevel@tonic-gate        }
659*0Sstevel@tonic-gate
660*0Sstevel@tonic-gateNote that C<switch>es can be nested within C<case> (or any other) blocks,
661*0Sstevel@tonic-gateand a series of C<case> statements can try different types of matches
662*0Sstevel@tonic-gate-- hash membership, pattern match, array intersection, simple equality,
663*0Sstevel@tonic-gateetc. -- against the same switch value.
664*0Sstevel@tonic-gate
665*0Sstevel@tonic-gateThe use of intersection tests against an array reference is particularly
666*0Sstevel@tonic-gateuseful for aggregating integral cases:
667*0Sstevel@tonic-gate
668*0Sstevel@tonic-gate        sub classify_digit
669*0Sstevel@tonic-gate        {
670*0Sstevel@tonic-gate                switch ($_[0]) { case 0            { return 'zero' }
671*0Sstevel@tonic-gate                                 case [2,4,6,8]    { return 'even' }
672*0Sstevel@tonic-gate                                 case [1,3,4,7,9]  { return 'odd' }
673*0Sstevel@tonic-gate                                 case /[A-F]/i     { return 'hex' }
674*0Sstevel@tonic-gate                               }
675*0Sstevel@tonic-gate        }
676*0Sstevel@tonic-gate
677*0Sstevel@tonic-gate
678*0Sstevel@tonic-gate=head2 Allowing fall-through
679*0Sstevel@tonic-gate
680*0Sstevel@tonic-gateFall-though (trying another case after one has already succeeded)
681*0Sstevel@tonic-gateis usually a Bad Idea in a switch statement. However, this
682*0Sstevel@tonic-gateis Perl, not a police state, so there I<is> a way to do it, if you must.
683*0Sstevel@tonic-gate
684*0Sstevel@tonic-gateIf a C<case> block executes an untargetted C<next>, control is
685*0Sstevel@tonic-gateimmediately transferred to the statement I<after> the C<case> statement
686*0Sstevel@tonic-gate(i.e. usually another case), rather than out of the surrounding
687*0Sstevel@tonic-gateC<switch> block.
688*0Sstevel@tonic-gate
689*0Sstevel@tonic-gateFor example:
690*0Sstevel@tonic-gate
691*0Sstevel@tonic-gate        switch ($val) {
692*0Sstevel@tonic-gate                case 1      { handle_num_1(); next }    # and try next case...
693*0Sstevel@tonic-gate                case "1"    { handle_str_1(); next }    # and try next case...
694*0Sstevel@tonic-gate                case [0..9] { handle_num_any(); }       # and we're done
695*0Sstevel@tonic-gate                case /\d/   { handle_dig_any(); next }  # and try next case...
696*0Sstevel@tonic-gate                case /.*/   { handle_str_any(); next }  # and try next case...
697*0Sstevel@tonic-gate        }
698*0Sstevel@tonic-gate
699*0Sstevel@tonic-gateIf $val held the number C<1>, the above C<switch> block would call the
700*0Sstevel@tonic-gatefirst three C<handle_...> subroutines, jumping to the next case test
701*0Sstevel@tonic-gateeach time it encountered a C<next>. After the thrid C<case> block
702*0Sstevel@tonic-gatewas executed, control would jump to the end of the enclosing
703*0Sstevel@tonic-gateC<switch> block.
704*0Sstevel@tonic-gate
705*0Sstevel@tonic-gateOn the other hand, if $val held C<10>, then only the last two C<handle_...>
706*0Sstevel@tonic-gatesubroutines would be called.
707*0Sstevel@tonic-gate
708*0Sstevel@tonic-gateNote that this mechanism allows the notion of I<conditional fall-through>.
709*0Sstevel@tonic-gateFor example:
710*0Sstevel@tonic-gate
711*0Sstevel@tonic-gate        switch ($val) {
712*0Sstevel@tonic-gate                case [0..9] { handle_num_any(); next if $val < 7; }
713*0Sstevel@tonic-gate                case /\d/   { handle_dig_any(); }
714*0Sstevel@tonic-gate        }
715*0Sstevel@tonic-gate
716*0Sstevel@tonic-gateIf an untargetted C<last> statement is executed in a case block, this
717*0Sstevel@tonic-gateimmediately transfers control out of the enclosing C<switch> block
718*0Sstevel@tonic-gate(in other words, there is an implicit C<last> at the end of each
719*0Sstevel@tonic-gatenormal C<case> block). Thus the previous example could also have been
720*0Sstevel@tonic-gatewritten:
721*0Sstevel@tonic-gate
722*0Sstevel@tonic-gate        switch ($val) {
723*0Sstevel@tonic-gate                case [0..9] { handle_num_any(); last if $val >= 7; next; }
724*0Sstevel@tonic-gate                case /\d/   { handle_dig_any(); }
725*0Sstevel@tonic-gate        }
726*0Sstevel@tonic-gate
727*0Sstevel@tonic-gate
728*0Sstevel@tonic-gate=head2 Automating fall-through
729*0Sstevel@tonic-gate
730*0Sstevel@tonic-gateIn situations where case fall-through should be the norm, rather than an
731*0Sstevel@tonic-gateexception, an endless succession of terminal C<next>s is tedious and ugly.
732*0Sstevel@tonic-gateHence, it is possible to reverse the default behaviour by specifying
733*0Sstevel@tonic-gatethe string "fallthrough" when importing the module. For example, the
734*0Sstevel@tonic-gatefollowing code is equivalent to the first example in L<"Allowing fall-through">:
735*0Sstevel@tonic-gate
736*0Sstevel@tonic-gate        use Switch 'fallthrough';
737*0Sstevel@tonic-gate
738*0Sstevel@tonic-gate        switch ($val) {
739*0Sstevel@tonic-gate                case 1      { handle_num_1(); }
740*0Sstevel@tonic-gate                case "1"    { handle_str_1(); }
741*0Sstevel@tonic-gate                case [0..9] { handle_num_any(); last }
742*0Sstevel@tonic-gate                case /\d/   { handle_dig_any(); }
743*0Sstevel@tonic-gate                case /.*/   { handle_str_any(); }
744*0Sstevel@tonic-gate        }
745*0Sstevel@tonic-gate
746*0Sstevel@tonic-gateNote the explicit use of a C<last> to preserve the non-fall-through
747*0Sstevel@tonic-gatebehaviour of the third case.
748*0Sstevel@tonic-gate
749*0Sstevel@tonic-gate
750*0Sstevel@tonic-gate
751*0Sstevel@tonic-gate=head2 Alternative syntax
752*0Sstevel@tonic-gate
753*0Sstevel@tonic-gatePerl 6 will provide a built-in switch statement with essentially the
754*0Sstevel@tonic-gatesame semantics as those offered by Switch.pm, but with a different
755*0Sstevel@tonic-gatepair of keywords. In Perl 6 C<switch> will be spelled C<given>, and
756*0Sstevel@tonic-gateC<case> will be pronounced C<when>. In addition, the C<when> statement
757*0Sstevel@tonic-gatewill not require switch or case values to be parenthesized.
758*0Sstevel@tonic-gate
759*0Sstevel@tonic-gateThis future syntax is also (largely) available via the Switch.pm module, by
760*0Sstevel@tonic-gateimporting it with the argument C<"Perl6">.  For example:
761*0Sstevel@tonic-gate
762*0Sstevel@tonic-gate        use Switch 'Perl6';
763*0Sstevel@tonic-gate
764*0Sstevel@tonic-gate        given ($val) {
765*0Sstevel@tonic-gate                when 1       { handle_num_1(); }
766*0Sstevel@tonic-gate                when ($str1) { handle_str_1(); }
767*0Sstevel@tonic-gate                when [0..9]  { handle_num_any(); last }
768*0Sstevel@tonic-gate                when /\d/    { handle_dig_any(); }
769*0Sstevel@tonic-gate                when /.*/    { handle_str_any(); }
770*0Sstevel@tonic-gate                default      { handle anything else; }
771*0Sstevel@tonic-gate        }
772*0Sstevel@tonic-gate
773*0Sstevel@tonic-gateNote that scalars still need to be parenthesized, since they would be
774*0Sstevel@tonic-gateambiguous in Perl 5.
775*0Sstevel@tonic-gate
776*0Sstevel@tonic-gateNote too that you can mix and match both syntaxes by importing the module
777*0Sstevel@tonic-gatewith:
778*0Sstevel@tonic-gate
779*0Sstevel@tonic-gate	use Switch 'Perl5', 'Perl6';
780*0Sstevel@tonic-gate
781*0Sstevel@tonic-gate
782*0Sstevel@tonic-gate=head2 Higher-order Operations
783*0Sstevel@tonic-gate
784*0Sstevel@tonic-gateOne situation in which C<switch> and C<case> do not provide a good
785*0Sstevel@tonic-gatesubstitute for a cascaded C<if>, is where a switch value needs to
786*0Sstevel@tonic-gatebe tested against a series of conditions. For example:
787*0Sstevel@tonic-gate
788*0Sstevel@tonic-gate        sub beverage {
789*0Sstevel@tonic-gate            switch (shift) {
790*0Sstevel@tonic-gate
791*0Sstevel@tonic-gate                case sub { $_[0] < 10 }  { return 'milk' }
792*0Sstevel@tonic-gate                case sub { $_[0] < 20 }  { return 'coke' }
793*0Sstevel@tonic-gate                case sub { $_[0] < 30 }  { return 'beer' }
794*0Sstevel@tonic-gate                case sub { $_[0] < 40 }  { return 'wine' }
795*0Sstevel@tonic-gate                case sub { $_[0] < 50 }  { return 'malt' }
796*0Sstevel@tonic-gate                case sub { $_[0] < 60 }  { return 'Moet' }
797*0Sstevel@tonic-gate                else                     { return 'milk' }
798*0Sstevel@tonic-gate            }
799*0Sstevel@tonic-gate        }
800*0Sstevel@tonic-gate
801*0Sstevel@tonic-gateThe need to specify each condition as a subroutine block is tiresome. To
802*0Sstevel@tonic-gateovercome this, when importing Switch.pm, a special "placeholder"
803*0Sstevel@tonic-gatesubroutine named C<__> [sic] may also be imported. This subroutine
804*0Sstevel@tonic-gateconverts (almost) any expression in which it appears to a reference to a
805*0Sstevel@tonic-gatehigher-order function. That is, the expression:
806*0Sstevel@tonic-gate
807*0Sstevel@tonic-gate        use Switch '__';
808*0Sstevel@tonic-gate
809*0Sstevel@tonic-gate        __ < 2 + __
810*0Sstevel@tonic-gate
811*0Sstevel@tonic-gateis equivalent to:
812*0Sstevel@tonic-gate
813*0Sstevel@tonic-gate        sub { $_[0] < 2 + $_[1] }
814*0Sstevel@tonic-gate
815*0Sstevel@tonic-gateWith C<__>, the previous ugly case statements can be rewritten:
816*0Sstevel@tonic-gate
817*0Sstevel@tonic-gate        case  __ < 10  { return 'milk' }
818*0Sstevel@tonic-gate        case  __ < 20  { return 'coke' }
819*0Sstevel@tonic-gate        case  __ < 30  { return 'beer' }
820*0Sstevel@tonic-gate        case  __ < 40  { return 'wine' }
821*0Sstevel@tonic-gate        case  __ < 50  { return 'malt' }
822*0Sstevel@tonic-gate        case  __ < 60  { return 'Moet' }
823*0Sstevel@tonic-gate        else           { return 'milk' }
824*0Sstevel@tonic-gate
825*0Sstevel@tonic-gateThe C<__> subroutine makes extensive use of operator overloading to
826*0Sstevel@tonic-gateperform its magic. All operations involving __ are overloaded to
827*0Sstevel@tonic-gateproduce an anonymous subroutine that implements a lazy version
828*0Sstevel@tonic-gateof the original operation.
829*0Sstevel@tonic-gate
830*0Sstevel@tonic-gateThe only problem is that operator overloading does not allow the
831*0Sstevel@tonic-gateboolean operators C<&&> and C<||> to be overloaded. So a case statement
832*0Sstevel@tonic-gatelike this:
833*0Sstevel@tonic-gate
834*0Sstevel@tonic-gate        case  0 <= __ && __ < 10  { return 'digit' }
835*0Sstevel@tonic-gate
836*0Sstevel@tonic-gatedoesn't act as expected, because when it is
837*0Sstevel@tonic-gateexecuted, it constructs two higher order subroutines
838*0Sstevel@tonic-gateand then treats the two resulting references as arguments to C<&&>:
839*0Sstevel@tonic-gate
840*0Sstevel@tonic-gate        sub { 0 <= $_[0] } && sub { $_[0] < 10 }
841*0Sstevel@tonic-gate
842*0Sstevel@tonic-gateThis boolean expression is inevitably true, since both references are
843*0Sstevel@tonic-gatenon-false. Fortunately, the overloaded C<'bool'> operator catches this
844*0Sstevel@tonic-gatesituation and flags it as a error.
845*0Sstevel@tonic-gate
846*0Sstevel@tonic-gate=head1 DEPENDENCIES
847*0Sstevel@tonic-gate
848*0Sstevel@tonic-gateThe module is implemented using Filter::Util::Call and Text::Balanced
849*0Sstevel@tonic-gateand requires both these modules to be installed.
850*0Sstevel@tonic-gate
851*0Sstevel@tonic-gate=head1 AUTHOR
852*0Sstevel@tonic-gate
853*0Sstevel@tonic-gateDamian Conway (damian@conway.org). The maintainer of this module is now Rafael
854*0Sstevel@tonic-gateGarcia-Suarez (rgarciasuarez@free.fr).
855*0Sstevel@tonic-gate
856*0Sstevel@tonic-gate=head1 BUGS
857*0Sstevel@tonic-gate
858*0Sstevel@tonic-gateThere are undoubtedly serious bugs lurking somewhere in code this funky :-)
859*0Sstevel@tonic-gateBug reports and other feedback are most welcome.
860*0Sstevel@tonic-gate
861*0Sstevel@tonic-gate=head1 LIMITATIONS
862*0Sstevel@tonic-gate
863*0Sstevel@tonic-gateDue to the heuristic nature of Switch.pm's source parsing, the presence
864*0Sstevel@tonic-gateof regexes specified with raw C<?...?> delimiters may cause mysterious
865*0Sstevel@tonic-gateerrors. The workaround is to use C<m?...?> instead.
866*0Sstevel@tonic-gate
867*0Sstevel@tonic-gateDue to the way source filters work in Perl, you can't use Switch inside
868*0Sstevel@tonic-gatean string C<eval>.
869*0Sstevel@tonic-gate
870*0Sstevel@tonic-gateIf your source file is longer then 1 million characters and you have a
871*0Sstevel@tonic-gateswitch statement that crosses the 1 million (or 2 million, etc.)
872*0Sstevel@tonic-gatecharacter boundary you will get mysterious errors. The workaround is to
873*0Sstevel@tonic-gateuse smaller source files.
874*0Sstevel@tonic-gate
875*0Sstevel@tonic-gate=head1 COPYRIGHT
876*0Sstevel@tonic-gate
877*0Sstevel@tonic-gate    Copyright (c) 1997-2003, Damian Conway. All Rights Reserved.
878*0Sstevel@tonic-gate    This module is free software. It may be used, redistributed
879*0Sstevel@tonic-gate        and/or modified under the same terms as Perl itself.
880