File Coverage

blib/lib/JSON/Relaxed.pm
Criterion Covered Total %
statement 231 247 93.5
branch 124 138 89.8
condition 4 5 80.0
subroutine 35 37 94.5
pod 1 1 100.0
total 395 428 92.2


line stmt bran cond sub pod time code
1             package JSON::Relaxed;
2 1     1   855 use strict;
  1         2  
  1         149  
3              
4             # debug tools
5             # use Debug::ShowStuff ':all';
6             # use Debug::ShowStuff::ShowVar;
7              
8             # version
9             our $VERSION = '0.05';
10              
11             # global error messages
12             our $err_id;
13             our $err_msg;
14              
15              
16             #------------------------------------------------------------------------------
17             # POD
18             #
19              
20             =head1 NAME
21              
22             JSON::Relaxed -- An extension of JSON that allows for better human-readability.
23              
24             =head1 SYNOPSIS
25              
26             my ($rjson, $hash, $parser);
27            
28             # raw RJSON code
29             $rjson = <<'(RAW)';
30             /* Javascript-like comments are allowed */
31             {
32             // single or double quotes allowed
33             a : 'Larry',
34             b : "Curly",
35            
36             // nested structures allowed like in JSON
37             c: [
38             {a:1, b:2},
39             ],
40            
41             // like Perl, trailing commas are allowed
42             d: "more stuff",
43             }
44             (RAW)
45            
46             # subroutine parsing
47             $hash = from_rjson($rjson);
48            
49             # object-oriented parsing
50             $parser = JSON::Relaxed::Parser->new();
51             $hash = $parser->parse($rjson);
52              
53              
54             =head1 INSTALLATION
55              
56             JSON::Relaxed can be installed with the usual routine:
57              
58             perl Makefile.PL
59             make
60             make test
61             make install
62              
63             =head1 DESCRIPTION
64              
65             JSON::Relaxed is a lightweight parser and serializer for an extension of JSON
66             called Relaxed JSON (RJSON). The intent of RJSON is to provide a format that
67             is more human-readable and human-editable than JSON. Most notably, RJSON allows
68             the use of JavaScript-like comments. By doing so, configuration files and other
69             human-edited files can include comments to indicate the intention of each
70             configuration.
71              
72             JSON::Relaxed is currently only a parser that reads in RJSON code and produces
73             a data structure. JSON::Relaxed does not currently encode data structures into
74             JSON/RJSON. That feature is planned.
75              
76             =head2 Why Relaxed JSON?
77              
78             There's been increasing support for the idea of expanding JSON to improve
79             human-readability. "Relaxed" JSON is a term that has been used to describe a
80             JSON-ish format that has some features that JSON doesn't. Although there isn't
81             yet any kind of official specification, descriptions of Relaxed JSON generally
82             include the following extensions to JSON:
83              
84             =over 4
85              
86             =item * comments
87              
88             RJSON supports JavaScript-like comments:
89              
90             /* inline comments */
91             // line-based comments
92              
93             =item * trailing commas
94              
95             Like Perl, RJSON allows treats commas as separators. If nothing is before,
96             after, or between commas, those commas are just ignored:
97              
98             [
99             , // nothing before this comma
100             "data",
101             , // nothing after this comma
102             ]
103              
104             =item * single quotes, double quotes, no quotes
105              
106             Strings can be quoted with either single or double quotes. Space-less strings
107             are also parsed as strings. So, the following data items are equivalent:
108              
109             [
110             "Starflower",
111             'Starflower',
112             Starflower
113             ]
114              
115             Note that unquoted boolean values are still treated as boolean values, so the
116             following are NOT the same:
117              
118             [
119             "true", // string
120             true, // boolean true
121            
122             "false", // string
123             false, // boolean false
124            
125             "null", // string
126             null, // what Perl programmers call undef
127             ]
128              
129             Because of this ambiguity, unquoted non-boolean strings should be considered
130             sloppy and not something you do in polite company.
131              
132             =item * documents that are just a single string
133              
134             Early versions of JSON require that a JSON document contains either a single
135             hash or a single array. Later versions also allow a single string. RJSON
136             follows that later rule, so the following is a valid RJSON document:
137              
138             "Hello world"
139              
140             =item * hash keys without values
141              
142             A hash in JSON can have a key that is followed by a comma or a closing C<}>
143             without a specified value. In that case the hash element is simply assigned
144             the undefined value. So, in the following example, C is assigned C<1>,
145             C is assigned 2, and C is assigned undef:
146              
147             {
148             a: 1,
149             b: 2,
150             c
151             }
152              
153             =back
154              
155             =cut
156              
157             #
158             # POD
159             #------------------------------------------------------------------------------
160              
161              
162              
163             #------------------------------------------------------------------------------
164             # from_rjson
165             #
166              
167             =head2 from_rjson()
168              
169             C is the simple way to quickly parse an RJSON string. Currently
170             C only takes a single parameter, the string itself. So in the
171             following example, C parses and returns the structure defined in
172             C<$rjson>.
173              
174             $structure = from_rjson($rjson);
175              
176             =cut
177              
178             sub from_rjson {
179 0     0 1 0 my ($raw) = @_;
180 0         0 my $parser = JSON::Relaxed::Parser->new();
181 0         0 return $parser->parse($raw);
182             }
183             #
184             # from_rjson
185             #------------------------------------------------------------------------------
186              
187              
188             #------------------------------------------------------------------------------
189             # object-oriented parsing
190             #
191              
192             =head2 Object-oriented parsing
193              
194             To parse using an object, create a C object, like this:
195              
196             $parser = JSON::Relaxed::Parser->new();
197              
198             Then call the parser's parse method, passing in the RJSON string:
199              
200             $structure = $parser->parse($rjson);
201              
202             B
203              
204             =over 4
205              
206             =item * $parser->extra_tokens_ok()
207              
208             C sets/gets the C property. By default,
209             C is false. If by C is true then the
210             C isn't triggered and the parser returns the first
211             structure it finds. So, for example, the following code would return undef and
212             sets the C error:
213              
214             $parser = JSON::Relaxed::Parser->new();
215             $structure = $parser->parse('{"x":1} []');
216              
217             However, by setting C to true, a hash structure is
218             returned, the extra code after that first hash is ignored, and no error is set:
219              
220             $parser = JSON::Relaxed::Parser->new();
221             $parser->extra_tokens_ok(1);
222             $structure = $parser->parse('{"x":1} []');
223              
224             =back
225              
226             =cut
227              
228             #
229             # object-oriented parsing
230             #------------------------------------------------------------------------------
231              
232              
233              
234             #------------------------------------------------------------------------------
235             # error codes
236             #
237              
238             =head2 Error codes
239              
240             When JSON::Relaxed encounters a parsing error it returns C and sets two
241             global variables:
242              
243             =over 4
244              
245             =item * $JSON::Relaxed::err_id
246              
247             C<$err_id> is a unique code for a specific error. Every code is set in only
248             one place in JSON::Relaxed.
249              
250             =item * $JSON::Relaxed::err_msg
251              
252             C<$err_msg> is an English description of the code. It would be cool to migrate
253             towards multi-language support for C<$err_msg>.
254              
255             =back
256              
257             Following is a list of all error codes in JSON::Relaxed:
258              
259             =over 4
260              
261             =item * C
262              
263             The string to be parsed was not sent to $parser->parse(). For example:
264              
265             $parser->parse()
266              
267             =item * C
268              
269             The string to be parsed is undefined. For example:
270              
271             $parser->parse(undef)
272              
273             =item * C
274              
275             The string to be parsed is zero-length. For example:
276              
277             $parser->parse('')
278              
279             =item * C
280              
281             The string to be parsed has no content beside space characters. For example:
282              
283             $parser->parse(' ')
284              
285             =item * C
286              
287             The string to be parsed has no content. This error is slightly different than
288             C in that it is triggered when the input contains only
289             comments, like this:
290              
291             $parser->parse('/* whatever */')
292              
293              
294             =item * C
295              
296             A comment was started with /* but was never closed. For example:
297              
298             $parser->parse('/*')
299              
300             =item * C
301              
302             The document opens with an invalid structural character like a comma or colon.
303             The following examples would trigger this error.
304              
305             $parser->parse(':')
306             $parser->parse(',')
307             $parser->parse('}')
308             $parser->parse(']')
309              
310             =item * C
311              
312             The document has multiple structures. JSON and RJSON only allow a document to
313             consist of a single hash, a single array, or a single string. The following
314             examples would trigger this error.
315              
316             $parse->parse('{}[]')
317             $parse->parse('{} "whatever"')
318             $parse->parse('"abc" "def"')
319              
320             =item * C
321              
322             A hash key may only be followed by the closing hash brace or a colon. Anything
323             else triggers C. So, the following examples would
324             trigger this error.
325              
326             $parse->parse("{a [ }") }
327             $parse->parse("{a b") }
328              
329             =item * C
330              
331             The parser encountered something besides a string where a hash key should be.
332             The following are examples of code that would trigger this error.
333              
334             $parse->parse('{{}}')
335             $parse->parse('{[]}')
336             $parse->parse('{]}')
337             $parse->parse('{:}')
338              
339             =item * C
340              
341             A hash has an opening brace but no closing brace. For example:
342              
343             $parse->parse('{x:1')
344              
345             =item * C
346              
347             An array has an opening brace but not a closing brace. For example:
348              
349             $parse->parse('["x", "y"')
350              
351             =item * C
352              
353             In a hash, a colon must be followed by a value. Anything else triggers this
354             error. For example:
355              
356             $parse->parse('{"a":,}')
357             $parse->parse('{"a":}')
358              
359             =item * C
360              
361             In an array, a comma must be followed by a value, another comma, or the closing
362             array brace. Anything else triggers this error. For example:
363              
364             $parse->parse('[ "x" "y" ]')
365             $parse->parse('[ "x" : ]')
366              
367             =item * C
368              
369             This error exists just in case there's an invalid token in an array that
370             somehow wasn't caught by C. This error
371             shouldn't ever be triggered. If it is please L.
372              
373             =item * C
374              
375             This error is triggered when a quote isn't closed. For example:
376              
377             $parse->parse("'whatever")
378             $parse->parse('"whatever') }
379              
380             =back
381              
382              
383             =cut
384              
385             #
386             # error codes
387             #------------------------------------------------------------------------------
388              
389              
390             #------------------------------------------------------------------------------
391             # export
392             #
393 1     1   4 use base 'Exporter';
  1         1  
  1         62  
394 1     1   13 use vars qw[@EXPORT_OK %EXPORT_TAGS];
  1         1  
  1         177  
395             push @EXPORT_OK, 'from_rjson';
396             %EXPORT_TAGS = ('all' => [@EXPORT_OK]);
397             #
398             # export
399             #------------------------------------------------------------------------------
400              
401              
402             #------------------------------------------------------------------------------
403             # JSON::Relaxed POD
404             #
405              
406             =head1 INTERNALS
407              
408             The following documentation is for if you want to edit the code of
409             JSON::Relaxed itself.
410              
411             =head2 JSON::Relaxed
412              
413             C is the parent package. Not a lot actually happens in
414             C, it mostly contains L and
415             definitions of various structures.
416              
417             =over 4
418              
419             =cut
420              
421             #
422             # JSON::Relaxed POD
423             #------------------------------------------------------------------------------
424              
425              
426             #------------------------------------------------------------------------------
427             # special character and string definitions
428             #
429              
430             =item Special character and string definitions
431              
432             The following hashes provide information about characters and strings that have
433             special meaning in RJSON.
434              
435             =over 4
436              
437             =item * Escape characters
438              
439             The C<%esc> hash defines the six escape characters in RJSON that are
440             changed to single characters. C<%esc> is defined as follows.
441              
442             our %esc = (
443             'b' => "\b", # Backspace
444             'f' => "\f", # Form feed
445             'n' => "\n", # New line
446             'r' => "\r", # Carriage return
447             't' => "\t", # Tab
448             'v' => chr(11), # Vertical tab
449             );
450              
451             =cut
452              
453             # escape characters
454             our %esc = (
455             'b' => "\b", # Backspace
456             'f' => "\f", # Form feed
457             'n' => "\n", # New line
458             'r' => "\r", # Carriage return
459             't' => "\t", # Tab
460             'v' => chr(11), # Vertical tab
461             );
462              
463             =item * Structural characters
464              
465             The C<%structural> hash defines the six characters in RJSON that define
466             the structure of the data object. The structural characters are defined as
467             follows.
468              
469             our %structural = (
470             '[' => 1, # beginning of array
471             ']' => 1, # end of array
472             '{' => 1, # beginning of hash
473             '}' => 1, # end of hash
474             ':' => 1, # delimiter between name and value of hash element
475             ',' => 1, # separator between elements in hashes and arrays
476             );
477              
478             =cut
479              
480             # structural
481             our %structural = (
482             '[' => 1, # beginning of array
483             ']' => 1, # end of array
484             '{' => 1, # beginning of hash
485             '}' => 1, # end of hash
486             ':' => 1, # delimiter between name and value of hash element
487             ',' => 1, # separator between elements in hashes and arrays
488             );
489              
490             =item * Quotes
491              
492             The C<%quotes> hash defines the two types of quotes recognized by RJSON: single
493             and double quotes. JSON only allows the use of double quotes to define strings.
494             Relaxed also allows single quotes. C<%quotes> is defined as follows.
495              
496             our %quotes = (
497             '"' => 1,
498             "'" => 1,
499             );
500              
501             =cut
502              
503             # quotes
504             our %quotes = (
505             '"' => 1,
506             "'" => 1,
507             );
508              
509             =item * End of line characters
510              
511             The C<%newlines> hash defines the three ways a line can end in a RJSON
512             document. Lines in Windows text files end with carriage-return newline
513             ("\r\n"). Lines in Unixish text files end with newline ("\n"). Lines in some
514             operating systems end with just carriage returns ("\n"). C<%newlines> is
515             defined as follows.
516              
517             our %newlines = (
518             "\r\n" => 1,
519             "\r" => 1,
520             "\n" => 1,
521             );
522              
523             =cut
524              
525             # newline tokens
526             our %newlines = (
527             "\r\n" => 1,
528             "\r" => 1,
529             "\n" => 1,
530             );
531              
532             =item * Boolean
533              
534             The C<%boolean> hash defines strings that are boolean values: true, false, and
535             null. (OK, 'null' isn't B a boolean value, but I couldn't think of what
536             else to call this hash.) C<%boolean> is defined as follows.
537              
538             our %boolean = (
539             'null' => 1,
540             'true' => 1,
541             'false' => 1,
542             );
543              
544             =back
545              
546             =cut
547              
548             # boolean values
549             our %boolean = (
550             'null' => undef,
551             'true' => 1,
552             'false' => 0,
553             );
554              
555             #
556             # special character definitions
557             #------------------------------------------------------------------------------
558              
559              
560             #------------------------------------------------------------------------------
561             # closing POD for JSON::Relaxed
562             #
563              
564             =back
565              
566             =cut
567              
568             #
569             # closing POD for JSON::Relaxed
570             #------------------------------------------------------------------------------
571              
572              
573             ###############################################################################
574             # JSON::Relaxed::Parser
575             #
576             package JSON::Relaxed::Parser;
577 1     1   3 use strict;
  1         1  
  1         1042  
578              
579              
580             # debugging
581             # use Debug::ShowStuff ':all';
582              
583              
584             #------------------------------------------------------------------------------
585             # POD
586             #
587              
588             =head2 JSON::Relaxed::Parser
589              
590             A C object parses the raw RJSON string. You don't
591             need to instantiate a parser if you just want to use the default settings.
592             In that case just use L.
593              
594             You would create a C object if you want to customize how
595             the string is parsed. I say "would" because there isn't actually any
596             customization in these early releases. When there is you'll use a parser
597             object.
598              
599             To parse in an object oriented manner, create the parser, then parse.
600              
601             $parser = JSON::Relaxed::Parser->new();
602             $structure = $parser->parse($string);
603              
604             =over 4
605              
606             =cut
607              
608             #
609             # POD
610             #------------------------------------------------------------------------------
611              
612              
613             #------------------------------------------------------------------------------
614             # new
615             #
616              
617             =item new
618              
619             Cnew()> creates a parser object. Its simplest and most
620             common use is without any parameters.
621              
622             my $parser = JSON::Relaxed::Parser->new();
623              
624             =over 4
625              
626             =item B unknown
627              
628             The C option sets the character which creates the
629             L. The unknown object
630             exists only for testing JSON::Relaxed. It has no purpose in production use.
631              
632             my $parser = JSON::Relaxed::Parser->new(unknown=>'~');
633              
634             =back
635              
636             =cut
637              
638             sub new {
639 74     74   44237 my ($class, %opts) = @_;
640 74         106 my $parser = bless({}, $class);
641            
642             # TESTING
643             # println subname(); ##i
644            
645             # "unknown" object character
646 74 100       147 if (defined $opts{'unknown'}) {
647 2         3 $parser->{'unknown'} = $opts{'unknown'};
648             }
649            
650             # return
651 74         179 return $parser;
652             }
653             #
654             # new
655             #------------------------------------------------------------------------------
656              
657              
658             #------------------------------------------------------------------------------
659             # extra_tokens_ok
660             #
661             sub extra_tokens_ok {
662 16     16   424 my ($parser) = @_;
663            
664             # set value
665 16 100       28 if (@_ > 1) {
666 2 100       6 $parser->{'extra_tokens_ok'} = $_[1] ? 1 : 0;
667             }
668            
669             # return
670 16 100       36 return $parser->{'extra_tokens_ok'} ? 1 : 0;
671             }
672             #
673             # extra_tokens_ok
674             #------------------------------------------------------------------------------
675              
676              
677             #------------------------------------------------------------------------------
678             # error
679             #
680             sub error {
681 40     40   36 my ($parser, $id, $msg) = @_;
682            
683             # set errors
684 40         25 $JSON::Relaxed::err_id = $id;
685 40         28 $JSON::Relaxed::err_msg = $msg;
686            
687             # return undef
688 40         86 return undef;
689             }
690             #
691             # error
692             #------------------------------------------------------------------------------
693              
694              
695             #------------------------------------------------------------------------------
696             # is_error
697             #
698             sub is_error {
699 69     69   56 my ($parser) = @_;
700            
701             # return true if there is an error, false otherwise
702 69 100       68 if ($JSON::Relaxed::err_id)
703 22         41 { return 1 }
704             else
705 47         68 { return 0 }
706             }
707             #
708             # is_error
709             #------------------------------------------------------------------------------
710              
711              
712              
713              
714             #------------------------------------------------------------------------------
715             # "is" methods
716             #
717              
718             =item Parser "is" methods
719              
720             The following methods indicate if a token has some specific property, such as
721             being a string object or a structural character.
722              
723             =over 4
724              
725             =cut
726              
727              
728              
729             =item * is_string()
730              
731             Returns true if the token is a string object, i.e. in the class
732             C.
733              
734             =cut
735              
736             # the object is a string object
737             sub is_string {
738 137     137   109 my ($parser, $object) = @_;
739 137         391 return UNIVERSAL::isa($object, 'JSON::Relaxed::Parser::Token::String');
740             }
741              
742              
743              
744             =item * is_struct_char()
745              
746             Returns true if the token is one of the structural characters of JSON, i.e.
747             one of the following:
748              
749             { } [ ] : ,
750              
751             =cut
752              
753             # the object is a structural character
754             sub is_struct_char {
755 0     0   0 my ($parser, $object) = @_;
756            
757             # if it's a reference, it's not a structural character
758 0 0       0 if (ref $object) {
    0          
759 0         0 return 0;
760             }
761            
762             # else if the object is defined
763             elsif (defined $object) {
764 0         0 return $JSON::Relaxed::structural{$object};
765             }
766            
767             # else whatever it is it isn't a structural character
768             else {
769 0         0 return 0;
770             }
771             }
772              
773              
774              
775             =item * is_unknown_char()
776              
777             Returns true if the token is the
778             L.
779              
780             =cut
781              
782             # the object is the "unknown" character
783             sub is_unknown_char {
784 47     47   38 my ($parser, $char) = @_;
785            
786             # if there even is a "unknown" character
787 47 100       73 if (defined $parser->{'unknown'}) {
788 2 50       4 if ($char eq $parser->{'unknown'})
789 2         4 { return 1 }
790             }
791            
792             # it's not the "unknown" character
793 45         57 return 0;
794             }
795              
796              
797              
798             =item * is_list_opener()
799              
800             Returns true if the token is the opening character for a hash or an array,
801             i.e. it is one of the following two characters:
802              
803             { [
804              
805             =cut
806              
807             # is_list_opener
808             sub is_list_opener {
809 32     32   27 my ($parser, $token) = @_;
810            
811             # if not defined, return false
812 32 50       36 if (! defined $token)
813 0         0 { return 0 }
814            
815             # if it's an object, return false
816 32 100       42 if (ref $token)
817 16         37 { return 0 }
818            
819             # opening brace for hash
820 16 100       23 if ($token eq '{')
821 2         4 { return 1 }
822            
823             # opening brace for array
824 14 100       16 if ($token eq '[')
825 3         6 { return 1 }
826            
827             # it's not a list opener
828 11         21 return 0;
829             }
830              
831              
832             =item * is_comment_opener()
833              
834             Returns true if the token is the opening character for a comment,
835             i.e. it is one of the following two couplets:
836              
837             /*
838             //
839              
840             =cut
841              
842             # is_comment_opener
843             sub is_comment_opener {
844 119     119   94 my ($parser, $token) = @_;
845            
846             # TESTING
847             # println subname(); ##i
848            
849             # if not defined, return false
850 119 50       134 if (! defined $token)
851 0         0 { return 0 }
852            
853             # if it's an object, return false
854 119 50       127 if (ref $token)
855 0         0 { return 0 }
856            
857             # opening inline comment
858 119 100       129 if ($token eq '/*')
859 1         2 { return 1 }
860            
861             # opening line comment
862 118 100       129 if ($token eq '//')
863 1         3 { return 1 }
864            
865             # it's not a comment opener
866 117         135 return 0;
867             }
868              
869              
870              
871             =back
872              
873             =cut
874              
875             #
876             # "is" methods
877             #------------------------------------------------------------------------------
878              
879              
880             #------------------------------------------------------------------------------
881             # parse
882             #
883              
884             =item parse()
885              
886             C is the method that does the work of parsing the RJSON string.
887             It returns the data structure that is defined in the RJSON string.
888             A typical usage would be as follows.
889              
890             my $parser = JSON::Relaxed::Parser->new();
891             my $structure = $parser->parse('["hello world"]');
892              
893             C does not take any options.
894              
895             =cut
896              
897             sub parse {
898 66     66   905 my ($parser, $raw) = @_;
899 66         41 my (@chars, @tokens, $rv);
900            
901             # TESTING
902             # println subname(); ##i
903            
904             # clear global error information
905 66         57 undef $JSON::Relaxed::err_id;
906 66         50 undef $JSON::Relaxed::err_msg;
907            
908             # must have at least two params
909 66 100       100 if (@_ < 2) {
910 1         2 return $parser->error(
911             'missing-parameter',
912             'the string to be parsed was not sent to $parser->parse()'
913             )
914             }
915            
916             # $raw must be defined
917 65 100       83 if (! defined $raw) {
918 1         2 return $parser->error(
919             'undefined-input',
920             'the string to be parsed is undefined'
921             );
922             }
923            
924             # $raw must not be an empty string
925 64 100       88 if ($raw eq '') {
926 1         2 return $parser->error(
927             'zero-length-input',
928             'the string to be parsed is zero-length'
929             );
930             }
931            
932             # $raw must have content
933 63 100       170 if ($raw !~ m|\S|s) {
934 1         2 return $parser->error(
935             'space-only-input',
936             'the string to be parsed has no content beside space characters'
937             );
938             }
939            
940             # get characters
941 62         78 @chars = $parser->parse_chars($raw);
942            
943             # get tokens
944 62         110 @tokens = $parser->tokenize(\@chars);
945            
946             # special case: entire structure is a single scalar
947             # NOTE: Some versions of JSON do not allow a single scalar as an entire
948             # JSON document.
949             #if (@tokens == 1) {
950             # # if single scalar is a string
951             # if ( $parser->is_string($tokens[0]) )
952             # { return $tokens[0]->as_perl() }
953             #}
954            
955             # must be at least one token
956 62 100       97 if (! @tokens) {
957 2         4 return $parser->error(
958             'no-content',
959             'the string to be parsed has no content'
960             )
961             }
962            
963             # build structure
964 60         80 $rv = $parser->structure(\@tokens, top=>1);
965             }
966             #
967             # parse
968             #------------------------------------------------------------------------------
969              
970              
971             #------------------------------------------------------------------------------
972             # parse_chars
973             #
974              
975             =item parse_chars()
976              
977             C parses the RJSON string into either individual characters
978             or two-character couplets. This method returns an array. The only input is the
979             raw RJSON string. So, for example, the following string:
980              
981             $raw = qq|/*x*/["y"]|;
982             @chars = $parser->parse_chars($raw);
983              
984             would be parsed into the following array:
985              
986             ( "/*", "x", "*/", "[", "\"", "y", "\""", "]" )
987              
988             Most of the elements in the array are single characters. However, comment
989             delimiters, escaped characters, and Windows-style newlines are parsed as
990             two-character couplets:
991              
992             =over 4
993              
994             =item * C<\> followed by any character
995              
996             =item * C<\r\n>
997              
998             =item * C
999              
1000             =item * C
1001              
1002             =item * C<*/>
1003              
1004             =back
1005              
1006             C should not produce any fatal errors.
1007              
1008             =cut
1009              
1010             sub parse_chars {
1011 75     75   311 my ($parser, $raw) = @_;
1012 75         59 my (@rv);
1013            
1014             # clear global error information
1015 75         50 undef $JSON::Relaxed::err_id;
1016 75         39 undef $JSON::Relaxed::err_msg;
1017            
1018             # split on any of the following couplets, or on single characters
1019             # \{any character}
1020             # \r\n
1021             # //
1022             # /*
1023             # */
1024             # {any character}
1025 75         605 @rv = split(m/(\\.|\r\n|\r|\n|\/\/|\/\*|\*\/|,|:|{|}|\[|\]|\s+|.)/sx, $raw);
1026            
1027             # remove empty strings
1028 75         92 @rv = grep {length($_)} @rv;
  1672         1289  
1029            
1030             # return
1031 75         258 return @rv;
1032             }
1033             #
1034             # parse_chars
1035             #------------------------------------------------------------------------------
1036              
1037              
1038             #------------------------------------------------------------------------------
1039             # tokenize
1040             #
1041              
1042             =item tokenize()
1043              
1044             C organizes the characters from
1045             C> into tokens. Those tokens can then be
1046             organized into a data structure with
1047             C>.
1048              
1049             Each token represents an item that is recognized by JSON. Those items include
1050             structural characters such as C<{> or C<}>, or strings such as
1051             C<"hello world">. Comments and insignificant whitespace are filtered out
1052             by C.
1053              
1054             For example, this code:
1055              
1056             $parser = JSON::Relaxed::Parser->new();
1057             $raw = qq|/*x*/ ["y"]|;
1058             @chars = $parser->parse_chars($raw);
1059             @tokens = $parser->tokenize(\@chars);
1060              
1061             would produce an array like this:
1062              
1063             (
1064             '[',
1065             JSON::Relaxed::Parser::Token::String::Quoted=HASH(0x20bf0e8),
1066             ']'
1067             )
1068              
1069             Strings are tokenized into string objects. When the parsing is complete they
1070             are returned as scalar variables, not objects.
1071              
1072             C should not produce any fatal errors.
1073              
1074             =cut
1075              
1076             sub tokenize {
1077 74     74   2290 my ($parser, $chars_org) = @_;
1078 74         43 my (@chars, @tokens);
1079            
1080             # TESTING
1081             # println subname(); ##i
1082            
1083             # create own array of characters
1084 74         157 @chars = @$chars_org;
1085            
1086             # TESTING
1087             # println '[', join('] [', @chars), ']';
1088            
1089             # loop through characters
1090             CHAR_LOOP:
1091 74         122 while (@chars) {
1092 324         232 my $char = shift(@chars);
1093            
1094             # // - line comment
1095             # remove everything up to and including the end of line
1096 324 100       1076 if ($char eq '//') {
    100          
    50          
    100          
    100          
    100          
    100          
1097             LINE_COMMENT_LOOP:
1098 11         13 while (@chars) {
1099 197         118 my $next = shift(@chars);
1100            
1101             # if character is any of the end of line strings
1102 197 100       322 if ($newlines{$next})
1103 11         15 { last LINE_COMMENT_LOOP }
1104             }
1105             }
1106            
1107             # /* */ - inline comments
1108             # remove everything until */
1109             elsif ($char eq '/*') {
1110             INLINE_COMMENT_LOOP:
1111 5         15 while (@chars) {
1112 38         50 my $next = shift(@chars);
1113            
1114             # if character is any of the end of line strings
1115 38 100       58 if ($next eq '*/')
1116 4         8 { next CHAR_LOOP }
1117             }
1118            
1119             # if we get this far then the comment was never closed
1120 1         3 return $parser->error(
1121             'unclosed-inline-comment',
1122             'a comment was started with /* but was never closed'
1123             );
1124             }
1125            
1126             # /* */ - inline comments
1127             # remove everything until */
1128             elsif ($char eq '/*') {
1129             INLINE_COMMENT_LOOP:
1130 0         0 while (@chars) {
1131 0         0 my $next = shift(@chars);
1132            
1133             # if character is any of the end of line strings
1134 0 0       0 if ($next eq '*/')
1135 0         0 { last INLINE_COMMENT_LOOP }
1136             }
1137             }
1138            
1139             # white space: ignore
1140             elsif ($char =~ m|\s+|) {
1141             }
1142            
1143             # structural characters
1144             elsif ($JSON::Relaxed::structural{$char}) {
1145 147         246 push @tokens, $char;
1146             }
1147            
1148             # quotes
1149             # remove everything until next quote of same type
1150             elsif ($JSON::Relaxed::quotes{$char}) {
1151 37         56 my $str = JSON::Relaxed::Parser::Token::String::Quoted->new($parser, $char, \@chars);
1152 37         67 push @tokens, $str;
1153             }
1154            
1155             # "unknown" object string
1156             elsif ($parser->is_unknown_char($char)) {
1157 2         3 my $unknown = JSON::Relaxed::Parser::Token::Unknown->new($char);
1158 2         4 push @tokens, $unknown;
1159             }
1160            
1161             # else it's an unquoted string
1162             else {
1163 45         58 my $str = JSON::Relaxed::Parser::Token::String::Unquoted->new($parser, $char, \@chars);
1164 45         69 push @tokens, $str;
1165             }
1166             }
1167            
1168             # return tokens
1169 73         154 return @tokens;
1170             }
1171             #
1172             # tokenize
1173             #------------------------------------------------------------------------------
1174              
1175              
1176             #------------------------------------------------------------------------------
1177             # structure
1178             #
1179              
1180             =item structure()
1181              
1182             C<$parser->structure()> organizes the tokens from C>
1183             into a data structure. C<$parser->structure()> returns a single string, single
1184             array reference, a single hash reference, or (if there are errors) undef.
1185              
1186             =cut
1187              
1188             sub structure {
1189 65     65   98 my ($parser, $tokens, %opts) = @_;
1190 65         37 my ($rv, $opener);
1191            
1192             # TESTING
1193             # println subname(); ##i
1194            
1195             # get opening token
1196 65 100       74 if (defined $opts{'opener'})
1197 5         4 { $opener = $opts{'opener'} }
1198             else
1199 60         57 { $opener = shift(@$tokens) }
1200            
1201             # if no opener that's an error, so we're done
1202 65 100       86 if (! defined $opener)
1203 3         8 { return undef }
1204            
1205             # string
1206 62 100       74 if ($parser->is_string($opener)) {
    100          
    100          
1207 14         17 $rv = $opener->as_perl();
1208             }
1209            
1210             # opening of hash
1211             elsif ($opener eq '{') {
1212 27         37 $rv = JSON::Relaxed::Parser::Structure::Hash->build($parser, $tokens);
1213             }
1214            
1215             # opening of array
1216             elsif ($opener eq '[') {
1217 17         25 $rv = JSON::Relaxed::Parser::Structure::Array->build($parser, $tokens);
1218             }
1219            
1220             # else invalid opening character
1221             else {
1222 4         5 return $parser->error(
1223             'invalid-structure-opening-character',
1224             'expected { or [ but got ' .
1225             $parser->invalid_token($opener) . ' ' .
1226             'instead'
1227             );
1228             }
1229            
1230             # If this is the outer structure, and there are any tokens left, then
1231             # that's a multiple structure document. We don't allow that sort of thing
1232             # around here unless extra_tokens_ok is explicitly set to ok
1233 58 100       93 if ($opts{'top'}) {
1234 53 100       61 if (! $parser->is_error) {
1235 34 100       53 if (@$tokens) {
1236 11 100       25 unless ($parser->extra_tokens_ok()) {
1237 8         9 return $parser->error(
1238             'multiple-structures',
1239             'the string being parsed contains two separate structures, only one is allowed'
1240             );
1241             }
1242             }
1243             }
1244             }
1245            
1246             # return
1247 50         134 return $rv;
1248             }
1249             #
1250             # structure
1251             #------------------------------------------------------------------------------
1252              
1253              
1254             #------------------------------------------------------------------------------
1255             # invalid_token
1256             #
1257             sub invalid_token {
1258 16     16   13 my ($parser, $token) = @_;
1259            
1260             # string
1261 16 100       18 if ($parser->is_string($token)) {
    100          
1262 2         6 return 'string';
1263             }
1264            
1265             # object
1266             elsif (ref $token) {
1267 1         3 return ref($token) . ' object';
1268             }
1269            
1270             # scalar
1271             else {
1272 13         34 return $token;
1273             }
1274             }
1275             #
1276             # invalid_token
1277             #------------------------------------------------------------------------------
1278              
1279              
1280             #------------------------------------------------------------------------------
1281             # closing POD
1282             #
1283              
1284             =back
1285              
1286             =cut
1287              
1288             #
1289             # closing POD
1290             #------------------------------------------------------------------------------
1291              
1292              
1293             #
1294             # JSON::Relaxed::Parser
1295             ###############################################################################
1296              
1297              
1298              
1299             ###############################################################################
1300             # JSON::Relaxed::Parser::Structure::Hash
1301             #
1302             package JSON::Relaxed::Parser::Structure::Hash;
1303 1     1   4 use strict;
  1         1  
  1         315  
