File Coverage

blib/lib/Test/Pcuke/Gherkin/Parser.pm
Criterion Covered Total %
statement 123 123 100.0
branch 15 16 93.7
condition 5 6 83.3
subroutine 28 28 100.0
pod 1 2 50.0
total 172 175 98.2


line stmt bran cond sub pod time code
1             package Test::Pcuke::Gherkin::Parser;
2              
3 2     2   231643 use warnings;
  2         4  
  2         71  
4 2     2   11 use strict;
  2         5  
  2         61  
5              
6 2     2   11 use Carp;
  2         9  
  2         130  
7              
8 2     2   1378 use Test::Pcuke::Gherkin::Executor;
  2         11  
  2         47  
9 2     2   1210 use Test::Pcuke::Gherkin::Node::Feature;
  2         8  
  2         61  
10 2     2   1517 use Test::Pcuke::Gherkin::Node::Background;
  2         6  
  2         87  
11 2     2   1319 use Test::Pcuke::Gherkin::Node::Scenario;
  2         5  
  2         54  
12 2     2   1247 use Test::Pcuke::Gherkin::Node::Outline;
  2         5  
  2         53  
13 2     2   1447 use Test::Pcuke::Gherkin::Node::Scenarios;
  2         6  
  2         56  
14 2     2   1584 use Test::Pcuke::Gherkin::Node::Table;
  2         6  
  2         64  
15 2     2   15 use Test::Pcuke::Gherkin::Node::Table::Row;
  2         5  
  2         37  
16 2     2   1860 use Test::Pcuke::Gherkin::Node::Step;
  2         6  
  2         4196  
