#!/usr/bin/env perl # fetch-morse.pl - Fetch morse code table from Wikipedia and parse it. # Programming > data entry. # # Ryan Thompson use 5.016; # fc() use warnings; use autodie; use strict; use utf8; use LWP::Simple; use Getopt::Long; use File::Slurper qw< read_text write_text >; use Carp; no warnings 'uninitialized'; binmode STDOUT, ':utf8'; my %o = get_options(); my $html = get_html(); my $table = get_table($html); my %morse = get_hash($table); write_text( $o{output}, perl_source(%morse), 'utf-8' ); run_tests() if $o{test}; exit; # # Helpers -- Most of the logic is broken out into the subs below # # Defaults + GetOptions to set up our configuration sub get_options { my %o = ( url => 'https://en.wikipedia.org/wiki/Morse_code', file => 'morse_code.html', output => 'morse_code.pl', ); GetOptions(\%o, qw< url=s file=s output=s force local force test >); croak "Specifying --local and --force together is ambiguous" if $o{local} and $o{force}; %o; } # This is the main parsing loop. Given the raw HTML text for the table # we want, grab each Morse code character and its code, and stuff it into # a hash for return. sub get_hash { # Here's the regex we'll use to grab each result. We'll be interested in # the named captures "ch" and "morse" when we build our hash of morse # codes my $re = qr{ \s* (? .+?) \s* .+? (?:\QShared by \E(? .+?))? # Unused .+? \Q\E (? .+?) \s* }sx; # Iterate over each match and add it to the hash my %ch; while ( $table =~ /$re/g ) { my @ch = chars_of($+{ch}); my $morse = morse_ASCII($+{morse}); @ch{@ch} = ($morse) x @ch; } %ch; } # Get the contents for the table we want sub get_table { $_[0] =~ m{ \Q\E (?
.+?)
\s* }sx or croak "Can't find span/table"; $+{table}; } # Return Perl source for the given hash sub perl_source { my %ch = @_; my $perl = < '%s',\n", "'$key'", $ch{$_}; } $perl .= " );\n}\n\n1; # END OF $o{output}\n"; $perl } # Run tests to make sure everything worked sub run_tests { use Test::More; use lib qw< . >; eval { require $o{output} }; croak "require failed: $@" if $@; my %morse = eval { morse_hash(); }; croak "morse_hash() failed: $@" if $@; is $morse{"'"}, '100001', 'Apostrophe'; is $morse{A}, '10', 'A'; is $morse{A}, $morse{a}, 'A = a'; is $morse{0}, '00000', 'Zero'; is $morse{ç}, '01011', 'C cedilla'; is $morse{ç}, $morse{Ć}, 'Cedilla = Acute'; } # Convert the utf-8 morse code to ASCII hyphen and period, and remove spaces sub morse_ASCII { $_[0] =~ tr/−·  /-./dr } # Parse the text containing the character(s) a particular morse code # applies to and return the character(s) in a list. The Wikipedia page # uses the following formats, so we handle them all: # 1. A -> ('A') # 2. A, a -> ('A', 'a') # 3. Comma [,] -> (',') # 4. Ampersand [&] -> ('&') # Special case sub chars_of { local $_ = shift; return '-' if /Hyphen/; return '/' if /Slash/; return $_ if /^\S+$/; return split /\s*,\s*/ if /^\S+\s*,\s*\S+$/; return '&' if /\[&\]$/; return $1 if /\[([^]])\]$/; croak "Unknown character format `$_'"; } # Maybe fetch, and then read the HTML from the Wikipedia page and return it sub get_html { unlink $o{file} if $o{force}; croak "--local specified, but $o{file} does not exist" unless -f $o{file}; unless ( $o{local} ) { my $URL = 'https://en.wikipedia.org/wiki/Morse_code'; my $code = mirror( $o{url}, $o{file} ); croak "Could not fetch $URL" if is_error( $code ); } read_text( $o{file} ); }