1304              
1305             # debugging
1306             # use Debug::ShowStuff ':all';
1307              
1308              
1309             #------------------------------------------------------------------------------
1310             # POD
1311             #
1312              
1313             =head2 JSON::Relaxed::Parser::Structure::Hash
1314              
1315             This package parses Relaxed into hash structures. It is a static package, i.e.
1316             it is not instantiated.
1317              
1318             =over 4
1319              
1320             =cut
1321              
1322             #
1323             # POD
1324             #------------------------------------------------------------------------------
1325              
1326              
1327             #------------------------------------------------------------------------------
1328             # build
1329             #
1330              
1331             =item build()
1332              
1333             This static method accepts the array of tokens and works through them building
1334             the hash reference that they represent. When C reaches the closing
1335             curly brace (C<}>) it returns the hash reference.
1336              
1337             =cut
1338              
1339             sub build {
1340 27     27   23 my ($class, $parser, $tokens) = @_;
1341 27         21 my $rv = {};
1342            
1343             # TESTING
1344             # println subname(); ##i
1345            
1346             # build hash
1347             # work through tokens until closing brace
1348             TOKENLOOP:
1349 27         50 while (@$tokens) {
1350 51         35 my $next = shift(@$tokens);
1351             # what is allowed after opening brace:
1352             # closing brace
1353             # comma
1354             # string
1355            
1356             # if closing brace, return
1357 51 100       103 if ($next eq '}') {
    100          
    100          
1358 12         16 return $rv;
1359             }
1360            
1361             # if comma, do nothing
1362             elsif ($next eq ',') {
1363             }
1364            
1365             # string
1366             # If the token is a string then it is a key. The token after that
1367             # should be a value.
1368             elsif ( $parser->is_string($next) ) {
1369 22         17 my ($key, $value, $t0);
1370 22         17 $t0 = $tokens->[0];
1371            
1372             # set key using string
1373 22         25 $key = $next->as_perl(always_string=>1);
1374            
1375             # if anything follows the string
1376 22 50       45 if (defined $t0) {
1377             # if next token is a colon then it should be followed by a value
1378 22 100       30 if ( $t0 eq ':' ) {
    100          
    50          
1379             # remove the colon
1380 18         17 shift(@$tokens);
1381            
1382             # if at end of token array, exit loop
1383 18 100       25 @$tokens or last TOKENLOOP;
1384            
1385             # get hash value
1386 16         22 $value = $class->get_value($parser, $tokens);
1387            
1388             # if there is a global error, return undef
1389 16 100       23 $parser->is_error() and return undef;
1390             }
1391            
1392             # a comma or closing brace is acceptable after a string
1393             elsif ($t0 eq ',') {
1394             }
1395             elsif ($t0 eq '}') {
1396             }
1397            
1398             # anything else is an error
1399             else {
1400 2         4 return $parser->error(
1401             'unknown-token-after-key',
1402             'expected comma or closing brace after a ' .
1403             'hash key, but got ' .
1404             $parser->invalid_token($t0) . ' ' .
1405             'instead'
1406             );
1407             }
1408             }
1409            
1410             # else nothing followed the string, so break out of token loop
1411             else {
1412 0         0 last TOKENLOOP;
1413             }
1414            
1415             # set key and value in return hash
1416 15         44 $rv->{$key} = $value;
1417             }
1418            
1419             # anything else is an error
1420             else {
1421 4         7 return $parser->error(
1422             'unknown-token-for-hash-key',
1423             'expected string, comma, or closing brace in a ' .
1424             'hash key, but got ' .
1425             $parser->invalid_token($next) . ' ' .
1426             'instead'
1427             );
1428             }
1429             }
1430            
1431             # if we get this far then unclosed brace
1432 6         7 return $parser->error(
1433             'unclosed-hash-brace',
1434             'do not find closing brace for hash'
1435             );
1436             }
1437             #
1438             # build
1439             #------------------------------------------------------------------------------
1440              
1441              
1442             #------------------------------------------------------------------------------
1443             # get_value
1444             #
1445              
1446             =item get_value
1447              
1448             This static method gets the value of a hash element. This method is called
1449             after a hash key is followed by a colon. A colon must be followed by a value.
1450             It may not be followed by the end of the tokens, a comma, or a closing brace.
1451              
1452             =cut
1453              
1454             sub get_value {
1455 16     16   11 my ($class, $parser, $tokens) = @_;
1456 16         13 my ($next);
1457            
1458             # TESTING
1459             # println subname(); ##i
1460            
1461             # get next token
1462 16         14 $next = shift(@$tokens);
1463            
1464             # next token must be string, array, or hash
1465             # string
1466 16 100       17 if ($parser->is_string($next)) {
    100          
1467 10         12 return $next->as_perl();
1468             }
1469            
1470             # token opens a hash
1471             elsif ($parser->is_list_opener($next)) {
1472 4         5 return $parser->structure($tokens, opener=>$next);
1473             }
1474            
1475             # at this point it's an illegal token
1476 2         5 return $parser->error(
1477             'unexpected-token-after-colon',
1478             'expected a value after a colon in a hash, got ' .
1479             $parser->invalid_token($next) . ' ' .
1480             'instead'
1481             );
1482             }
1483             #
1484             # get_value
1485             #------------------------------------------------------------------------------
1486              
1487              
1488             #------------------------------------------------------------------------------
1489             # closing POD
1490             #
1491              
1492             =back
1493              
1494             =cut
1495              
1496             #
1497             # closing POD
1498             #------------------------------------------------------------------------------
1499              
1500              
1501             #
1502             # JSON::Relaxed::Parser::Structure::Hash
1503             ###############################################################################
1504              
1505              
1506             ###############################################################################
1507             # JSON::Relaxed::Parser::Structure::Array
1508             #
1509             package JSON::Relaxed::Parser::Structure::Array;
1510 1     1   4 use strict;
  1         1  
  1         223  
