File Coverage

blib/lib/Silly/Werder.pm
Criterion Covered Total %
statement 113 478 23.6
branch 27 168 16.0
condition 8 23 34.7
subroutine 14 33 42.4
pod 16 18 88.8
total 178 720 24.7


line stmt bran cond sub pod time code
1             # Copyright 2000-2002 Dave Olszewski. All rights reserved.
2             # Perlish way to generate snoof (language which appears to be real but is
3             # in fact, not)
4             # Distributed under the terms of GPL Version 2
5              
6             package Silly::Werder;
7              
8             $Silly::Werder::VERSION='0.90';
9              
10 1     1   733 use strict;
  1         2  
  1         45  
11 1     1   8 use Exporter;
  1         2  
  1         36  
12 1     1   1198 use Storable;
  1         4051  
  1         74  
13 1     1   1197 use File::Spec::Functions;
  1         1054  
  1         116  
14              
15 1     1   9 use constant SENTENCE => ".";
  1         2  
  1         88  
16 1     1   6 use constant QUESTION => "?";
  1         2  
  1         64  
17 1     1   5 use constant EXCLAMATION => "!";
  1         2  
  1         49  
18              
19 1         5130 use vars qw($VERSION $PACKAGE
20             @ISA
21             @EXPORT_OK
22 1     1   6 );
  1         2  
