Anri-chan/Source/Coding Standards/Perl

From SDA Knowledge Base

Jump to: navigation, search

NOTE: we haven't yet made the final decision for what anri 4 will be coded in. these guidelines are only for reference and may have no meaning if we decide to go with a language other than perl.

Headers

#!/usr/bin/perl
use warnings;
use strict 'subs';
package anri;

Whitespace and blocks

Use tabs (rather than spaces) for indentation. Mediawiki supports them, as do the major text editors. 8 spaces per tab.

Blocks example:

for (<*.ac3>) {
	if (m/^([^ ]+) .+DELAY (-?[0-9]+)ms/) {
		$delay=($2/1000);
	} elsif (m/^([^.]+)\.ac3$/) {
		$delay=0;
	}
	print "ac3source(MPEG2source(\"$1.d2v\",upconv=1),\"$_\").delayaudio(".$delay.")\n";
}

Comments

Immediately before the described functionality. Do not mix code and comments unless this is a regex-to-English translation block (see below). Also, comment subroutines before the subroutine declaration, not before calls to it.

############################################################################
#                               Initialisation
#
# Some obvious stuff in here. Desktop location is set using the win32api
# module under Windows and the Carbon API under Mac OS X. Linux users will
# be prompted.
############################################################################

$anri_ver = 4;
$dgmpgdec_dir = 'dgmpgdec150';
$vdub = 'virtualdub-1.7.6';

Variable scope

Anri is a small project and will never be distributed as a Perl module. Therefore, to simplify things, all variables have global scope. Exception: increment variable initialization in for loops, e.g.:

for (my $i = 0; $i < 10; $i++) {

Variables in strings

${variable}

Files and directories as variables

Always use single forward slash as the path delimiter. A directory variable should never end in /.

$afile = "sda.avs";
$adir = "c:/program files/anri";
$sdaavs = "${adir}/${afile}";

for versus foreach

Up to the discretion of the programmer. But if the scope of $_ is ever confusing (e.g. you have to use $_ inside a doubly-nested block), use foreach with a descriptive variable name.

Regex

Document all regex with example input and output strings. Also, use /x to add English comments where the pattern is particularly dense. The below pattern is not particularly dense, but it keeps things short for the sake of example. Note the tabs before the comments.

#Write the source declaration if we have AC3 audio with a delay.
#input example: contra T01 2_0ch 256Kbps DELAY -64ms.ac3
#output example: ac3source(MPEG2source("contra.d2v",upconv=1),"contra T01 2_0ch 256Kbps DELAY -64ms.ac3").delayaudio(-0.064)

/ 		#start of pattern
^ 		#start of input
([^ ]+) 	#one or more character other than a space. this is our "basename" for the .d2v, going to be $1
[ ] 		#a space
.+ 		#one or more of any character other than a newline
DELAY 		#the literal word DELAY
[ ] 		#a space
(-?[0-9]+) 	#Maybe we have a negative sign, maybe we don't. Also one or more numbers. This is going to be $2, our delay value.
ms 		#literal string for milliseconds in filename
/x; 		#end of pattern with comment flag

print "ac3source(MPEG2source(\"$1.d2v\",upconv=1),\"$_\").delayaudio(".($2/1000).")\n";

Some good info on regex in perl is available at perl.org.

Passing arguments to subroutines

No need to call them subroutines, BTW. Functions, methods, verbs, whatever - we all know what you mean.

  • Save commonly used regex in simple subroutines as shown below to avoid having to change several lines when only a small change in the pattern is needed.
  • Prefix all calls to subroutines with & (except for subroutines with prototypes - see below - you will make the prototyping worthless if you call a prototyped subroutine with &).
  • Put all subroutines at the end of the file. One blank line between subroutines; two after the main program before the start of the subroutines.
  • Be careful that you understand all of the caveats of using subroutines before you write or use one. See this page for a good tutorial. Basically, you want to operate on $_[0], $_[1] etc (as in the below example) instead of @_ directly. Remember, all variables in Anri are global.
$charlie = "   charlie";
$snoopy = "snoopy    ";
$woodstock = "woodstock";
&trim_space($charlie,$snoopy,$woodstock);


sub trim_space {
	foreach (@_) {s/^\s*(.*?)\s*$/$1/}
}
print "${charlie}, ${snoopy}, ${woodstock}\n";

Note that there is no problem operating on arrays:

$charlie = "   charlie";
$snoopy = "snoopy    ";
$woodstock = "woodstock";
@characters = ($charlie, $snoopy, $woodstock);
&trim_space(@characters);
foreach (@characters) {
	print "$_\n";
}


sub trim_space {
	foreach (@_) {s/^\s*(.*?)\s*$/$1/}
}

Check q_boo() in anri.pl for an example of a prototyped subroutine. Note the required scalar $ and the optional scalar $ (separated by a semicolon). Prototyping is only necessary in cases where the argument structure passed to a subroutine is going to vary a lot (e.g. optional parameters).

Personal tools