Perlingo - the Perl style guide


version 1.12
LUDIT - KULeuvenNet
sst@kuleuven.net

Perlingo is a style guide for Perl. It defines the house style of KULeuvenNet, the backbone network team of the University of Leuven, Belgium. The perlingo style ensures that all our scripts have the same look and feel, independent of the programmer that coded them. This significantly simplifies team code development and maintenance.

The style defined by Perlingo is pretty authoritative - i.e. it's not just a bunch of guidelines, but the rules should be followed to the letter. Care has been taken to provide only rules that enforce a uniform look and feel, or that constitute common sense. No attempt is made to enforce a particular programming style upon you. By applying these guidelines, you'll find it much easier to read source code produced by other programmers on the team (or even your own source code, a few months after you've written it).

This style guide is derived from various recommendations we found on the web, and from our own experiences with Perl scripting. This is a public domain document - use it as you see fit.


Table of Contents


1. Code Layout and Comments

Rule 1.1 Every source file (script or module) contains the following 3 sections:
  • a file header
  • a POD definition
  • the actual Perl source

The file header provides important information about the global contents of the file, and has the following layout:

#! /usr/bin/perl -wT
use strict;
########################################################################
# 
# File   :  foobar.pm
# History:  24-jun-2002 (spock) added logic section to the module
#           20-jun-2002 (scotty) first implementation of the foobar
#                       module, including transwarp driver code
#
########################################################################
#
# This is an (optional) global comment on the contents of this file.
# The documentation provided here is targeted at the code developer,
# not at the script/module user. This section typically explains the
# data structure and algorithms that were used.
# 
########################################################################
Rule 1.2 Every function in the Perl source is preceded by a comment block,
specifying a synopsis for the defined function.

#-----------------------------------------------------------------------
# $result = &foobarFunction( $input1, $input2, \$output1, \$output2 );
#-----------------------------------------------------------------------
#
# This is an (optional) comment for the defined function, targeted at
# the code developer. This section typically explains the data 
# structure and algorithm that were used.
#
#-----------------------------------------------------------------------

Rule 1.3 Comments in the code shall be written in English

Rule 1.4 Use the following indentation style:
  • 4-column indent (no hard tabs)
  • opening curly on same line as keyword, if possible, otherwise line up
  • closing curly lines up with the keyword that started the block
  • blank lines between chunks that do different things
  • no space between function name and its opening parenthesis
  • space after each comma
  • line up corresponding items vertically
  • use parentheses to indicate evaluation order
  • use spaces where it improves readability

#-- indentation style examples

if ( $color eq "red" ) {
    &processRedBatch( @batch );
} elsif ( $color eq "blue" ) {
    &processBlueBatch( @batch );
} else {
    &rejectBatch( @batch );
}

$result = &randomNumber( $seed, $start, $end );

$res = (($a + $b) / $c) ** 10;

$line       = 1;
$up         = 2;
$things     = 3;
$vertically = 4;

2. Naming Conventions

Rule 2.1 Constants are written in all upper case, with underscores used to separate the components in a long name. Constants are preferably defined with the "use constant" pragma.

#-- classic-style constants
my $PI        = 3.14159;
my $BETTER_PI = 4 * atan2( 1, 1 );

$PI = 3.0;   # legal, but don't ever do this !!!
print "The value of PI = $BETTER_PI\n";


#-- pragma-style constants
use constant PI        => 3.14159;
use constant BETTER_PI => 4 * atan2( 1, 1 );

PI = 3.0;    # compile-time error !
print "The value of PI = " . BETTER_PI . "\n";
Rule 2.2
  • Global variable names are written in mixed upper/lower case,
    and start with an upper-case letter
  • Local variable names are written in mixed upper/lower case,
    and start with a lower-case letter

my $Verbose = 0;
my @StateTable = ( 1, 2, 3 );

foreach my $value ( @StateTable ) {
    my $line;
    $line = &getLine( $value );
    ...
}

Rule 2.3 The names of reference variables are preceded with "ref" for local variables, or "Ref" for global variables

my @StateTable = ( "low", "normal", "high" );
my $RefStateTable = \@StateTable;

do {
    my refStateTable = \@StateTable;
    ...
}
Rule 2.4 Function names are written in mixed upper/lower case,
and start with a lower-case letter

3. Coding Conventions

Rule 3.1 The first thing to do in a function is to fetch its parameters

my $result = &raiseToPower( $value, $power );
...
sub raiseToPower {
    my( $value, $power ) = @_;
    return $value ** $power;
}
Rule 3.2 Switches are implemented with a labeled for statement. Use the word "SWITCH:" as a label.
SWITCH: for ( $trafficLight ) {
    /green/  && do {
        &driveOn();    
        last; 
    };

    /orange/  && do {
        &tryToStop(); 
        last; 
    };

    /red/  && do {
        &alwaysStop(); 
        last; 
    };

    { 
        croak "unknown color for traffic light: $trafficLight\n"; 
    }
}

Like a series of elsifs, a switch should always have a default case, even if you know the default case can't happen.

Rule 3.3 The main program is implemented as a named block.

#-- global variables
my $GlobalVar = 1;

#-- main program
MAIN: {
    my mainVar = "not visible outside main block";
    ...
}

#-- functions and subroutines

Rule 3.4
  • A package MyPackage is placed in its own file with name MyPackage.pm
  • use the Exporter module to export a package's public symbols

Use the following template when defining packages:

#! /usr/bin/perl -wT
use strict;

package MyPackage;

use Exporter;

#-- inherit from Exporter
@MyPackage::ISA = qw( Exporter );

#-- define export symbols
@MyPackage::EXPORT = qw( first second );

#-- subroutine definitions
sub first { ... }
sub second { ... }

#-- end package
1;

4. Programming Tips

This section provides some tips for more effective Perl programming.

Tip 4.1 Make a habit of defensive programming
  • always check function return values
  • watch for external program failures in $?
  • always check your input (including command line arguments)
  • always have an else after a chain of elsif-s (even when the else case is empty)

    if ( $number == 1 ) {
        &doSomething( $number );
    } elsif ( $number > 1 ) {
        &doSomethingElse( $number );
    } else {
        #-- EMPTY
    }
Tip 4.2 Regular expressions and hashes are your friends.

Although Perl doesn't have records, hashes can easily replace them. Learn to use hashes of record field names, instead of using parallel arrays. Don't write this:

    $age{"Jason"} = 23;
    $sex{"Jason"} = "male";
when you should be writing this:
    $people{"Jason"}{AGE} = 23;
    $people{"Jason"}{SEX} = "male";
Tip 4.3 Pattern matching and "en passant" changing

It is possible to copy and change values in one step:

#-- strip to basename: /usr/local/bin/perl -> perl
$pathname = "/usr/local/bin/perl";
( $basename = $pathname ) =~ s!.*/!!;

#-- Capitalize The First Letter of Words
$sentence = "capitalize the first letter of words";
($capped = $sentence) =~ s/(\w+)/\u\L$1/g;

#-- change bin-dirs to lib-dirs
@bindirs = qw( /usr/bin /bin /usr/local/bin );
for ( @libdirs = @bindirs ) { 
    s/bin/lib/ ;
}
print "@libdirs\n"; 

    ==> prints: /usr/lib /lib /usr/local/lib
Tip 4.4 Use "foreach" loops to iterate over data structures

Tip 4.5 Use the Carp module's carp/croak instead of warn/die when programming modules.

 1: #-- application.pl
 2: MAIN: {
 3:     &Top::top;
 4:     print "main ends\n";
 5: }
 6: 
 7: package Top;
 8: sub top {
 9:     &Middle::middle;
10: }
11:
12: package Middle;
13: sub middle {
14:     &Bottom::bottom;
15: }
16:
17: package Bottom;
18: use Carp qw( carp croak );
19: sub bottom {
20:     carp "here we are";
21:     croak "that's all folks";
22: }

5. POD Definition for a Script

This section shows how to write a POD definition for a Perl script. If you adhere to this style, then it will be easier for others to read and understand your documentation.

=head1 NAME

circle - draws a circle on the screen
  
The NAME section gives the name of the script, and a one-line description.

The name and description are separated by a dash. It is important to adhere to this format so that the POD can be converted to a proper man page.

=head1 SYNOPSIS

B<circle> [B<-c> I<color>] [B<-M>] I<radius>
  
The SYNOPSIS section shows how to run the script from the command line.

Literal text, such as the program name, is formatted in bold face (B<>). Text that the user supplies is formatted in italic (I<>). Optional items are enclosed in square brackets ([ ]). An ellipsis (...) indicates that an item may be repeated.

The synopsis must not be indented, so that the B<> and I<> markups will work.

=head1 DESCRIPTION

B<circle> draws a circle in the center of
the screen with the specified I<radius>.
By default, the circle is drawn in black;
a different color may be specified with
B<-c>.
  
This is a description of the script.

It should be written in terms that are relevant to the user, rather than the programmer.

  • what does it do for the user ?
  • how do you use it ?
  • what files does it read and write ?
  • what assumptions does it make ?
=head1 OPTIONS

=over 4

=item B<-c> I<color>

draw the circle in I<color>.

=item B<-M>

print man page

=back
  
The OPTIONS section lists and describes all the command-line options that the script takes.
=head1 FILES

=over 4

=back
  
The FILES section lists all the files that the script reads or writes. It may be omitted if there are none.
=head1 DIAGNOSTICS

=over 4

=item Can't open %s: %s

(E) The file wasn't available for the
given reason

=back
  
The DIAGNOSTICS section gives the text of every error message that the program may issue, and explains its meaning.

Error messages are classified as follows:

  • (W) a warning
  • (E) an error

When errors are issued, the program should exit with a non-zero exit code.

=head1 EXAMPLES

#-- draw a circle with radius 100
circle 100

#-- draw a red circle with radius 100
circle -c red 100
  
The EXAMPLES section provides the user with some typical examples concerning the use of the program.
=head1 REQUIRES

Perl 5.005, Getopt::Std, Pod::Usage
  
The REQUIRES section tells the user what they will need in order to run the program.
=head1 SEE ALSO

perl(1), square(1)
  
This is the usual list of related scripts and modules
=head1 AUTHOR

A.U. Thor, a.u.thor@enterprise.org
  
This includes the name and email address of the authors, in case anyone needs to contact them regarding the program
=cut
  
The =cut line denotes the end of POD text.

6. POD Definition for a Module

This section shows how to write a POD definition for a Perl module. If you adhere to this style, then it will be easier for others to read and understand your documentation.

=head1 NAME

Stack - simple stack module
  
The NAME section gives the name of the module and a one-line description.

The name and description are separated by a dash. It is important to adhere to this format so that the POD can be converted to a proper man page.

=head1 SYNOPSIS

 use Stack;

 $refStack = Stack::new();
             $refStack->push( $elem );
 $elem     = $refStack->pop();
 $size     = $refStack->size();
  
The SYNOPSIS section specifies the signature of all methods exported from the module.

Use a one-space indentation for this section. This ensures that the synopsis is handled as pre-formatted text.

=head1 DESCRIPTION

This module implements a stack data type. 
The following methods are available:

=over 4

=item $refStack = Stack::new();

The constructor takes no arguments and 
creates a reference to a new stack object. 
Failure is indicated by returning a nil 
reference.

=item $refStack->push( $elem )

Pushes the specified element onto the stack

=item $elem = $refStack->pop();

Pops an element from the stack. Returns 
an undefined value when the stack is empty

=item $size = $refStack->size();

Returns the number of elements in the stack

=back
  
This is a description of the module and its methods.

It should be written in terms that are relevant to the user, rather than the programmer.

  • what does it do for the user ?
  • how do you use the module ?
  • what files does it read and write ?
  • what assumptions does it make ?
=head1 FILES

=over 4

=back
  
The FILES section lists all the files that the program reads or writes. It may be omitted if there are none.
=head1 EXAMPLES

$refStack = Stack::new();
$refStack->push( 10 );
$item = $refStack->pop();
  
The EXAMPLES provides the user with some typical examples concerning the use of the module.
=head1 REQUIRES

Perl 5.005, Pod::Usage
  
The REQUIRES section tells the user what they will need in order to make use of the module.
=head1 SEE ALSO

perl(1), queue(3)
  
This is the usual list of related programs and modules.
=head1 AUTHOR

A.U. Thor, a.u.thor@enterprise.org
  
You should include the name and email address oa the authors, in case anyone needs to contact you regarding the module.
=cut
  
The =cut line denotes the end of POD text.

7. Sample Script

#! /usr/bin/perl -wT
##############################################################################
#
# File   :  maskinfo
# History:  04-jul-2002 (herman) fixed bug in argument handling
#           03-jul-2002 (herman) first implementation
#
##############################################################################
#
#  Subnet masks are presented in one of two styles:
#  ==> subnet style = 255.255.255.0
#  ==> cidr style   = /24
#
##############################################################################

=head1 NAME

maskinfo - convert between subnet/cidr mask style

=head1 SYNOPSIS

B<maskinfo> [B<-h>] [I<mask>]

=head1 DESCRIPTION

B<maskinfo> takes a subnet mask in either I<subnet> style (e.g. 255.255.255.0)
or I<cidr> style (e.g. /24), and provides the other mask style as output.

 maskinfo 255.255.255.0   ==>  /24
 maskinfo /24             ==>  255.255.255.0

=head1 OPTIONS

=over 4

=item B<-h>

show usage information

=back

=head1 DIAGNOSTICS

=over 4

=item invalid cidr mask (I<mask>)

(E) The specified cidr mask is not in the range /0 .. /32

=item invalid subnet mask (I<mask>)

(E) The specified subnet mask is invalid, i.e. the binary representation
does not consist of a consecutive set of one-bits followed by a
consecutive set of zero-bits

=back

=head1 AUTHOR

KULeuvenNet, sst@kuleuven.net

=cut

##############################################################################

MAIN: {
    my( $arg, $mask, $cidr );

    if ( $#ARGV != 0 ) {
        &usage();
        exit 1;
    }

    SWITCH: for( $arg ) {
        /-h/ && do {
            &usage();
            last;
        };

        /^\/(\d+)$/ && do {
            if ( $1 > 32 ) {
                print "invalid cidr mask ($arg)\n";
                exit 1;
            }
            $mask = &cidr2mask( $arg );
            print "$mask\n";
            last;
        };

        /^\d+\.\d+\.\d+\.\d+$/ && do {
            if ( ! &validMask( $arg ) ) {
                print "invalid subnet mask ($arg)\n";
                exit 1;
            }
            $cidr = &mask2cidr( $arg );
            print "$cidr\n";
            last;
        };

        {
            &usage();
            exit 1;
        }
    }
}


# ----------------------------------------------------------------------------
# &usage
# ----------------------------------------------------------------------------

sub usage {
    print "Usage: maskinfo [-h] [<mask>]\n\n";
    print "    e.g. maskinfo 255.255.255.0 => /24\n";
    print "    e.g. maskinfo /24 => 255.255.255.0\n";
}


# ----------------------------------------------------------------------------
# $yesno = &validMask( $mask )
# ----------------------------------------------------------------------------

sub validMask {
    my( $mask ) = @_;
    my( @d, $n, $str );

    @d = split /\./, $mask;
    $n = ((((($d[0] * 256) + $d[1])  * 256) + $d[2]) * 256) + $d[3];
    $str = sprintf "%b", $n;
    return ( $str =~ /^1+0*$/ );
}


# ----------------------------------------------------------------------------
# $mask = &cidr2mask( $cidr )
# ----------------------------------------------------------------------------

sub cidr2mask {
    my( $cidr ) = @_;
    my( $one32 ) = 0b11111111111111111111111111111111;
    my( @d, $n, $bits );

    if ( $cidr eq "/0" ) {
        return "0.0.0.0";
    }
    $cidr =~ /\/(\d+)/;
    $bits = $1;
    $n = $one32 << (32 - $bits);
    $d[3] = $n % 256; $n = int( $n / 256);
    $d[2] = $n % 256; $n = int( $n / 256);
    $d[1] = $n % 256; $n = int( $n / 256);
    $d[0] = $n;
    return join '.', @d;
}


# ----------------------------------------------------------------------------
# $cidr = &mask2cidr( $mask )
# ----------------------------------------------------------------------------

sub mask2cidr {
    my( $mask ) = @_;
    my( @d, $n, $bits );

    if ( $mask eq "0.0.0.0" ) {
        return "/0";
    }
    @d = split /\./, $mask;
    $n = ((((($d[0] * 256) + $d[1])  * 256) + $d[2]) * 256) + $d[3];
    $bits = 32;
    while ( ($n % 2) == 0 ) {
        $n >>= 1;
        $bits -= 1;
    }
    return "/$bits";
}

8. Sample Module

#! /usr/bin/perl -wT
##############################################################################
#
# File   :  IpMask.pm
# History:  11-oct-2002 (herman) first implementation
#
##############################################################################
#
#  Subnet masks are presented in one of two styles:
#  ==> subnet style = 255.255.255.0
#  ==> cidr style   = /24
#
##############################################################################
use strict;

=head1 NAME

IpMask - convert between subnet/cidr mask style

=head1 SYNOPSIS

 use IpMask;

 $mask = &cidr2mask( $cidr );
 $cidr = &mask2cidr( $mask );

=head1 DESCRIPTION

The B<IpMask> module defines functions that convert between a subnet mask
in either I<subnet> style (e.g. 255.255.255.0) or I<cidr> style (e.g. /24).

=over 4

=item $mask = &cidr2mask( $cidr );

Takes a I<cidr> style mask as argument (in the range /0 to /32) , and returns 
a string containing the corresponding I<subnet> style mask.
In case of an invalid argument, the function returns I<undef>.

=item $cidr = &mask2cidr( $mask );

Takes a I<subnet> style mask as argument, and returns a string containing 
the corresponding I<cidr> style mask.
In case of an invalid argument, the function returns I<undef>.

=back

=head1 EXAMPLES

 use IpMask;

 $cidr = &mask2cidr( "255.255.255.0" );  # return "/24"
 $cidr = &mask2cidr( "255.255.253.0" );  # returns undef

 $mask = &cidr2Mask( "/22" );  # returns "255.255.252.0"
 $mask = &cidr2Mask( "/33" );  # returns undef

=head1 REQUIRES

Perl 5.6

=head1 AUTHOR

KULeuvenNet, sst@kuleuven.net

=cut

##############################################################################

package IpMask;

use Exporter;
@IpMask::ISA    = qw( Exporter );
@IpMask::EXPORT = qw( mask2cidr cidr2mask );

# ----------------------------------------------------------------------------
# $mask = &cidr2mask( $cidr )
# ----------------------------------------------------------------------------

sub cidr2mask {
    my( $cidr ) = @_;
    my( $one32 ) = 0xffffffff;
    my( @d, $n, $bits );

    if ( $cidr eq "/0" ) {
        return "0.0.0.0";
    }

    if ( $cidr !~ /\/(\d+)/ ) {
        return undef;
    }
    $bits = $1;

    if ( $bits > 32 ) {
        return undef;
    }

    #-- convert to subnet-style mask
    $n = $one32 << (32 - $bits);
    $d[3] = $n % 256; $n = int( $n / 256);
    $d[2] = $n % 256; $n = int( $n / 256);
    $d[1] = $n % 256; $n = int( $n / 256);
    $d[0] = $n;
    return join '.', @d;
}


# ----------------------------------------------------------------------------
# $cidr = &mask2cidr( $mask )
# ----------------------------------------------------------------------------

sub mask2cidr {
    my( $mask ) = @_;
    my( @d, $n, $bits );
    
    if ( $mask eq "0.0.0.0" ) {
        return "/0";
    }
    
    if ( ! &validMask( $mask ) ) {
        return undef;
    }

    @d = split /\./, $mask;
    $n = ((((($d[0] * 256) + $d[1])  * 256) + $d[2]) * 256) + $d[3];
    $bits = 32;
    while ( ($n % 2) == 0 ) {
        $n >>= 1;
        $bits -= 1;
    }
    return "/$bits";
}


# ----------------------------------------------------------------------------
# $yesno = &validMask( $mask )
# ----------------------------------------------------------------------------

sub validMask {
    my( $mask ) = @_;
    my( @d, $n, $str );

    @d = split /\./, $mask;
    $n = ((((($d[0] * 256) + $d[1]) * 256) + $d[2]) * 256) + $d[3];
    $str = sprintf "%b", $n;
    return ( $str =~ /^1+0*$/ );
}

1;