23              
24             @ISA = 'Exporter';
25              
26             my @werder_functions = qw(line sentence question exclaimation exclamation
27             set_werds_num set_syllables_num set_language
28             set_hard_syllable_max end_with_newline get_werd
29             set_unlinked dump_syllables dump_grammar build_grammar
30             load_grammar_file load_syllable_file
31             set_cons_weight);
32              
33             @EXPORT_OK = (@werder_functions);
34              
35             my $self;
36              
37             sub new {
38 0     0 0 0 my $self = {};
39 0         0 bless $self;
40              
41             # Initialize the internal variables
42 0         0 $self->_init($self);
43              
44 0         0 return $self;
45             }
46              
47 0     0   0 sub DESTROY { }
48              
49             ##########################################################################
50             # Sets the min and max number of werds that will go into the sentence
51             ##########################################################################
52             sub set_werds_num($$) {
53              
54 0     0 1 0 my $obj;
55 0 0       0 if(scalar(@_) == 3) {
56 0         0 $obj = shift;
57             }
58              
59 0         0 my ($min, $max) = @_;
60 0         0 my $target;
61 0 0       0 if($min > $max) { return -1; }
  0         0  
62              
63 0 0       0 if(ref $obj) { $target = $obj; }
  0         0  
64 0         0 else { $target = $self; }
65              
66 0         0 $target->{"min_werds"} = $min;
67 0         0 $target->{"max_werds"} = $max;
68              
69 0         0 return 0;
70              
71             }
72              
73              
74             ##########################################################################
75             # Sets the min and max number of syllables that can go into a werd
76             ##########################################################################
77             sub set_syllables_num($$) {
78              
79 0     0 1 0 my $obj;
80 0 0       0 if(scalar(@_) == 3) {
81 0         0 $obj = shift;
82             }
83              
84 0         0 my ($min, $max) = @_;
85 0         0 my $target;
86 0 0       0 if($min > $max) { return -1; }
  0         0  
87              
88 0 0       0 if(ref $obj) { $target = $obj; }
  0         0  
89 0         0 else { $target = $self; }
90              
91 0         0 $target->{"min_syllables"} = $min;
92 0         0 $target->{"max_syllables"} = $max;
93              
94 0         0 return 0;
95              
96             }
97              
98              
99             ##########################################################################
100             # Sets a hard max syllables per werd
101             ##########################################################################
102             sub set_hard_syllable_max($) {
103              
104 0     0 1 0 my $obj;
105 0 0       0 if(scalar(@_) == 2) {
106 0         0 $obj = shift;
107             }
108              
109 0         0 my $target;
110 0 0       0 if(ref $obj) { $target = $obj; }
  0         0  
111 0         0 else { $target = $self; }
112              
113 0         0 my $max = shift;
114 0 0       0 if($max < 0) { return -1; }
  0         0  
115 0 0       0 if($max < $target->{"syllables_min"}) { return -1; }
  0         0  
116              
117 0         0 $target->{"hard_syllable_max"} = $max;
118              
119 0         0 return 0;
120             }
121              
122              
123             ##########################################################################
124             # Sets whether you want to end sentences in a newline
125             ##########################################################################
126             sub end_with_newline($) {
127              
128 0     0 1 0 my $obj;
129 0 0       0 if(scalar(@_) == 2) {
130 0         0 $obj = shift;
131             }
132            
133 0         0 my $target;
134 0 0       0 if(ref $obj) { $target = $obj; }
  0         0  
135 0         0 else { $target = $self; }
136            
137 0         0 my $yesno = shift;
138            
139 0         0 $target->{"end_with_newline"} = $yesno;
140            
141 0         0 return 0;
142             }
143              
144              
145             ##########################################################################
146             # Sets whether you want fully random mode or not (not recommended)
147             ##########################################################################
148             sub set_unlinked($) {
149 0     0 1 0 my $obj;
150 0 0       0 if(scalar(@_) == 2) {
151 0         0 $obj = shift;
152             }
153              
154 0         0 my $target;
155 0 0       0 if(ref $obj) { $target = $obj; }
  0         0  
156 0         0 else { $target = $self; }
157              
158 0         0 my $yesno = shift;
159            
160 0         0 $target->{"unlinked"} = $yesno;
161            
162 0         0 return 0;
163             }
164              
165             ##########################################################################
166             # Set the percentage of the time a werd will start with a consonant
167             # This function is OBSOLETE and is only kept for compatibility
168             ##########################################################################
169             sub set_cons_weight($) {
170 0     0 0 0 return 0;
171             }
172              
173             ##########################################################################
174             # Create a random type of sentence
175             ##########################################################################
176             sub line {
177 1     1 1 19 my $obj;
178 1 50       5 if(scalar(@_) == 1) {
179 1         2 $obj = shift;
180             }
181              
182 1         2 my ($line, $target);
183 1         46 my $which_kind = int(rand() * 3);
184              
185            
186 1 50       5 if(ref $obj) { $target = $obj; }
  0         0  
187 1         3 else { $target = $self; }
188              
189 1 50       4 if($which_kind == 0) { $line = _make_line($target, SENTENCE); }
  0         0  
190 1 50       4 if($which_kind == 1) { $line = _make_line($target, QUESTION); }
  1         6  
191 1 50       4 if($which_kind == 2) { $line = _make_line($target, EXCLAMATION); }
  0         0  
192              
193 1         8 return $line;
194             }
195              
196             ##########################################################################
197             # Create a sentence with a period
198             ##########################################################################
199             sub sentence {
200 0     0 1 0 my $obj;
201 0 0       0 if(scalar(@_) == 1) {
202 0         0 $obj = shift;
203             }
204              
205 0         0 my $target;
206              
207 0 0       0 if(ref $obj) { $target = $obj; }
  0         0  
208 0         0 else { $target = $self; }
209              
210 0         0 my $line = _make_line($target, SENTENCE);
211 0         0 return $line;
212             }
213              
214             ##########################################################################
215             # Create a question
216             ##########################################################################
217             sub question {
218 0     0 1 0 my $obj;
219 0 0       0 if(scalar(@_) == 1) {
220 0         0 $obj = shift;
221             }
222              
223 0         0 my $target;
224              
225 0 0       0 if(ref $obj) { $target = $obj; }
  0         0  
226 0         0 else { $target = $self; }
227              
228 0         0 my $line = _make_line($target, QUESTION);
229 0         0 return $line;
230             }
231              
232             ##########################################################################
233             # Create an exclamation
234             ##########################################################################
235             sub exclamation {
236 0     0 1 0 my $obj;
237 0 0       0 if(scalar(@_) == 1) {
238 0         0 $obj = shift;
239             }
240              
241 0         0 my $target;
242              
243 0 0       0 if(ref $obj) { $target = $obj; }
  0         0  
244 0         0 else { $target = $self; }
245              
246 0         0 my $line = _make_line($target, EXCLAMATION);
247 0         0 return $line;
248             }
249              
250             ##########################################################################
251             # Make and return a single werd
252             ##########################################################################
253             sub get_werd {
254 0     0 1 0 my $obj = shift;
255              
256 0         0 my $target;
257            
258 0 0       0 if(ref $obj) { $target = $obj; }
  0         0  
259 0         0 else { $target = $self; }
260            
261 0         0 my $werd = _make_werd($target);
262 0         0 return $werd;
263             }
264              
265             # For backwards compatibility with spelling error from previous release
266             # Do it twice to quelch "only used once" warnings
267             *exclaimation = *exclamation;
268             *exclaimation = *exclamation;
269              
270             ##########################################################################
271             # Set the language/grammar to use
272             ##########################################################################
273             sub set_language($) {
274 1     1 1 3 my ($obj, $language, $variant) = @_;
275 1         23 my $target;
276              
277 1 50       4 if(ref $obj) { $target = $obj; }
  1         2  
278 0         0 else { $target = $self; }
279              
280 1         4 my $module = "Silly::Werder::" . $language;
281 1         86 eval "require $module";
282 1         39 import $module (qw/LoadGrammar/);
283              
284 1         4 my ($grammar, $index) = LoadGrammar($variant);
285 1         13 $obj->{"grammar"} = $grammar;
286 1         5 $obj->{"index"} = $index;
287             }
288              
289              
290              
291             ##########################################################################
292             # Initialize class/object data
293             ##########################################################################
294             sub _init {
295 1     1   2 my $obj = shift;
296              
297 1 50       5 if(ref $obj) {
298 0         0 $obj->{"min_werds"} = 5;
299 0         0 $obj->{"max_werds"} = 9;
300              
301 0         0 $obj->{"min_syllables"} = 3;
302 0         0 $obj->{"max_syllables"} = 7;
303             }
304             else {
305 1         4 $self->{"min_werds"} = 5;
306 1         2 $self->{"max_werds"} = 9;
307              
308 1         2 $self->{"min_syllables"} = 3;
309 1         3 $self->{"max_syllables"} = 7;
310             }
311             }
312              
313             # Call the init function at the time we load the module to initialize the vars
314             # for class methods
315             _init();
316              
317             ##########################################################################
318             # Called from _make_werd to make sure a grammar is loaded
319             ##########################################################################
320             sub _check_grammar {
321 8     8   14 my $obj = shift;
322              
323 8 100 66     54 if(!$obj->{"grammar"} or !$obj->{"index"}) {
324 1         3 bless $obj; # a hack to get set_language to work
325 1         5 $obj->set_language("English");
326             }
327             }
328              
329              
330             ##########################################################################
331             # Internal method to make a single werd
332             ##########################################################################
333             sub _make_werd {
334 8     8   19 my ($obj, $target, $i);
335              
336 8 50       19 if(scalar(@_) == 1) {
337 8         15 $obj = shift;
338             }
339 8 50       26 if(ref $obj) { $target = $obj; }
  8         11  
340 0         0 else { $target = $self; }
341              
342 8         22 _check_grammar($target);
343              
344 8         16 my $syl = "_BEGIN_";
345 8         12 my $werd = "";
346 8         11 my $which;
347 8         12 my $sylcount = 0;
348              
349             # Full random mode
350 8 50       23 if($target->{"unlinked"}) {
351 0         0 my $syl_num = int(rand() * ($target->{"max_syllables"}
352             - $target->{"min_syllables"} + 1)) + $target->{"min_syllables"};
353              
354 0         0 for(my $i = 0; $i < $syl_num; $i++) {
355              
356 0   0     0 do {
357 0         0 $which = int(rand() * scalar(@{$target->{"grammar"}}));
  0         0  
358             } while(($target->{"grammar"}[$which][0] eq "_BEGIN_") or
359             ($target->{"grammar"}[$which][0] eq "_END_"));
360              
361 0         0 $werd .= $target->{"grammar"}[$which][0];
362             }
363 0         0 return($werd);
364             }
365             # Regular linked mode
366             else {
367 8         22 while($syl ne "_END_") {
368            
369             # End the word on time if we have a hard max set
370 45 50 33     121 if(($target->{"hard_syllable_max"}) and
371             ($sylcount >= $target->{"hard_syllable_max"})) {
372 0         0 last;
373             }
374            
375 45         49 $which = -1;
376 45 100       89 if($syl ne "_BEGIN_") { $werd .= $syl; }
  37         47  
377 45         112 my $offset = $target->{index}{$syl};
378 45 50       124 my $count = scalar(@{$target->{"grammar"}[$offset][1]})
  45         82  
379             if $target->{"grammar"}[$offset][1];
380 45 100       113 if($sylcount >= ($target->{max_syllables} - 1)) {
381             # Try to choose an ending
382 4         7 $which = -1;
383 4         12 for($i = 0; $i < $count; $i++) {
384 275 100       898 if($target->{"grammar"}[$offset][1][$i][0] eq "_END_") {
385 4         6 $which = $i;
386 4         4 last;
387             }
388             }
389             }
390 45 100       87 if($which < 0) {
391 41         52 my ($freq_total, $freq);
392            
393 41         48 foreach $freq (@{$target->{"grammar"}[$offset][2]}) {
  41         112  
394 18253         19600 $freq_total+= $freq;
395             }
396              
397 41   66     62 do {
      100        
398 42         43 my ($freq_sum, $i, $which_freq);
399            
400 42         92 $which_freq = int(rand() * $freq_total + 1);
401 42         70 for($i = 0; $i < scalar(@{$target->{"grammar"}[$offset][2]}); $i++) {
  8590         22175  
402 8590         12009 $freq_sum += $target->{"grammar"}[$offset][2][$i];
403 8590 100       17877 if($freq_sum >= $which_freq) {
404 42         48 $which = $i;
405 42         249 last;
406             }
407             }
408             } while(($target->{"grammar"}[$offset][1][$which][0] eq "_END_") and
409             ($count > 1) and ($sylcount < $target->{"min_syllables"}));
410             }
411 45         90 $syl = $target->{"grammar"}[$offset][1][$which][0];
412 45         97 $sylcount++;
413             }
414             }
415              
416 8         52 return($werd);
417             }
418              
419              
420             ##########################################################################
421             # Internal method to make a line of werds
422             ##########################################################################
423             sub _make_line {
424 1     1   2 my ($obj, $target);
425              
426 1 50       4 if(scalar(@_) == 2) {
427 1         2 $obj = shift;
428             }
429 1 50       4 if(ref $obj) { $target = $obj; }
  1         3  
430 0         0 else { $target = $self; }
431              
432 1         2 my $ending = shift;
433 1         2 my ($line, $num_werds, $werd_counter);
434              
435 1         5 $num_werds = int(rand() * ($target->{"max_werds"}
436             - $target->{"min_werds"} + 1) + $target->{"min_werds"});
437              
438 1         5 for($werd_counter = 0; $werd_counter < $num_werds; $werd_counter++) {
439 8         28 $line .= " " . _make_werd($target);
440             }
441              
442 1         9 $line =~ s/^.(.)/uc($1)/e;
  1         8  
443 1         4 $line .= $ending;
444              
445 1 50       8 if($target->{"end_with_newline"}) {
446 0         0 $line .= "\n";
447             }
448              
449 1         6 return $line;
450             }
451              
452              
453             ###########################################################
454             ### GRAMMAR FUNCTIONS ###
455             ###########################################################
456              
457             ##########################################################################
458             # Load the syllable file
459             ##########################################################################
460             sub _load_syllables {
461 0     0     my $obj = shift;
462 0           my $indexed_syllables;
463              
464             my $target;
465 0 0         if(ref $obj) { $target = $obj; }
  0            
466 0           else { $target = $self; }
467              
468 0           (my $dir = $INC{'Silly/Werder.pm'}) =~ s/\.pm//;
469 0           $dir = catdir($dir, 'data');
470 0           my $syllable_file = catfile($dir, 'syllables');
471              
472             # Load the syllable list
473 0 0         open SYLS, $syllable_file or return(-1);
474 0           chomp(my @syllables = );
475 0           close SYLS;
476              
477             # Sort the list, but sorting longer words higher
478 0 0         @syllables=sort { my $min=(length($a) < length($b)) ? length($a) : length($b);
  0            
479 0 0         ( substr(lc($a),0,$min) cmp substr(lc($b),0,$min) ||
480             length($b) <=> length($a) )
481             } @syllables;
482              
483             # Remove duplicates (important for recursive parse)
484 0           for(my $i = 1; $i < scalar(@syllables); $i++) {
485 0 0         if(lc($syllables[$i]) eq lc($syllables[$i - 1])) {
486 0           @syllables = splice(@syllables, $i, 1);
487 0           $i--;
488             }
489             }
490              
491 0           my $syl;
492 0           foreach $syl (@syllables) {
493 0           $syl =~ /^((.).?)/;
494 0           my $first = lc($2);
495 0           my $firsttwo = lc($1);
496 0 0         if($first eq $firsttwo) { $firsttwo = "_"; }
  0            
497 0           push @{$indexed_syllables->{$first}{$firsttwo}}, $syl;
  0            
498             }
499              
500 0           $target->{"syllables"} = $indexed_syllables;
501              
502 0           return(0);
503             }
504              
505             ##########################################################################
506             # Dump the syllables to a user named file
507             ##########################################################################
508             sub dump_syllables($) {
509 0     0 1   my $obj = shift;
510 0           my (%syls, $syl, @syl_sort);
511              
512 0           my $target;
513 0 0         if(ref $obj) { $target = $obj; }
  0            
514 0           else { $target = $self; }
515              
516 0 0         if(!$target->{"syllables"}) {
517 0 0         if(_load_syllables($target) < 0) { return -1; }
  0            
518             }
519              
520 0           my $syl_out_file = shift;
521 0 0         open SYL_OUT, ">$syl_out_file" or return -1;
522              
523             # Un-hash the syllables into one big hash to make sure there's no dups
524 0           foreach my $first (keys %{$target->{"syllables"}}) {
  0            
525 0           foreach $syl (@{$target->{"syllables"}{$first}{_}}) {
  0            
526 0           $syls{$syl} = 1;
527             }
528 0           foreach my $firsttwo (keys %{$target->{"syllables"}{$first}}) {
  0            
529 0           foreach $syl (@{$target->{"syllables"}{$first}{$firsttwo}}) {
  0            
530 0           $syls{$syl} = 1;
531             }
532             }
533             }
534              
535             # Sort the list, but sorting longer words higher
536 0 0         @syl_sort=sort { my $min=(length($a) < length($b)) ? length($a) : length($b);
  0            
537 0 0         ( substr($a,0,$min) cmp substr($b,0,$min) ||
538             length($b) <=> length($a) )
539             } keys(%syls);
540              
541              
542 0           foreach $syl(@syl_sort) {
543 0           print SYL_OUT "$syl\n";
544             }
545              
546 0           close SYL_OUT;
547              
548 0           return 0;
549             }
550              
551             ##########################################################################
552             # Parse a word and pass back the syllable pairs and number of word variants
553             ##########################################################################
554             sub _parse_werd($$$;$$$);
555             # Needs a prototype because it's recursive and Perl cries otherwise
556             sub _parse_werd($$$;$$$) {
557 0     0     my ($target, $werd, $werd_account, $werd_parts, $variations, $start_at) = @_;
558 0           my ($syl, $ready, $next_syl);
559              
560             # Make sure ref contains something;
561 0           ${$variations} += 0;
  0            
562              
563 0 0         if($werd eq "") {
564 0           my $first = $werd_parts->[0];
565 0           my $last = $werd_parts->[$#{$werd_parts}];
  0            
566 0           my $i;
567              
568 0           $werd_account->{"_BEGIN_"}{$first} = 1;
569 0           $werd_account->{$last}{"_END_"} = 1;
570              
571 0           for($i = 1; $i <= $#{$werd_parts}; $i++) {
  0            
572 0           $werd_account->{$werd_parts->[$i-1]}{$werd_parts->[$i]} = 1;
573             }
574              
575 0           ${$variations}++;
  0            
576              
577 0           undef $werd_parts;
578             }
579              
580 0           $werd =~ /^((.).?)/;
581 0           my $first = lc($2);
582 0           my $firsttwo = lc($1);
583             # gotta remember to check for syls that are just 1 in length
584 0           foreach $syl (@{$target->{"syllables"}{$first}{_}},
  0            
  0            
585             @{$target->{"syllables"}{$first}{$firsttwo}}) {
586 0           $next_syl = 0;
587 0 0 0       if($start_at && !$ready) { $next_syl = 1; }
  0            
588 0 0 0       if(($syl eq $start_at) || !$start_at) { $ready = 1; }
  0            
589 0 0         next if $next_syl;
590              
591              
592 0 0         if($werd =~ /^$syl(.*)$/si) {
593 0           push @{$werd_parts}, $syl;
  0            
594 0           _parse_werd($target, $1, $werd_account, $werd_parts, $variations);
595 0           return(${$variations});
  0            
596             }
597             }
598              
599              
600 0 0 0       if($werd_parts and scalar(@{$werd_parts})) {
  0            
601 0           my $oldsyl = pop @{$werd_parts};
  0            
602 0           _parse_werd($target, $oldsyl . $werd, $werd_account,
603             $werd_parts, $variations, $oldsyl);
604 0           return(${$variations});
  0            
605             }
606             else {
607 0           return(${$variations});
  0            
608             }
609             }
610              
611             ##########################################################################
612             # Build a grammar from a passed in block of text
613             ##########################################################################
614             sub build_grammar($;$$$) {
615              
616 0     0 1   require POSIX;
617 0           import POSIX (qw/locale_h/);
618 0           require locale;
619              
620 0           my $charset = "A-Za-z\xa0-\xbf\xc0-\xd6\xda-\xdd\xdf-\xf6\xf9-\xfd\xff'\\-";
621 0           my (%account, $grammar, $index, $couldnts);
622              
623 0           my $obj = shift;
624              
625 0           my $target;
626 0 0         if(ref $obj) { $target = $obj; }
  0            
627 0           else { $target = $self; }
628              
629 0           my ($text, $appears_threshold, $follower_threshold, $locale) = @_;
630              
631 0 0         if($locale) {
632 0           my $ret = setlocale("LC_CTYPE", $locale);
633 0 0         if(!defined($ret)) {
634 0           print STDERR "Could not load locale $locale: $!\n";
635             }
636             }
637              
638 0 0         if(_load_syllables($target) < 0) { return; }
  0            
639              
640 0           while($text =~ /[^$charset]*([$charset]+)[^$charset]*/sig) {
641 0           my $werd = $1;
642 0           $werd =~ s/^['\-]*//;
643 0           $werd =~ s/['\-]*$//;
644 0 0         if($werd eq "") { next; }
  0            
645              
646 0           my $werd_account = {};
647 0           my $variations = _parse_werd($target, $werd, $werd_account);
648 0 0         if($variations == 0) {
649 0           push @{$couldnts}, $werd;
  0            
650             }
651             else {
652 0           my $syllable;
653 0           foreach $syllable (keys %{$werd_account}) {
  0            
654 0           my $follower;
655 0           foreach $follower (keys %{$werd_account->{$syllable}}) {
  0            
656 0           $account{$syllable}{$follower}++;
657             }
658             }
659             }
660             }
661              
662 0           $grammar->[0][0] = "_BEGIN_";
663 0           $index->{"_BEGIN_"} = 0;
664              
665             # Go through list and remove links that appear less than appears_threshold
666 0           my $syllable;
667 0           foreach $syllable (keys %account) {
668 0           my $follower;
669 0           foreach $follower (keys %{$account{$syllable}}) {
  0            
670 0 0         if($account{$syllable}{$follower} < $appears_threshold) {
671 0           delete $account{$syllable}{$follower};
672             }
673             }
674             }
675              
676 0           my $syls_removed = 1;
677 0           while($syls_removed) {
678 0           $syls_removed = 0;
679              
680 0           foreach $syllable (keys %account) {
681              
682 0           my $explicit_keep = 0;
683 0           my $linkcount = scalar(keys %{$account{$syllable}});
  0            
684              
685             # check for _END_ first, so good endings don't get removed
686 0           my $follower;
687 0           foreach $follower (keys %{$account{$syllable}}) {
  0            
688 0 0         if($follower eq "_END_") { $explicit_keep = 1; }
  0            
689             }
690 0 0         if($explicit_keep) { next; }
  0            
691              
692 0 0         if($linkcount < $follower_threshold) {
693              
694 0           my $prior;
695             # sub loop to remove links to this node
696 0           foreach $prior (keys %account) {
697 0 0         if($account{$prior}{$syllable}) {
698 0           delete $account{$prior}{$syllable};
699             }
700             }
701              
702 0           delete $account{$syllable};
703 0           $syls_removed = 1;
704             }
705             }
706             }
707              
708 0           my $syllable_count;
709 0           foreach $syllable (keys %account) {
710              
711 0           my $offset = $index->{$syllable};
712 0 0         if(!defined($index->{$syllable})) {
713 0           $syllable_count++;
714 0           $grammar->[$syllable_count][0] = $syllable;
715 0           $offset = $syllable_count;
716 0           $index->{$syllable} = $offset;
717             }
718              
719 0           my $follower;
720 0           foreach $follower (keys %{$account{$syllable}}) {
  0            
721              
722 0           my $follower_offset = $index->{$follower};
723 0 0         if(!$follower_offset) {
724 0           $syllable_count++;
725 0           $grammar->[$syllable_count][0] = $follower;
726 0           $follower_offset = $syllable_count;
727 0           $index->{$follower} = $follower_offset;
728             }
729              
730 0           push @{$grammar->[$offset][1]}, \@{$grammar->[$follower_offset]};
  0            
  0            
731 0           push @{$grammar->[$offset][2]}, $account{$syllable}{$follower};
  0            
732             }
733             }
734              
735              
736 0           $target->{"grammar"} = $grammar;
737 0           $target->{"index"} = $index;
738              
739 0           return($couldnts);
740             }
741              
742              
743             ##########################################################################
744             # Dump the current grammar to a user named file
745             ##########################################################################
746             sub dump_grammar($) {
747 0     0 1   my $obj = shift;
748            
749 0           my $target;
750 0 0         if(ref $obj) { $target = $obj; }
  0            
751 0           else { $target = $self; }
752            
753 0 0         if(!$target->{"grammar"}) {
754 0           bless $target; # a hack to get set_language to work
755 0           $target->set_language("English");
756             }
757              
758 0           my $grammar_out_file = shift;
759            
760 0 0         if(!Storable::nstore($target->{"grammar"}, $grammar_out_file)) {
761 0           return -1;
762             }
763              
764 0           return 0;
765             }
766              
767             ##########################################################################
768             # Load in a user-specified grammar from an external file
769             ##########################################################################
770             sub load_grammar_file($) {
771 0     0 1   my $obj = shift;
772 0           my $index_ref;
773              
774             my $target;
775 0 0         if(ref $obj) { $target = $obj; }
  0            
776 0           else { $target = $self; }
777            
778 0           my $grammar_in_file = shift;
779            
780 0           my $grammar_ref = retrieve($grammar_in_file);
781 0 0         if(!defined($grammar_ref)) { return -1; }
  0            
782              
783 0           my $count = scalar(@{$grammar_ref});
  0            
784              
785 0           for(my $i = 0; $i < $count; $i++) {
786 0           $index_ref->{$grammar_ref->[$i][0]} = $i;
787             }
788              
789 0           $target->{"grammar"} = $grammar_ref;
790 0           $target->{"index"} = $index_ref;
791              
792 0           return 0;
793             }
794              
795             ##########################################################################
796             # Load in a user-specified grammar from an external file
797             ##########################################################################
798             sub load_syllable_file($) {
799 0     0 1   my $obj = shift;
800 0           my $indexed_syllables;
801            
802             my $target;
803 0 0         if(ref $obj) { $target = $obj; }
  0            
804 0           else { $target = $self; }
805            
806 0           my $syllable_file = shift;
807              
808             # Load the syllable list
809 0 0         open SYLS, $syllable_file or return -1;
810 0           chomp(my @syllables = );
811 0           close SYLS;
812              
813             # Sort the list, but sorting longer words higher
814 0 0         @syllables=sort { my $min=(length($a) < length($b)) ? length($a) : length($b);
  0            
815 0 0         ( substr(lc($a),0,$min) cmp substr(lc($b),0,$min) ||
816             length($b) <=> length($a) )
817             } @syllables;
818              
819             # Remove duplicates (important for recursive parse)
820 0           for(my $i = 1; $i < scalar(@syllables); $i++) {
821 0 0         if(lc($syllables[$i]) eq lc($syllables[$i - 1])) {
822 0           @syllables = splice(@syllables, $i, 1);
823 0           $i--;
824             }
825             }
826              
827 0           my $syl;
828 0           foreach $syl (@syllables) {
829 0           $syl =~ /^((.).?)/;
830 0           my $first = lc($2);
831 0           my $firsttwo = lc($1);
832 0 0         if($first eq $firsttwo) { $firsttwo = "_"; }
  0            
833 0           push @{$indexed_syllables->{$first}{$firsttwo}}, $syl;
  0            
834             }
835              
836 0           $target->{"syllables"} = $indexed_syllables;
837              
838 0           return 0;
839             }
840              
841             1;
842              
843             __END__