File Coverage

blib/lib/MarpaX/ESLIF/ECMA404.pm
Criterion Covered Total %
statement 45 80 56.2
branch 14 42 33.3
condition 1 2 50.0
subroutine 9 10 90.0
pod 2 2 100.0
total 71 136 52.2


line stmt bran cond sub pod time code
1              
2 1     1   141037 use strict;
  1         10  
  1         33  
3 1     1   5 use warnings FATAL => 'all';
  1         2  
  1         69  
4              
5             package MarpaX::ESLIF::ECMA404;
6              
7             # ABSTRACT: JSON Data Interchange Format following ECMA-404 specification
8              
9             our $VERSION = '0.012'; # VERSION
10              
11             our $AUTHORITY = 'cpan:JDDPAUSE'; # AUTHORITY
12              
13              
14 1     1   5 use Carp qw/croak/;
  1         2  
  1         47  
15 1     1   414 use MarpaX::ESLIF 3.0.12;
  1         126331  
  1         46  
16 1     1   554 use MarpaX::ESLIF::ECMA404::RecognizerInterface;
  1         2  
  1         30  
17 1     1   414 use MarpaX::ESLIF::ECMA404::ValueInterface;
  1         4  
  1         34  
18 1     1   8 use Scalar::Util qw/looks_like_number/;
  1         2  
  1         836  
