$ ./tutorial-p5.pl -g -d >& p5.txt
$ ./tutorial-p6.pl -g -d .& p6.txt
####
#!/usr/bin/env perl
# file: tutorial-p5.pl
# PRELIMS ========================
use v5.10; # features 'say' and 'state'
use strict;
use warnings;
use File::Basename; # p6: no such module yet
use Data::Dumper;
my $default_infile = 'tutorial-data.txt';
my $prog = basename $0;
my $debug = 0;
my $infile = 0;
my $usage = "$prog: --go | --infile=";
$usage .= ' | --help | ? [--debug[=N]]';
# ARG/OPTION HANDLING ========================
if (!@ARGV) {
say $usage . "\n";
exit;
}
foreach my $arg (@ARGV) {
my $oarg = $arg; # save original for error handling
my $val = undef;
my $idx = index $arg, q{=};
if ($idx >= 0) {
$val = substr $arg, $idx+1;
$arg = substr $arg, 0, $idx;
}
if ($arg eq '-g' || $arg eq '--go') {
$infile = $default_infile;
}
elsif ($arg eq '-i' || $arg eq '--infile') {
$infile = $val;
}
elsif ($arg eq '-d' || $arg eq '--debug') {
$debug = defined $val ? $val : 1;
}
elsif ($arg eq '-h' || $arg eq '--help' || $arg eq q{?}) {
long_help();
}
else {
die "FATAL: Unknown argument '$oarg'.\n";
}
}
# MAIN PROGRAM ========================
die "FATAL: No such file '$infile'.\n"
if (! -e $infile);
my %user;
my @keywords = qw(last first job);
my %keywords;
@keywords{@keywords} = ();
parse_data_file($infile, \%user, $debug);
if ($debug) {
say 'DEBUG: Dumping user hash after loading:';
print Dumper(\%user);
}
else {
say 'Normal end.';
}
#### SUBROUTINES ========================
sub parse_data_file {
my $fname = shift @_;
my $href = shift @_;
my $debug = shift @_ || 0;
say "Parsing input file '$fname'...";
open my $fp, '<', $fname
or die "$fname: $!";
my $uid = undef;
my $linenum = 0;
while (defined(my $line = <$fp>)) {
++$linenum;
my $err = 0;
# remove comments
my $idx = index $line, q{#};
if ($idx >= 0) {
$line = substr $line, 0, $idx;
}
# skip blank lines
next if $line !~ /\S/xms;
# every valid line must have a colon (':')
# following a key word
$idx = index $line, q{:};
if ($idx >= 0) {
# ensure the key is lower case
my $k = lc substr $line, 0, $idx;
# trim ws on both ends
$k =~ s{\A \s* | \s* \z}{}gxms;
my $val = substr $line, $idx+1;
# also needs trimming
$val =~ s{\A \s* | \s* \z}{}gxms;
# User attributes
if ($k eq 'user') {
$uid = $val;
die 'FATAL: $uid not defined.'
if !defined $uid;
if ($uid =~ /\D/xms) {
say 'ERROR: User ID not an integer.';
++$err;
}
elsif ($uid <= 0) {
say 'ERROR: User ID not an integer > 0.';
++$err;
}
elsif (exists $href->{$uid}) {
say 'ERROR: User ID is not unique.';
++$err;
}
next;
}
# for the following keys, an exception will be
# thrown if $uid is not defined
if (!defined $uid) {
say 'ERROR: User ID is not defined for this user.';
++$err;
}
elsif ($k eq 'hobbies') {
$href->{$uid}{hobbies} = [];
my @h = split q{,}, $val;
foreach my $h (@h) {
# trim ws on both ends
$h =~ s{\A \s* | \s* \z}{}gxms;
push @{$href->{$uid}{hobbies}}, $h;
}
}
elsif (exists $keywords{$k}) {
$href->{$uid}{$k} = $val;
}
else {
chomp $line;
say 'ERROR: Unknown line format:';
say " '$line'";
++$err;
}
}
else {
say 'ERROR: Unknown line format.';
++$err;
}
if ($debug) {
chomp $line;
say STDERR "DEBUG: line = '$line'";
}
if ($err) {
chomp $line;
say "FATAL error in file '$fname' at line $linenum:";
say " '$line'";
exit;
}
}
} # parse_data_file
sub long_help {
say <<"HERE";
Usage (one of the following three):
$prog --go (or '-g')
$prog --infile= (or '-i=')
$prog --help (or '-h' or '?')
The '--go' option uses the default input file:
$default_infile
Any of the first two options can use the '-d' (or '--debug')
flag for debugging.
A debug number may be provided with '-d=N' (or '--debug=N').
HERE
exit;
} # long_help
# EOF ========================
##
##
#!/usr/bin/env perl6
# file: tutorial-p6.pl
# PRELIMS ========================
use v6.0; # not required, but good practice for
# maintenance
# 'strict' and 'warnings' are the default
# Note: Using perl6 -v =>
# '2015.07.1-66-g0dcbba7 built on MoarVM version 2015.07-8-gb8fdeae'
use Data::Dump;
my $default_infile = 'tutorial-data.txt';
my $prog = basename($*PROGRAM);
my $debug = 0;
my $infile = 0;
my $usage = "$prog: --go | --infile=";
$usage ~= ' | --help | ? [--debug[=N]]'; # '~=' instead of '.='
# ARG/OPTION HANDLING ========================
# See [http://design.perl6.org/S06.html]
# for built-in methods similar to Getopts::Long
if !@*ARGS.elems {
say $usage ~ "\n"; # '~' instead of '.'
exit;
}
for @*ARGS -> $arg is copy { # 'is copy' allows modifying locally
my $oarg = $arg; # save original for error handling
my $val = Any; # 'Any' instead of 'undef'
my $idx = index $arg, '=';
if $idx.defined { # index is defined if an index is found
$val = substr $arg, $idx+1; # use substr function
$arg = substr $arg, 0, $idx;
}
if ($arg eq '-g' || $arg eq '--go') {
$infile = $default_infile;
}
elsif ($arg eq '-i' || $arg eq '--infile') {
$infile = $val;
}
elsif ($arg eq '-d' || $arg eq '--debug') {
$debug = defined $val ? $val : 1;
}
elsif ($arg eq '-h' || $arg eq '--help' || $arg eq q{?}) {
long_help();
}
else {
die "FATAL: Unknown argument '$oarg'.\n";
}
}
# MAIN PROGRAM ========================
die "FATAL: No such file '$infile'.\n"
if $infile.IO !~~ :f;
my %user;
my @keywords = ;
my %keywords;
%keywords{@keywords} = ();
parse_data_file($infile, %user, $debug);
if $debug {
say "DEBUG: Dumping user hash after loading:";
say Dump(%user);
}
else {
say 'Normal end.';
}
#### SUBROUTINES ========================
sub parse_data_file(Str $fname, # declare args
#Any $href,
%href,
Int $debug = 0) {
say "Parsing input file '$fname'...";
my $uid = Any; # p6 doesn't use 'undef'
my $linenum = 0;
for $fname.IO.lines -> $line is copy {
++$linenum;
my $err = 0;
# remove comments
my $idx = index $line, '#', 0;
if defined $idx {
$line = $line.substr(0, $idx);
}
# skip blank lines
next if $line !~~ /\S/; # '~~' and '!~~' for matching
# every valid line must have a colon (':')
# following a key word
$idx = $line.index(':');
if $idx.defined {
# ensure the key is lower case
my $k = $line.substr(0, $idx).lc;
# trim ws on both ends
$k = $k.trim; # string object method
my $val = $line.substr($idx+1); # use object method
# also needs trimming
$val = $val.trim;
# User attributes
if $k eq 'user' {
$uid = $val;
die "FATAL: \$uid not defined."
if !$uid.defined;
if $uid ~~ /\D/ {
say "ERROR: User ID not an integer.";
++$err;
}
elsif $uid <= 0 {
say "ERROR: User ID not an integer > 0.";
++$err;
}
elsif %href{$uid}:exists { # 'exists' adverb
say "ERROR: User ID is not unique.";
++$err;
}
next;
}
# for the following keys, an exception will be
# thrown if $uid is not defined
if !$uid.defined {
say "ERROR: User ID is not defined for this user.";
++$err;
}
elsif $k eq 'hobbies' {
# literal string keys must be quoted
%href{$uid}<> = [];
my @h = split ',', $val;
for @h -> $h is rw {
# trim ws on both ends
$h .= trim;
# literal string keys must be quoted
# use '@()' instead of '@{}
push @(%href{$uid}), $h;
}
}
elsif %keywords{$k}:exists {
%href{$uid}{$k} = $val;
}
else {
$line .= chomp;
say "ERROR: Unknown line format, \$k = '$k'.";
say " '$line'";
++$err;
}
}
else {
say 'ERROR: Unknown line format (no key).';
++$err;
}
if $debug {
$line .= chomp;
say "DEBUG: line = '$line'";
}
if $err {
$line .= chomp;
say "FATAL error in file '$fname' at line $linenum:";
say " '$line'";
exit;
}
}
} # parse_data_file
sub long_help {
# note indent is taken from position of ending token
say qq:to/HERE/;
Usage (one of the following three):
$prog --go (or '-g')
$prog --infile= (or '-i=')
$prog --help (or '-h' or '?')
The '--go' option uses the default input file:
$default_infile
Any of the first two options can use the '-d' (or '--debug')
flag for debugging.
A debug number may be provided with '-d=N' (or '--debug=N').
HERE
exit;
} # long_help
sub basename(IO::Path $fname) {
# no File::Basename module in p6 yet
my $idx = rindex $fname, '/';
if $idx {
return $fname.substr($idx+1); # use substr method
}
return $fname;
} # basename
# EOF ========================
##
##
# file: tutorial-data.txt
# a data file of users and their attributes
# note all valid lines are in format "key: value..."
user: 1234 # unique ID (an integer > zero)
last: Brown
first: Sam
job: gunsmith
# hobbies may be a comma-separated list
hobbies: hunting, Perl Monging
user: 2316
last: Doe
first: Jane
job: financial analyst
hobbies: Python open source, bowling