diff --git a/IMPORTANT b/IMPORTANT new file mode 100644 index 0000000..16d44f3 --- /dev/null +++ b/IMPORTANT @@ -0,0 +1 @@ +don't edit the jsx_utfx.erl source files directly, edit /priv/jsx_decoder.erl and run `./priv/backends.escript create` to regenerate the jsx_utfx.erl source files. \ No newline at end of file diff --git a/README.markdown b/README.markdown index 0780e79..f7e5796 100644 --- a/README.markdown +++ b/README.markdown @@ -89,6 +89,8 @@ jsx is stream based and allows the parsing of naked, unwrapped json values. toge ### notes ### +don't edit the various jsx\_utfx.erl files in the src dir directly, see /priv/jsx\_decoder.erl for why + jsx supports utf8, utf16 (little and big endian) and utf32 (little and big endian). future support is planned for erlang iolists diff --git a/makefile b/makefile index 36cfa2a..f4ec36d 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,5 @@ -compile: expand +compile: ./rebar compile - ./priv/backends.escript clean expand: ./priv/backends.escript create @@ -13,7 +12,6 @@ prove: compile clean: ./rebar clean - ./priv/backends.escript clean install: compile ./rebar -f install \ No newline at end of file diff --git a/priv/jsx_decoder.erl b/priv/jsx_decoder.erl index 8f6b073..84398a3 100644 --- a/priv/jsx_decoder.erl +++ b/priv/jsx_decoder.erl @@ -21,8 +21,10 @@ %% THE SOFTWARE. -%% this is a template for the utf8, utf16, utf16le, utf32 and utf32le decoders. it should -%% not be compiled directly, see the build script in /priv for details +%% this is the template for the utf backends for the jsx decoder. run +%% `./priv/backends.escript create` to regenerate the source code for the various +%% decoders if you edit this file. do not edit the resulting source files directly. + -module(?name). -author("alisdairsullivan@yahoo.ca"). diff --git a/src/jsx_utf16.erl b/src/jsx_utf16.erl new file mode 100644 index 0000000..8eca30e --- /dev/null +++ b/src/jsx_utf16.erl @@ -0,0 +1,1284 @@ +-file("priv/jsx_decoder.erl", 1). + +-module(jsx_utf16). + +-author("alisdairsullivan@yahoo.ca"). + +-export([parse/2]). + +-file("./include/jsx_decoder.hrl", 1). + +-file("priv/jsx_decoder.erl", 38). + +-file("./include/jsx_types.hrl", 1). + +-type jsx_opts() :: [jsx_opt()]. + +-type jsx_opt() :: {comments, true | false} + | {escaped_unicode, ascii | codepoint | none} + | {multi_term, true | false} + | {encoding, + auto | + utf8 | + utf16 | + {utf16, little} | + utf32 | + {utf32, little}}. + +-type unicode_codepoint() :: 0..1114111. + +-type unicode_string() :: [unicode_codepoint()]. + +-type jsx_event() :: start_object + | end_object + | start_array + | end_array + | end_json + | {key, unicode_string()} + | {string, unicode_string()} + | {integer, unicode_string()} + | {float, unicode_string()} + | {literal, true} + | {literal, false} + | {literal, null}. + +-type jsx_parser() :: fun((binary()) -> jsx_parser_result()). + +-type jsx_parser_result() :: {event, + jsx_event(), + fun(() -> jsx_parser_result())} + | {incomplete, jsx_parser()} + | {error, badjson} + | ok. + +-type json() :: json_object() | json_array(). + +-type json_array() :: [json_term()]. + +-type json_object() :: [{json_key(), json_term()}]. + +-type json_key() :: binary() | atom(). + +-type json_term() :: json_array() + | json_object() + | json_string() + | json_number() + | true + | false + | null. + +-type json_string() :: binary(). + +-type json_number() :: float() | integer(). + +-type supported_utf() :: utf8 + | utf16 + | {utf16, little} + | utf32 + | {utf32, little}. + +-type encoder_opts() :: [encoder_opt()]. + +-type encoder_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {space, integer()} + | space + | {indent, integer()} + | indent. + +-type decoder_opts() :: [decoder_opt()]. + +-type decoder_opt() :: {strict, true | false} + | {comments, true | false} + | {encoding, supported_utf()} + | {label, atom | binary | existing_atom} + | {float, true | false}. + +-type verify_opts() :: [verify_opt()]. + +-type verify_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {comments, true | false}. + +-type format_opts() :: [format_opt()]. + +-type format_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {comments, true | false} + | {space, integer()} + | space + | {indent, integer()} + | indent + | {output_encoding, supported_utf()}. + +-file("priv/jsx_decoder.erl", 39). + +-spec parse(JSON :: json(), Opts :: jsx_opts()) -> jsx_parser_result(). + +parse(JSON, Opts) -> + start(JSON, [], Opts). + +start(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + start(Rest, Stack, Opts); +start(<<123/utf16,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +start(<<91/utf16,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +start(<<34/utf16,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +start(<<$t/utf16,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +start(<<$f/utf16,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +start(<<$n/utf16,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +start(<<45/utf16,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +start(<<48/utf16,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +start(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +start(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + start(Resume, Stack, Opts) + end); +start(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + start(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +maybe_done(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + maybe_done(Rest, Stack, Opts); +maybe_done(<<125/utf16,Rest/binary>>, [object|Stack], Opts) -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +maybe_done(<<93/utf16,Rest/binary>>, [array|Stack], Opts) -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +maybe_done(<<44/utf16,Rest/binary>>, [object|Stack], Opts) -> + key(Rest, [key|Stack], Opts); +maybe_done(<<44/utf16,Rest/binary>>, [array|_] = Stack, Opts) -> + value(Rest, Stack, Opts); +maybe_done(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + maybe_done(Resume, Stack, Opts) + end); +maybe_done(Rest, [], {_,_,_,true,_} = Opts) -> + {event, + end_json, + fun() -> + start(Rest, [], Opts) + end}; +maybe_done(Rest, [], Opts) -> + done(Rest, Opts); +maybe_done(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_done(<>, + Stack, + Opts) + end}; + false -> + {error,badjson} + end. + +done(<>, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + done(Rest, Opts); +done(<<47/utf16,Rest/binary>>, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + done(Resume, Opts) + end); +done(<<>>, Opts) -> + {event, + end_json, + fun() -> + {incomplete, + fun(end_stream) -> + done(<<>>, Opts); + (Stream) -> + done(Stream, Opts) + end} + end}; +done(Bin, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + done(<>, Opts) + end}; + false -> + {error,badjson} + end. + +object(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + object(Rest, Stack, Opts); +object(<<34/utf16,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +object(<<125/utf16,Rest/binary>>, [key|Stack], Opts) -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +object(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + object(Resume, Stack, Opts) + end); +object(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + object(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +array(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + array(Rest, Stack, Opts); +array(<<34/utf16,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +array(<<$t/utf16,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +array(<<$f/utf16,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +array(<<$n/utf16,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +array(<<45/utf16,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +array(<<48/utf16,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +array(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +array(<<123/utf16,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +array(<<91/utf16,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +array(<<93/utf16,Rest/binary>>, [array|Stack], Opts) -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +array(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + array(Resume, Stack, Opts) + end); +array(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + array(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +value(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + value(Rest, Stack, Opts); +value(<<34/utf16,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +value(<<$t/utf16,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +value(<<$f/utf16,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +value(<<$n/utf16,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +value(<<45/utf16,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +value(<<48/utf16,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +value(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +value(<<123/utf16,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +value(<<91/utf16,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +value(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + value(Resume, Stack, Opts) + end); +value(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + value(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +colon(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + colon(Rest, Stack, Opts); +colon(<<58/utf16,Rest/binary>>, [key|Stack], Opts) -> + value(Rest, [object|Stack], Opts); +colon(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + colon(Resume, Stack, Opts) + end); +colon(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + colon(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +key(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + key(Rest, Stack, Opts); +key(<<34/utf16,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +key(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + key(Resume, Stack, Opts) + end); +key(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + key(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +string(<<34/utf16,Rest/binary>>, [key|_] = Stack, Opts, Acc) -> + {event, + {key,lists:reverse(Acc)}, + fun() -> + colon(Rest, Stack, Opts) + end}; +string(<<34/utf16,Rest/binary>>, Stack, Opts, Acc) -> + {event, + {string,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +string(<<92/utf16,Rest/binary>>, Stack, Opts, Acc) -> + escape(Rest, Stack, Opts, Acc); +string(<>, Stack, Opts, Acc) when S >= 32 -> + string(Rest, Stack, Opts, [S] ++ Acc); +string(Bin, Stack, Opts, Acc) -> + case partial_utf(Bin) of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + string(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +partial_utf(<<>>) -> + true; +partial_utf(<<_X>>) -> + true; +partial_utf(<>) when X >= 216, X =< 223 -> + true; +partial_utf(<>) when X >= 216, X =< 223, Z >= 220, Z =< 223 -> + true; +partial_utf(_) -> + false. + +escape(<<$b/utf16,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\b" ++ Acc); +escape(<<$f/utf16,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\f" ++ Acc); +escape(<<$n/utf16,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\n" ++ Acc); +escape(<<$r/utf16,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\r" ++ Acc); +escape(<<$t/utf16,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\t" ++ Acc); +escape(<<$u/utf16,Rest/binary>>, Stack, Opts, Acc) -> + escaped_unicode(Rest, Stack, Opts, Acc, []); +escape(<>, Stack, Opts, Acc) + when S =:= 34; S =:= 47; S =:= 92 -> + string(Rest, Stack, Opts, [S] ++ Acc); +escape(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + escape(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +escaped_unicode(<>, + Stack, + {_,_,ascii,_,_} = Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X < 128 -> + string(Rest, Stack, Opts, [X] ++ String); + _ -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String) + end; +escaped_unicode(<>, + Stack, + {_,_,codepoint,_,_} = Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X >= 56320, X =< 57343 -> + case check_acc_for_surrogate(String) of + false -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String); + {Y,NewString} -> + string(Rest, + Stack, + Opts, + [surrogate_to_codepoint(Y, X)] ++ NewString) + end; + X when X < 55296; X > 57343, X < 65534 -> + string(Rest, Stack, Opts, [X] ++ String); + _ -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String) + end; +escaped_unicode(<>, Stack, Opts, String, [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String); +escaped_unicode(<>, Stack, Opts, String, Acc) + when + S >= $a + andalso + S =< $z; + S >= $A + andalso + S =< $Z; + S >= $0 + andalso + S =< $9 -> + escaped_unicode(Rest, Stack, Opts, String, [S] ++ Acc); +escaped_unicode(Bin, Stack, Opts, String, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + escaped_unicode(<>, + Stack, + Opts, + String, + Acc) + end}; + false -> + {error,badjson} + end. + +check_acc_for_surrogate([D,C,B,A,$u,92|Rest]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9, + C >= $a + andalso + C =< $z; + C >= $A + andalso + C =< $Z; + C >= $0 + andalso + C =< $9, + B >= $a + andalso + B =< $z; + B >= $A + andalso + B =< $Z; + B >= $0 + andalso + B =< $9, + A >= $a + andalso + A =< $z; + A >= $A + andalso + A =< $Z; + A >= $0 + andalso + A =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X >= 55296, X =< 56319 -> + {X,Rest}; + _ -> + false + end; +check_acc_for_surrogate(_) -> + false. + +surrogate_to_codepoint(High, Low) -> + (High - 55296) * 1024 + (Low - 56320) + 65536. + +negative(<<$0/utf16,Rest/binary>>, Stack, Opts, Acc) -> + zero(Rest, Stack, Opts, "0" ++ Acc); +negative(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S] ++ Acc); +negative(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + negative(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +zero(<<125/utf16,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +zero(<<93/utf16,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +zero(<<44/utf16,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +zero(<<44/utf16,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +zero(<<46/utf16,Rest/binary>>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [46] ++ Acc); +zero(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +zero(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + zero(Resume, Stack, Opts, Acc) + end); +zero(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + zero(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + zero(Stream, [], Opts, Acc) + end}; +zero(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + zero(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +integer(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S] ++ Acc); +integer(<<125/utf16,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +integer(<<93/utf16,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +integer(<<44/utf16,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +integer(<<44/utf16,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +integer(<<46/utf16,Rest/binary>>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [46] ++ Acc); +integer(<<48/utf16,Rest/binary>>, Stack, Opts, Acc) -> + integer(Rest, Stack, Opts, [48] ++ Acc); +integer(<<$e/utf16,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<<$E/utf16,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +integer(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + integer(Resume, Stack, Opts, Acc) + end); +integer(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + integer(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + integer(Stream, [], Opts, Acc) + end}; +integer(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + integer(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +initial_decimal(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +initial_decimal(<<48/utf16,Rest/binary>>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [48] ++ Acc); +initial_decimal(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + initial_decimal(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +decimal(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +decimal(<<125/utf16,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +decimal(<<93/utf16,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +decimal(<<44/utf16,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +decimal(<<44/utf16,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +decimal(<<48/utf16,Rest/binary>>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [48] ++ Acc); +decimal(<<$e/utf16,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<<$E/utf16,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +decimal(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + decimal(Resume, Stack, Opts, Acc) + end); +decimal(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + decimal(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + decimal(Stream, [], Opts, Acc) + end}; +decimal(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + decimal(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +e(<>, Stack, Opts, Acc) + when + S =:= 48; + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +e(<>, Stack, Opts, Acc) when S =:= 43; S =:= 45 -> + ex(Rest, Stack, Opts, [S] ++ Acc); +e(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + e(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +ex(<>, Stack, Opts, Acc) + when + S =:= 48; + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +ex(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + ex(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +exp(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +exp(<<125/utf16,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +exp(<<93/utf16,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +exp(<<44/utf16,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +exp(<<44/utf16,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +exp(<<48/utf16,Rest/binary>>, Stack, Opts, Acc) -> + exp(Rest, Stack, Opts, [48] ++ Acc); +exp(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +exp(<<47/utf16,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + exp(Resume, Stack, Opts, Acc) + end); +exp(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + exp(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + exp(Stream, [], Opts, Acc) + end}; +exp(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + exp(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +tr(<<$r/utf16,Rest/binary>>, Stack, Opts) -> + tru(Rest, Stack, Opts); +tr(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + tr(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +tru(<<$u/utf16,Rest/binary>>, Stack, Opts) -> + true(Rest, Stack, Opts); +tru(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + tru(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +true(<<$e/utf16,Rest/binary>>, Stack, Opts) -> + {event, + {literal,true}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +true(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + true(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fa(<<$a/utf16,Rest/binary>>, Stack, Opts) -> + fal(Rest, Stack, Opts); +fa(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fa(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fal(<<$l/utf16,Rest/binary>>, Stack, Opts) -> + fals(Rest, Stack, Opts); +fal(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fal(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fals(<<$s/utf16,Rest/binary>>, Stack, Opts) -> + false(Rest, Stack, Opts); +fals(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fals(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +false(<<$e/utf16,Rest/binary>>, Stack, Opts) -> + {event, + {literal,false}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +false(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + false(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +nu(<<$u/utf16,Rest/binary>>, Stack, Opts) -> + nul(Rest, Stack, Opts); +nu(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + nu(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +nul(<<$l/utf16,Rest/binary>>, Stack, Opts) -> + null(Rest, Stack, Opts); +nul(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + nul(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +null(<<$l/utf16,Rest/binary>>, Stack, Opts) -> + {event, + {literal,null}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +null(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + null(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +maybe_comment(<<42/utf16,Rest/binary>>, Resume) -> + comment(Rest, Resume); +maybe_comment(Bin, Resume) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_comment(<>, Resume) + end}; + false -> + {error,badjson} + end. + +comment(<<42/utf16,Rest/binary>>, Resume) -> + maybe_comment_done(Rest, Resume); +comment(<<_/utf16,Rest/binary>>, Resume) -> + comment(Rest, Resume); +comment(Bin, Resume) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + comment(<>, Resume) + end}; + false -> + {error,badjson} + end. + +maybe_comment_done(<<47/utf16,Rest/binary>>, Resume) -> + Resume(Rest); +maybe_comment_done(<<_/utf16,Rest/binary>>, Resume) -> + comment(Rest, Resume); +maybe_comment_done(Bin, Resume) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_comment_done(<>, + Resume) + end}; + false -> + {error,badjson} + end. + + + diff --git a/src/jsx_utf16le.erl b/src/jsx_utf16le.erl new file mode 100644 index 0000000..94c9b5b --- /dev/null +++ b/src/jsx_utf16le.erl @@ -0,0 +1,1301 @@ +-file("priv/jsx_decoder.erl", 1). + +-module(jsx_utf16le). + +-author("alisdairsullivan@yahoo.ca"). + +-export([parse/2]). + +-file("./include/jsx_decoder.hrl", 1). + +-file("priv/jsx_decoder.erl", 38). + +-file("./include/jsx_types.hrl", 1). + +-type jsx_opts() :: [jsx_opt()]. + +-type jsx_opt() :: {comments, true | false} + | {escaped_unicode, ascii | codepoint | none} + | {multi_term, true | false} + | {encoding, + auto | + utf8 | + utf16 | + {utf16, little} | + utf32 | + {utf32, little}}. + +-type unicode_codepoint() :: 0..1114111. + +-type unicode_string() :: [unicode_codepoint()]. + +-type jsx_event() :: start_object + | end_object + | start_array + | end_array + | end_json + | {key, unicode_string()} + | {string, unicode_string()} + | {integer, unicode_string()} + | {float, unicode_string()} + | {literal, true} + | {literal, false} + | {literal, null}. + +-type jsx_parser() :: fun((binary()) -> jsx_parser_result()). + +-type jsx_parser_result() :: {event, + jsx_event(), + fun(() -> jsx_parser_result())} + | {incomplete, jsx_parser()} + | {error, badjson} + | ok. + +-type json() :: json_object() | json_array(). + +-type json_array() :: [json_term()]. + +-type json_object() :: [{json_key(), json_term()}]. + +-type json_key() :: binary() | atom(). + +-type json_term() :: json_array() + | json_object() + | json_string() + | json_number() + | true + | false + | null. + +-type json_string() :: binary(). + +-type json_number() :: float() | integer(). + +-type supported_utf() :: utf8 + | utf16 + | {utf16, little} + | utf32 + | {utf32, little}. + +-type encoder_opts() :: [encoder_opt()]. + +-type encoder_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {space, integer()} + | space + | {indent, integer()} + | indent. + +-type decoder_opts() :: [decoder_opt()]. + +-type decoder_opt() :: {strict, true | false} + | {comments, true | false} + | {encoding, supported_utf()} + | {label, atom | binary | existing_atom} + | {float, true | false}. + +-type verify_opts() :: [verify_opt()]. + +-type verify_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {comments, true | false}. + +-type format_opts() :: [format_opt()]. + +-type format_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {comments, true | false} + | {space, integer()} + | space + | {indent, integer()} + | indent + | {output_encoding, supported_utf()}. + +-file("priv/jsx_decoder.erl", 39). + +-spec parse(JSON :: json(), Opts :: jsx_opts()) -> jsx_parser_result(). + +parse(JSON, Opts) -> + start(JSON, [], Opts). + +start(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + start(Rest, Stack, Opts); +start(<<123/utf16-little,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +start(<<91/utf16-little,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +start(<<34/utf16-little,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +start(<<$t/utf16-little,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +start(<<$f/utf16-little,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +start(<<$n/utf16-little,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +start(<<45/utf16-little,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +start(<<48/utf16-little,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +start(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +start(<<47/utf16-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + start(Resume, Stack, Opts) + end); +start(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + start(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +maybe_done(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + maybe_done(Rest, Stack, Opts); +maybe_done(<<125/utf16-little,Rest/binary>>, [object|Stack], Opts) -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +maybe_done(<<93/utf16-little,Rest/binary>>, [array|Stack], Opts) -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +maybe_done(<<44/utf16-little,Rest/binary>>, [object|Stack], Opts) -> + key(Rest, [key|Stack], Opts); +maybe_done(<<44/utf16-little,Rest/binary>>, [array|_] = Stack, Opts) -> + value(Rest, Stack, Opts); +maybe_done(<<47/utf16-little,Rest/binary>>, + Stack, + {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + maybe_done(Resume, Stack, Opts) + end); +maybe_done(Rest, [], {_,_,_,true,_} = Opts) -> + {event, + end_json, + fun() -> + start(Rest, [], Opts) + end}; +maybe_done(Rest, [], Opts) -> + done(Rest, Opts); +maybe_done(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_done(<>, + Stack, + Opts) + end}; + false -> + {error,badjson} + end. + +done(<>, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + done(Rest, Opts); +done(<<47/utf16-little,Rest/binary>>, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + done(Resume, Opts) + end); +done(<<>>, Opts) -> + {event, + end_json, + fun() -> + {incomplete, + fun(end_stream) -> + done(<<>>, Opts); + (Stream) -> + done(Stream, Opts) + end} + end}; +done(Bin, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + done(<>, Opts) + end}; + false -> + {error,badjson} + end. + +object(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + object(Rest, Stack, Opts); +object(<<34/utf16-little,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +object(<<125/utf16-little,Rest/binary>>, [key|Stack], Opts) -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +object(<<47/utf16-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + object(Resume, Stack, Opts) + end); +object(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + object(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +array(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + array(Rest, Stack, Opts); +array(<<34/utf16-little,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +array(<<$t/utf16-little,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +array(<<$f/utf16-little,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +array(<<$n/utf16-little,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +array(<<45/utf16-little,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +array(<<48/utf16-little,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +array(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +array(<<123/utf16-little,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +array(<<91/utf16-little,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +array(<<93/utf16-little,Rest/binary>>, [array|Stack], Opts) -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +array(<<47/utf16-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + array(Resume, Stack, Opts) + end); +array(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + array(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +value(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + value(Rest, Stack, Opts); +value(<<34/utf16-little,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +value(<<$t/utf16-little,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +value(<<$f/utf16-little,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +value(<<$n/utf16-little,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +value(<<45/utf16-little,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +value(<<48/utf16-little,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +value(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +value(<<123/utf16-little,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +value(<<91/utf16-little,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +value(<<47/utf16-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + value(Resume, Stack, Opts) + end); +value(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + value(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +colon(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + colon(Rest, Stack, Opts); +colon(<<58/utf16-little,Rest/binary>>, [key|Stack], Opts) -> + value(Rest, [object|Stack], Opts); +colon(<<47/utf16-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + colon(Resume, Stack, Opts) + end); +colon(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + colon(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +key(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + key(Rest, Stack, Opts); +key(<<34/utf16-little,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +key(<<47/utf16-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + key(Resume, Stack, Opts) + end); +key(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + key(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +string(<<34/utf16-little,Rest/binary>>, [key|_] = Stack, Opts, Acc) -> + {event, + {key,lists:reverse(Acc)}, + fun() -> + colon(Rest, Stack, Opts) + end}; +string(<<34/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + {event, + {string,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +string(<<92/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + escape(Rest, Stack, Opts, Acc); +string(<>, Stack, Opts, Acc) when S >= 32 -> + string(Rest, Stack, Opts, [S] ++ Acc); +string(Bin, Stack, Opts, Acc) -> + case partial_utf(Bin) of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + string(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +partial_utf(<<>>) -> + true; +partial_utf(<<_X>>) -> + true; +partial_utf(<<_Y,X>>) when X >= 216, X =< 223 -> + true; +partial_utf(<<_Y,X,_Z>>) when X >= 216, X =< 223 -> + true; +partial_utf(_) -> + false. + +escape(<<$b/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\b" ++ Acc); +escape(<<$f/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\f" ++ Acc); +escape(<<$n/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\n" ++ Acc); +escape(<<$r/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\r" ++ Acc); +escape(<<$t/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\t" ++ Acc); +escape(<<$u/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + escaped_unicode(Rest, Stack, Opts, Acc, []); +escape(<>, Stack, Opts, Acc) + when S =:= 34; S =:= 47; S =:= 92 -> + string(Rest, Stack, Opts, [S] ++ Acc); +escape(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + escape(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +escaped_unicode(<>, + Stack, + {_,_,ascii,_,_} = Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X < 128 -> + string(Rest, Stack, Opts, [X] ++ String); + _ -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String) + end; +escaped_unicode(<>, + Stack, + {_,_,codepoint,_,_} = Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X >= 56320, X =< 57343 -> + case check_acc_for_surrogate(String) of + false -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String); + {Y,NewString} -> + string(Rest, + Stack, + Opts, + [surrogate_to_codepoint(Y, X)] ++ NewString) + end; + X when X < 55296; X > 57343, X < 65534 -> + string(Rest, Stack, Opts, [X] ++ String); + _ -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String) + end; +escaped_unicode(<>, + Stack, + Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String); +escaped_unicode(<>, + Stack, + Opts, + String, + Acc) + when + S >= $a + andalso + S =< $z; + S >= $A + andalso + S =< $Z; + S >= $0 + andalso + S =< $9 -> + escaped_unicode(Rest, Stack, Opts, String, [S] ++ Acc); +escaped_unicode(Bin, Stack, Opts, String, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + escaped_unicode(<>, + Stack, + Opts, + String, + Acc) + end}; + false -> + {error,badjson} + end. + +check_acc_for_surrogate([D,C,B,A,$u,92|Rest]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9, + C >= $a + andalso + C =< $z; + C >= $A + andalso + C =< $Z; + C >= $0 + andalso + C =< $9, + B >= $a + andalso + B =< $z; + B >= $A + andalso + B =< $Z; + B >= $0 + andalso + B =< $9, + A >= $a + andalso + A =< $z; + A >= $A + andalso + A =< $Z; + A >= $0 + andalso + A =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X >= 55296, X =< 56319 -> + {X,Rest}; + _ -> + false + end; +check_acc_for_surrogate(_) -> + false. + +surrogate_to_codepoint(High, Low) -> + (High - 55296) * 1024 + (Low - 56320) + 65536. + +negative(<<$0/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + zero(Rest, Stack, Opts, "0" ++ Acc); +negative(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S] ++ Acc); +negative(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + negative(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +zero(<<125/utf16-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +zero(<<93/utf16-little,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +zero(<<44/utf16-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +zero(<<44/utf16-little,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +zero(<<46/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [46] ++ Acc); +zero(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +zero(<<47/utf16-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + zero(Resume, Stack, Opts, Acc) + end); +zero(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + zero(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + zero(Stream, [], Opts, Acc) + end}; +zero(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + zero(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +integer(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S] ++ Acc); +integer(<<125/utf16-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +integer(<<93/utf16-little,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +integer(<<44/utf16-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +integer(<<44/utf16-little,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +integer(<<46/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [46] ++ Acc); +integer(<<48/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + integer(Rest, Stack, Opts, [48] ++ Acc); +integer(<<$e/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<<$E/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +integer(<<47/utf16-little,Rest/binary>>, + Stack, + {_,true,_,_,_} = Opts, + Acc) -> + maybe_comment(Rest, + fun(Resume) -> + integer(Resume, Stack, Opts, Acc) + end); +integer(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + integer(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + integer(Stream, [], Opts, Acc) + end}; +integer(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + integer(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +initial_decimal(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +initial_decimal(<<48/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [48] ++ Acc); +initial_decimal(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + initial_decimal(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +decimal(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +decimal(<<125/utf16-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +decimal(<<93/utf16-little,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +decimal(<<44/utf16-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +decimal(<<44/utf16-little,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +decimal(<<48/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [48] ++ Acc); +decimal(<<$e/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<<$E/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +decimal(<<47/utf16-little,Rest/binary>>, + Stack, + {_,true,_,_,_} = Opts, + Acc) -> + maybe_comment(Rest, + fun(Resume) -> + decimal(Resume, Stack, Opts, Acc) + end); +decimal(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + decimal(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + decimal(Stream, [], Opts, Acc) + end}; +decimal(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + decimal(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +e(<>, Stack, Opts, Acc) + when + S =:= 48; + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +e(<>, Stack, Opts, Acc) + when S =:= 43; S =:= 45 -> + ex(Rest, Stack, Opts, [S] ++ Acc); +e(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + e(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +ex(<>, Stack, Opts, Acc) + when + S =:= 48; + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +ex(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + ex(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +exp(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +exp(<<125/utf16-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +exp(<<93/utf16-little,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +exp(<<44/utf16-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +exp(<<44/utf16-little,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +exp(<<48/utf16-little,Rest/binary>>, Stack, Opts, Acc) -> + exp(Rest, Stack, Opts, [48] ++ Acc); +exp(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +exp(<<47/utf16-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + exp(Resume, Stack, Opts, Acc) + end); +exp(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + exp(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + exp(Stream, [], Opts, Acc) + end}; +exp(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + exp(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +tr(<<$r/utf16-little,Rest/binary>>, Stack, Opts) -> + tru(Rest, Stack, Opts); +tr(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + tr(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +tru(<<$u/utf16-little,Rest/binary>>, Stack, Opts) -> + true(Rest, Stack, Opts); +tru(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + tru(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +true(<<$e/utf16-little,Rest/binary>>, Stack, Opts) -> + {event, + {literal,true}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +true(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + true(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fa(<<$a/utf16-little,Rest/binary>>, Stack, Opts) -> + fal(Rest, Stack, Opts); +fa(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fa(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fal(<<$l/utf16-little,Rest/binary>>, Stack, Opts) -> + fals(Rest, Stack, Opts); +fal(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fal(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fals(<<$s/utf16-little,Rest/binary>>, Stack, Opts) -> + false(Rest, Stack, Opts); +fals(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fals(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +false(<<$e/utf16-little,Rest/binary>>, Stack, Opts) -> + {event, + {literal,false}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +false(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + false(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +nu(<<$u/utf16-little,Rest/binary>>, Stack, Opts) -> + nul(Rest, Stack, Opts); +nu(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + nu(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +nul(<<$l/utf16-little,Rest/binary>>, Stack, Opts) -> + null(Rest, Stack, Opts); +nul(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + nul(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +null(<<$l/utf16-little,Rest/binary>>, Stack, Opts) -> + {event, + {literal,null}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +null(Bin, Stack, Opts) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + null(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +maybe_comment(<<42/utf16-little,Rest/binary>>, Resume) -> + comment(Rest, Resume); +maybe_comment(Bin, Resume) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_comment(<>, Resume) + end}; + false -> + {error,badjson} + end. + +comment(<<42/utf16-little,Rest/binary>>, Resume) -> + maybe_comment_done(Rest, Resume); +comment(<<_/utf16-little,Rest/binary>>, Resume) -> + comment(Rest, Resume); +comment(Bin, Resume) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + comment(<>, Resume) + end}; + false -> + {error,badjson} + end. + +maybe_comment_done(<<47/utf16-little,Rest/binary>>, Resume) -> + Resume(Rest); +maybe_comment_done(<<_/utf16-little,Rest/binary>>, Resume) -> + comment(Rest, Resume); +maybe_comment_done(Bin, Resume) -> + case byte_size(Bin) < 2 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_comment_done(<>, + Resume) + end}; + false -> + {error,badjson} + end. + + + diff --git a/src/jsx_utf32.erl b/src/jsx_utf32.erl new file mode 100644 index 0000000..03f49ed --- /dev/null +++ b/src/jsx_utf32.erl @@ -0,0 +1,1278 @@ +-file("priv/jsx_decoder.erl", 1). + +-module(jsx_utf32). + +-author("alisdairsullivan@yahoo.ca"). + +-export([parse/2]). + +-file("./include/jsx_decoder.hrl", 1). + +-file("priv/jsx_decoder.erl", 38). + +-file("./include/jsx_types.hrl", 1). + +-type jsx_opts() :: [jsx_opt()]. + +-type jsx_opt() :: {comments, true | false} + | {escaped_unicode, ascii | codepoint | none} + | {multi_term, true | false} + | {encoding, + auto | + utf8 | + utf16 | + {utf16, little} | + utf32 | + {utf32, little}}. + +-type unicode_codepoint() :: 0..1114111. + +-type unicode_string() :: [unicode_codepoint()]. + +-type jsx_event() :: start_object + | end_object + | start_array + | end_array + | end_json + | {key, unicode_string()} + | {string, unicode_string()} + | {integer, unicode_string()} + | {float, unicode_string()} + | {literal, true} + | {literal, false} + | {literal, null}. + +-type jsx_parser() :: fun((binary()) -> jsx_parser_result()). + +-type jsx_parser_result() :: {event, + jsx_event(), + fun(() -> jsx_parser_result())} + | {incomplete, jsx_parser()} + | {error, badjson} + | ok. + +-type json() :: json_object() | json_array(). + +-type json_array() :: [json_term()]. + +-type json_object() :: [{json_key(), json_term()}]. + +-type json_key() :: binary() | atom(). + +-type json_term() :: json_array() + | json_object() + | json_string() + | json_number() + | true + | false + | null. + +-type json_string() :: binary(). + +-type json_number() :: float() | integer(). + +-type supported_utf() :: utf8 + | utf16 + | {utf16, little} + | utf32 + | {utf32, little}. + +-type encoder_opts() :: [encoder_opt()]. + +-type encoder_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {space, integer()} + | space + | {indent, integer()} + | indent. + +-type decoder_opts() :: [decoder_opt()]. + +-type decoder_opt() :: {strict, true | false} + | {comments, true | false} + | {encoding, supported_utf()} + | {label, atom | binary | existing_atom} + | {float, true | false}. + +-type verify_opts() :: [verify_opt()]. + +-type verify_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {comments, true | false}. + +-type format_opts() :: [format_opt()]. + +-type format_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {comments, true | false} + | {space, integer()} + | space + | {indent, integer()} + | indent + | {output_encoding, supported_utf()}. + +-file("priv/jsx_decoder.erl", 39). + +-spec parse(JSON :: json(), Opts :: jsx_opts()) -> jsx_parser_result(). + +parse(JSON, Opts) -> + start(JSON, [], Opts). + +start(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + start(Rest, Stack, Opts); +start(<<123/utf32,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +start(<<91/utf32,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +start(<<34/utf32,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +start(<<$t/utf32,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +start(<<$f/utf32,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +start(<<$n/utf32,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +start(<<45/utf32,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +start(<<48/utf32,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +start(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +start(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + start(Resume, Stack, Opts) + end); +start(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + start(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +maybe_done(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + maybe_done(Rest, Stack, Opts); +maybe_done(<<125/utf32,Rest/binary>>, [object|Stack], Opts) -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +maybe_done(<<93/utf32,Rest/binary>>, [array|Stack], Opts) -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +maybe_done(<<44/utf32,Rest/binary>>, [object|Stack], Opts) -> + key(Rest, [key|Stack], Opts); +maybe_done(<<44/utf32,Rest/binary>>, [array|_] = Stack, Opts) -> + value(Rest, Stack, Opts); +maybe_done(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + maybe_done(Resume, Stack, Opts) + end); +maybe_done(Rest, [], {_,_,_,true,_} = Opts) -> + {event, + end_json, + fun() -> + start(Rest, [], Opts) + end}; +maybe_done(Rest, [], Opts) -> + done(Rest, Opts); +maybe_done(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_done(<>, + Stack, + Opts) + end}; + false -> + {error,badjson} + end. + +done(<>, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + done(Rest, Opts); +done(<<47/utf32,Rest/binary>>, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + done(Resume, Opts) + end); +done(<<>>, Opts) -> + {event, + end_json, + fun() -> + {incomplete, + fun(end_stream) -> + done(<<>>, Opts); + (Stream) -> + done(Stream, Opts) + end} + end}; +done(Bin, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + done(<>, Opts) + end}; + false -> + {error,badjson} + end. + +object(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + object(Rest, Stack, Opts); +object(<<34/utf32,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +object(<<125/utf32,Rest/binary>>, [key|Stack], Opts) -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +object(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + object(Resume, Stack, Opts) + end); +object(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + object(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +array(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + array(Rest, Stack, Opts); +array(<<34/utf32,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +array(<<$t/utf32,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +array(<<$f/utf32,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +array(<<$n/utf32,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +array(<<45/utf32,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +array(<<48/utf32,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +array(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +array(<<123/utf32,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +array(<<91/utf32,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +array(<<93/utf32,Rest/binary>>, [array|Stack], Opts) -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +array(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + array(Resume, Stack, Opts) + end); +array(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + array(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +value(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + value(Rest, Stack, Opts); +value(<<34/utf32,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +value(<<$t/utf32,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +value(<<$f/utf32,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +value(<<$n/utf32,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +value(<<45/utf32,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +value(<<48/utf32,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +value(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +value(<<123/utf32,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +value(<<91/utf32,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +value(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + value(Resume, Stack, Opts) + end); +value(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + value(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +colon(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + colon(Rest, Stack, Opts); +colon(<<58/utf32,Rest/binary>>, [key|Stack], Opts) -> + value(Rest, [object|Stack], Opts); +colon(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + colon(Resume, Stack, Opts) + end); +colon(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + colon(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +key(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + key(Rest, Stack, Opts); +key(<<34/utf32,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +key(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + key(Resume, Stack, Opts) + end); +key(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + key(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +string(<<34/utf32,Rest/binary>>, [key|_] = Stack, Opts, Acc) -> + {event, + {key,lists:reverse(Acc)}, + fun() -> + colon(Rest, Stack, Opts) + end}; +string(<<34/utf32,Rest/binary>>, Stack, Opts, Acc) -> + {event, + {string,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +string(<<92/utf32,Rest/binary>>, Stack, Opts, Acc) -> + escape(Rest, Stack, Opts, Acc); +string(<>, Stack, Opts, Acc) when S >= 32 -> + string(Rest, Stack, Opts, [S] ++ Acc); +string(Bin, Stack, Opts, Acc) -> + case partial_utf(Bin) of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + string(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +partial_utf(<<_:32>>) -> + false; +partial_utf(_) -> + true. + +escape(<<$b/utf32,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\b" ++ Acc); +escape(<<$f/utf32,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\f" ++ Acc); +escape(<<$n/utf32,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\n" ++ Acc); +escape(<<$r/utf32,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\r" ++ Acc); +escape(<<$t/utf32,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\t" ++ Acc); +escape(<<$u/utf32,Rest/binary>>, Stack, Opts, Acc) -> + escaped_unicode(Rest, Stack, Opts, Acc, []); +escape(<>, Stack, Opts, Acc) + when S =:= 34; S =:= 47; S =:= 92 -> + string(Rest, Stack, Opts, [S] ++ Acc); +escape(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + escape(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +escaped_unicode(<>, + Stack, + {_,_,ascii,_,_} = Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X < 128 -> + string(Rest, Stack, Opts, [X] ++ String); + _ -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String) + end; +escaped_unicode(<>, + Stack, + {_,_,codepoint,_,_} = Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X >= 56320, X =< 57343 -> + case check_acc_for_surrogate(String) of + false -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String); + {Y,NewString} -> + string(Rest, + Stack, + Opts, + [surrogate_to_codepoint(Y, X)] ++ NewString) + end; + X when X < 55296; X > 57343, X < 65534 -> + string(Rest, Stack, Opts, [X] ++ String); + _ -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String) + end; +escaped_unicode(<>, Stack, Opts, String, [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String); +escaped_unicode(<>, Stack, Opts, String, Acc) + when + S >= $a + andalso + S =< $z; + S >= $A + andalso + S =< $Z; + S >= $0 + andalso + S =< $9 -> + escaped_unicode(Rest, Stack, Opts, String, [S] ++ Acc); +escaped_unicode(Bin, Stack, Opts, String, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + escaped_unicode(<>, + Stack, + Opts, + String, + Acc) + end}; + false -> + {error,badjson} + end. + +check_acc_for_surrogate([D,C,B,A,$u,92|Rest]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9, + C >= $a + andalso + C =< $z; + C >= $A + andalso + C =< $Z; + C >= $0 + andalso + C =< $9, + B >= $a + andalso + B =< $z; + B >= $A + andalso + B =< $Z; + B >= $0 + andalso + B =< $9, + A >= $a + andalso + A =< $z; + A >= $A + andalso + A =< $Z; + A >= $0 + andalso + A =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X >= 55296, X =< 56319 -> + {X,Rest}; + _ -> + false + end; +check_acc_for_surrogate(_) -> + false. + +surrogate_to_codepoint(High, Low) -> + (High - 55296) * 1024 + (Low - 56320) + 65536. + +negative(<<$0/utf32,Rest/binary>>, Stack, Opts, Acc) -> + zero(Rest, Stack, Opts, "0" ++ Acc); +negative(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S] ++ Acc); +negative(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + negative(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +zero(<<125/utf32,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +zero(<<93/utf32,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +zero(<<44/utf32,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +zero(<<44/utf32,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +zero(<<46/utf32,Rest/binary>>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [46] ++ Acc); +zero(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +zero(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + zero(Resume, Stack, Opts, Acc) + end); +zero(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + zero(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + zero(Stream, [], Opts, Acc) + end}; +zero(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + zero(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +integer(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S] ++ Acc); +integer(<<125/utf32,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +integer(<<93/utf32,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +integer(<<44/utf32,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +integer(<<44/utf32,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +integer(<<46/utf32,Rest/binary>>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [46] ++ Acc); +integer(<<48/utf32,Rest/binary>>, Stack, Opts, Acc) -> + integer(Rest, Stack, Opts, [48] ++ Acc); +integer(<<$e/utf32,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<<$E/utf32,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +integer(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + integer(Resume, Stack, Opts, Acc) + end); +integer(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + integer(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + integer(Stream, [], Opts, Acc) + end}; +integer(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + integer(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +initial_decimal(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +initial_decimal(<<48/utf32,Rest/binary>>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [48] ++ Acc); +initial_decimal(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + initial_decimal(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +decimal(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +decimal(<<125/utf32,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +decimal(<<93/utf32,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +decimal(<<44/utf32,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +decimal(<<44/utf32,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +decimal(<<48/utf32,Rest/binary>>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [48] ++ Acc); +decimal(<<$e/utf32,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<<$E/utf32,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +decimal(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + decimal(Resume, Stack, Opts, Acc) + end); +decimal(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + decimal(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + decimal(Stream, [], Opts, Acc) + end}; +decimal(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + decimal(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +e(<>, Stack, Opts, Acc) + when + S =:= 48; + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +e(<>, Stack, Opts, Acc) when S =:= 43; S =:= 45 -> + ex(Rest, Stack, Opts, [S] ++ Acc); +e(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + e(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +ex(<>, Stack, Opts, Acc) + when + S =:= 48; + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +ex(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + ex(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +exp(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +exp(<<125/utf32,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +exp(<<93/utf32,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +exp(<<44/utf32,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +exp(<<44/utf32,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +exp(<<48/utf32,Rest/binary>>, Stack, Opts, Acc) -> + exp(Rest, Stack, Opts, [48] ++ Acc); +exp(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +exp(<<47/utf32,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + exp(Resume, Stack, Opts, Acc) + end); +exp(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + exp(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + exp(Stream, [], Opts, Acc) + end}; +exp(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + exp(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +tr(<<$r/utf32,Rest/binary>>, Stack, Opts) -> + tru(Rest, Stack, Opts); +tr(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + tr(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +tru(<<$u/utf32,Rest/binary>>, Stack, Opts) -> + true(Rest, Stack, Opts); +tru(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + tru(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +true(<<$e/utf32,Rest/binary>>, Stack, Opts) -> + {event, + {literal,true}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +true(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + true(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fa(<<$a/utf32,Rest/binary>>, Stack, Opts) -> + fal(Rest, Stack, Opts); +fa(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fa(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fal(<<$l/utf32,Rest/binary>>, Stack, Opts) -> + fals(Rest, Stack, Opts); +fal(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fal(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fals(<<$s/utf32,Rest/binary>>, Stack, Opts) -> + false(Rest, Stack, Opts); +fals(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fals(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +false(<<$e/utf32,Rest/binary>>, Stack, Opts) -> + {event, + {literal,false}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +false(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + false(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +nu(<<$u/utf32,Rest/binary>>, Stack, Opts) -> + nul(Rest, Stack, Opts); +nu(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + nu(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +nul(<<$l/utf32,Rest/binary>>, Stack, Opts) -> + null(Rest, Stack, Opts); +nul(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + nul(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +null(<<$l/utf32,Rest/binary>>, Stack, Opts) -> + {event, + {literal,null}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +null(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + null(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +maybe_comment(<<42/utf32,Rest/binary>>, Resume) -> + comment(Rest, Resume); +maybe_comment(Bin, Resume) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_comment(<>, Resume) + end}; + false -> + {error,badjson} + end. + +comment(<<42/utf32,Rest/binary>>, Resume) -> + maybe_comment_done(Rest, Resume); +comment(<<_/utf32,Rest/binary>>, Resume) -> + comment(Rest, Resume); +comment(Bin, Resume) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + comment(<>, Resume) + end}; + false -> + {error,badjson} + end. + +maybe_comment_done(<<47/utf32,Rest/binary>>, Resume) -> + Resume(Rest); +maybe_comment_done(<<_/utf32,Rest/binary>>, Resume) -> + comment(Rest, Resume); +maybe_comment_done(Bin, Resume) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_comment_done(<>, + Resume) + end}; + false -> + {error,badjson} + end. + + + diff --git a/src/jsx_utf32le.erl b/src/jsx_utf32le.erl new file mode 100644 index 0000000..ed8ac4f --- /dev/null +++ b/src/jsx_utf32le.erl @@ -0,0 +1,1295 @@ +-file("priv/jsx_decoder.erl", 1). + +-module(jsx_utf32le). + +-author("alisdairsullivan@yahoo.ca"). + +-export([parse/2]). + +-file("./include/jsx_decoder.hrl", 1). + +-file("priv/jsx_decoder.erl", 38). + +-file("./include/jsx_types.hrl", 1). + +-type jsx_opts() :: [jsx_opt()]. + +-type jsx_opt() :: {comments, true | false} + | {escaped_unicode, ascii | codepoint | none} + | {multi_term, true | false} + | {encoding, + auto | + utf8 | + utf16 | + {utf16, little} | + utf32 | + {utf32, little}}. + +-type unicode_codepoint() :: 0..1114111. + +-type unicode_string() :: [unicode_codepoint()]. + +-type jsx_event() :: start_object + | end_object + | start_array + | end_array + | end_json + | {key, unicode_string()} + | {string, unicode_string()} + | {integer, unicode_string()} + | {float, unicode_string()} + | {literal, true} + | {literal, false} + | {literal, null}. + +-type jsx_parser() :: fun((binary()) -> jsx_parser_result()). + +-type jsx_parser_result() :: {event, + jsx_event(), + fun(() -> jsx_parser_result())} + | {incomplete, jsx_parser()} + | {error, badjson} + | ok. + +-type json() :: json_object() | json_array(). + +-type json_array() :: [json_term()]. + +-type json_object() :: [{json_key(), json_term()}]. + +-type json_key() :: binary() | atom(). + +-type json_term() :: json_array() + | json_object() + | json_string() + | json_number() + | true + | false + | null. + +-type json_string() :: binary(). + +-type json_number() :: float() | integer(). + +-type supported_utf() :: utf8 + | utf16 + | {utf16, little} + | utf32 + | {utf32, little}. + +-type encoder_opts() :: [encoder_opt()]. + +-type encoder_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {space, integer()} + | space + | {indent, integer()} + | indent. + +-type decoder_opts() :: [decoder_opt()]. + +-type decoder_opt() :: {strict, true | false} + | {comments, true | false} + | {encoding, supported_utf()} + | {label, atom | binary | existing_atom} + | {float, true | false}. + +-type verify_opts() :: [verify_opt()]. + +-type verify_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {comments, true | false}. + +-type format_opts() :: [format_opt()]. + +-type format_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {comments, true | false} + | {space, integer()} + | space + | {indent, integer()} + | indent + | {output_encoding, supported_utf()}. + +-file("priv/jsx_decoder.erl", 39). + +-spec parse(JSON :: json(), Opts :: jsx_opts()) -> jsx_parser_result(). + +parse(JSON, Opts) -> + start(JSON, [], Opts). + +start(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + start(Rest, Stack, Opts); +start(<<123/utf32-little,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +start(<<91/utf32-little,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +start(<<34/utf32-little,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +start(<<$t/utf32-little,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +start(<<$f/utf32-little,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +start(<<$n/utf32-little,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +start(<<45/utf32-little,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +start(<<48/utf32-little,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +start(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +start(<<47/utf32-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + start(Resume, Stack, Opts) + end); +start(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + start(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +maybe_done(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + maybe_done(Rest, Stack, Opts); +maybe_done(<<125/utf32-little,Rest/binary>>, [object|Stack], Opts) -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +maybe_done(<<93/utf32-little,Rest/binary>>, [array|Stack], Opts) -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +maybe_done(<<44/utf32-little,Rest/binary>>, [object|Stack], Opts) -> + key(Rest, [key|Stack], Opts); +maybe_done(<<44/utf32-little,Rest/binary>>, [array|_] = Stack, Opts) -> + value(Rest, Stack, Opts); +maybe_done(<<47/utf32-little,Rest/binary>>, + Stack, + {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + maybe_done(Resume, Stack, Opts) + end); +maybe_done(Rest, [], {_,_,_,true,_} = Opts) -> + {event, + end_json, + fun() -> + start(Rest, [], Opts) + end}; +maybe_done(Rest, [], Opts) -> + done(Rest, Opts); +maybe_done(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_done(<>, + Stack, + Opts) + end}; + false -> + {error,badjson} + end. + +done(<>, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + done(Rest, Opts); +done(<<47/utf32-little,Rest/binary>>, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + done(Resume, Opts) + end); +done(<<>>, Opts) -> + {event, + end_json, + fun() -> + {incomplete, + fun(end_stream) -> + done(<<>>, Opts); + (Stream) -> + done(Stream, Opts) + end} + end}; +done(Bin, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + done(<>, Opts) + end}; + false -> + {error,badjson} + end. + +object(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + object(Rest, Stack, Opts); +object(<<34/utf32-little,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +object(<<125/utf32-little,Rest/binary>>, [key|Stack], Opts) -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +object(<<47/utf32-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + object(Resume, Stack, Opts) + end); +object(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + object(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +array(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + array(Rest, Stack, Opts); +array(<<34/utf32-little,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +array(<<$t/utf32-little,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +array(<<$f/utf32-little,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +array(<<$n/utf32-little,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +array(<<45/utf32-little,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +array(<<48/utf32-little,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +array(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +array(<<123/utf32-little,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +array(<<91/utf32-little,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +array(<<93/utf32-little,Rest/binary>>, [array|Stack], Opts) -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +array(<<47/utf32-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + array(Resume, Stack, Opts) + end); +array(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + array(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +value(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + value(Rest, Stack, Opts); +value(<<34/utf32-little,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +value(<<$t/utf32-little,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +value(<<$f/utf32-little,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +value(<<$n/utf32-little,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +value(<<45/utf32-little,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +value(<<48/utf32-little,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +value(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +value(<<123/utf32-little,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +value(<<91/utf32-little,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +value(<<47/utf32-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + value(Resume, Stack, Opts) + end); +value(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + value(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +colon(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + colon(Rest, Stack, Opts); +colon(<<58/utf32-little,Rest/binary>>, [key|Stack], Opts) -> + value(Rest, [object|Stack], Opts); +colon(<<47/utf32-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + colon(Resume, Stack, Opts) + end); +colon(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + colon(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +key(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + key(Rest, Stack, Opts); +key(<<34/utf32-little,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +key(<<47/utf32-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + key(Resume, Stack, Opts) + end); +key(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + key(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +string(<<34/utf32-little,Rest/binary>>, [key|_] = Stack, Opts, Acc) -> + {event, + {key,lists:reverse(Acc)}, + fun() -> + colon(Rest, Stack, Opts) + end}; +string(<<34/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + {event, + {string,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +string(<<92/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + escape(Rest, Stack, Opts, Acc); +string(<>, Stack, Opts, Acc) when S >= 32 -> + string(Rest, Stack, Opts, [S] ++ Acc); +string(Bin, Stack, Opts, Acc) -> + case partial_utf(Bin) of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + string(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +partial_utf(<<_:32>>) -> + false; +partial_utf(_) -> + true. + +escape(<<$b/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\b" ++ Acc); +escape(<<$f/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\f" ++ Acc); +escape(<<$n/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\n" ++ Acc); +escape(<<$r/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\r" ++ Acc); +escape(<<$t/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\t" ++ Acc); +escape(<<$u/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + escaped_unicode(Rest, Stack, Opts, Acc, []); +escape(<>, Stack, Opts, Acc) + when S =:= 34; S =:= 47; S =:= 92 -> + string(Rest, Stack, Opts, [S] ++ Acc); +escape(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + escape(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +escaped_unicode(<>, + Stack, + {_,_,ascii,_,_} = Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X < 128 -> + string(Rest, Stack, Opts, [X] ++ String); + _ -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String) + end; +escaped_unicode(<>, + Stack, + {_,_,codepoint,_,_} = Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X >= 56320, X =< 57343 -> + case check_acc_for_surrogate(String) of + false -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String); + {Y,NewString} -> + string(Rest, + Stack, + Opts, + [surrogate_to_codepoint(Y, X)] ++ NewString) + end; + X when X < 55296; X > 57343, X < 65534 -> + string(Rest, Stack, Opts, [X] ++ String); + _ -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String) + end; +escaped_unicode(<>, + Stack, + Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String); +escaped_unicode(<>, + Stack, + Opts, + String, + Acc) + when + S >= $a + andalso + S =< $z; + S >= $A + andalso + S =< $Z; + S >= $0 + andalso + S =< $9 -> + escaped_unicode(Rest, Stack, Opts, String, [S] ++ Acc); +escaped_unicode(Bin, Stack, Opts, String, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + escaped_unicode(<>, + Stack, + Opts, + String, + Acc) + end}; + false -> + {error,badjson} + end. + +check_acc_for_surrogate([D,C,B,A,$u,92|Rest]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9, + C >= $a + andalso + C =< $z; + C >= $A + andalso + C =< $Z; + C >= $0 + andalso + C =< $9, + B >= $a + andalso + B =< $z; + B >= $A + andalso + B =< $Z; + B >= $0 + andalso + B =< $9, + A >= $a + andalso + A =< $z; + A >= $A + andalso + A =< $Z; + A >= $0 + andalso + A =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X >= 55296, X =< 56319 -> + {X,Rest}; + _ -> + false + end; +check_acc_for_surrogate(_) -> + false. + +surrogate_to_codepoint(High, Low) -> + (High - 55296) * 1024 + (Low - 56320) + 65536. + +negative(<<$0/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + zero(Rest, Stack, Opts, "0" ++ Acc); +negative(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S] ++ Acc); +negative(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + negative(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +zero(<<125/utf32-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +zero(<<93/utf32-little,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +zero(<<44/utf32-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +zero(<<44/utf32-little,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +zero(<<46/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [46] ++ Acc); +zero(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +zero(<<47/utf32-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + zero(Resume, Stack, Opts, Acc) + end); +zero(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + zero(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + zero(Stream, [], Opts, Acc) + end}; +zero(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + zero(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +integer(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S] ++ Acc); +integer(<<125/utf32-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +integer(<<93/utf32-little,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +integer(<<44/utf32-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +integer(<<44/utf32-little,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +integer(<<46/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [46] ++ Acc); +integer(<<48/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + integer(Rest, Stack, Opts, [48] ++ Acc); +integer(<<$e/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<<$E/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +integer(<<47/utf32-little,Rest/binary>>, + Stack, + {_,true,_,_,_} = Opts, + Acc) -> + maybe_comment(Rest, + fun(Resume) -> + integer(Resume, Stack, Opts, Acc) + end); +integer(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + integer(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + integer(Stream, [], Opts, Acc) + end}; +integer(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + integer(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +initial_decimal(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +initial_decimal(<<48/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [48] ++ Acc); +initial_decimal(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + initial_decimal(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +decimal(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +decimal(<<125/utf32-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +decimal(<<93/utf32-little,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +decimal(<<44/utf32-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +decimal(<<44/utf32-little,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +decimal(<<48/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [48] ++ Acc); +decimal(<<$e/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<<$E/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +decimal(<<47/utf32-little,Rest/binary>>, + Stack, + {_,true,_,_,_} = Opts, + Acc) -> + maybe_comment(Rest, + fun(Resume) -> + decimal(Resume, Stack, Opts, Acc) + end); +decimal(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + decimal(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + decimal(Stream, [], Opts, Acc) + end}; +decimal(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + decimal(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +e(<>, Stack, Opts, Acc) + when + S =:= 48; + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +e(<>, Stack, Opts, Acc) + when S =:= 43; S =:= 45 -> + ex(Rest, Stack, Opts, [S] ++ Acc); +e(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + e(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +ex(<>, Stack, Opts, Acc) + when + S =:= 48; + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +ex(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + ex(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +exp(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +exp(<<125/utf32-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +exp(<<93/utf32-little,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +exp(<<44/utf32-little,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +exp(<<44/utf32-little,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +exp(<<48/utf32-little,Rest/binary>>, Stack, Opts, Acc) -> + exp(Rest, Stack, Opts, [48] ++ Acc); +exp(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +exp(<<47/utf32-little,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + exp(Resume, Stack, Opts, Acc) + end); +exp(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + exp(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + exp(Stream, [], Opts, Acc) + end}; +exp(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + exp(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +tr(<<$r/utf32-little,Rest/binary>>, Stack, Opts) -> + tru(Rest, Stack, Opts); +tr(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + tr(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +tru(<<$u/utf32-little,Rest/binary>>, Stack, Opts) -> + true(Rest, Stack, Opts); +tru(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + tru(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +true(<<$e/utf32-little,Rest/binary>>, Stack, Opts) -> + {event, + {literal,true}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +true(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + true(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fa(<<$a/utf32-little,Rest/binary>>, Stack, Opts) -> + fal(Rest, Stack, Opts); +fa(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fa(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fal(<<$l/utf32-little,Rest/binary>>, Stack, Opts) -> + fals(Rest, Stack, Opts); +fal(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fal(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fals(<<$s/utf32-little,Rest/binary>>, Stack, Opts) -> + false(Rest, Stack, Opts); +fals(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fals(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +false(<<$e/utf32-little,Rest/binary>>, Stack, Opts) -> + {event, + {literal,false}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +false(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + false(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +nu(<<$u/utf32-little,Rest/binary>>, Stack, Opts) -> + nul(Rest, Stack, Opts); +nu(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + nu(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +nul(<<$l/utf32-little,Rest/binary>>, Stack, Opts) -> + null(Rest, Stack, Opts); +nul(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + nul(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +null(<<$l/utf32-little,Rest/binary>>, Stack, Opts) -> + {event, + {literal,null}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +null(Bin, Stack, Opts) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + null(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +maybe_comment(<<42/utf32-little,Rest/binary>>, Resume) -> + comment(Rest, Resume); +maybe_comment(Bin, Resume) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_comment(<>, Resume) + end}; + false -> + {error,badjson} + end. + +comment(<<42/utf32-little,Rest/binary>>, Resume) -> + maybe_comment_done(Rest, Resume); +comment(<<_/utf32-little,Rest/binary>>, Resume) -> + comment(Rest, Resume); +comment(Bin, Resume) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + comment(<>, Resume) + end}; + false -> + {error,badjson} + end. + +maybe_comment_done(<<47/utf32-little,Rest/binary>>, Resume) -> + Resume(Rest); +maybe_comment_done(<<_/utf32-little,Rest/binary>>, Resume) -> + comment(Rest, Resume); +maybe_comment_done(Bin, Resume) -> + case byte_size(Bin) < 4 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_comment_done(<>, + Resume) + end}; + false -> + {error,badjson} + end. + + + diff --git a/src/jsx_utf8.erl b/src/jsx_utf8.erl new file mode 100644 index 0000000..6bf2ef3 --- /dev/null +++ b/src/jsx_utf8.erl @@ -0,0 +1,1296 @@ +-file("priv/jsx_decoder.erl", 1). + +-module(jsx_utf8). + +-author("alisdairsullivan@yahoo.ca"). + +-export([parse/2]). + +-file("./include/jsx_decoder.hrl", 1). + +-file("priv/jsx_decoder.erl", 38). + +-file("./include/jsx_types.hrl", 1). + +-type jsx_opts() :: [jsx_opt()]. + +-type jsx_opt() :: {comments, true | false} + | {escaped_unicode, ascii | codepoint | none} + | {multi_term, true | false} + | {encoding, + auto | + utf8 | + utf16 | + {utf16, little} | + utf32 | + {utf32, little}}. + +-type unicode_codepoint() :: 0..1114111. + +-type unicode_string() :: [unicode_codepoint()]. + +-type jsx_event() :: start_object + | end_object + | start_array + | end_array + | end_json + | {key, unicode_string()} + | {string, unicode_string()} + | {integer, unicode_string()} + | {float, unicode_string()} + | {literal, true} + | {literal, false} + | {literal, null}. + +-type jsx_parser() :: fun((binary()) -> jsx_parser_result()). + +-type jsx_parser_result() :: {event, + jsx_event(), + fun(() -> jsx_parser_result())} + | {incomplete, jsx_parser()} + | {error, badjson} + | ok. + +-type json() :: json_object() | json_array(). + +-type json_array() :: [json_term()]. + +-type json_object() :: [{json_key(), json_term()}]. + +-type json_key() :: binary() | atom(). + +-type json_term() :: json_array() + | json_object() + | json_string() + | json_number() + | true + | false + | null. + +-type json_string() :: binary(). + +-type json_number() :: float() | integer(). + +-type supported_utf() :: utf8 + | utf16 + | {utf16, little} + | utf32 + | {utf32, little}. + +-type encoder_opts() :: [encoder_opt()]. + +-type encoder_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {space, integer()} + | space + | {indent, integer()} + | indent. + +-type decoder_opts() :: [decoder_opt()]. + +-type decoder_opt() :: {strict, true | false} + | {comments, true | false} + | {encoding, supported_utf()} + | {label, atom | binary | existing_atom} + | {float, true | false}. + +-type verify_opts() :: [verify_opt()]. + +-type verify_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {comments, true | false}. + +-type format_opts() :: [format_opt()]. + +-type format_opt() :: {strict, true | false} + | {encoding, auto | supported_utf()} + | {comments, true | false} + | {space, integer()} + | space + | {indent, integer()} + | indent + | {output_encoding, supported_utf()}. + +-file("priv/jsx_decoder.erl", 39). + +-spec parse(JSON :: json(), Opts :: jsx_opts()) -> jsx_parser_result(). + +parse(JSON, Opts) -> + start(JSON, [], Opts). + +start(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + start(Rest, Stack, Opts); +start(<<123/utf8,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +start(<<91/utf8,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +start(<<34/utf8,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +start(<<$t/utf8,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +start(<<$f/utf8,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +start(<<$n/utf8,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +start(<<45/utf8,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +start(<<48/utf8,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +start(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +start(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + start(Resume, Stack, Opts) + end); +start(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + start(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +maybe_done(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + maybe_done(Rest, Stack, Opts); +maybe_done(<<125/utf8,Rest/binary>>, [object|Stack], Opts) -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +maybe_done(<<93/utf8,Rest/binary>>, [array|Stack], Opts) -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +maybe_done(<<44/utf8,Rest/binary>>, [object|Stack], Opts) -> + key(Rest, [key|Stack], Opts); +maybe_done(<<44/utf8,Rest/binary>>, [array|_] = Stack, Opts) -> + value(Rest, Stack, Opts); +maybe_done(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + maybe_done(Resume, Stack, Opts) + end); +maybe_done(Rest, [], {_,_,_,true,_} = Opts) -> + {event, + end_json, + fun() -> + start(Rest, [], Opts) + end}; +maybe_done(Rest, [], Opts) -> + done(Rest, Opts); +maybe_done(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_done(<>, + Stack, + Opts) + end}; + false -> + {error,badjson} + end. + +done(<>, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + done(Rest, Opts); +done(<<47/utf8,Rest/binary>>, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + done(Resume, Opts) + end); +done(<<>>, Opts) -> + {event, + end_json, + fun() -> + {incomplete, + fun(end_stream) -> + done(<<>>, Opts); + (Stream) -> + done(Stream, Opts) + end} + end}; +done(Bin, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + done(<>, Opts) + end}; + false -> + {error,badjson} + end. + +object(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + object(Rest, Stack, Opts); +object(<<34/utf8,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +object(<<125/utf8,Rest/binary>>, [key|Stack], Opts) -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +object(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + object(Resume, Stack, Opts) + end); +object(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + object(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +array(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + array(Rest, Stack, Opts); +array(<<34/utf8,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +array(<<$t/utf8,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +array(<<$f/utf8,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +array(<<$n/utf8,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +array(<<45/utf8,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +array(<<48/utf8,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +array(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +array(<<123/utf8,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +array(<<91/utf8,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +array(<<93/utf8,Rest/binary>>, [array|Stack], Opts) -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +array(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + array(Resume, Stack, Opts) + end); +array(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + array(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +value(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + value(Rest, Stack, Opts); +value(<<34/utf8,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +value(<<$t/utf8,Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +value(<<$f/utf8,Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +value(<<$n/utf8,Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +value(<<45/utf8,Rest/binary>>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +value(<<48/utf8,Rest/binary>>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +value(<>, Stack, Opts) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S]); +value(<<123/utf8,Rest/binary>>, Stack, Opts) -> + {event, + start_object, + fun() -> + object(Rest, [key|Stack], Opts) + end}; +value(<<91/utf8,Rest/binary>>, Stack, Opts) -> + {event, + start_array, + fun() -> + array(Rest, [array|Stack], Opts) + end}; +value(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + value(Resume, Stack, Opts) + end); +value(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + value(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +colon(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + colon(Rest, Stack, Opts); +colon(<<58/utf8,Rest/binary>>, [key|Stack], Opts) -> + value(Rest, [object|Stack], Opts); +colon(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + colon(Resume, Stack, Opts) + end); +colon(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + colon(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +key(<>, Stack, Opts) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + key(Rest, Stack, Opts); +key(<<34/utf8,Rest/binary>>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +key(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts) -> + maybe_comment(Rest, + fun(Resume) -> + key(Resume, Stack, Opts) + end); +key(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + key(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +string(<<34/utf8,Rest/binary>>, [key|_] = Stack, Opts, Acc) -> + {event, + {key,lists:reverse(Acc)}, + fun() -> + colon(Rest, Stack, Opts) + end}; +string(<<34/utf8,Rest/binary>>, Stack, Opts, Acc) -> + {event, + {string,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +string(<<92/utf8,Rest/binary>>, Stack, Opts, Acc) -> + escape(Rest, Stack, Opts, Acc); +string(<>, Stack, Opts, Acc) when S >= 32 -> + string(Rest, Stack, Opts, [S] ++ Acc); +string(Bin, Stack, Opts, Acc) -> + case partial_utf(Bin) of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + string(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +partial_utf(<<>>) -> + true; +partial_utf(<>) when X >= 194, X =< 223 -> + true; +partial_utf(<>) when X >= 224, X =< 239 -> + case Rest of + <<>> -> + true; + <> when Y >= 128, Y =< 191 -> + true + end; +partial_utf(<>) when X >= 240, X =< 244 -> + case Rest of + <<>> -> + true; + <> when Y >= 128, Y =< 191 -> + true; + <> when Y >= 128, Y =< 191, Z >= 128, Z =< 191 -> + true + end; +partial_utf(_) -> + false. + +escape(<<$b/utf8,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\b" ++ Acc); +escape(<<$f/utf8,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\f" ++ Acc); +escape(<<$n/utf8,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\n" ++ Acc); +escape(<<$r/utf8,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\r" ++ Acc); +escape(<<$t/utf8,Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\t" ++ Acc); +escape(<<$u/utf8,Rest/binary>>, Stack, Opts, Acc) -> + escaped_unicode(Rest, Stack, Opts, Acc, []); +escape(<>, Stack, Opts, Acc) + when S =:= 34; S =:= 47; S =:= 92 -> + string(Rest, Stack, Opts, [S] ++ Acc); +escape(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + escape(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +escaped_unicode(<>, + Stack, + {_,_,ascii,_,_} = Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X < 128 -> + string(Rest, Stack, Opts, [X] ++ String); + _ -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String) + end; +escaped_unicode(<>, + Stack, + {_,_,codepoint,_,_} = Opts, + String, + [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X >= 56320, X =< 57343 -> + case check_acc_for_surrogate(String) of + false -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String); + {Y,NewString} -> + string(Rest, + Stack, + Opts, + [surrogate_to_codepoint(Y, X)] ++ NewString) + end; + X when X < 55296; X > 57343, X < 65534 -> + string(Rest, Stack, Opts, [X] ++ String); + _ -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String) + end; +escaped_unicode(<>, Stack, Opts, String, [C,B,A]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9 -> + string(Rest, Stack, Opts, [D,C,B,A,$u,92] ++ String); +escaped_unicode(<>, Stack, Opts, String, Acc) + when + S >= $a + andalso + S =< $z; + S >= $A + andalso + S =< $Z; + S >= $0 + andalso + S =< $9 -> + escaped_unicode(Rest, Stack, Opts, String, [S] ++ Acc); +escaped_unicode(Bin, Stack, Opts, String, Acc) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + escaped_unicode(<>, + Stack, + Opts, + String, + Acc) + end}; + false -> + {error,badjson} + end. + +check_acc_for_surrogate([D,C,B,A,$u,92|Rest]) + when + D >= $a + andalso + D =< $z; + D >= $A + andalso + D =< $Z; + D >= $0 + andalso + D =< $9, + C >= $a + andalso + C =< $z; + C >= $A + andalso + C =< $Z; + C >= $0 + andalso + C =< $9, + B >= $a + andalso + B =< $z; + B >= $A + andalso + B =< $Z; + B >= $0 + andalso + B =< $9, + A >= $a + andalso + A =< $z; + A >= $A + andalso + A =< $Z; + A >= $0 + andalso + A =< $9 -> + case erlang:list_to_integer([A,B,C,D], 16) of + X when X >= 55296, X =< 56319 -> + {X,Rest}; + _ -> + false + end; +check_acc_for_surrogate(_) -> + false. + +surrogate_to_codepoint(High, Low) -> + (High - 55296) * 1024 + (Low - 56320) + 65536. + +negative(<<$0/utf8,Rest/binary>>, Stack, Opts, Acc) -> + zero(Rest, Stack, Opts, "0" ++ Acc); +negative(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S] ++ Acc); +negative(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + negative(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +zero(<<125/utf8,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +zero(<<93/utf8,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +zero(<<44/utf8,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +zero(<<44/utf8,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +zero(<<46/utf8,Rest/binary>>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [46] ++ Acc); +zero(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +zero(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + zero(Resume, Stack, Opts, Acc) + end); +zero(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + zero(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + zero(Stream, [], Opts, Acc) + end}; +zero(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + zero(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +integer(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + integer(Rest, Stack, Opts, [S] ++ Acc); +integer(<<125/utf8,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +integer(<<93/utf8,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +integer(<<44/utf8,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +integer(<<44/utf8,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +integer(<<46/utf8,Rest/binary>>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [46] ++ Acc); +integer(<<48/utf8,Rest/binary>>, Stack, Opts, Acc) -> + integer(Rest, Stack, Opts, [48] ++ Acc); +integer(<<$e/utf8,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<<$E/utf8,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +integer(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + integer(Resume, Stack, Opts, Acc) + end); +integer(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {integer,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + integer(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + integer(Stream, [], Opts, Acc) + end}; +integer(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + integer(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +initial_decimal(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +initial_decimal(<<48/utf8,Rest/binary>>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [48] ++ Acc); +initial_decimal(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + initial_decimal(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +decimal(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +decimal(<<125/utf8,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +decimal(<<93/utf8,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +decimal(<<44/utf8,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +decimal(<<44/utf8,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +decimal(<<48/utf8,Rest/binary>>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [48] ++ Acc); +decimal(<<$e/utf8,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<<$E/utf8,Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +decimal(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + decimal(Resume, Stack, Opts, Acc) + end); +decimal(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + decimal(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + decimal(Stream, [], Opts, Acc) + end}; +decimal(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + decimal(<>, + Stack, + Opts, + Acc) + end}; + false -> + {error,badjson} + end. + +e(<>, Stack, Opts, Acc) + when + S =:= 48; + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +e(<>, Stack, Opts, Acc) when S =:= 43; S =:= 45 -> + ex(Rest, Stack, Opts, [S] ++ Acc); +e(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + e(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +ex(<>, Stack, Opts, Acc) + when + S =:= 48; + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +ex(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + ex(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +exp(<>, Stack, Opts, Acc) + when + S >= $1 + andalso + S =< $9 -> + exp(Rest, Stack, Opts, [S] ++ Acc); +exp(<<125/utf8,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_object, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +exp(<<93/utf8,Rest/binary>>, [array|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_array, + fun() -> + maybe_done(Rest, Stack, Opts) + end} + end}; +exp(<<44/utf8,Rest/binary>>, [object|Stack], Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + key(Rest, [key|Stack], Opts) + end}; +exp(<<44/utf8,Rest/binary>>, [array|_] = Stack, Opts, Acc) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + value(Rest, Stack, Opts) + end}; +exp(<<48/utf8,Rest/binary>>, Stack, Opts, Acc) -> + exp(Rest, Stack, Opts, [48] ++ Acc); +exp(<>, Stack, Opts, Acc) + when S =:= 32; S =:= 9; S =:= 13; S =:= 10 -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +exp(<<47/utf8,Rest/binary>>, Stack, {_,true,_,_,_} = Opts, Acc) -> + maybe_comment(Rest, + fun(Resume) -> + exp(Resume, Stack, Opts, Acc) + end); +exp(<<>>, [], Opts, Acc) -> + {incomplete, + fun(end_stream) -> + {event, + {float,lists:reverse(Acc)}, + fun() -> + {event, + end_json, + fun() -> + exp(<<>>, [], Opts, Acc) + end} + end}; + (Stream) -> + exp(Stream, [], Opts, Acc) + end}; +exp(Bin, Stack, Opts, Acc) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + exp(<>, Stack, Opts, Acc) + end}; + false -> + {error,badjson} + end. + +tr(<<$r/utf8,Rest/binary>>, Stack, Opts) -> + tru(Rest, Stack, Opts); +tr(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + tr(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +tru(<<$u/utf8,Rest/binary>>, Stack, Opts) -> + true(Rest, Stack, Opts); +tru(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + tru(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +true(<<$e/utf8,Rest/binary>>, Stack, Opts) -> + {event, + {literal,true}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +true(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + true(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fa(<<$a/utf8,Rest/binary>>, Stack, Opts) -> + fal(Rest, Stack, Opts); +fa(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fa(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fal(<<$l/utf8,Rest/binary>>, Stack, Opts) -> + fals(Rest, Stack, Opts); +fal(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fal(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +fals(<<$s/utf8,Rest/binary>>, Stack, Opts) -> + false(Rest, Stack, Opts); +fals(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + fals(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +false(<<$e/utf8,Rest/binary>>, Stack, Opts) -> + {event, + {literal,false}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +false(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + false(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +nu(<<$u/utf8,Rest/binary>>, Stack, Opts) -> + nul(Rest, Stack, Opts); +nu(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + nu(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +nul(<<$l/utf8,Rest/binary>>, Stack, Opts) -> + null(Rest, Stack, Opts); +nul(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + nul(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +null(<<$l/utf8,Rest/binary>>, Stack, Opts) -> + {event, + {literal,null}, + fun() -> + maybe_done(Rest, Stack, Opts) + end}; +null(Bin, Stack, Opts) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + null(<>, Stack, Opts) + end}; + false -> + {error,badjson} + end. + +maybe_comment(<<42/utf8,Rest/binary>>, Resume) -> + comment(Rest, Resume); +maybe_comment(Bin, Resume) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_comment(<>, Resume) + end}; + false -> + {error,badjson} + end. + +comment(<<42/utf8,Rest/binary>>, Resume) -> + maybe_comment_done(Rest, Resume); +comment(<<_/utf8,Rest/binary>>, Resume) -> + comment(Rest, Resume); +comment(Bin, Resume) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + comment(<>, Resume) + end}; + false -> + {error,badjson} + end. + +maybe_comment_done(<<47/utf8,Rest/binary>>, Resume) -> + Resume(Rest); +maybe_comment_done(<<_/utf8,Rest/binary>>, Resume) -> + comment(Rest, Resume); +maybe_comment_done(Bin, Resume) -> + case byte_size(Bin) < 1 of + true -> + {incomplete, + fun(end_stream) -> + {error,badjson}; + (Stream) -> + maybe_comment_done(<>, + Resume) + end}; + false -> + {error,badjson} + end. + + +