1511              
1512             # debugging
1513             # use Debug::ShowStuff ':all';
1514              
1515              
1516             #------------------------------------------------------------------------------
1517             # POD
1518             #
1519              
1520             =head2 JSON::Relaxed::Parser::Structure::Array
1521              
1522             This package parses Relaxed into array structures. It is a static package, i.e.
1523             it is not instantiated.
1524              
1525             =over 4
1526              
1527             =cut
1528              
1529             #
1530             # POD
1531             #------------------------------------------------------------------------------
1532              
1533              
1534              
1535             #------------------------------------------------------------------------------
1536             # build
1537             #
1538              
1539             =item build()
1540              
1541             This static method accepts the array of tokens and works through them building
1542             the array reference that they represent. When C reaches the closing
1543             square brace (C<]>) it returns the array reference.
1544              
1545             =cut
1546              
1547             sub build {
1548 17     17   15 my ($class, $parser, $tokens) = @_;
1549 17         17 my $rv = [];
1550            
1551             # TESTING
1552             # println subname(); ##i
1553            
1554             # build array
1555             # work through tokens until closing brace
1556 17         24 while (@$tokens) {
1557 38         37 my $next = shift(@$tokens);
1558            
1559             # closing brace: we're done building this array
1560 38 100       56 if ($next eq ']') {
    100          
    100          
    100          
1561 12         17 return $rv;
1562             }
1563            
1564             # opening of hash or array
1565             elsif ($parser->is_list_opener($next)) {
1566 1         2 my $object = $parser->structure($tokens, opener=>$next);
1567 1 50       3 defined($object) or return undef;
1568 1         2 push @$rv, $object;
1569             }
1570            
1571             # comma: if we get to a comma at this point, do nothing with it
1572             elsif ($next eq ',') {
1573             }
1574            
1575             # if string, add it to the array
1576             elsif ($parser->is_string($next)) {
1577             # add the string to the array
1578 15         17 push @$rv, $next->as_perl();
1579            
1580             # check following token, which must be either a comma or
1581             # the closing brace
1582 15 100       22 if (@$tokens) {
1583 14   50     21 my $n2 = $tokens->[0] || '';
1584            
1585             # the next element must be a comma or the closing brace,
1586             # anything else is an error
1587 14 100 100     59 unless ( ($n2 eq ',') || ($n2 eq ']') ) {
1588 2         3 return missing_comma($parser, $n2);
1589             }
1590             }
1591             }
1592            
1593             # else unkown object or character, so throw error
1594             else {
1595 2         4 return invalid_array_token($parser, $next);
1596             }
1597             }
1598            
1599             # if we get this far then unclosed brace
1600 1         2 return $parser->error(
1601             'unclosed-array-brace',
1602             'do not find closing brace for array'
1603             );
1604             }
1605             #
1606             # build
1607             #------------------------------------------------------------------------------
1608              
1609              
1610             #------------------------------------------------------------------------------
1611             # missing_comma
1612             #
1613              
1614             =item missing_comma()
1615              
1616             This static method build the C error
1617             message.
1618              
1619             =cut
1620              
1621             sub missing_comma {
1622 2     2   3 my ($parser, $token) = @_;
1623            
1624             # initialize error message
1625 2         2 return $parser->error(
1626             'missing-comma-between-array-elements',
1627             'expected comma or closing array brace, got ' .
1628             $parser->invalid_token($token) . ' ' .
1629             'instead'
1630             );
1631             }
1632             #
1633             # missing_comma
1634             #------------------------------------------------------------------------------
1635              
1636              
1637             #------------------------------------------------------------------------------
1638             # invalid_array_token
1639             #
1640              
1641             =item invalid_array_token)
1642              
1643             This static method build the C error message.
1644              
1645             =cut
1646              
1647             sub invalid_array_token {
1648 2     2   2 my ($parser, $token) = @_;
1649            
1650             # initialize error message
1651 2         2 return $parser->error(
1652             'unknown-array-token',
1653             'unexpected item in array: got ' .
1654             $parser->invalid_token($token)
1655             );
1656             }
1657             #
1658             # invalid_array_token
1659             #------------------------------------------------------------------------------
1660              
1661              
1662             #------------------------------------------------------------------------------
1663             # closing POD
1664             #
1665              
1666             =back
1667              
1668             =cut
1669              
1670             #
1671             # closing POD
1672             #------------------------------------------------------------------------------
1673              
1674              
1675              
1676             #
1677             # JSON::Relaxed::Parser::Structure::Array
1678             ###############################################################################
1679              
1680              
1681              
1682             ###############################################################################
1683             # JSON::Relaxed::Parser::Token::String::Quoted
1684             #
1685             package JSON::Relaxed::Parser::Token::String;
1686 1     1   3 use strict;
  1         1  
  1         51  
