File Coverage

blib/lib/ABNF/Grammar.pm
Criterion Covered Total %
statement 122 135 90.3
branch 47 80 58.7
condition 14 33 42.4
subroutine 31 32 96.8
pod n/a
total 214 280 76.4


line stmt bran cond sub pod time code
1             package ABNF::Grammar;
2              
3             =pod
4              
5             =head1 NAME
6              
7             B<ABNF-Grammar> - validator and generator for ABNF grammars.
8              
9             B<ABNF::Grammar> - class for inner representation ABNF-grammar.
10              
11             =head1 VERSION
12              
13             This document describes B<ABNF::Grammar> version 0.08
14              
15             =head1 SYNOPSIS
16              
17             use ABNF::Grammar qw(Grammar);
18              
19             use ABNF::Generator qw(asStrings);
20              
21             use ABNF::Generator::Honest qw(Honest);
22              
23             use ABNF::Generator::Liar qw(Liar);
24              
25             use ABNF::Validator qw(Validator);
26              
27             my $grammar = Grammar->new("smtp.bnf", qw(ehlo helo mail rcpt data rset vrfy noop quit data data-terminate));
28             my $valid = Validator->new($grammar);
29             my $liar = Liar->new($grammar, $valid);
30             my $honest = Honest->new($grammar, $valid);
31              
32             $valid->validate("vrfy", "string");
33              
34             my @strings = $liar->withoutArguments("vrfy");
35              
36             my $string = $liar->unExistedCommand("vrfy");
37              
38             my $string = $liar->endlessCommand("vrfy");
39              
40             my $string = $liar->generate("helo");
41              
42             my $string = $honest->generate("helo");
43              
44             =head1 DESCRIPTION
45              
46             This module parses IETF ABNF (STD 68, RFC 5234, 4234, 2234) grammars
47             via B<Parse::ABNF> and provides tools to :
48              
49             =over 4
50              
51             =item * verify validity of string
52              
53             =item * generate valid messages
54              
55             =item * generate invalid messages
56              
57             =back
58              
59             =head1 METHODS
60              
61             =cut
62              
63 4     4   117026 use 5.014;
  4         16  
  4         142  
64              
65 4     4   23 use strict;
  4         6  
  4         128  
66 4     4   18 use warnings;
  4         10  
  4         117  
67              
68 4     4   21 use Carp;
  4         9  
  4         351  
69 4     4   4152 use Readonly;
  4         12030  
  4         255  
70 4     4   4148 use Method::Signatures;
  4         547470  
  4         30  
71 4     4   3975 use Data::Dumper;
  4         19830  
  4         286  
72              
73 4     4   3536 use Parse::ABNF;
  4         2457239  
  4         1070  
74 4     4   7325 use Storable qw(dclone);
  4         18607  
  4         369  
75              
76 4     4   70 use base "Exporter";
  4         7  
  4         1236  
