|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
  
 
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package ThaiSchema::JSON;  | 
| 
2
 | 
3
 | 
 
 | 
 
 | 
  
3
  
 | 
 
 | 
28913
 | 
 use strict;  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
73
 | 
    | 
| 
3
 | 
3
 | 
 
 | 
 
 | 
  
3
  
 | 
 
 | 
12
 | 
 use warnings;  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
60
 | 
    | 
| 
4
 | 
3
 | 
 
 | 
 
 | 
  
3
  
 | 
 
 | 
11
 | 
 use utf8;  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
6
 | 
3
 | 
 
 | 
 
 | 
  
3
  
 | 
 
 | 
653
 | 
 use ThaiSchema;  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
185
 | 
    | 
| 
7
 | 
3
 | 
 
 | 
 
 | 
  
3
  
 | 
 
 | 
1345
 | 
 use Encode       ();  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21278
 | 
    | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5997
 | 
    | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Licensed under the Artistic 2.0 license.  | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # See http://www.perlfoundation.org/artistic_license_2_0.  | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # This module is based on JSON::Tiny 0.22  | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $FALSE = \0;  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $TRUE  = \1;  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ddf {  | 
| 
18
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
     require Data::Dumper;  | 
| 
19
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     local $Data::Dumper::Terse = 1;  | 
| 
20
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     Data::Dumper::Dumper(@_);  | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Escaped special character map (with u2028 and u2029)  | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my %ESCAPE = (  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     '"'     => '"',  | 
| 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     '\\'    => '\\',  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     '/'     => '/',  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'b'     => "\x07",  | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'f'     => "\x0C",  | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'n'     => "\x0A",  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'r'     => "\x0D",  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     't'     => "\x09",  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'u2028' => "\x{2028}",  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'u2029' => "\x{2029}"  | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my %REVERSE = map { $ESCAPE{$_} => "\\$_" } keys %ESCAPE;  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 for ( 0x00 .. 0x1F, 0x7F ) { $REVERSE{ pack 'C', $_ } //= sprintf '\u%.4X', $_ }  | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Unicode encoding detection  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $UTF_PATTERNS = {  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'UTF-32BE' => qr/^\0\0\0[^\0]/,  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'UTF-16BE' => qr/^\0[^\0]\0[^\0]/,  | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'UTF-32LE' => qr/^[^\0]\0\0\0/,  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'UTF-16LE' => qr/^[^\0]\0[^\0]\0/  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 };  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $WHITESPACE_RE = qr/[\x20\x09\x0a\x0d]*/;  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $FAIL;  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our @_ERRORS;  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $_NAME = '';  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub new {  | 
| 
54
 | 
41
 | 
 
 | 
 
 | 
  
41
  
 | 
  
0
  
 | 
2260
 | 
     my $class = shift;  | 
| 
55
 | 
41
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
113
 | 
     bless @_ ? @_ > 1 ? {@_} : { %{ $_[0] } } : {}, $class;  | 
| 
 
 | 
  
0
  
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub error {  | 
| 
59
 | 
98
 | 
  
100
  
 | 
 
 | 
  
98
  
 | 
  
0
  
 | 
276
 | 
     $_[0]->{error} = $_[1] if @_ > 1;  | 
| 
60
 | 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
189
 | 
     return $_[0]->{error};  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub validate {  | 
| 
64
 | 
41
 | 
 
 | 
 
 | 
  
41
  
 | 
  
0
  
 | 
595
 | 
     my ( $self, $bytes, $schema ) = @_;  | 
| 
65
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
88
 | 
     $schema = _schema($schema);  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
67
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
61
 | 
     local $FAIL;  | 
| 
68
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
69
 | 
     local @_ERRORS;  | 
| 
69
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
64
 | 
     local $_NAME = '';  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Cleanup  | 
| 
72
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
126
 | 
     $self->error(undef);  | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Missing input  | 
| 
75
 | 
41
 | 
  
 50
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
103
 | 
     $self->error('Missing or empty input') and return undef  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       unless $bytes;    ## no critic (undef)  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Remove BOM  | 
| 
79
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
105
 | 
     $bytes =~  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       s/^(?:\357\273\277|\377\376\0\0|\0\0\376\377|\376\377|\377\376)//g;  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Wide characters  | 
| 
83
 | 
41
 | 
  
 50
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
120
 | 
     $self->error('Wide character in input')  | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       and return undef    ## no critic (undef)  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       unless utf8::downgrade( $bytes, 1 );  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Detect and decode Unicode  | 
| 
88
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
69
 | 
     my $encoding = 'UTF-8';  | 
| 
89
 | 
41
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
409
 | 
     $bytes =~ $UTF_PATTERNS->{$_} and $encoding = $_ for keys %$UTF_PATTERNS;  | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
91
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
90
 | 
     my $d_res = eval { $bytes = Encode::decode( $encoding, $bytes, 1 ); 1 };  | 
| 
 
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
117
 | 
    | 
| 
 
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2305
 | 
    | 
| 
92
 | 
41
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
110
 | 
     $bytes = undef unless $d_res;  | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Object or array  | 
| 
95
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
69
 | 
     my $res = eval {  | 
| 
96
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
63
 | 
         local $_ = $bytes;  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Leading whitespace  | 
| 
99
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
269
 | 
         m/\G$WHITESPACE_RE/gc;  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Array  | 
| 
102
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
76
 | 
         my $ref;  | 
| 
103
 | 
41
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
138
 | 
         if (m/\G\[/gc) {  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
104
 | 
20
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
58
 | 
             unless ($schema->is_array()) {  | 
| 
105
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
                 _exception2("Unexpected array found.");  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
107
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
             $ref = _decode_array($schema->schema)  | 
| 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Object  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         elsif (m/\G\{/gc) {  | 
| 
112
 | 
21
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
62
 | 
             unless ($schema->is_hash()) {  | 
| 
113
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
                 _exception2("Unexpected object found.");  | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
115
 | 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
             $ref = _decode_object($schema)  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Unexpected  | 
| 
119
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         else { _exception('Expected array or object') }  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Leftover data  | 
| 
122
 | 
33
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
152
 | 
         unless (m/\G$WHITESPACE_RE\z/gc) {  | 
| 
123
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             my $got = ref $ref eq 'ARRAY' ? 'array' : 'object';  | 
| 
124
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             _exception("Unexpected data after $got");  | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
127
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
90
 | 
         $ref;  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Exception  | 
| 
131
 | 
41
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
136
 | 
     if ( !$res && ( my $e = $@ ) ) {  | 
| 
132
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
         chomp $e;  | 
| 
133
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
         $self->error($e);  | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
136
 | 
41
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
87
 | 
     if ($self->error) {  | 
| 
137
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
         push @_ERRORS, $self->error;  | 
| 
138
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
         $FAIL++;  | 
| 
139
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
141
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # return ($ok, \@errors);  | 
| 
142
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
171
 | 
     return (!$FAIL, \@_ERRORS);  | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _fail {  | 
| 
146
 | 
16
 | 
 
 | 
 
 | 
  
16
  
 | 
 
 | 
34
 | 
     my ($got, $schema) = @_;  | 
| 
147
 | 
16
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
59
 | 
     _fail2(($_NAME ? "$_NAME: " : '') . $schema->name . " is expected, but $got is found");  | 
| 
148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
149
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
150
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _fail2 {  | 
| 
151
 | 
17
 | 
 
 | 
 
 | 
  
17
  
 | 
 
 | 
34
 | 
     my ($msg) = @_;  | 
| 
152
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26
 | 
     $FAIL++;  | 
| 
153
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
     push @_ERRORS, $msg;  | 
| 
154
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
155
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
156
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 sub false { $FALSE }  | 
| 
157
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
 sub true  { $TRUE }  | 
| 
158
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _decode_array {  | 
| 
160
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
49
 | 
     my $schema = _schema(shift);  | 
| 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
162
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
     my @array;  | 
| 
163
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
42
 | 
     my $i = 0;  | 
| 
164
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
138
 | 
     until (m/\G$WHITESPACE_RE\]/gc) {  | 
| 
165
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
61
 | 
         local $_NAME = $_NAME . "[$i]";  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
167
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Value  | 
| 
168
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
53
 | 
         push @array, _decode_value($schema);  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
170
 | 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36
 | 
         $i++;  | 
| 
171
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Separator  | 
| 
173
 | 
20
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
81
 | 
         redo if m/\G$WHITESPACE_RE,/gc;  | 
| 
174
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # End  | 
| 
176
 | 
20
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
123
 | 
         last if m/\G$WHITESPACE_RE\]/gc;  | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Invalid character  | 
| 
179
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         _exception(  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             'Expected comma or right square bracket while parsing array');  | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
183
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
60
 | 
     return \@array;  | 
| 
184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _decode_object {  | 
| 
187
 | 
25
 | 
 
 | 
 
 | 
  
25
  
 | 
 
 | 
47
 | 
     my $schema = _schema(shift);  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
189
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
     my %hash;  | 
| 
190
 | 
25
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
120
 | 
     my %schema = $schema->isa("ThaiSchema::Maybe") ? %{$schema->schema->schema} : %{$schema->schema};  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
62
 | 
    | 
| 
191
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
187
 | 
     until (m/\G$WHITESPACE_RE\}/gc) {  | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Quote  | 
| 
194
 | 
28
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
182
 | 
         m/\G$WHITESPACE_RE"/gc  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           or _exception('Expected string while parsing object');  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Key  | 
| 
198
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
62
 | 
         my $key = _decode_string();  | 
| 
199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Colon  | 
| 
201
 | 
28
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
156
 | 
         m/\G$WHITESPACE_RE:/gc  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           or _exception('Expected colon while parsing object');  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Value  | 
| 
205
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
75
 | 
         local $_NAME = $_NAME . ".$key";  | 
| 
206
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
69
 | 
         my $cschema = delete $schema{$key};  | 
| 
207
 | 
28
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
75
 | 
         if ($cschema) {  | 
| 
208
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
53
 | 
             $hash{$key} = _decode_value($cschema);  | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
210
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
             if ($ThaiSchema::ALLOW_EXTRA) {  | 
| 
211
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
                 $hash{$key} = _decode_value(ThaiSchema::Extra->new());  | 
| 
212
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
213
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
                 _exception2("There is extra key: $key");  | 
| 
214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Separator  | 
| 
218
 | 
26
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
140
 | 
         redo if m/\G$WHITESPACE_RE,/gc;  | 
| 
219
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
220
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # End  | 
| 
221
 | 
21
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
123
 | 
         last if m/\G$WHITESPACE_RE\}/gc;  | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Invalid character  | 
| 
224
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         _exception(  | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             'Expected comma or right curly bracket while parsing object');  | 
| 
226
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
228
 | 
23
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
62
 | 
     if (%schema) {  | 
| 
229
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
         _fail2('There is missing keys: ' . join(', ', keys %schema));  | 
| 
230
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
232
 | 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
56
 | 
     return \%hash;  | 
| 
233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _decode_string {  | 
| 
236
 | 
34
 | 
 
 | 
 
 | 
  
34
  
 | 
 
 | 
73
 | 
     my $pos = pos;  | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Extract string with escaped characters  | 
| 
239
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
149
 | 
     m#\G(((?:[^\x00-\x1F\\"]|\\(?:["\\/bfnrt]|u[[:xdigit:]]{4})){0,32766})*)#gc;  | 
| 
240
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
90
 | 
     my $str = $1;  | 
| 
241
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Missing quote  | 
| 
243
 | 
34
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
94
 | 
     unless (m/\G"/gc) {  | 
| 
244
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         _exception(  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             'Unexpected character or invalid escape while parsing string')  | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           if m/\G[\x00-\x1F\\]/;  | 
| 
247
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         _exception('Unterminated string');  | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
249
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
250
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Unescape popular characters  | 
| 
251
 | 
34
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
118
 | 
     if ( index( $str, '\\u' ) < 0 ) {  | 
| 
252
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
66
 | 
         $str =~ s!\\(["\\/bfnrt])!$ESCAPE{$1}!gs;  | 
| 
253
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
78
 | 
         return $str;  | 
| 
254
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Unescape everything else  | 
| 
257
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $buffer = '';  | 
| 
258
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     while ( $str =~ m/\G([^\\]*)\\(?:([^u])|u(.{4}))/gc ) {  | 
| 
259
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $buffer .= $1;  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Popular character  | 
| 
262
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         if ($2) { $buffer .= $ESCAPE{$2} }  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
263
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
264
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Escaped  | 
| 
265
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         else {  | 
| 
266
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             my $ord = hex $3;  | 
| 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Surrogate pair  | 
| 
269
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             if ( ( $ord & 0xF800 ) == 0xD800 ) {  | 
| 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # High surrogate  | 
| 
272
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 ( $ord & 0xFC00 ) == 0xD800  | 
| 
273
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                   or pos($_) = $pos + pos($str),  | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                   _exception('Missing high-surrogate');  | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
276
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # Low surrogate  | 
| 
277
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 $str =~ m/\G\\u([Dd][C-Fc-f]..)/gc  | 
| 
278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                   or pos($_) = $pos + pos($str),  | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                   _exception('Missing low-surrogate');  | 
| 
280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # Pair  | 
| 
282
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 $ord =  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                   0x10000 + ( $ord - 0xD800 ) * 0x400 + ( hex($1) - 0xDC00 );  | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
285
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
286
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Character  | 
| 
287
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $buffer .= pack 'U', $ord;  | 
| 
288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
289
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
290
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
291
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # The rest  | 
| 
292
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return $buffer . substr $str, pos($str), length($str);  | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
295
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _schema {  | 
| 
296
 | 
145
 | 
 
 | 
 
 | 
  
145
  
 | 
 
 | 
213
 | 
     my $schema = shift;  | 
| 
297
 | 
145
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
377
 | 
     if (ref $schema eq 'HASH') {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
298
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
         return ThaiSchema::Hash->new(schema => $schema);  | 
| 
299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif (ref $schema eq 'ARRAY') {  | 
| 
300
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
         if (@$schema > 1) {  | 
| 
301
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             Carp::confess("Invalid schema: too many elements in arrayref: " . ddf($schema));  | 
| 
302
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
303
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
         return ThaiSchema::Array->new(schema => _schema($schema->[0]));  | 
| 
304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
305
 | 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
255
 | 
         return $schema;  | 
| 
306
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
308
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _decode_value {  | 
| 
310
 | 
51
 | 
 
 | 
 
 | 
  
51
  
 | 
 
 | 
92
 | 
     my $schema = _schema(shift);  | 
| 
311
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
312
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Leading whitespace  | 
| 
313
 | 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
178
 | 
     m/\G$WHITESPACE_RE/gc;  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
315
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # String  | 
| 
316
 | 
51
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
158
 | 
     if (m/\G"/gc) {  | 
| 
317
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
36
 | 
         unless ($schema->is_string) {  | 
| 
318
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
             _fail('string', $schema);  | 
| 
319
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
320
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
         return _decode_string();  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Array  | 
| 
324
 | 
45
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
106
 | 
     if (m/\G\[/gc) {  | 
| 
325
 | 
9
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
31
 | 
         unless ($schema->is_array) {  | 
| 
326
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
             _fail('array', $schema);  | 
| 
327
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
             _exception2("Unexpected array.");  | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
329
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
         return _decode_array($schema->schema);  | 
| 
330
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
331
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
332
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Object  | 
| 
333
 | 
36
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
85
 | 
     if (m/\G\{/gc) {  | 
| 
334
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
53
 | 
         unless ($schema->is_hash) {  | 
| 
335
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
             _fail('object', $schema);  | 
| 
336
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
             _exception2("Unexpected hash.");  | 
| 
337
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
338
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
         return _decode_object($schema);  | 
| 
339
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
340
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
341
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Number  | 
| 
342
 | 
28
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
98
 | 
     if (m/\G([-]?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?)/gc) {  | 
| 
343
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
         my $number = 0+$1;  | 
| 
344
 | 
16
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
53
 | 
         unless ($schema->is_number) {  | 
| 
345
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
             _fail('number', $schema);  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
347
 | 
16
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
44
 | 
         if ($schema->is_integer && int($number) != $number) {  | 
| 
348
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
             push @_ERRORS, "integer is expected, but you got $number";  | 
| 
349
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
             $FAIL++;  | 
| 
350
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
351
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
50
 | 
         return $number;  | 
| 
352
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
354
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # True  | 
| 
355
 | 
12
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
36
 | 
     if (m/\Gtrue/gc) {  | 
| 
356
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
22
 | 
         unless ($schema->is_bool) {  | 
| 
357
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
             _fail('true', $schema);  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
359
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
         return $TRUE;  | 
| 
360
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # False  | 
| 
363
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
29
 | 
     if (m/\Gfalse/gc) {  | 
| 
364
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
         unless ($schema->is_bool) {  | 
| 
365
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
             _fail('false', $schema);  | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
367
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
         return $FALSE;  | 
| 
368
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
369
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
370
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Null  | 
| 
371
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
20
 | 
     if (m/\Gnull/gc) {  | 
| 
372
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
21
 | 
         unless ($schema->is_null) {  | 
| 
373
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
             _fail('null', $schema);  | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ## no critic (return)  | 
| 
376
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
         return undef;  | 
| 
377
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
378
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Invalid data  | 
| 
380
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     _exception('Expected string, array, object, number, boolean or null');  | 
| 
381
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _exception2 {  | 
| 
384
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Leading whitespace  | 
| 
385
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
39
 | 
     m/\G$WHITESPACE_RE/gc;  | 
| 
386
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
387
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Context  | 
| 
388
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
     my $context;  | 
| 
389
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
24
 | 
        $context .= "$_NAME: " if $_NAME;  | 
| 
390
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
        $context .= shift;  | 
| 
391
 | 
8
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
22
 | 
     if (m/\G\z/gc) { $context .= ' before end of data' }  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else {  | 
| 
393
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
         my @lines = split /\n/, substr( $_, 0, pos );  | 
| 
394
 | 
8
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
33
 | 
         $context .=  | 
| 
395
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           ' at line ' . @lines . ', offset ' . length( pop @lines || '' );  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
397
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Throw  | 
| 
399
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
     die "$context\n";  | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
402
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _exception {  | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Leading whitespace  | 
| 
405
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     m/\G$WHITESPACE_RE/gc;  | 
| 
406
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
407
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Context  | 
| 
408
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $context = 'Malformed JSON: ' . shift;  | 
| 
409
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (m/\G\z/gc) { $context .= ' before end of data' }  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else {  | 
| 
411
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my @lines = split /\n/, substr( $_, 0, pos );  | 
| 
412
 | 
  
0
  
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
         $context .=  | 
| 
413
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           ' at line ' . @lines . ', offset ' . length( pop @lines || '' );  | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
415
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Throw  | 
| 
417
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     die "$context\n";  | 
| 
418
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
420
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
421
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__  |