1687              
1688             # debugging
1689             # use Debug::ShowStuff ':all';
1690              
1691              
1692             #------------------------------------------------------------------------------
1693             # POD
1694             #
1695              
1696             =head2 JSON::Relaxed::Parser::Token::String
1697              
1698             Base class . Nothing actually happens in this package, it's just a base class
1699             for JSON::Relaxed::Parser::Token::String::Quoted and
1700             JSON::Relaxed::Parser::Token::String::Unquoted.
1701              
1702             =cut
1703              
1704             #
1705             # POD
1706             #------------------------------------------------------------------------------
1707              
1708              
1709             #
1710             # JSON::Relaxed::Parser::Token::String
1711             ###############################################################################
1712              
1713              
1714              
1715             ###############################################################################
1716             # JSON::Relaxed::Parser::Token::String::Quoted
1717             #
1718             package JSON::Relaxed::Parser::Token::String::Quoted;
1719 1     1   6 use strict;
  1         1  
  1         22  
1720 1     1   3 use base 'JSON::Relaxed::Parser::Token::String';
  1         5  
  1         419  
1721              
1722             # debugging
1723             # use Debug::ShowStuff ':all';
1724              
1725              
1726             #------------------------------------------------------------------------------
1727             # POD
1728             #
1729              
1730             =head2 JSON::Relaxed::Parser::Token::String::Quoted
1731              
1732             A C object represents a string
1733             in the document that is delimited with single or double quotes. In the
1734             following example, I and I would be represented by C
1735             objects by I would not.
1736              
1737             [
1738             "Larry",
1739             'Curly',
1740             Moe
1741             ]
1742              
1743             C objects are created by C<$parser-Etokenize()> when it works
1744             through the array of characters in the document.
1745              
1746             =over 4
1747              
1748             =cut
1749              
1750             #
1751             # POD
1752             #------------------------------------------------------------------------------
1753              
1754              
1755              
1756             #------------------------------------------------------------------------------
1757             # new
1758             #
1759              
1760             =item * C
1761              
1762             C instantiates a C object
1763             and slurps in all the characters in the characters array until it gets to the
1764             closing quote. Then it returns the new C object.
1765              
1766             A C object has the following two properties:
1767              
1768             C: the string that is inside the quotes. If the string contained any
1769             escape characters then the escapes are processed and the unescaped characters
1770             are in C. So, for example, C<\n> would become an actual newline.
1771              
1772             C: the delimiting quote, i.e. either a single quote or a double quote.
1773              
1774              
1775             =cut
1776              
1777             sub new {
1778 37     37   39 my ($class, $parser, $quote, $chars) = @_;
1779 37         48 my $str = bless({}, $class);
1780            
1781             # TESTING
1782             # println subname(); ##i
1783            
1784             # initialize hash
1785 37         52 $str->{'quote'} = $quote;
1786 37         37 $str->{'raw'} = '';
1787            
1788             # loop through remaining characters until we find another quote
1789             CHAR_LOOP:
1790 37         50 while (@$chars) {
1791 146         92 my $next = shift(@$chars);
1792            
1793             # if this is the matching quote, we're done
1794 146 100       195 if ($next eq $str->{'quote'})
1795 35         50 { return $str }
1796            
1797             # if leading slash, check if it's a special escape character
1798 111 100       147 if ($next =~ s|^\\(.)|$1|s) {
1799 9 100       15 if ($JSON::Relaxed::esc{$next})
1800 6         4 { $next = $JSON::Relaxed::esc{$next} }
1801             }
1802            
1803             # add to raw
1804 111         154 $str->{'raw'} .= $next;
1805             }
1806            
1807             # if we get this far then we never found the closing quote
1808 2         3 return $parser->error(
1809             'unclosed-quote',
1810             'string does not have closing quote before end of file'
1811             );
1812             }
1813             #
1814             # new
1815             #------------------------------------------------------------------------------
1816              
1817              
1818             #------------------------------------------------------------------------------
1819             # as_perl
1820             #
1821              
1822             =item * C
1823              
1824             C returns the string that was in quotes (without the quotes).
1825              
1826             =cut
1827              
1828             sub as_perl {
1829 20     20   18 my ($str) = @_;
1830 20         21 return $str->{'raw'};
1831             }
1832             #
1833             # as_perl
1834             #------------------------------------------------------------------------------
1835              
1836              
1837             #------------------------------------------------------------------------------
1838             # close POD item list
1839             #
1840              
1841             =back
1842              
1843             =cut
1844              
1845             #
1846             # close POD item list
1847             #------------------------------------------------------------------------------
1848              
1849              
1850             #
1851             # JSON::Relaxed::Parser::Token::String::Quoted
1852             ###############################################################################
1853              
1854              
1855             ###############################################################################
1856             # JSON::Relaxed::Parser::Token::String::Unquoted
1857             #
1858             package JSON::Relaxed::Parser::Token::String::Unquoted;
1859 1     1   5 use strict;
  1         1  
  1         17  
