File Coverage

blib/lib/Text/Spintax.pm
Criterion Covered Total %
statement 73 80 91.2
branch 10 14 71.4
condition n/a
subroutine 15 17 88.2
pod 2 11 18.1
total 100 122 81.9


line stmt bran cond sub pod time code
1             package Text::Spintax;
2              
3 2     2   61028 use 5.006;
  2         9  
  2         127  
4 2     2   13 use strict;
  2         150  
  2         117  
5 2     2   14 use warnings FATAL => 'all';
  2         11  
  2         299  
6 2     2   1167 use Text::Spintax::grammar;
  2         7  
  2         229  
7 2     2   1035 use Text::Spintax::RenderNode;
  2         7  
  2         81  
8 2     2   1948 use Parse::Lex;
  2         70882  
  2         3092  
9              
10             =head1 NAME
11              
12             Text::Spintax - A parser and renderer for spintax formatted text.
13              
14             =head1 VERSION
15              
16             Version 0.05
17              
18             =cut
19              
20             our $VERSION = '0.05';
21              
22              
23             =head1 SYNOPSIS
24              
25             use Text::Spintax;
26              
27             my $node = Text::Spintax->new->parse("This {is|was|will be} some {varied|random} text");
28             $node->equal_path_weight;
29             my $text = $node->render;
30              
31             =head1 DESCRIPTION
32              
33             Text::Spintax implements a parser and renderer for spintax formatted text. Spintax is a commonly used method for
34             generating "randomized" text. For example,
35              
36             This {is|was} a test
37              
38             would be rendered as
39              
40             * This is a test
41             * This was a test
42              
43             Spintax can be nested indefinitely, for example:
44              
45             This is nested {{very|quite} deeply|deep}.
46              
47             would be rendered as
48              
49             * This is nested very deeply.
50             * This is nested quite deeply.
51             * This is nested deep.
52              
53             The number of possible combinations is easy to calculate, but the proportion of renders has two options. The initial Text::Spintax::RenderNode has weight 1 for all nodes, meaning that for the previous example the probabilities of each render would be
54              
55             25% This is nested very deeply.
56             25% This is nested quite deeply.
57             50% This is nested deep.
58              
59             If you want every possible outcome to be equally likely, then call equal_path_weight on the Text::Spintax::RenderNode object and you'll get this outcome:
60              
61             33% This is nested very deeply.
62             33% This is nested quite deeply.
63             33% This is nested deep.
64              
65             =cut
66              
67 1 50   1 0 9 sub root { scalar @_ == 2 and $_[0]->{root} = $_[1]; return $_[0]->{root} }
  1         3  
68 19 100   19 0 55 sub curr { scalar @_ == 2 and $_[0]->{curr} = $_[1]; return $_[0]->{curr} }
  19         101  
