#!/usr/bin/perl -w # # usage: # normalise [NORM-OPTIONS...] [--] INPUT-FILES... # # NORM-OPTIONS are # +OPTNAME[=OPTVAL] Election option # CAND=[DESCRIPTION] # +CAND+OPTNAME[=OPTVAL] Candidate option # -- End of options to normalise # -... Reserved for future options to normalise use strict; our @options; our %candidates; # $candidates{CAND}{Desc}, {Opts}[] our @ballots; my $candvoter_re = '\w+'; my $opt_re = '\w+(?:=\S*)?'; sub badinput ($) { die "bad input: $_[0]"; } sub normalise_opts_list ($) { my ($os) = @_; $os //= ''; my @o; foreach my $o (split /\s+/, $os) { if ($o =~ m/^\w+$/) { push @o, $&; } elsif ($o =~ m/^\w+\=\S+$/) { push @o, $&; } elsif ($o !~ m/\S/) { } else { badinput "bad option \`$o'"; } } return @o; } sub normalise_opts ($) { my ($os) = @_; my @o = normalise_opts_list $os; return " | @o"; } sub setcanddesc ($$) { my ($cand,$desc) = @_; if (length $desc) { badinput "multiple descriptions for $cand" if defined $candidates{$cand}{Desc}; $candidates{$cand}{Desc} = $desc; } } while (@ARGV) { $_ = shift @ARGV; if (m/^--$/) { last; } elsif (m/^(\w+)=([^|]+)$/) { setcanddesc $1, $2; } elsif (m/^\+($opt_re)$/) { push @options, $1; } elsif (m/^\+(\w+)\+($opt_re)$/) { push @{ $candidates{$1}{Opts} }, $2; } elsif (m/^-/) { die "unknown normalise option \`$_'\n"; } else { # oh! unshift @ARGV, $_; last; } } while (<>) { next unless m/\S/; next if m/^\#/; s/^\s+//; s/\s+$//; if (m/^\|/) { push @options, normalise_opts_list $'; } elsif (m/^($candvoter_re?)\s*=\s*([^|]+?)\s*\|(.*)?$/o) { use Data::Dumper; print STDERR Dumper($1,$2,$3); my ($cand,$desc,$opts) = ($1,$2,$3); push @{ $candidates{$cand}{Opts} }, normalise_opts $opts; setcanddesc $cand, $desc; } elsif (m/^($candvoter_re?)?\s*\:([^|]*)(?:\|(.*))?$/) { my ($voter,$opts) = ($1,$3); my @p; foreach my $p (split /\s+/, $2) { if ($p =~ m/^\w+(?:\=\w+)*$/) { push @p, $&; $candidates{$_} //= { } foreach $p =~ m/\w+/g; } elsif ($p eq '') { # empty entry can only happen if voter casts no prefs at all } else { badinput "bad vote preference \`$p'"; } } push @ballots, "$voter : @p".normalise_opts $opts; } elsif (m/^\.$/) { } else { badinput "unknown line format \`$_'"; } } print "| @options\n" or die $!; foreach my $cand (sort keys %candidates) { my $c = $candidates{$cand}; $c->{Desc} //= $cand; $c->{Opts} //= [ ]; my $opts = $c->{Opts}; print "$cand = $c->{Desc} | @$opts\n" or die $!; } sub vsortkey { $_[0] =~ m/:/; return "$' : $`"; } print $_,"\n" or die $! foreach sort { vsortkey($a) cmp vsortkey($b) } @ballots; print ".\n" or die $!;