77             our @EXPORT_OK = qw(splitRule Grammar $BASIC_RULES);
78             our $VERSION = "0.08";
79              
80             Readonly our $BASIC_RULES => do {
81             my $res = {};
82             foreach my $rule ( @{$Parse::ABNF::CoreRules} ) {
83             die "Multiple definitions for $rule->{name}" if exists($res->{$rule->{name}});
84             $res->{$rule->{name}} = $rule;
85             }
86              
87             $res;
88             };
89              
90             =pod
91              
92             =head1 ABNF::Grammar->C<new>($fname, @commands)
93              
94             Creates a new B<ABNF::Grammar> object.
95              
96             Read ABNF rules from file with $fname.
97              
98             @commands consists of main command names for generation and validation.
99              
100             =cut
101              
102 4 50 66 4   853185 method new(Str $fname, @commands) {
  5 50   5   12  
  5         32  
  5         197  
  5         12129  
  5         37  
103              
104 5   33     32 my $class = ref($self) || $self;
105              
106 5         15 $self = {_commands => { map {$_ => 1} @commands} };
  22         58  
107              
108 5         19 bless($self, $class);
109              
110              
111 5 50       279 open(my $file, $fname)
112             or croak "Cant open $fname";
113              
114 5         339 my $content = join("", <$file>) . "\n";
115              
116 5 50       111 close($file)
117             or carp "Cant close $fname";
118              
119 5         33 $self->_init($content);
120              
121 5         21027 foreach my $command ( @commands ) {
122 22 100       513 croak "Grammar doesn't have command $command" unless exists($self->{_rules}->{$command});
123             }
124              
125 4         79 return $self;
126             }
127              
128             =pod
129              
130             =head1 ABNF::Grammar->C<fromString>($content, @commands)
131              
132             Creates a new B<ABNF::Grammar> object.
133              
134             Get ABNF rules from string $rule
135              
136             @commands consists of main command names for generation and validation.
137              
138             =cut
139              
140 4 0 0 4   13133 method fromString(Str $content, @commands) {
  0 0   0   0  
  0         0  
  0         0  
  0         0  
  0         0  
141              
142 0   0     0 my $class = ref($self) || $self;
143              
144 0         0 $self = {_commands => { map {$_ => 1} @commands} };
  0         0  
145              
146 0         0 bless($self, $class);
147              
148 0         0 $self->_init($content . "\n");
149              
150 0         0 foreach my $command ( @commands ) {
151 0 0       0 croak "Grammar doesn't have command $command" unless exists($self->{_rules}->{$command});
152             }
153              
154 0         0 return $self;
155             }
156              
157 4 50   4   7663 method _init($content) {
  5 50   5   11  
  5         25  
  5         13  
  5         23  
158              
159 5         61 my $parser = Parse::ABNF->new();
160 5 50       972741 my $rules = $parser->parse($content)
161             or croak "Bad rules";
162              
163 5         814839 foreach my $rule ( @$rules ) {
164 30 50       177 croak "Multiple definitions for $rule->{name}" if exists($self->{_rules}->{$rule->{name}});
165 30         163 $self->{_rules}->{$rule->{name}} = $rule;
166             }
167              
168             }
169              
170             =pod
171              
172             =head1 $grammar->C<rule>($name)
173              
174             Return rule form $name with name $name.
175              
176             Result structure is identical to B<Parse::ABNF> structure.
177              
178             For debug only.
179              
180             Do not modify result structure.
181              
182             =cut
183              
184 4 50 33 4   10855 method rule(Str $name) {
  158420 50   158420   199689  
  158420 50       381854  
  158420         1232346  
  158420         276178  
  158420         361515  
185 158420 100       437228 croak "Unexisted rule $name" unless exists($self->{_rules}->{$name});
186 158419         512342 $self->{_rules}->{$name};
187             }
188              
189             =pod
190              
191             =head1 $grammar->C<rules>()
192              
193             Return all rules.
194              
195             Result structures is identical to B<Parse::ABNF> structure.
196              
197             For debug only.
198              
199             Do not modify result structure.
200              
201             =cut
202              
203 4 50   4   2537 method rules() {
  4     4   504  
  4         31  
204 4         55 $self->{_rules};
205             }
206              
207             =pod
208              
209             =head1 $grammar->C<replaceRule>($rule, $value)
210              
211             Replace $rule with $value.
212              
213             For debug use only.
214              
215             dies if there is no rule like $rule.
216              
217             =cut
218              
219 4 50 33 4   11125 method replaceRule(Str $rule, $value) {
  3 50   3   1618  
  3 50       11  
  3 50       46  
  3         7  
  3         10  
  3         4  
  3         9  
220 3 100       133 croak "Unexisted rule $rule" unless exists($self->{_rules}->{$rule});
221 2 100       142 croak "new value name must be equal to rule" unless $value->{name} eq $rule;
222 1         6 $self->{_rules}->{$rule} = $value;
223             }
224              
225             =pod
226              
227             =head1 $grammar->C<replaceBasicRule>($rule, $value)
228              
229             Replace $rule with $value.
230              
231             For debug use only.
232              
233             dies if there is no rule like $rule.
234              
235             =cut
236              
237 4 50 33 4   11412 method replaceBasicRule(Str $rule, $value) {
  6 50   6   1021  
  6 50       29  
  6 50       377  
  6         18  
  6         29  
  6         13  
  6         28  
238 6 100       48 croak "Unexisted rule $rule" unless exists($BASIC_RULES->{$rule});
239 5 100       236 croak "new value name must be equal to rule" unless $value->{name} eq $rule;
240 4         15 $BASIC_RULES->{$rule} = $value;
241             }
242              
243              
244             =pod
245              
246             =head1 $grammar->C<hasCommand>($name)
247              
248             Return 1 if $name is command, 0 otherwise.
249              
250             =cut
251              
252 4 50 33 4   9101 method hasCommand(Str $name) {
  53 50   53   65  
  53 50       125  
  53         336  
  53         74  
  53         114  
253 53         1274 exists $self->{_commands}->{$name};
254             }
255              
256             =pod
257              
258             =head1 $grammar->C<commands>()
259              
260             Return all grammar commands as arrayref.
261              
262             =cut
263              
264 4 50   4   2505 method commands() {
  3     3   7  
  3         14  
265 3         23 [ keys $self->{_commands} ]
266             }
267              
268             =pod
269              
270             =head1 FUNCTIONS
271              
272             =head1 C<splitRule>($rule)
273              
274             In scalar context return prefix only, in list -- prefix and arguments rules.
275              
276             $rule is structure that returns from C<rule> and like in B<Parse::ABNF>.
277              
278             =cut
279              
280 4 50   4   7671 func splitRule($rule) {
  40 50   40   117  
  40         55  
  40         104  
281 40         71 my $value = $rule->{value};
282 40         70 my $prefix = "";
283              
284 40 100 100     312 if (
285             $value->{class} eq 'Group'
286             && $value->{value}->[0]->{class} eq 'Literal'
287             ) {
288 31         64 $prefix = $value->{value}->[0]->{value};
289 31         1158 $value = dclone($value);
290 31         56 shift($value->{value});
291 31 100 66     221 if (
292             $value->{value}->[0]->{class} eq 'Reference'
293             && $value->{value}->[0]->{name} eq 'SP'
294             ) {
295 26         34 $prefix .= "\x20";
296 26         39 shift($value->{value});
297             }
298              
299 31 100 66     296 if (
300             $value->{value}->[-1]->{class} eq 'Reference'
301             && $value->{value}->[-1]->{name} eq 'CRLF'
302             ) {
303 5         12 pop($value->{value});
304             }
305             }
306              
307 40 100       242 return wantarray ? ($prefix, $value) : $prefix;
308             }
309              
310             =pod
311              
312             =head1 C<Grammar>()
313              
314             Return __PACKAGE__ to reduce class name :3
315              
316             =cut
317              
318 4 50   4   3450 func Grammar() {
  5     5   594  
319 5         61 return __PACKAGE__;
320             }
321              
322              
323             1;
324              
325             __END__
326              
327             =pod
328              
329             =head1 DEPENDENCIES
330              
331             =over 4
332              
333             =item B<Parse::ABNF>
334              
335             =item B<Regexp::Grammars>
336              
337             =item B<Storable>
338              
339             =item B<Method::Signatures>
340              
341             =item B<Readonly>
342              
343             =item B<perl 5.014>
344              
345             =back
346              
347             =head1 BUG REPORTS
348              
349             Please report bugs in this module via <nyaapa@cpan.org>
350              
351             =head1 SEE ALSO
352              
353             =over 4
354              
355             =item * ABNF RFC
356              
357             L<http://www.ietf.org/rfc/rfc5234.txt>
358              
359             =item * Abnf parser
360              
361             L<Parse::ABNF>
362              
363             =item * Validator base
364              
365             L<Regexp::Grammars>
366              
367             =item * Cool guy from monks with idea how to validate
368              
369             L<http://www.perlmonks.org/?node_id=957506>
370              
371             =back
372              
373             =head1 AUTHOR / COPYRIGHT / LICENSE
374              
375             Copyright (c) 2013 Arseny Krasikov <nyaapa@cpan.org>.
376              
377             This module is licensed under the same terms as Perl itself.
378              
379             =cut