19              
20             our $_BNF = do { local $/; };
21              
22              
23             sub new {
24 1     1 1 94 my ($pkg, %options) = @_;
25              
26 1         3 my $bnf = $_BNF;
27              
28 1 50       4 if ($options{unlimited_commas}) {
29 0         0 my $tag = quotemeta('# /* Unlimited commas */');
30 0         0 $bnf =~ s/$tag//g;
31 0         0 $bnf =~ s/\bseparator\s*=>\s*comma\b/separator => commas/g;
32             }
33 1 50       4 if ($options{trailing_separator}) {
34 0         0 $bnf =~ s/\bproper\s*=>\s*1\b/proper => 0/g;
35             }
36 1 50       6 if ($options{perl_comment}) {
37 0         0 my $tag = quotemeta('# /* Perl comment */');
38 0         0 $bnf =~ s/$tag//g;
39             }
40 1 50       3 if ($options{cplusplus_comment}) {
41 0         0 my $tag = quotemeta('# /* C++ comment */');
42 0         0 $bnf =~ s/$tag//g;
43             }
44 1 50       3 if ($options{bignum}) {
45 0         0 my $tag = quotemeta('# /* bignum */');
46 0         0 $bnf =~ s/$tag//g;
47             }
48 1 50       4 if ($options{inf}) {
49 0         0 my $tag = quotemeta('# /* inf */');
50 0         0 $bnf =~ s/$tag//g;
51             }
52 1 50       5 if ($options{nan}) {
53 0         0 my $tag = quotemeta('# /* nan */');
54 0         0 $bnf =~ s/$tag//g;
55             }
56 1 50       4 if ($options{cntrl}) {
57 0         0 my $tag = quotemeta('# /* cntrl */');
58 0         0 $bnf =~ s/$tag//g;
59             }
60             #
61             # Check that max_depth looks like a number
62             #
63 1   50     8 my $max_depth = $options{max_depth} //= 0;
64 1 50       6 croak "max_depth option does not look like a number" unless looks_like_number $max_depth;
65             #
66             # And that it is an integer
67             #
68 1         4 $max_depth =~ s/\s//g;
69 1 50       6 croak "max_depth option does not look an integer >= 0" unless $max_depth =~ /^\+?\d+/;
70 1         3 $options{max_depth} = int($max_depth);
71 1 50       3 if ($options{max_depth}) {
72 0         0 my $tag = quotemeta('# /* max_depth */');
73 0         0 $bnf =~ s/$tag//g;
74             }
75              
76             bless {
77 1         10 grammar => MarpaX::ESLIF::Grammar->new(MarpaX::ESLIF->new($options{logger}), $bnf),
78             %options
79             }, $pkg
80             }
81              
82              
83             sub decode {
84 354     354 1 871441 my ($self, $input, $encoding) = @_;
85              
86             # ----------------------------------
87             # Instanciate a recognizer interface
88             # ----------------------------------
89 354         2201 my $recognizerInterface = MarpaX::ESLIF::ECMA404::RecognizerInterface->new(data => $input, encoding => $encoding);
90              
91             # -----------------------------
92             # Instanciate a value interface
93             # -----------------------------
94 354         2436 my $valueInterface = MarpaX::ESLIF::ECMA404::ValueInterface->new(logger => $self->{logger}, disallow_dupkeys => $self->{disallow_dupkeys});
95              
96             # ---------------
97             # Parse the input
98             # ---------------
99 354         1002 my $max_depth = $self->{max_depth};
100 354 50       811 if ($max_depth) {
101 0         0 $self->{cur_depth} = 0;
102             #
103             # We need to use the recognizer loop to have access to the inc/dec events
104             #
105 0         0 my $eslifRecognizer = MarpaX::ESLIF::Recognizer->new($self->{grammar}, $recognizerInterface);
106 0 0       0 $eslifRecognizer->scan() || croak "scan() failed";
107 0         0 $self->_manage_events($eslifRecognizer);
108 0 0       0 if ($eslifRecognizer->isCanContinue) {
109 0         0 do {
110 0 0       0 $eslifRecognizer->resume || croak 'resume() failed';
111 0         0 $self->_manage_events($eslifRecognizer)
112             } while ($eslifRecognizer->isCanContinue)
113             }
114             #
115             # We configured value interface to not accept ambiguity not null parse.
116             # So no need to loop on value()
117             #
118 0 0       0 MarpaX::ESLIF::Value->new($eslifRecognizer, $valueInterface)->value() || croak 'Valuation failed'
119             } else {
120 354 100       14578 $self->{grammar}->parse($recognizerInterface, $valueInterface) || croak 'Parse failed'
121             }
122              
123             # ------------------------
124             # Return the value
125             # ------------------------
126 149         477 return $valueInterface->getResult
127             }
128              
129             sub _manage_events {
130 0     0     my ($self, $eslifRecognizer) = @_;
131              
132 0           foreach (@{$eslifRecognizer->events()}) {
  0            
133 0           my $event = $_->{event};
134 0 0         next unless $event; # Can be undef for exhaustion
135 0 0         if ($event eq 'inc[]') {
    0          
136             croak "Maximum depth $self->{max_depth} reached" if ++$self->{cur_depth} > $self->{max_depth}
137 0 0         } elsif ($event eq 'dec[]') {
138             --$self->{cur_depth}
139 0           }
140             }
141             }
142              
143              
144             1;
145              
146             =pod
147              
148             =encoding UTF-8
149              
150             =head1 NAME
151              
152             MarpaX::ESLIF::ECMA404 - JSON Data Interchange Format following ECMA-404 specification
153              
154             =head1 VERSION
155              
156             version 0.012
157              
158             =head1 SYNOPSIS
159              
160             use MarpaX::ESLIF::ECMA404;
161              
162             my $ecma404 = MarpaX::ESLIF::ECMA404->new();
163             my $input = '["JSON",{},[]]';
164             my $json = $ecma404->decode($input);
165              
166             =head1 DESCRIPTION
167              
168             This module decodes strict JSON input using L.
169              
170             =for html Travis CI build status GitHub version License Perl5
171              
172             =head1 SUBROUTINES/METHODS
173              
174             =head2 new($class, %options)
175              
176             Instantiate a new object. Takes as parameter an optional hash of options that can be:
177              
178             =over
179              
180             =item logger
181              
182             An optional logger object instance that must do methods compliant with L interface.
183              
184             =back
185              
186             and the following extensions:
187              
188             =over
189              
190             =item unlimited_commas
191              
192             Allow unlimited number of commas between object pairs or array elements.
193              
194             =item trailing_separator
195              
196             Allow trailing separator (i.e. a comma, eventually an unlimited number of them (c.f. C option) after object pairs or array elements.
197              
198             =item perl_comment
199              
200             Allow perl style comments.
201              
202             =item cplusplus_comment
203              
204             Allow C++ style comments.
205              
206             =item bignum
207              
208             Use perl's bignum to store numbers. Default perl's bignum accuracy and precision will be in effect.
209              
210             =item inf
211              
212             Support of C or C, case insensitive, eventually preceded by a C<+> or a C<-> sign.
213              
214             =item nan
215              
216             Support of C, case insensitive, eventually preceded by a C<+> or a C<-> sign (even if this is meaningless).
217              
218             =item cntrl
219              
220             Support of Unicode's control characters (i.e. the range C<[\x00-\x1F]>).
221              
222             =item disallow_dupkeys
223              
224             Dot not allow duplicate key in an object.
225              
226             =back
227              
228             =head2 decode($self, $input, $encoding)
229              
230             Parses JSON that is in C<$input> and returns a perl variable containing the corresponding structured representation (which can be C), or croaks in case of failure. C<$encoding> is an optional parameter: JSON parser is using L that will I about the encoding if not specified, this guess is not 100% reliable - so if you know the encoding of your data, in particular if it is not in UTF-8, you should give the information to the parser. Default is to guess.
231              
232             =head1 SEE ALSO
233              
234             L, L
235              
236             =head1 AUTHOR
237              
238             Jean-Damien Durand
239              
240             =head1 COPYRIGHT AND LICENSE
241              
242             This software is copyright (c) 2017 by Jean-Damien Durand.
243              
244             This is free software; you can redistribute it and/or modify it under
245             the same terms as the Perl 5 programming language system itself.
246              
247             =cut
248              
249             __DATA__