File Coverage

blib/lib/Text/Spintax.pm
Criterion Covered Total %
statement 69 76 90.7
branch 7 10 70.0
condition n/a
subroutine 13 15 86.6
pod 1 8 12.5
total 90 109 82.5


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