69              
70             =head1 SUBROUTINES/METHODS
71              
72             =head2 new
73              
74             Returns a Text::Spintax object
75              
76             =cut
77              
78             sub new {
79 1     1 1 321 my $class = shift;
80 1         5 my $self = bless {}, $class;
81 1         7 return $self;
82             }
83              
84             =head2 parse
85              
86             Parses the spintax and returns a Text::Spintax::Node that is suitable for rendering.
87              
88             =cut
89              
90             our $lexer;
91              
92             sub parse {
93 1     1 1 4 my $self = shift;
94 1         4 my ($text) = @_;
95 1         8 my @lex = qw(
96             OBRACE {
97             EBRACE }
98             PIPE \|
99             TEXT [^{}|]+
100             );
101 1 50       6 if (not defined $lexer) {
102 1         25 $lexer = Parse::Lex->new(@lex);
103 1         813 $lexer->skip('');
104             }
105 1         37 $lexer->from($text);
106              
107 1         6970 my $parser = new Text::Spintax::grammar();
108 1         9 $parser->YYData->{lexer} = $lexer;
109 1         11 my $root = Text::Spintax::RenderNode->new(type => "sequence", weight => 1);
110 1         5 $self->root($root);
111 1         5 $self->curr($root);
112              
113 1         5 $parser->YYData->{tree} = $self;
114 1         12 my $value = $parser->YYParse(yylex => \&lexer, yyerror =>\&error);
115 1         53 return $root;
116             }
117              
118             sub last_child {
119 1     1 0 2 my $self = shift;
120 1         5 return $self->curr->{children}[-1];
121             }
122              
123             sub obrace {
124 1     1 0 3 my $self = shift;
125 1         12 my $child = Text::Spintax::RenderNode->new(parent => $self->curr, weight => 1);
126 1         2 push @{$self->curr->{children}}, $child;
  1         4  
127 1         9 $self->curr($child);
128             }
129              
130             sub ebrace {
131 1     1 0 3 my $self = shift;
132 1         4 my @groups = ([]);
133 1         2 foreach my $child (@{$self->curr->children}) {
  1         4  
134 3 100       11 if ($child->type eq "pipe") {
135 1         3 push @groups, [];
136             }
137             else {
138 2         3 push @{$groups[-1]}, $child;
  2         9  
139             }
140             }
141 1         4 my @children;
142 1         3 foreach my $group (@groups) {
143 2 50       7 if (scalar @$group == 1) {
144 2         6 push @children, $group->[0];
145             }
146             else {
147 0         0 push @children, Text::Spintax::RenderNode->new(parent => $self->curr, children => $group, type => "sequence", weight => 1);
148             }
149             }
150 1         12 $self->curr->children(\@children);
151 1         4 $self->curr($self->curr->parent);
152             }
153             sub add_child {
154 5     5 0 8 my $self = shift;
155 5         12 my ($type,$text,$offset) = @_;
156 5         14 my $child = Text::Spintax::RenderNode->new(parent => $self->curr, type => $type, text => $text, offset => $offset, weight => 1);
157 5         7 push @{$self->curr->{children}}, $child;
  5         14  
158             }
159              
160             sub type {
161 0     0 0 0 my $self = shift;
162 0         0 my ($type) = @_;
163 0         0 $self->curr->{type} = $type;
164             }
165              
166             sub lexer {
167 8     8 0 14 my $parser = shift;
168 8         26 my $lexer = $parser->YYData->{lexer};
169 8         25 my $token = $parser->YYData->{lexer}->next;
170 8 50       802 if (not defined $token) {
171 0         0 return ('', undef);
172             }
173             else {
174 8         38 $parser->YYData->{DATA} = [$token->name, $token->text, $lexer->offset];
175             }
176 8 100       49 return ('', undef) if $lexer->eoi;
177 7         57 return ($token->name, $token->text);
178             }
179              
180             sub error {
181 0     0 0   print STDERR "error: ",Dump(\@_);
182 0           return;
183             }
184              
185             =head1 AUTHOR
186              
187             Dale Evans, C<< >>
188              
189             =head1 BUGS
190              
191             Please report any bugs or feature requests to C, or through
192             the web interface at L. I will be notified, and then you'll
193             automatically be notified of progress on your bug as I make changes.
194              
195              
196              
197              
198             =head1 SUPPORT
199              
200             You can find documentation for this module with the perldoc command.
201              
202             perldoc Text::Spintax
203              
204              
205             You can also look for information at:
206              
207             =over 4
208              
209             =item * RT: CPAN's request tracker (report bugs here)
210              
211             L
212              
213             =item * AnnoCPAN: Annotated CPAN documentation
214              
215             L
216              
217             =item * CPAN Ratings
218              
219             L
220              
221             =item * Search CPAN
222              
223             L
224              
225             =back
226              
227              
228             =head1 ACKNOWLEDGEMENTS
229              
230             Francois Desarmenien for writing Parse::YAPP
231             Philippe Verdret for writing Parse::Lex
232              
233             =head1 LICENSE AND COPYRIGHT
234              
235             Copyright 2013 Dale Evans.
236              
237             This program is free software; you can redistribute it and/or modify it
238             under the terms of the the Artistic License (2.0). You may obtain a
239             copy of the full license at:
240              
241             L
242              
243             Any use, modification, and distribution of the Standard or Modified
244             Versions is governed by this Artistic License. By using, modifying or
245             distributing the Package, you accept this license. Do not use, modify,
246             or distribute the Package, if you do not accept this license.
247              
248             If your Modified Version has been derived from a Modified Version made
249             by someone other than you, you are nevertheless required to ensure that
250             your Modified Version complies with the requirements of this license.
251              
252             This license does not grant you the right to use any trademark, service
253             mark, tradename, or logo of the Copyright Holder.
254              
255             This license includes the non-exclusive, worldwide, free-of-charge
256             patent license to make, have made, use, offer to sell, sell, import and
257             otherwise transfer the Package with respect to any patent claims
258             licensable by the Copyright Holder that are necessarily infringed by the
259             Package. If you institute patent litigation (including a cross-claim or
260             counterclaim) against any party alleging that the Package constitutes
261             direct or contributory patent infringement, then this Artistic License
262             to you shall terminate on the date that such litigation is filed.
263              
264             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
265             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
266             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
267             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
268             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
269             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
270             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
271             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272              
273             The Parse::Yapp module and its related modules and shell scripts are copyright (c) 1998-2001 Francois Desarmenien, France. All rights reserved.
274              
275             You may use and distribute them under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file.
276              
277             =cut
278              
279             1; # End of Text::Spintax