Switch::Plain - a simple switch statement for Perl
use Switch::Plain; # string version sswitch (get_me_a_string()) { # return value of get_me_a_string() is bound to $_ in this block case 'foo': { # runs if $_ equals 'foo' } case 'bar': { # runs if $_ equals 'bar' } case 'melonlord' if $DEBUG: { # runs if $_ equals 'melonlord' and $DEBUG is true } default if $VERBOSE > 1: { # runs if nothing else matched so far and $VERBOSE is greater than 1 } default: { # runs if nothing else matched so far } } # number version nswitch (get_me_a_number()) { # return value of get_me_a_number() is bound to $_ in this block case 1: { # runs if $_ equals 1 } case 2: { # runs if $_ equals 2 } case 99 if $DEBUG: { # runs if $_ equals 99 and $DEBUG is true } default if $VERBOSE > 1: { # runs if nothing else matched so far and $VERBOSE is greater than 1 } default: { # runs if nothing else matched so far } }
This module provides (yet another) switch statement for Perl. Differences between this module and Switch include:
It's not a source filter. (It uses perl's pluggable keywords instead.)
It generates non-horrible code. If you want to see this for yourself, run some sample code through perl -MO=Deparse.
perl -MO=Deparse
It doesn't try to be smart about matching fancy data structures; it only does simple string or numeric equality tests. This also sets it apart from perl's built in given statement and smartmatch operator ~~.
given
~~
This module understands the following grammar:
switch_statement := switch_keyword switch_scrutinee switch_body switch_keyword := 'sswitch' | 'nswitch' switch_scrutinee := '(' EXPR ')' switch_body := '{' case_clause* '}' case_clause := case_pattern+ BLOCK case_pattern := case_keyword case_modifier? ':' case_keyword := 'default' | 'case' EXPR case_modifier := 'if' EXPR | 'unless' EXPR
*, +, and ? have their usual regex meaning; BLOCK and EXPR represent standard Perl blocks and expressions, respectively.
*
+
?
BLOCK
EXPR
The meaning of a switch statement is given by the following translation rules:
sswitch (FOO) { ... } and nswitch (FOO) { ... } turn into
sswitch (FOO) { ... }
nswitch (FOO) { ... }
do { local *_ = \FOO; ... };
That is, they alias $_ to FOO within the body of the switch statement.
$_
FOO
A series of case clauses in the switch body turns into a single if/elsif chain. That is, the first clause becomes an if; every subsequent clause becomes an elsif.
if
elsif
case FOO: becomes if ($_ eq FOO) for sswitch and if ($_ == FOO) for nswitch.
case FOO:
if ($_ eq FOO)
sswitch
if ($_ == FOO)
nswitch
default: becomes if (1).
default:
if (1)
case FOO if BAR: becomes if ($_ eq FOO && BAR) for sswitch and if ($_ == FOO && BAR) for nswitch.
case FOO if BAR:
if ($_ eq FOO && BAR)
if ($_ == FOO && BAR)
default if BAR: becomes if (BAR).
default if BAR:
if (BAR)
... unless BAR works similarly, but with the condition inverted (!BAR).
... unless BAR
!BAR
If there are multiple case/defaults before a single block, their conditions are combined with ||.
case
default
||
Here's an example demonstrating all combinations:
sswitch (SCRUTINEE) { case FOO0: { BODY0 } case FOO1: case FOO2 if BAR1: case FOO3 unless BAR2: default if BAR3: default unless BAR4: { BODY1 } default: { BODY2 } }
This is equivalent to:
do { # temporarily alias $_ to SCRUTINEE within this block: local *_ = \SCRUTINEE; if ($_ eq FOO0) { BODY0 } elsif ( $_ eq FOO1 || ($_ eq FOO2 && BAR1) || ($_ eq FOO3 && !BAR2) || BAR3 || !BAR4 ) { BODY1 } elsif (1) { BODY2 } };
Switch::Plain
switch
C's switch is limited to integer scrutinees. Switch::Plain supports any number or string.
C's case labels must be compile-time constants. Switch::Plain allows any expression and even additional arbitrary conditions via the if/unless case modifiers.
unless
C's case labels are actual labels in that they can appear anywhere within the switch statement's body (even nested). With Switch::Plain all cases must be at the top level.
In C the order of the cases does not matter since they're all known at compile time and guaranteed to be distinct. Switch::Plain evaluates them in the order they're written: If you put default: { ... } in the middle of a switch, it will intercept all values, and any following cases will be ignored (this is like writing ... elsif (1) { ... } else { ... }).
default: { ... }
... elsif (1) { ... } else { ... }
Since C's case labels are actual labels and C's switch is effectively a dynamic goto, C actually has no concept of a "case clause" or a "case block". switch simply transfers control to one of the cases and that's it. This has the side effect of "fallthrough" behavior if you want to use C's switch to check for multiple distinct cases; that is, you must insert an explicit break; to leave the switch statement when you're done with your case.
goto
break;
Switch::Plain has nothing of the kind. Because it turns into a single if/elsif chain and every case block is clearly delimited, execution of sswitch/nswitch stops as soon one case pattern matches. However, it is possible to attach multiple case patterns to a single block:
case 2: case 3: case 5: { ... }
This trivial case works the same way as fallthrough would in C (any value of 2, 3, or 5 is accepted).
Since C's break refers to the innermost enclosing switch or loop, you can't use it in switch to leave a surrounding loop (you have to use goto instead). This particular problem would be avoidable in Perl thanks to loop labels; however, this isn't even necessary because sswitch/nswitch work like if: They don't count as loops and last/next/redo ignore them.
break
last
next
redo
This module is a lexical pragma, i.e. the effects of use Switch::Plain (turning sswitch and nswitch into keywords) are scoped to the innermost enclosing block (or the whole file if there is no enclosing block).
use Switch::Plain
If you are a module author who wants to wrap Switch::Plain from another module, simply call Switch::Plain->import from your own import method. It will affect whatever scope is currently being compiled (i.e. your caller).
Switch::Plain->import
import
Lukas Mai, <l.mai at web.de>
<l.mai at web.de>
Copyright 2012-2013 Lukas Mai.
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.
To install Switch::Plain, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Switch::Plain
CPAN shell
perl -MCPAN -e shell install Switch::Plain
For more information on module installation, please visit the detailed CPAN module installation guide.