17              
18             =head1 NAME
19              
20             Test::Pcuke::Gherkin::Parser - parses tokens
21              
22             =head1 SYNOPSIS
23              
24             TODO SYNOPSIS
25              
26             use Test::Pcuke::Gherkin::Parser;
27              
28             my $tree = Test::Pcuke::Gherkin::Parser->parse( $tokens );
29             ...
30              
31             =head1 METHODS
32              
33             =head2 parse
34              
35             =cut
36              
37             my $processors = {
38             root => {
39             'PRAG' => \&_trash_processor,
40             'TAG' => \&_tag_processor,
41             'FEAT' => \&_feature_processor,
42             },
43             feature => {
44             'NARR' => \&_narrative_processor,
45             'BGR' => \&_background_processor,
46             'SCEN' => \&_scenario_processor,
47             'OUTL' => \&_outline_processor,
48             },
49             background => {
50             'STEP' => \&_step_processor,
51             },
52             scenario => {
53             'STEP' => \&_step_processor,
54             },
55             outline => {
56             'STEP' => \&_step_processor,
57             'SCENS' => \&_examples_processor,
58             },
59             example => {
60             'TROW' => \&_table_processor,
61             },
62             step => {
63             'TEXT' => \&_text_processor,
64             'TROW' => \&_table_processor,
65             },
66             };
67              
68             sub new {
69 3     3 0 10545 my ($self, $args) = @_;
70 3         6 my $instance;
71            
72             # TODO executor->can(execute) ?
73 3         10 $instance->{executor} = $args->{executor};
74            
75 3         18 bless $instance, $self;
76             }
77              
78             sub _get_executor {
79 24     24   31 my ($self) = @_;
80            
81 24 100 66     165 return $self->{executor}
82             if ref $self && $self->{executor};
83            
84 12         51 return Test::Pcuke::Gherkin::Executor->new();
85            
86             }
87              
88             sub parse {
89 2     2 1 15 my ($self, $tokens, $tree) = @_;
90 2         4 my $level = 'root';
91            
92 2         11 $tree = $self->_subtree_collector($tokens, $tree, $processors->{$level});
93 2         7 $tree->{feature}->{tags} = $tree->{tags};
94 2         13 return Test::Pcuke::Gherkin::Node::Feature->new( $tree->{feature} );
95             }
96              
97              
98             ###
99             ### Processor methods
100             ###
101              
102             ## if a processor returns a list ($key, $value) then $tree->{$key} = $value
103             ## if a processir returns arrayref [$key, $value] then push @{ $tree->{$key} }, $value
104             ## if a processor returns ('_trash', '_trash') then skip
105              
106             sub _feature_processor {
107 2     2   3 my ($self, $tokens) = @_;
108 2         4 my $token = shift @$tokens;
109 2         7 my $tree = { title => $token->[1] };
110 2         16 return ( 'feature', $self->_subtree_collector($tokens, $tree, $processors->{'feature'}) );
111             }
112              
113             sub _tag_processor {
114 2     2   4 my ($self, $tokens) = @_;
115            
116 2         8 my @tags = map { $_->[1] } $self->_aggregate_tokens('TAG', $tokens);
  4         12  
117            
118 2         10 return ('tags', \@tags );
119             }
120              
121             sub _narrative_processor {
122 2     2   9 my ($self, $tokens) = @_;
123            
124 2         7 my @lines = map { $_->[1] } $self->_aggregate_tokens('NARR', $tokens);
  4         12  
125            
126 2         11 return ('narrative', join ( "\n", @lines ) );
127             }
128              
129             sub _text_processor {
130 4     4   7 my ($self, $tokens) = @_;
131            
132 4         8 my @lines = map { $_->[1] } $self->_aggregate_tokens('TEXT', $tokens);
  12         25  
133            
134 4         20 return ('text', join ( "\n", @lines ) );
135             }
136              
137             sub _background_processor {
138 2     2   5 my ($self, $tokens) = @_;
139            
140 2         5 my $token = shift @$tokens;
141 2         5 my $tree = { title => $token->[1] };
142            
143 2         9 $tree = $self->_subtree_collector($tokens, $tree, $processors->{'background'});
144            
145 2         13 return ('background', Test::Pcuke::Gherkin::Node::Background->new( $tree ) );
146             }
147              
148             sub _scenario_processor {
149 4     4   7 my ($self, $tokens) = @_;
150 4         7 my $token = shift @$tokens;
151 4         12 my $tree = { title => $token->[1] };
152            
153 4         13 $tree = $self->_subtree_collector($tokens, $tree, $processors->{'scenario'} );
154 4         11 $tree->{executor} = $self->_get_executor;
155            
156 4         22 return [ 'scenarios', Test::Pcuke::Gherkin::Node::Scenario->new( $tree ) ];
157             }
158              
159             sub _outline_processor {
160 2     2   5 my ($self, $tokens) = @_;
161 2         4 my $token = shift @$tokens;
162 2         7 my $tree = { title => $token->[1] };
163            
164 2         6 $tree = $self->_subtree_collector($tokens, $tree, $processors->{'outline'});
165 2         22 return [ 'scenarios', Test::Pcuke::Gherkin::Node::Outline->new( $tree ) ];
166             }
167              
168             sub _examples_processor {
169 2     2   4 my ($self, $tokens) = @_;
170 2         4 my $token = shift @$tokens;
171 2         6 my $tree = { title => $token->[1] };
172            
173 2         6 $tree = $self->_subtree_collector($tokens, $tree, $processors->{'example'} );
174            
175 2         13 return ['examples', Test::Pcuke::Gherkin::Node::Scenarios->new( $tree ) ];
176             }
177            
178             sub _step_processor {
179 16     16   22 my ($self, $tokens) = @_;
180 16         23 my $token = shift @$tokens;
181 16         62 my $tree = { type => $token->[1], title => $token->[2] };
182 16         45 $tree = $self->_subtree_collector($tokens, $tree, $processors->{'step'});
183            
184 16         37 $tree->{executor} = $self->_get_executor;
185            
186 16         60 return [ 'steps', Test::Pcuke::Gherkin::Node::Step->new( $tree ) ];
187             }
188              
189             sub _table_processor {
190 4     4   7 my ($self, $tokens) = @_;
191 4         11 my @rows = map { shift @$_; $_ } $self->_aggregate_tokens('TROW', $tokens);
  20         26  
  20         31  
192            
193 4         20 my $tree = {
194             headings => shift @rows,
195             rows => [@rows],
196             executor => $self->_get_executor
197             };
198            
199 4         21 return ('table', Test::Pcuke::Gherkin::Node::Table->new( $tree ) );
200             }
201              
202             sub _trash_processor {
203 2     2   49 my ($self, $tokens) = @_;
204 2         6 shift @$tokens;
205 2         9 return ('_trash', '_trash');
206             }
207              
208              
209              
210             ### $tokens
211             ### $tree hashref with the initial tree
212             ### $processors hashref, keys are token labels, values are coderefs
213             ### upgrades $tree and returns it
214             sub _subtree_collector {
215 30     30   53 my ($self, $tokens, $tree, $processors) = @_;
216            
217 30         64 while ( @$tokens ) {
218 66 100       152 if ( !$tokens->[0]->[0] ) {
219 2         3 shift @$tokens; # --- what a trash?
220 2         6 next;
221             }
222 64         61 my ($key, $value);
223 64         143 for ( keys %$processors ) {
224 106 100       279 next unless $tokens->[0]->[0] eq $_;
225 42         111 ($key, $value) = $processors->{$_}->($self, $tokens);
226 42         8083 last;
227             }
228 64 100       159 last if !$key;
229 42 100       125 next if $key eq '_trash';
230 40 100       95 push @{ $tree->{ $key->[0] } }, $key->[1] if ref $key eq 'ARRAY';
  24         66  
231 40 100       201 $tree->{$key} = $value if $value;
232             }
233            
234 30         64 return $tree;
235             }
236              
237              
238             ###
239             ### aggregates tokens with the label
240             ###
241             sub _aggregate_tokens {
242 12     12   19 my ($self, $label, $tokens) = @_;
243 12         13 my @collection;
244            
245 12 50       26 confess "tokens are undefined, check the arguments!" unless $tokens;
246            
247 12   100     64 while ( $tokens->[0]->[0] && $tokens->[0]->[0] eq $label ) {
248 40         51 my $token = shift @$tokens;
249 40         173 push @collection, $token;
250             }
251            
252 12         34 return @collection;
253             }
254              
255              
256             =head1 AUTHOR
257              
258             Andrei V. Toutoukine, C<< >>
259              
260             =head1 BUGS
261              
262             Please report any bugs or feature requests to C, or through
263             the web interface at L. I will be notified, and then you'll
264             automatically be notified of progress on your bug as I make changes.
265              
266              
267              
268              
269             =head1 SUPPORT
270              
271             You can find documentation for this module with the perldoc command.
272              
273             perldoc Test::Pcuke::Gherkin::Parser
274              
275              
276             You can also look for information at:
277              
278             =over 4
279              
280             =item * RT: CPAN's request tracker
281              
282             L
283              
284             =item * AnnoCPAN: Annotated CPAN documentation
285              
286             L
287              
288             =item * CPAN Ratings
289              
290             L
291              
292             =item * Search CPAN
293              
294             L
295              
296             =back
297              
298              
299             =head1 ACKNOWLEDGEMENTS
300              
301              
302             =head1 LICENSE AND COPYRIGHT
303              
304             Copyright 2011 Andrei V. Toutoukine.
305              
306             This program is released under the following license: artistic
307              
308              
309             =cut
310              
311             1; # End of Test::Pcuke::Gherkin::Parser