NAME
Switch::Plain - a simple switch statement for Perl
SYNOPSIS
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
}
}
DESCRIPTION
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.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
givenstatement and smartmatch operator~~.
Syntax
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.
Semantics
The meaning of a switch statement is given by the following translation rules:
sswitch (FOO) { ... }andnswitch (FOO) { ... }turn intodo { local *_ = \FOO; ... };That is, they alias
$_toFOOwithin the body of the switch statement.A series of case clauses in the switch body turns into a single
if/elsifchain. That is, the first clause becomes anif; every subsequent clause becomes anelsif.case FOO:becomesif ($_ eq FOO)forsswitchandif ($_ == FOO)fornswitch.default:becomesif (1).case FOO if BAR:becomesif ($_ eq FOO && BAR)forsswitchandif ($_ == FOO && BAR)fornswitch.default if BAR:becomesif (BAR).... unless BARworks similarly, but with the condition inverted (!BAR).If there are multiple
case/defaults before a single block, their conditions are combined with||.
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
}
};
Differences between Switch::Plain and C's switch
C's
switchis limited to integer scrutinees.Switch::Plainsupports any number or string.C's
caselabels must be compile-time constants.Switch::Plainallows any expression and even additional arbitrary conditions via theif/unlesscase modifiers.C's
caselabels are actual labels in that they can appear anywhere within theswitchstatement's body (even nested). WithSwitch::Plainallcases 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::Plainevaluates them in the order they're written: If you putdefault: { ... }in the middle of aswitch, it will intercept all values, and any followingcases will be ignored (this is like writing... elsif (1) { ... } else { ... }).Since C's
caselabels are actual labels and C'sswitchis effectively a dynamicgoto, C actually has no concept of a "case clause" or a "case block".switchsimply transfers control to one of thecases and that's it. This has the side effect of "fallthrough" behavior if you want to use C'sswitchto check for multiple distinct cases; that is, you must insert an explicitbreak;to leave theswitchstatement when you're done with your case.Switch::Plainhas nothing of the kind. Because it turns into a singleif/elsifchain and every case block is clearly delimited, execution ofsswitch/nswitchstops 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
breakrefers to the innermost enclosingswitchor loop, you can't use it inswitchto leave a surrounding loop (you have to usegotoinstead). This particular problem would be avoidable in Perl thanks to loop labels; however, this isn't even necessary becausesswitch/nswitchwork likeif: They don't count as loops andlast/next/redoignore them.
Scoping
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).
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).
AUTHOR
Lukas Mai, <l.mai at web.de>
COPYRIGHT & LICENSE
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.