File Coverage

lib/Convert/Morse.pm
Criterion Covered Total %
statement 63 63 100.0
branch 15 18 83.3
condition 6 10 60.0
subroutine 12 12 100.0
pod 6 6 100.0
total 102 109 93.5


line stmt bran cond sub pod time code
1             #############################################################################
2             # Convert/Morse.pm -- package to convert between ASCII and MORSE code.
3             #
4             #############################################################################
5              
6             # TODO:
7              
8             # German umlaute etc (how to represent in ASCII?).
9             # see: http://member.nifty.ne.jp/je1trv/CW_J_e.htm
10             # see: http://burks.brighton.ac.uk/burks/foldoc/61/75.htm
11              
12             package Convert::Morse;
13 1     1   25026 use vars qw($VERSION);
  1         3  
  1         58  
14             $VERSION = 0.06; # Current version of this package
15 1     1   20 use 5.008001; # requires this Perl version or later
  1         3  
  1         32  
16              
17 1     1   13 use Exporter;
  1         6  
  1         76  
18             @ISA = qw(Exporter);
19             @EXPORT_OK = qw( as_morse as_ascii is_morse is_morsable);
20 1     1   4 use strict;
  1         2  
  1         842  
21              
22             #############################################################################
23             # global variables
24              
25             my $morse_ascii; # hash of morse symbols (morse => ascii)
26             my $ascii_morse; # hash of ascii symbols (ascii => morse)
27             my $regexp_ascii_morse; # compiled regexp
28             my $regexp_morse_ascii; # compiled regexp
29             my $error; # last error message
30              
31             sub as_morse
32             {
33             # convert ASCII text into morse code
34 19     19 1 16143 my $ascii = shift; # no || "" because fail for '0'
35 19 100 66     125 return "" if !defined $ascii || $ascii eq "";
36 18         171 undef $error;
37 18         34 $ascii = uc($ascii); # 'Helo' => 'HELO'
38 18         352 $ascii =~ s/\G$regexp_ascii_morse/_convert($1,$ascii_morse);/ge;
  42         87  
39 18         86 $ascii =~ s/\s\z//; # remove last space
40 18         135 $ascii;
41             }
42              
43             sub as_ascii
44             {
45             # convert morse text into ascii code
46 20     20 1 10831 my $morse = shift;
47 20 100 66     154 return "" if !defined $morse || $morse eq "";
48             # because regexps expects a space (to avoid testing for \s|$)
49 19 50       64 $morse .= ' ' if substr($morse,-1,1) ne ' ';
50 19         24 undef $error;
51 19         503 $morse =~ s/\G$regexp_morse_ascii/_convert($1,$morse_ascii);/ge;
  46         84  
52 19         63 $morse =~ s/ +/ /; # collapse multiple spaces
53 19         31 $morse =~ s/\s\z//; # remove last space
54 19         133 $morse;
55             }
56              
57             sub _convert
58             {
59 88     88   321 my ($token,$hash) = @_;
60 88 50       281 return '' if !defined $token;
61 88 100       233 $token =~ s/\s$// if length($token) > 1; # remove trailing space if not ' '
62 88         179 my $sym = $hash->{$token};
63 88 100       135 if (!defined $sym)
64             {
65 9         14 $error = "Undefined token '$token'"; return $token;
  9         31  
66             }
67             #print "'",quotemeta($token),"' => '",quotemeta($sym),"'\n";
68 79         254 $sym;
69             }
70              
71             sub is_morsable
72             {
73             # returns true wether input can be completely expressed as morse
74 2   50 2 1 16 my $text = shift || "";
75 2         9 my $morse = as_morse($text);
76 2 100       8 error() ? undef : 1;
77             }
78              
79             sub is_morse
80             {
81             # returns true wether input is valid Morse code
82 2   50 2 1 558 my $text = shift || "";
83 2         6 my $ascii = as_ascii($text);
84 2 100       4 error() ? undef : 1;
85             }
86              
87             sub error
88             {
89             # return last parse error or undef for ok
90 4     4 1 105 $error;
91             }
92              
93             #############################################################################
94             # self initalization
95              
96             sub tokens
97             {
98             # set/return hash of valid/invalid tokens (in form of ascii => morse)
99 1     1 1 2 my $tokens = shift;
100 1 50       5 if (defined $tokens)
101             {
102 1         9 $morse_ascii = {}; $ascii_morse = {};
  1         2  
103 1         9 foreach (keys %$tokens)
104             {
105 57         104 $ascii_morse->{$_} = $tokens->{$_}.' ';
106 57         134 $morse_ascii->{$tokens->{$_}} = $_;
107             }
108             # fix space handling
109 1         4 foreach (" ")
110             {
111 1         2 $ascii_morse->{$_} = $_;
112 1         3 $morse_ascii->{$_} = $_;
113             }
114             # preserve spaces
115             # compile a big regexp for token parsing
116 58         80 $regexp_ascii_morse = '(' .
117 1         7 join('|', map { quotemeta } keys %$ascii_morse)
118             . '|.|[\n\r\t])';
119 58         103 $regexp_morse_ascii = '(' .
120 1         13 join('\s|', map { quotemeta } keys %$morse_ascii)
121             . '\s|.|[\n\r\t])';
122             #print STDERR "$regexp_ascii_morse\n";
123             #print STDERR "$regexp_morse_ascii\n";
124             #foreach (keys %$ascii_morse)
125             # {
126             # print "'$_' => '$ascii_morse->{$_}'\n";
127             # }
128             }
129             # return current token set
130 1         9 my $copy = {};
131 1         8 foreach (keys %$ascii_morse) { $copy->{$_} = $ascii_morse->{$_}; }
  58         91  
132 1         56 $copy;
133             }
134              
135             BEGIN
136             {
137 1     1   59 tokens( {
138             '.' => '.-.-.-',
139             ',' => '--..--',
140             ':' => '---...',
141             '?' => '..--..',
142             "'" => '.----.',
143             '-' => '-....-',
144             ';' => '-.-.-',
145             '/' => '-..-.',
146             '(' => '-.--.',
147             ')' => '-.--.-',
148             '"' => '.-..-.',
149             '_' => '..--.-',
150             '=' => '-...-',
151             '+' => '.-.-.',
152             '!' => '-.-.--',
153             '@' => '.--.-.',
154             qw(
155             A .-
156             B -...
157             C -.-.
158             D -..
159             E .
160             F ..-.
161             G --.
162             H ....
163             I ..
164             J .---
165             K -.-
166             L .-..
167             M --
168             N -.
169             O ---
170             P .--.
171             Q --.-
172             R .-.
173             S ...
174             T -
175             U ..-
176             V ...-
177             W .--
178             X -..-
179             Y -.--
180             Z --..
181             0 -----
182             1 .----
183             2 ..---
184             3 ...--
185             4 ....-
186             5 .....
187             6 -....
188             7 --...
189             8 ---..
190             9 ----.),
191             # russian
192             qw(
193             EH ..-..
194             YU ..--
195             YA .-.-
196             CHEH ---.
197             SHA ----
198             ),
199             # japanese (WABUN) (not done, needs support for
200             # escaping sequences DO & SN
201             qw(
202             )
203             } );
204             # Ä .-.-
205             # Ö ---.
206             # Ü ..--
207             # adash .--.-
208             # angstroem .--.- (same as adash? huh?)
209             # ch ----
210             # Edash ..-..
211             # N ntilde --.--
212             }
213            
214             #############################################################################
215             1;
216              
217             __END__