1860 1     1   2 use base 'JSON::Relaxed::Parser::Token::String';
  1         1  
  1         368  
1861              
1862             # debugging
1863             # use Debug::ShowStuff ':all';
1864              
1865              
1866              
1867              
1868             #------------------------------------------------------------------------------
1869             # POD
1870             #
1871              
1872             =head2 JSON::Relaxed::Parser::Token::String::Unquoted
1873              
1874             A C object represents a string
1875             in the document that was not delimited quotes. In the following example,
1876             I would be represented by an C object, but I and I
1877             would not.
1878              
1879             [
1880             "Larry",
1881             'Curly',
1882             Moe
1883             ]
1884              
1885             C objects are created by C<$parser-Etokenize()> when it works
1886             through the array of characters in the document.
1887              
1888             An C object has one property, C, which is the string. Escaped
1889             characters are resolved in C.
1890              
1891             =over 4
1892              
1893             =cut
1894              
1895             #
1896             # POD
1897             #------------------------------------------------------------------------------
1898              
1899              
1900              
1901             #------------------------------------------------------------------------------
1902             # new
1903             #
1904              
1905             =item * C
1906              
1907             C instantiates a C
1908             object and slurps in all the characters in the characters array until it gets
1909             to a space character, a comment, or one of the structural characters such as
1910             C<{> or C<:>.
1911              
1912             =cut
1913              
1914             sub new {
1915 45     45   38 my ($class, $parser, $char, $chars) = @_;
1916 45         50 my $str = bless({}, $class);
1917            
1918             # TESTING
1919             # println subname(); ##i
1920            
1921             # initialize hash
1922 45         72 $str->{'raw'} = $char;
1923            
1924             # loop while not space or structural characters
1925             TOKEN_LOOP:
1926 45         61 while (@$chars) {
1927             # if structural character, we're done
1928 157 100       191 if ($JSON::Relaxed::structural{$chars->[0]})
1929 29         30 { last TOKEN_LOOP }
1930            
1931             # if space character, we're done
1932 128 100       183 if ($chars->[0] =~ m|\s+|s)
1933 9         13 { last TOKEN_LOOP }
1934            
1935             # if opening of a comment, we're done
1936 119 100       114 if ($parser->is_comment_opener($chars->[0]))
1937 2         2 { last TOKEN_LOOP }
1938            
1939             # add to raw string
1940 117         188 $str->{'raw'} .= shift(@$chars);
1941             }
1942            
1943             # return
1944 45         45 return $str;
1945             }
1946             #
1947             # new
1948             #------------------------------------------------------------------------------
1949              
1950              
1951             #------------------------------------------------------------------------------
1952             # as_perl
1953             #
1954              
1955             =item * C
1956              
1957             C returns the unquoted string or a boolean value, depending on how
1958             it is called.
1959              
1960             If the string is a boolean value, i.e. I, I, then the C
1961             return 1 (for true), 0 (for false) or undef (for null), B the
1962             C option is sent, in which case the string itself is returned.
1963             If the string does not represent a boolean value then it is returned as-is.
1964              
1965             C<$parser-Estructure()> sends the C when the token is a key
1966             in a hash. The following example should clarify how C is used:
1967              
1968             {
1969             // key: the literal string "larry"
1970             // value: 1
1971             larry : true,
1972            
1973             // key: the literal string "true"
1974             // value: 'x'
1975             true : 'x',
1976            
1977             // key: the literal string "null"
1978             // value: 'y'
1979             null : 'y',
1980            
1981             // key: the literal string "z"
1982             // value: undef
1983             z : null,
1984             }
1985              
1986             =cut
1987              
1988             sub as_perl {
1989 41     41   41 my ($str, %opts) = @_;
1990 41         41 my $rv = $str->{'raw'};
1991            
1992             # if string is one of the unquoted boolean values
1993             # unless options indicate to always return the value as a string, check it
1994             # the value is one of the boolean string
1995 41 100       49 unless ($opts{'always_string'}) {
1996 26 100       45 if (exists $JSON::Relaxed::boolean{lc $rv}) {
1997 13         15 $rv = $JSON::Relaxed::boolean{lc $rv};
1998             }
1999             }
2000            
2001             # return
2002 41         57 return $rv;
2003             }
2004             #
2005             # as_perl
2006             #------------------------------------------------------------------------------
2007              
2008              
2009             #------------------------------------------------------------------------------
2010             # close POD item list
2011             #
2012              
2013             =back
2014              
2015             =cut
2016              
2017             #
2018             # close POD item list
2019             #------------------------------------------------------------------------------
2020              
2021              
2022             #
2023             # JSON::Relaxed::Parser::Token::String::Unquoted
2024             ###############################################################################
2025              
2026              
2027             ###############################################################################
2028             # JSON::Relaxed::Parser::Token::Unknown
2029             #
2030             package JSON::Relaxed::Parser::Token::Unknown;
2031 1     1   4 use strict;
  1         4  
  1         63  
2032              
2033             #------------------------------------------------------------------------------
2034             # POD
2035             #
2036              
2037             =head2 JSON::Relaxed::Parser::Token::Unknown
2038              
2039             This class is just used for development of JSON::Relaxed. It has no use in
2040             production. This class allows testing for when a token is an unknown object.
2041              
2042             To implement this class, add the 'unknown' option to JSON::Relaxed->new(). The
2043             value of the option should be the character that creates an unknown object.
2044             For example, the following option sets the tilde (~) as an unknown object.
2045              
2046             my $parser = JSON::Relaxed::Parser->new(unknown=>'~');
2047              
2048             The "unknown" character must not be inside quotes or inside an unquoted string.
2049              
2050             =cut
2051              
2052             #
2053             # POD
2054             #------------------------------------------------------------------------------
2055              
2056              
2057              
2058              
2059             #------------------------------------------------------------------------------
2060             # new
2061             #
2062             sub new {
2063 2     2   2 my ($class, $char) = @_;
2064 2         3 my $unknown = bless({}, $class);
2065 2         6 $unknown->{'raw'} = $char;
2066 2         3 return $unknown;
2067             }
2068             #
2069             # new
2070             #------------------------------------------------------------------------------
2071              
2072             #
2073             # JSON::Relaxed::Parser::Token::Unknown
2074             ###############################################################################
2075              
2076              
2077             # return true
2078             1;
2079              
2080              
2081             __END__