From e690d7723ab2b4d83f759e03cb0fc025a40c0972 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Fri, 11 Jun 2010 18:14:24 -0700 Subject: [PATCH] first pass at new api --- src/jsx.erl | 87 +++-- src/jsx_decoder.erl | 820 ++++++++++++++++++++------------------------ 2 files changed, 423 insertions(+), 484 deletions(-) diff --git a/src/jsx.erl b/src/jsx.erl index 5cda8e6..70bc502 100644 --- a/src/jsx.erl +++ b/src/jsx.erl @@ -56,31 +56,30 @@ parser() -> parser(Opts) -> F = fun(end_json, State) -> lists:reverse(State) - ; (reset, _State) -> [] ; (Event, State) -> [Event] ++ State end, parser({F, []}, Opts). -parser({F, _} = Callbacks, OptsList) when is_list(OptsList), is_function(F) -> - start(Callbacks, OptsList); +parser({F, S} = Callback, OptsList) when is_list(OptsList), is_function(F) -> + start(Callback, OptsList); parser({Mod, Fun, State}, OptsList) when is_list(OptsList), is_atom(Mod), is_atom(Fun) -> start({fun(E, S) -> Mod:Fun(E, S) end, State}, OptsList). -start(Callbacks, OptsList) -> +start(Callback, OptsList) -> F = case proplists:get_value(encoding, OptsList, auto) of - utf8 -> fun jsx_utf8:start/4 - ; utf16 -> fun jsx_utf16:start/4 - ; utf32 -> fun jsx_utf32:start/4 - ; {utf16, little} -> fun jsx_utf16le:start/4 - ; {utf32, little} -> fun jsx_utf32le:start/4 - ; auto -> fun detect_encoding/4 + utf8 -> fun jsx_utf8:parse/3 + ; utf16 -> fun jsx_utf16:parse/3 + ; utf32 -> fun jsx_utf32:parse/3 + ; {utf16, little} -> fun jsx_utf16le:parse/3 + ; {utf32, little} -> fun jsx_utf32le:parse/3 + ; auto -> fun detect_encoding/3 end, - start(Callbacks, OptsList, F). + start(F, Callback, OptsList). -start(Callbacks, OptsList, F) -> +start(F, Callback, OptsList) -> Opts = parse_opts(OptsList), - fun(Stream) -> F(Stream, [], Callbacks, Opts) end. + fun(Stream) -> F(Stream, Callback, Opts) end. parse_opts(Opts) -> @@ -104,76 +103,76 @@ parse_opts([{stream_mode, Value}|Rest], {Comments, EscapedUnicode, _Stream}) -> %% which may delay failure later than if an encoding is explicitly provided. %% utf8 bom detection -detect_encoding(<<16#ef, 16#bb, 16#bf, Rest/binary>>, Stack, Callbacks, Opts) -> - jsx_utf8:start(Rest, Stack, Callbacks, Opts); +detect_encoding(<<16#ef, 16#bb, 16#bf, Rest/binary>>, Callback, Opts) -> + jsx_utf8:parse(Rest, Callback, Opts); %% utf32-little bom detection (this has to come before utf16-little) -detect_encoding(<<16#ff, 16#fe, 0, 0, Rest/binary>>, Stack, Callbacks, Opts) -> - jsx_utf32le:start(Rest, Stack, Callbacks, Opts); +detect_encoding(<<16#ff, 16#fe, 0, 0, Rest/binary>>, Callback, Opts) -> + jsx_utf32le:parse(Rest, Callback, Opts); %% utf16-big bom detection -detect_encoding(<<16#fe, 16#ff, Rest/binary>>, Stack, Callbacks, Opts) -> - jsx_utf16:start(Rest, Stack, Callbacks, Opts); +detect_encoding(<<16#fe, 16#ff, Rest/binary>>, Callback, Opts) -> + jsx_utf16:parse(Rest, Callback, Opts); %% utf16-little bom detection -detect_encoding(<<16#ff, 16#fe, Rest/binary>>, Stack, Callbacks, Opts) -> - jsx_utf16le:start(Rest, Stack, Callbacks, Opts); +detect_encoding(<<16#ff, 16#fe, Rest/binary>>, Callback, Opts) -> + jsx_utf16le:parse(Rest, Callback, Opts); %% utf32-big bom detection -detect_encoding(<<0, 0, 16#fe, 16#ff, Rest/binary>>, Stack, Callbacks, Opts) -> - jsx_utf32:start(Rest, Stack, Callbacks, Opts); +detect_encoding(<<0, 0, 16#fe, 16#ff, Rest/binary>>, Callback, Opts) -> + jsx_utf32:parse(Rest, Callback, Opts); %% utf32-little null order detection -detect_encoding(<> = JSON, Stack, Callbacks, Opts) when X =/= 0 -> - jsx_utf32le:start(JSON, Stack, Callbacks, Opts); +detect_encoding(<> = JSON, Callback, Opts) when X =/= 0 -> + jsx_utf32le:parse(JSON, Callback, Opts); %% utf16-big null order detection -detect_encoding(<<0, X, 0, Y, _Rest/binary>> = JSON, Stack, Callbacks, Opts) when X =/= 0, Y =/= 0 -> - jsx_utf16:start(JSON, Stack, Callbacks, Opts); +detect_encoding(<<0, X, 0, Y, _Rest/binary>> = JSON, Callback, Opts) when X =/= 0, Y =/= 0 -> + jsx_utf16:parse(JSON, Callback, Opts); %% utf16-little null order detection -detect_encoding(<> = JSON, Stack, Callbacks, Opts) when X =/= 0, Y =/= 0 -> - jsx_utf16le:start(JSON, Stack, Callbacks, Opts); +detect_encoding(<> = JSON, Callback, Opts) when X =/= 0, Y =/= 0 -> + jsx_utf16le:parse(JSON, Callback, Opts); %% utf32-big null order detection -detect_encoding(<<0, 0, 0, X, _Rest/binary>> = JSON, Stack, Callbacks, Opts) when X =/= 0 -> - jsx_utf32:start(JSON, Stack, Callbacks, Opts); +detect_encoding(<<0, 0, 0, X, _Rest/binary>> = JSON, Callback, Opts) when X =/= 0 -> + jsx_utf32:parse(JSON, Callback, Opts); %% utf8 null order detection -detect_encoding(<> = JSON, Stack, Callbacks, Opts) when X =/= 0, Y =/= 0 -> - jsx_utf8:start(JSON, Stack, Callbacks, Opts); +detect_encoding(<> = JSON, Callback, Opts) when X =/= 0, Y =/= 0 -> + jsx_utf8:parse(JSON, Callback, Opts); %% a problem, to autodetect naked single digits' encoding, there is not enough data %% to conclusively determine the encoding correctly. below is an attempt to solve %% the problem -detect_encoding(<>, Stack, Callbacks, Opts) when X =/= 0 -> - {try {Result, _} = jsx_utf8:start(<>, [], Callbacks, Opts), Result +detect_encoding(<>, Callback, Opts) when X =/= 0 -> + {try {Result, _} = jsx_utf8:parse(<>, Callback, Opts), Result catch error:function_clause -> incomplete end, fun(Stream) -> - detect_encoding(<>, Stack, Callbacks, Opts) + detect_encoding(<>, Callback, Opts) end }; -detect_encoding(<<0, X>>, Stack, Callbacks, Opts) when X =/= 0 -> - {try {Result, _} = jsx_utf16:start(<<0, X>>, [], Callbacks, Opts), Result +detect_encoding(<<0, X>>, Callback, Opts) when X =/= 0 -> + {try {Result, _} = jsx_utf16:parse(<<0, X>>, Callback, Opts), Result catch error:function_clause -> incomplete end, fun(Stream) -> - detect_encoding(<<0, X, Stream/binary>>, Stack, Callbacks, Opts) + detect_encoding(<<0, X, Stream/binary>>, Callback, Opts) end }; -detect_encoding(<>, Stack, Callbacks, Opts) when X =/= 0 -> - {try {Result, _} = jsx_utf16le:start(<>, [], Callbacks, Opts), Result +detect_encoding(<>, Callback, Opts) when X =/= 0 -> + {try {Result, _} = jsx_utf16le:parse(<>, Callback, Opts), Result catch error:function_clause -> incomplete end, fun(Stream) -> - detect_encoding(<>, Stack, Callbacks, Opts) + detect_encoding(<>, Callback, Opts) end }; %% not enough input, request more -detect_encoding(Bin, Stack, Callbacks, Opts) -> +detect_encoding(Bin, Callback, Opts) -> {incomplete, fun(Stream) -> - detect_encoding(<>, Stack, Callbacks, Opts) + detect_encoding(<>, Callback, Opts) end }. \ No newline at end of file diff --git a/src/jsx_decoder.erl b/src/jsx_decoder.erl index d93b2c5..80c8aa7 100644 --- a/src/jsx_decoder.erl +++ b/src/jsx_decoder.erl @@ -29,16 +29,28 @@ -include("jsx_decoder.hrl"). --export([start/4]). +-export([parse/3]). +-compile(inline). -%% callbacks to our handler are roughly equivalent to a fold over the events, incremental -%% rather than all at once. +parse(JSON, {Callback, State}, Opts) -> + parse(Callback, State, fun() -> start(JSON, [], Opts) end, Opts). + +parse(Callback, State, F, Opts) -> + case F() of + {incomplete, Next} -> {incomplete, Next} + ; {end_json, Next} -> {Callback(end_json, State), Next} + ; {error, badjson} -> {error, badjson} + ; {Event, Next} -> parse(Callback, Callback(Event, State), Next, Opts) + end. + -fold(end_json, {F, State}) -> - F(end_json, State); -fold(Event, {F, State}) -> - {F, F(Event, State)}. +emit(incomplete, F) -> + {incomplete, F}; +emit(end_json, F) -> + {end_json, F}; +emit(Event, F) -> + {Event, F}. %% this code is mostly autogenerated and mostly ugly. apologies. for more insight on @@ -49,179 +61,156 @@ fold(Event, {F, State}) -> %% compiler for efficient matching, but you shouldn't be using naked values or comments %% anyways, they are horrible and contrary to the spec. -start(<>, Stack, Callbacks, Opts) when ?is_whitespace(S) -> - start(Rest, Stack, Callbacks, Opts); -start(<>, Stack, Callbacks, Opts) -> - object(Rest, [key|Stack], fold(start_object, Callbacks), Opts); -start(<>, Stack, Callbacks, Opts) -> - array(Rest, [array|Stack], fold(start_array, Callbacks), Opts); -start(<>, Stack, Callbacks, Opts) -> - string(Rest, Stack, Callbacks, Opts, []); -start(<<$t/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - tr(Rest, Stack, Callbacks, Opts); -start(<<$f/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - fa(Rest, Stack, Callbacks, Opts); -start(<<$n/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - nu(Rest, Stack, Callbacks, Opts); -start(<>, Stack, Callbacks, Opts) -> - negative(Rest, Stack, Callbacks, Opts, "-"); -start(<>, Stack, Callbacks, Opts) -> - zero(Rest, Stack, Callbacks, Opts, "0"); -start(<>, Stack, Callbacks, Opts) when ?is_nonzero(S) -> - integer(Rest, Stack, Callbacks, Opts, [S]); -start(<>, Stack, Callbacks, ?comments_enabled(Opts)) -> - maybe_comment(Rest, fun(Resume) -> start(Resume, Stack, Callbacks, Opts) end); -start(Bin, Stack, Callbacks, Opts) -> +start(<>, Stack, Opts) when ?is_whitespace(S) -> + start(Rest, Stack, Opts); +start(<>, Stack, Opts) -> + emit(start_object, fun() -> object(Rest, [key|Stack], Opts) end); +start(<>, Stack, Opts) -> + emit(start_array, fun() -> array(Rest, [array|Stack], Opts) end); +start(<>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +start(<<$t/?encoding, Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +start(<<$f/?encoding, Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +start(<<$n/?encoding, Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +start(<>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +start(<>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +start(<>, Stack, Opts) when ?is_nonzero(S) -> + integer(Rest, Stack, Opts, [S]); +start(<>, Stack, ?comments_enabled(Opts)) -> + maybe_comment(Rest, fun(Resume) -> start(Resume, Stack, Opts) end); +start(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> start(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> start(<>, Stack, Opts) end) end. -maybe_done(<>, Stack, Callbacks, Opts) when ?is_whitespace(S) -> - maybe_done(Rest, Stack, Callbacks, Opts); -maybe_done(<>, [object|Stack], Callbacks, Opts) -> - maybe_done(Rest, Stack, fold(end_object, Callbacks), Opts); -maybe_done(<>, [array|Stack], Callbacks, Opts) -> - maybe_done(Rest, Stack, fold(end_array, Callbacks), Opts); -maybe_done(<>, [object|Stack], Callbacks, Opts) -> - key(Rest, [key|Stack], Callbacks, Opts); -maybe_done(<>, [array|_] = Stack, Callbacks, Opts) -> - value(Rest, Stack, Callbacks, Opts); -maybe_done(<>, Stack, Callbacks, ?comments_enabled(Opts)) -> - maybe_comment(Rest, fun(Resume) -> maybe_done(Resume, Stack, Callbacks, Opts) end); -maybe_done(Bin, [], Callbacks, ?stream_mode(Opts)) -> - {fold(end_json, Callbacks), fun(Stream) -> - start(<>, [], fold(reset, Callbacks), Opts) - end}; -maybe_done(<<>>, [], Callbacks, Opts) -> - {fold(end_json, Callbacks), fun(Stream) -> maybe_done(Stream, [], Callbacks, Opts) end}; -maybe_done(Bin, Stack, Callbacks, Opts) -> +maybe_done(<>, Stack, Opts) when ?is_whitespace(S) -> + maybe_done(Rest, Stack, Opts); +maybe_done(<>, [object|Stack], Opts) -> + emit(end_object, fun() -> maybe_done(Rest, Stack, Opts) end); +maybe_done(<>, [array|Stack], Opts) -> + emit(end_array, fun() -> maybe_done(Rest, Stack, Opts) end); +maybe_done(<>, [object|Stack], Opts) -> + key(Rest, [key|Stack], Opts); +maybe_done(<>, [array|_] = Stack, Opts) -> + value(Rest, Stack, Opts); +maybe_done(<>, Stack, ?comments_enabled(Opts)) -> + maybe_comment(Rest, fun(Resume) -> maybe_done(Resume, Stack, Opts) end); +maybe_done(Bin, [], ?stream_mode(Opts)) -> + emit(end_json, fun() -> start(Bin, [], Opts) end); +maybe_done(<<>>, [], Opts) -> + emit(end_json, fun() -> emit(incomplete, fun(Stream) -> maybe_done(Stream, [], Opts) end) end); +maybe_done(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> maybe_done(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> maybe_done(<>, Stack, Opts) end) end. -object(<>, Stack, Callbacks, Opts) when ?is_whitespace(S) -> - object(Rest, Stack, Callbacks, Opts); -object(<>, Stack, Callbacks, Opts) -> - string(Rest, Stack, Callbacks, Opts, []); -object(<>, [key|Stack], Callbacks, Opts) -> - maybe_done(Rest, Stack, fold(end_object, Callbacks), Opts); -object(<>, Stack, Callbacks, ?comments_enabled(Opts)) -> - maybe_comment(Rest, fun(Resume) -> object(Resume, Stack, Callbacks, Opts) end); -object(Bin, Stack, Callbacks, Opts) -> +object(<>, Stack, Opts) when ?is_whitespace(S) -> + object(Rest, Stack, Opts); +object(<>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +object(<>, [key|Stack], Opts) -> + emit(end_object, fun() -> maybe_done(Rest, Stack, Opts) end); +object(<>, Stack, ?comments_enabled(Opts)) -> + maybe_comment(Rest, fun(Resume) -> object(Resume, Stack, Opts) end); +object(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> object(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> object(<>, Stack, Opts) end) end. -array(<>, Stack, Callbacks, Opts) when ?is_whitespace(S) -> - array(Rest, Stack, Callbacks, Opts); -array(<>, Stack, Callbacks, Opts) -> - string(Rest, Stack, Callbacks, Opts, []); -array(<<$t/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - tr(Rest, Stack, Callbacks, Opts); -array(<<$f/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - fa(Rest, Stack, Callbacks, Opts); -array(<<$n/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - nu(Rest, Stack, Callbacks, Opts); -array(<>, Stack, Callbacks, Opts) -> - negative(Rest, Stack, Callbacks, Opts, "-"); -array(<>, Stack, Callbacks, Opts) -> - zero(Rest, Stack, Callbacks, Opts, "0"); -array(<>, Stack, Callbacks, Opts) when ?is_nonzero(S) -> - integer(Rest, Stack, Callbacks, Opts, [S]); -array(<>, Stack, Callbacks, Opts) -> - object(Rest, [key|Stack], fold(start_object, Callbacks), Opts); -array(<>, Stack, Callbacks, Opts) -> - array(Rest, [array|Stack], fold(start_array, Callbacks), Opts); -array(<>, [array|Stack], Callbacks, Opts) -> - maybe_done(Rest, Stack, fold(end_array, Callbacks), Opts); -array(<>, Stack, Callbacks, ?comments_enabled(Opts)) -> - maybe_comment(Rest, fun(Resume) -> array(Resume, Stack, Callbacks, Opts) end); -array(Bin, Stack, Callbacks, Opts) -> +array(<>, Stack, Opts) when ?is_whitespace(S) -> + array(Rest, Stack, Opts); +array(<>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +array(<<$t/?encoding, Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +array(<<$f/?encoding, Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +array(<<$n/?encoding, Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +array(<>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +array(<>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +array(<>, Stack, Opts) when ?is_nonzero(S) -> + integer(Rest, Stack, Opts, [S]); +array(<>, Stack, Opts) -> + emit(start_object, fun() -> object(Rest, [key|Stack], Opts) end); +array(<>, Stack, Opts) -> + emit(start_array, fun() -> array(Rest, [array|Stack], Opts) end); +array(<>, [array|Stack], Opts) -> + emit(end_array, fun() -> maybe_done(Rest, Stack, Opts) end); +array(<>, Stack, ?comments_enabled(Opts)) -> + maybe_comment(Rest, fun(Resume) -> array(Resume, Stack, Opts) end); +array(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> array(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> array(<>, Stack, Opts) end) end. -value(<>, Stack, Callbacks, Opts) when ?is_whitespace(S) -> - value(Rest, Stack, Callbacks, Opts); -value(<>, Stack, Callbacks, Opts) -> - string(Rest, Stack, Callbacks, Opts, []); -value(<<$t/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - tr(Rest, Stack, Callbacks, Opts); -value(<<$f/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - fa(Rest, Stack, Callbacks, Opts); -value(<<$n/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - nu(Rest, Stack, Callbacks, Opts); -value(<>, Stack, Callbacks, Opts) -> - negative(Rest, Stack, Callbacks, Opts, "-"); -value(<>, Stack, Callbacks, Opts) -> - zero(Rest, Stack, Callbacks, Opts, "0"); -value(<>, Stack, Callbacks, Opts) when ?is_nonzero(S) -> - integer(Rest, Stack, Callbacks, Opts, [S]); -value(<>, Stack, Callbacks, Opts) -> - object(Rest, [key|Stack], fold(start_object, Callbacks), Opts); -value(<>, Stack, Callbacks, Opts) -> - array(Rest, [array|Stack], fold(start_array, Callbacks), Opts); -value(<>, Stack, Callbacks, ?comments_enabled(Opts)) -> - maybe_comment(Rest, fun(Resume) -> value(Resume, Stack, Callbacks, Opts) end); -value(Bin, Stack, Callbacks, Opts) -> +value(<>, Stack, Opts) when ?is_whitespace(S) -> + value(Rest, Stack, Opts); +value(<>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +value(<<$t/?encoding, Rest/binary>>, Stack, Opts) -> + tr(Rest, Stack, Opts); +value(<<$f/?encoding, Rest/binary>>, Stack, Opts) -> + fa(Rest, Stack, Opts); +value(<<$n/?encoding, Rest/binary>>, Stack, Opts) -> + nu(Rest, Stack, Opts); +value(<>, Stack, Opts) -> + negative(Rest, Stack, Opts, "-"); +value(<>, Stack, Opts) -> + zero(Rest, Stack, Opts, "0"); +value(<>, Stack, Opts) when ?is_nonzero(S) -> + integer(Rest, Stack, Opts, [S]); +value(<>, Stack, Opts) -> + emit(start_object, fun() -> object(Rest, [key|Stack], Opts) end); +value(<>, Stack, Opts) -> + emit(start_array, fun() -> array(Rest, [array|Stack], Opts) end); +value(<>, Stack, ?comments_enabled(Opts)) -> + maybe_comment(Rest, fun(Resume) -> value(Resume, Stack, Opts) end); +value(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> value(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> value(<>, Stack, Opts) end) end. -colon(<>, Stack, Callbacks, Opts) when ?is_whitespace(S) -> - colon(Rest, Stack, Callbacks, Opts); -colon(<>, [key|Stack], Callbacks, Opts) -> - value(Rest, [object|Stack], Callbacks, Opts); -colon(<>, Stack, Callbacks, ?comments_enabled(Opts)) -> - maybe_comment(Rest, fun(Resume) -> colon(Resume, Stack, Callbacks, Opts) end); -colon(Bin, Stack, Callbacks, Opts) -> +colon(<>, Stack, Opts) when ?is_whitespace(S) -> + colon(Rest, Stack, Opts); +colon(<>, [key|Stack], Opts) -> + value(Rest, [object|Stack], Opts); +colon(<>, Stack, ?comments_enabled(Opts)) -> + maybe_comment(Rest, fun(Resume) -> colon(Resume, Stack, Opts) end); +colon(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> colon(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> colon(<>, Stack, Opts) end) end. -key(<>, Stack, Callbacks, Opts) when ?is_whitespace(S) -> - key(Rest, Stack, Callbacks, Opts); -key(<>, Stack, Callbacks, Opts) -> - string(Rest, Stack, Callbacks, Opts, []); -key(<>, Stack, Callbacks, ?comments_enabled(Opts)) -> - maybe_comment(Rest, fun(Resume) -> key(Resume, Stack, Callbacks, Opts) end); -key(Bin, Stack, Callbacks, Opts) -> +key(<>, Stack, Opts) when ?is_whitespace(S) -> + key(Rest, Stack, Opts); +key(<>, Stack, Opts) -> + string(Rest, Stack, Opts, []); +key(<>, Stack, ?comments_enabled(Opts)) -> + maybe_comment(Rest, fun(Resume) -> key(Resume, Stack, Opts) end); +key(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> key(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> key(<>, Stack, Opts) end) end. @@ -230,22 +219,18 @@ key(Bin, Stack, Callbacks, Opts) -> %% unicode codepoints is faster than constructing binaries, many of which will be %% converted back to lists by the user anyways. -string(<>, [key|_] = Stack, Callbacks, Opts, Acc) -> - colon(Rest, Stack, fold({key, lists:reverse(Acc)}, Callbacks), Opts); -string(<>, Stack, Callbacks, Opts, Acc) -> - maybe_done(Rest, Stack, fold({string, lists:reverse(Acc)}, Callbacks), Opts); -string(<>, Stack, Callbacks, Opts, Acc) -> - escape(Rest, Stack, Callbacks, Opts, Acc); -string(<>, Stack, Callbacks, Opts, Acc) when ?is_noncontrol(S) -> - string(Rest, Stack, Callbacks, Opts, [S] ++ Acc); -string(Bin, Stack, Callbacks, Opts, Acc) -> +string(<>, [key|_] = Stack, Opts, Acc) -> + emit({key, lists:reverse(Acc)}, fun() -> colon(Rest, Stack, Opts) end); +string(<>, Stack, Opts, Acc) -> + emit({string, lists:reverse(Acc)}, fun() -> maybe_done(Rest, Stack, Opts) end); +string(<>, Stack, Opts, Acc) -> + escape(Rest, Stack, Opts, Acc); +string(<>, Stack, Opts, Acc) when ?is_noncontrol(S) -> + string(Rest, Stack, Opts, [S] ++ Acc); +string(Bin, Stack, Opts, Acc) -> case partial_utf(Bin) of - true -> - {incomplete, - fun(Stream) -> string(<>, Stack, Callbacks, Opts, Acc) end - } - ; false -> - {error, badjson} + false -> {error, badjson} + ; _ -> emit(incomplete, fun(Stream) -> string(<>, Stack, Opts, Acc) end) end. @@ -301,28 +286,25 @@ partial_utf(_) -> true. %% to hold the codepoint sequence. unescessary, but nicer than using the string %% accumulator. -escape(<<$b/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - string(Rest, Stack, Callbacks, Opts, "\b" ++ Acc); -escape(<<$f/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - string(Rest, Stack, Callbacks, Opts, "\f" ++ Acc); -escape(<<$n/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - string(Rest, Stack, Callbacks, Opts, "\n" ++ Acc); -escape(<<$r/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - string(Rest, Stack, Callbacks, Opts, "\r" ++ Acc); -escape(<<$t/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - string(Rest, Stack, Callbacks, Opts, "\t" ++ Acc); -escape(<<$u/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - escaped_unicode(Rest, Stack, Callbacks, Opts, Acc, []); -escape(<>, Stack, Callbacks, Opts, Acc) +escape(<<$b/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\b" ++ Acc); +escape(<<$f/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\f" ++ Acc); +escape(<<$n/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\n" ++ Acc); +escape(<<$r/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\r" ++ Acc); +escape(<<$t/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + string(Rest, Stack, Opts, "\t" ++ Acc); +escape(<<$u/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + escaped_unicode(Rest, Stack, Opts, Acc, []); +escape(<>, Stack, Opts, Acc) when S =:= ?quote; S =:= ?solidus; S =:= ?rsolidus -> - string(Rest, Stack, Callbacks, Opts, [S] ++ Acc); -escape(Bin, Stack, Callbacks, Opts, Acc) -> + string(Rest, Stack, Opts, [S] ++ Acc); +escape(Bin, Stack, Opts, Acc) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> escape(<>, Stack, Callbacks, Opts, Acc) end - } + ; _ -> emit(incomplete, fun(Stream) -> escape(<>, Stack, Opts, Acc) end) end. @@ -336,20 +318,18 @@ escape(Bin, Stack, Callbacks, Opts, Acc) -> escaped_unicode(<>, Stack, - Callbacks, ?escaped_unicode_to_ascii(Opts), String, [C, B, A]) when ?is_hex(D) -> case erlang:list_to_integer([A, B, C, D], 16) of X when X < 128 -> - string(Rest, Stack, Callbacks, Opts, [X] ++ String) + string(Rest, Stack, Opts, [X] ++ String) ; _ -> - string(Rest, Stack, Callbacks, Opts, [D, C, B, A, $u, ?rsolidus] ++ String) + string(Rest, Stack, Opts, [D, C, B, A, $u, ?rsolidus] ++ String) end; escaped_unicode(<>, Stack, - Callbacks, ?escaped_unicode_to_codepoint(Opts), String, [C, B, A]) @@ -358,26 +338,25 @@ escaped_unicode(<>, X when X >= 16#dc00, X =< 16#dfff -> case check_acc_for_surrogate(String) of false -> - string(Rest, Stack, Callbacks, Opts, [D, C, B, A, $u, ?rsolidus] ++ String) + string(Rest, Stack, Opts, [D, C, B, A, $u, ?rsolidus] ++ String) ; {Y, NewString} -> - string(Rest, Stack, Callbacks, Opts, [surrogate_to_codepoint(Y, X)] ++ NewString) + string(Rest, Stack, Opts, [surrogate_to_codepoint(Y, X)] ++ NewString) end ; X when X < 16#d800; X > 16#dfff, X < 16#fffe -> - string(Rest, Stack, Callbacks, Opts, [X] ++ String) + string(Rest, Stack, Opts, [X] ++ String) ; _ -> - string(Rest, Stack, Callbacks, Opts, [D, C, B, A, $u, ?rsolidus] ++ String) + string(Rest, Stack, Opts, [D, C, B, A, $u, ?rsolidus] ++ String) end; -escaped_unicode(<>, Stack, Callbacks, Opts, String, [C, B, A]) when ?is_hex(D) -> - string(Rest, Stack, Callbacks, Opts, [D, C, B, A, $u, ?rsolidus] ++ String); -escaped_unicode(<>, Stack, Callbacks, Opts, String, Acc) when ?is_hex(S) -> - escaped_unicode(Rest, Stack, Callbacks, Opts, String, [S] ++ Acc); -escaped_unicode(Bin, Stack, Callbacks, Opts, String, Acc) -> +escaped_unicode(<>, Stack, Opts, String, [C, B, A]) when ?is_hex(D) -> + string(Rest, Stack, Opts, [D, C, B, A, $u, ?rsolidus] ++ String); +escaped_unicode(<>, Stack, Opts, String, Acc) when ?is_hex(S) -> + escaped_unicode(Rest, Stack, Opts, String, [S] ++ Acc); +escaped_unicode(Bin, Stack, Opts, String, Acc) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> escaped_unicode(<>, Stack, Callbacks, Opts, String, Acc) end - } + ; _ -> emit(incomplete, fun(Stream) -> + escaped_unicode(<>, Stack, Opts, String, Acc) + end) end. %% upon encountering a low pair json/hex encoded value, check to see if there's a high @@ -403,324 +382,290 @@ surrogate_to_codepoint(High, Low) -> %% like strings, numbers are collected in an intermediate accumulator before %% being emitted to the callback handler. -negative(<<$0/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - zero(Rest, Stack, Callbacks, Opts, "0" ++ Acc); -negative(<>, Stack, Callbacks, Opts, Acc) when ?is_nonzero(S) -> - integer(Rest, Stack, Callbacks, Opts, [S] ++ Acc); -negative(Bin, Stack, Callbacks, Opts, Acc) -> +negative(<<$0/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + zero(Rest, Stack, Opts, "0" ++ Acc); +negative(<>, Stack, Opts, Acc) when ?is_nonzero(S) -> + integer(Rest, Stack, Opts, [S] ++ Acc); +negative(Bin, Stack, Opts, Acc) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> negative(<>, Stack, Callbacks, Opts, Acc) end - } + ; _ -> emit(incomplete, fun(Stream) -> negative(<>, Stack, Opts, Acc) end) end. -zero(<>, [object|Stack], Callbacks, Opts, Acc) -> - maybe_done(Rest, Stack, fold(end_object, fold({integer, lists:reverse(Acc)}, Callbacks)), Opts); -zero(<>, [array|Stack], Callbacks, Opts, Acc) -> - maybe_done(Rest, Stack, fold(end_array, fold({integer, lists:reverse(Acc)}, Callbacks)), Opts); -zero(<>, [object|Stack], Callbacks, Opts, Acc) -> - key(Rest, [key|Stack], fold({integer, lists:reverse(Acc)}, Callbacks), Opts); -zero(<>, [array|_] = Stack, Callbacks, Opts, Acc) -> - value(Rest, Stack, fold({integer, lists:reverse(Acc)}, Callbacks), Opts); -zero(<>, Stack, Callbacks, Opts, Acc) -> - initial_decimal(Rest, Stack, Callbacks, Opts, [?decimalpoint] ++ Acc); -zero(<>, Stack, Callbacks, Opts, Acc) when ?is_whitespace(S) -> - maybe_done(Rest, Stack, fold({integer, lists:reverse(Acc)}, Callbacks), Opts); -zero(<>, Stack, Callbacks, ?comments_enabled(Opts), Acc) -> - maybe_comment(Rest, fun(Resume) -> zero(Resume, Stack, Callbacks, Opts, Acc) end); -zero(Bin, [], Callbacks, ?stream_mode(Opts), Acc) -> - CB = fold({integer, lists:reverse(Acc)}, Callbacks), - {fold(end_json, CB), fun(Stream) -> - start(<>, [], fold(reset, CB), Opts) - end}; -zero(<<>>, [], Callbacks, Opts, Acc) -> - {fold(end_json, fold({integer, lists:reverse(Acc)}, Callbacks)), - fun(Stream) -> zero(Stream, [], Callbacks, Opts, Acc) end}; -zero(Bin, Stack, Callbacks, Opts, Acc) -> +zero(<>, [object|Stack], Opts, Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> + emit(end_object, fun() -> maybe_done(Rest, Stack, Opts) end) + end); +zero(<>, [array|Stack], Opts, Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> + emit(end_array, fun() -> maybe_done(Rest, Stack, Opts) end) + end); +zero(<>, [object|Stack], Opts, Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> key(Rest, [key|Stack], Opts) end); +zero(<>, [array|_] = Stack, Opts, Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> value(Rest, Stack, Opts) end); +zero(<>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [?decimalpoint] ++ Acc); +zero(<>, Stack, Opts, Acc) when ?is_whitespace(S) -> + emit({integer, lists:reverse(Acc)}, fun() -> maybe_done(Rest, Stack, Opts) end); +zero(<>, Stack, ?comments_enabled(Opts), Acc) -> + maybe_comment(Rest, fun(Resume) -> zero(Resume, Stack, Opts, Acc) end); +zero(Bin, [], ?stream_mode(Opts), Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> + emit(end_json, fun() -> start(Bin, [], Opts) end) + end); +zero(<<>>, [], Opts, Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> + emit(end_json, fun() -> emit(incomplete, fun(Stream) -> maybe_done(Stream, [], Opts) end) + end) + end); +zero(Bin, Stack, Opts, Acc) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> zero(<>, Stack, Callbacks, Opts, Acc) end - } + ; _ -> emit(incomplete, fun(Stream) -> zero(<>, Stack, Opts, Acc) end) end. -integer(<>, Stack, Callbacks, Opts, Acc) when ?is_nonzero(S) -> - integer(Rest, Stack, Callbacks, Opts, [S] ++ Acc); -integer(<>, [object|Stack], Callbacks, Opts, Acc) -> - maybe_done(Rest, Stack, fold(end_object, fold({integer, lists:reverse(Acc)}, Callbacks)), Opts); -integer(<>, [array|Stack], Callbacks, Opts, Acc) -> - maybe_done(Rest, Stack, fold(end_array, fold({integer, lists:reverse(Acc)}, Callbacks)), Opts); -integer(<>, [object|Stack], Callbacks, Opts, Acc) -> - key(Rest, [key|Stack], fold({integer, lists:reverse(Acc)}, Callbacks), Opts); -integer(<>, [array|_] = Stack, Callbacks, Opts, Acc) -> - value(Rest, Stack, fold({integer, lists:reverse(Acc)}, Callbacks), Opts); -integer(<>, Stack, Callbacks, Opts, Acc) -> - initial_decimal(Rest, Stack, Callbacks, Opts, [?decimalpoint] ++ Acc); -integer(<>, Stack, Callbacks, Opts, Acc) -> - integer(Rest, Stack, Callbacks, Opts, [?zero] ++ Acc); -integer(<<$e/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - e(Rest, Stack, Callbacks, Opts, "e0." ++ Acc); -integer(<<$E/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - e(Rest, Stack, Callbacks, Opts, "e0." ++ Acc); -integer(<>, Stack, Callbacks, Opts, Acc) when ?is_whitespace(S) -> - maybe_done(Rest, Stack, fold({integer, lists:reverse(Acc)}, Callbacks), Opts); -integer(<>, Stack, Callbacks, ?comments_enabled(Opts), Acc) -> - maybe_comment(Rest, fun(Resume) -> integer(Resume, Stack, Callbacks, Opts, Acc) end); -integer(Bin, [], Callbacks, ?stream_mode(Opts), Acc) -> - CB = fold({integer, lists:reverse(Acc)}, Callbacks), - {fold(end_json, CB), fun(Stream) -> - start(<>, [], fold(reset, CB), Opts) - end}; -integer(<<>>, [], Callbacks, Opts, Acc) -> - {fold(end_json, fold({integer, lists:reverse(Acc)}, Callbacks)), - fun(Stream) -> integer(Stream, [], Callbacks, Opts, Acc) end}; -integer(Bin, Stack, Callbacks, Opts, Acc) -> +integer(<>, Stack, Opts, Acc) when ?is_nonzero(S) -> + integer(Rest, Stack, Opts, [S] ++ Acc); +integer(<>, [object|Stack], Opts, Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> + emit(end_object, fun() -> maybe_done(Rest, Stack, Opts) end) + end); +integer(<>, [array|Stack], Opts, Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> + emit(end_array, fun() -> maybe_done(Rest, Stack, Opts) end) + end); +integer(<>, [object|Stack], Opts, Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> key(Rest, [key|Stack], Opts) end); +integer(<>, [array|_] = Stack, Opts, Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> value(Rest, Stack, Opts) end); +integer(<>, Stack, Opts, Acc) -> + initial_decimal(Rest, Stack, Opts, [?decimalpoint] ++ Acc); +integer(<>, Stack, Opts, Acc) -> + integer(Rest, Stack, Opts, [?zero] ++ Acc); +integer(<<$e/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<<$E/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e0." ++ Acc); +integer(<>, Stack, Opts, Acc) when ?is_whitespace(S) -> + emit({integer, lists:reverse(Acc)}, fun() -> maybe_done(Rest, Stack, Opts) end); +integer(<>, Stack, ?comments_enabled(Opts), Acc) -> + maybe_comment(Rest, fun(Resume) -> integer(Resume, Stack, Opts, Acc) end); +integer(Bin, [], ?stream_mode(Opts), Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> + emit(end_json, fun() -> start(Bin, [], Opts) end) + end); +integer(<<>>, [], Opts, Acc) -> + emit({integer, lists:reverse(Acc)}, fun() -> + emit(end_json, fun() -> emit(incomplete, fun(Stream) -> maybe_done(Stream, [], Opts) end) + end) + end); +integer(Bin, Stack, Opts, Acc) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> integer(<>, Stack, Callbacks, Opts, Acc) end - } + ; _ -> emit(incomplete, fun(Stream) -> integer(<>, Stack, Opts, Acc) end) end. -initial_decimal(<>, Stack, Callbacks, Opts, Acc) when ?is_nonzero(S) -> - decimal(Rest, Stack, Callbacks, Opts, [S] ++ Acc); -initial_decimal(<>, Stack, Callbacks, Opts, Acc) -> - decimal(Rest, Stack, Callbacks, Opts, [?zero] ++ Acc); -initial_decimal(Bin, Stack, Callbacks, Opts, Acc) -> +initial_decimal(<>, Stack, Opts, Acc) when ?is_nonzero(S) -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +initial_decimal(<>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [?zero] ++ Acc); +initial_decimal(Bin, Stack, Opts, Acc) -> +case byte_size(Bin) >= ?symbol_size of + true -> {error, badjson} + ; _ -> emit(incomplete, fun(Stream) -> initial_decimal(<>, Stack, Opts, Acc) end) +end. + + +decimal(<>, Stack, Opts, Acc) when ?is_nonzero(S) -> + decimal(Rest, Stack, Opts, [S] ++ Acc); +decimal(<>, [object|Stack], Opts, Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> + emit(end_object, fun() -> maybe_done(Rest, Stack, Opts) end) + end); +decimal(<>, [array|Stack], Opts, Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> + emit(end_array, fun() -> maybe_done(Rest, Stack, Opts) end) + end); +decimal(<>, [object|Stack], Opts, Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> key(Rest, [key|Stack], Opts) end); +decimal(<>, [array|_] = Stack, Opts, Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> value(Rest, Stack, Opts) end); +decimal(<>, Stack, Opts, Acc) -> + decimal(Rest, Stack, Opts, [?zero] ++ Acc); +decimal(<<$e/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<<$E/?encoding, Rest/binary>>, Stack, Opts, Acc) -> + e(Rest, Stack, Opts, "e" ++ Acc); +decimal(<>, Stack, Opts, Acc) when ?is_whitespace(S) -> + emit({float, lists:reverse(Acc)}, fun() -> maybe_done(Rest, Stack, Opts) end); +decimal(<>, Stack, ?comments_enabled(Opts), Acc) -> + maybe_comment(Rest, fun(Resume) -> decimal(Resume, Stack, Opts, Acc) end); +decimal(Bin, [], ?stream_mode(Opts), Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> + emit(end_json, fun() -> start(Bin, [], Opts) end) + end); +decimal(<<>>, [], Opts, Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> + emit(end_json, fun() -> emit(incomplete, fun(Stream) -> maybe_done(Stream, [], Opts) end) + end) + end); +decimal(Bin, Stack, Opts, Acc) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> initial_decimal(<>, Stack, Callbacks, Opts, Acc) end - } + ; _ -> emit(incomplete, fun(Stream) -> decimal(<>, Stack, Opts, Acc) end) end. -decimal(<>, Stack, Callbacks, Opts, Acc) when ?is_nonzero(S) -> - decimal(Rest, Stack, Callbacks, Opts, [S] ++ Acc); -decimal(<>, [object|Stack], Callbacks, Opts, Acc) -> - maybe_done(Rest, Stack, fold(end_object, fold({float, lists:reverse(Acc)}, Callbacks)), Opts); -decimal(<>, [array|Stack], Callbacks, Opts, Acc) -> - maybe_done(Rest, Stack, fold(end_array, fold({float, lists:reverse(Acc)}, Callbacks)), Opts); -decimal(<>, [object|Stack], Callbacks, Opts, Acc) -> - key(Rest, [key|Stack], fold({float, lists:reverse(Acc)}, Callbacks), Opts); -decimal(<>, [array|_] = Stack, Callbacks, Opts, Acc) -> - value(Rest, Stack, fold({float, lists:reverse(Acc)}, Callbacks), Opts); -decimal(<>, Stack, Callbacks, Opts, Acc) -> - decimal(Rest, Stack, Callbacks, Opts, [?zero] ++ Acc); -decimal(<<$e/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - e(Rest, Stack, Callbacks, Opts, "e" ++ Acc); -decimal(<<$E/?encoding, Rest/binary>>, Stack, Callbacks, Opts, Acc) -> - e(Rest, Stack, Callbacks, Opts, "e" ++ Acc); -decimal(<>, Stack, Callbacks, Opts, Acc) when ?is_whitespace(S) -> - maybe_done(Rest, Stack, fold({float, lists:reverse(Acc)}, Callbacks), Opts); -decimal(<>, Stack, Callbacks, ?comments_enabled(Opts), Acc) -> - maybe_comment(Rest, fun(Resume) -> decimal(Resume, Stack, Callbacks, Opts, Acc) end); -decimal(Bin, [], Callbacks, ?stream_mode(Opts), Acc) -> - CB = fold({float, lists:reverse(Acc)}, Callbacks), - {fold(end_json, CB), fun(Stream) -> - start(<>, [], fold(reset, CB), Opts) - end}; -decimal(<<>>, [], Callbacks, Opts, Acc) -> - {fold(end_json, fold({float, lists:reverse(Acc)}, Callbacks)), - fun(Stream) -> decimal(Stream, [], Callbacks, Opts, Acc) end}; -decimal(Bin, Stack, Callbacks, Opts, Acc) -> +e(<>, Stack, Opts, Acc) when S =:= ?zero; ?is_nonzero(S) -> + exp(Rest, Stack, Opts, [S] ++ Acc); +e(<>, Stack, Opts, Acc) when S =:= ?positive; S =:= ?negative -> + ex(Rest, Stack, Opts, [S] ++ Acc); +e(Bin, Stack, Opts, Acc) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> decimal(<>, Stack, Callbacks, Opts, Acc) end - } + ; _ -> emit(incomplete, fun(Stream) -> e(<>, Stack, Opts, Acc) end) end. -e(<>, Stack, Callbacks, Opts, Acc) when S =:= ?zero; ?is_nonzero(S) -> - exp(Rest, Stack, Callbacks, Opts, [S] ++ Acc); -e(<>, Stack, Callbacks, Opts, Acc) when S =:= ?positive; S =:= ?negative -> - ex(Rest, Stack, Callbacks, Opts, [S] ++ Acc); -e(Bin, Stack, Callbacks, Opts, Acc) -> +ex(<>, Stack, Opts, Acc) when S =:= ?zero; ?is_nonzero(S) -> + exp(Rest, Stack, Opts, [S] ++ Acc); +ex(Bin, Stack, Opts, Acc) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> e(<>, Stack, Callbacks, Opts, Acc) end - } + ; _ -> emit(incomplete, fun(Stream) -> ex(<>, Stack, Opts, Acc) end) end. -ex(<>, Stack, Callbacks, Opts, Acc) when S =:= ?zero; ?is_nonzero(S) -> - exp(Rest, Stack, Callbacks, Opts, [S] ++ Acc); -ex(Bin, Stack, Callbacks, Opts, Acc) -> +exp(<>, Stack, Opts, Acc) when ?is_nonzero(S) -> + exp(Rest, Stack, Opts, [S] ++ Acc); +exp(<>, [object|Stack], Opts, Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> + emit(end_object, fun() -> maybe_done(Rest, Stack, Opts) end) + end); +exp(<>, [array|Stack], Opts, Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> + emit(end_array, fun() -> maybe_done(Rest, Stack, Opts) end) + end); +exp(<>, [object|Stack], Opts, Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> key(Rest, [key|Stack], Opts) end); +exp(<>, [array|_] = Stack, Opts, Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> value(Rest, Stack, Opts) end); +exp(<>, Stack, Opts, Acc) -> + exp(Rest, Stack, Opts, [?zero] ++ Acc); +exp(<>, Stack, Opts, Acc) when ?is_whitespace(S) -> + emit({float, lists:reverse(Acc)}, fun() -> maybe_done(Rest, Stack, Opts) end); +exp(<>, Stack, ?comments_enabled(Opts), Acc) -> + maybe_comment(Rest, fun(Resume) -> exp(Resume, Stack, Opts, Acc) end); +exp(Bin, [], ?stream_mode(Opts), Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> + emit(end_json, fun() -> start(Bin, [], Opts) end) + end); +exp(<<>>, [], Opts, Acc) -> + emit({float, lists:reverse(Acc)}, fun() -> + emit(end_json, fun() -> emit(incomplete, fun(Stream) -> maybe_done(Stream, [], Opts) end) + end) + end); +exp(Bin, Stack, Opts, Acc) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> ex(<>, Stack, Callbacks, Opts, Acc) end - } + ; _ -> emit(incomplete, fun(Stream) -> exp(<>, Stack, Opts, Acc) end) end. -exp(<>, Stack, Callbacks, Opts, Acc) when ?is_nonzero(S) -> - exp(Rest, Stack, Callbacks, Opts, [S] ++ Acc); -exp(<>, [object|Stack], Callbacks, Opts, Acc) -> - maybe_done(Rest, Stack, fold(end_object, fold({float, lists:reverse(Acc)}, Callbacks)), Opts); -exp(<>, [array|Stack], Callbacks, Opts, Acc) -> - maybe_done(Rest, Stack, fold(end_array, fold({float, lists:reverse(Acc)}, Callbacks)), Opts); -exp(<>, [object|Stack], Callbacks, Opts, Acc) -> - key(Rest, [key|Stack], fold({float, lists:reverse(Acc)}, Callbacks), Opts); -exp(<>, [array|_] = Stack, Callbacks, Opts, Acc) -> - value(Rest, Stack, fold({float, lists:reverse(Acc)}, Callbacks), Opts); -exp(<>, Stack, Callbacks, Opts, Acc) -> - exp(Rest, Stack, Callbacks, Opts, [?zero] ++ Acc); -exp(<>, Stack, Callbacks, ?comments_enabled(Opts), Acc) -> - maybe_comment(Rest, fun(Resume) -> exp(Resume, Stack, Callbacks, Opts, Acc) end); -exp(<>, Stack, Callbacks, Opts, Acc) when ?is_whitespace(S) -> - maybe_done(Rest, Stack, fold({float, lists:reverse(Acc)}, Callbacks), Opts); -exp(Bin, [], Callbacks, ?stream_mode(Opts), Acc) -> - CB = fold({float, lists:reverse(Acc)}, Callbacks), - {fold(end_json, CB), fun(Stream) -> - start(<>, [], fold(reset, CB), Opts) - end}; -exp(<<>>, [], Callbacks, Opts, Acc) -> - {fold(end_json, fold({float, lists:reverse(Acc)}, Callbacks)), - fun(Stream) -> exp(Stream, [], Callbacks, Opts, Acc) end}; -exp(Bin, Stack, Callbacks, Opts, Acc) -> +tr(<<$r/?encoding, Rest/binary>>, Stack, Opts) -> + tru(Rest, Stack, Opts); +tr(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> exp(<>, Stack, Callbacks, Opts, Acc) end - } + ; _ -> emit(incomplete, fun(Stream) -> tr(<>, Stack, Opts) end) end. -tr(<<$r/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - tru(Rest, Stack, Callbacks, Opts); -tr(Bin, Stack, Callbacks, Opts) -> +tru(<<$u/?encoding, Rest/binary>>, Stack, Opts) -> + true(Rest, Stack, Opts); +tru(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> tr(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> tru(<>, Stack, Opts) end) end. -tru(<<$u/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - true(Rest, Stack, Callbacks, Opts); -tru(Bin, Stack, Callbacks, Opts) -> +true(<<$e/?encoding, Rest/binary>>, Stack, Opts) -> + emit({literal, true}, fun() -> maybe_done(Rest, Stack, Opts) end); +true(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> tru(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> true(<>, Stack, Opts) end) end. -true(<<$e/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - maybe_done(Rest, Stack, fold({literal, true}, Callbacks), Opts); -true(Bin, Stack, Callbacks, Opts) -> +fa(<<$a/?encoding, Rest/binary>>, Stack, Opts) -> + fal(Rest, Stack, Opts); +fa(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> true(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> fa(<>, Stack, Opts) end) end. -fa(<<$a/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - fal(Rest, Stack, Callbacks, Opts); -fa(Bin, Stack, Callbacks, Opts) -> +fal(<<$l/?encoding, Rest/binary>>, Stack, Opts) -> + fals(Rest, Stack, Opts); +fal(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> fa(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> fal(<>, Stack, Opts) end) end. -fal(<<$l/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - fals(Rest, Stack, Callbacks, Opts); -fal(Bin, Stack, Callbacks, Opts) -> +fals(<<$s/?encoding, Rest/binary>>, Stack, Opts) -> + false(Rest, Stack, Opts); +fals(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> fal(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> fals(<>, Stack, Opts) end) end. -fals(<<$s/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - false(Rest, Stack, Callbacks, Opts); -fals(Bin, Stack, Callbacks, Opts) -> +false(<<$e/?encoding, Rest/binary>>, Stack, Opts) -> + emit({literal, false}, fun() -> maybe_done(Rest, Stack, Opts) end); +false(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> fals(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> false(<>, Stack, Opts) end) end. -false(<<$e/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - maybe_done(Rest, Stack, fold({literal, false}, Callbacks), Opts); -false(Bin, Stack, Callbacks, Opts) -> +nu(<<$u/?encoding, Rest/binary>>, Stack, Opts) -> + nul(Rest, Stack, Opts); +nu(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> false(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> nu(<>, Stack, Opts) end) end. -nu(<<$u/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - nul(Rest, Stack, Callbacks, Opts); -nu(Bin, Stack, Callbacks, Opts) -> +nul(<<$l/?encoding, Rest/binary>>, Stack, Opts) -> + null(Rest, Stack, Opts); +nul(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> nu(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> nul(<>, Stack, Opts) end) end. -nul(<<$l/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - null(Rest, Stack, Callbacks, Opts); -nul(Bin, Stack, Callbacks, Opts) -> +null(<<$l/?encoding, Rest/binary>>, Stack, Opts) -> + emit({literal, null}, fun() -> maybe_done(Rest, Stack, Opts) end); +null(Bin, Stack, Opts) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> nul(<>, Stack, Callbacks, Opts) end - } + ; _ -> emit(incomplete, fun(Stream) -> null(<>, Stack, Opts) end) end. -null(<<$l/?encoding, Rest/binary>>, Stack, Callbacks, Opts) -> - maybe_done(Rest, Stack, fold({literal, null}, Callbacks), Opts); -null(Bin, Stack, Callbacks, Opts) -> - case byte_size(Bin) >= ?symbol_size of - true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> null(<>, Stack, Callbacks, Opts) end - } - end. - - %% comments are c style, /* blah blah */ and are STRONGLY discouraged. any unicode %% character is valid in a comment, except, obviously the */ sequence which ends %% the comment. they're implemented as a closure called when the comment ends that @@ -732,10 +677,7 @@ maybe_comment(<>, Resume) -> maybe_comment(Bin, Resume) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, - fun(Stream) -> maybe_comment(<>, Resume) end - } + ; _ -> emit(incomplete, fun(Stream) -> maybe_comment(<>, Resume) end) end. @@ -746,8 +688,7 @@ comment(<<_/?encoding, Rest/binary>>, Resume) -> comment(Bin, Resume) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, fun(Stream) -> comment(<>, Resume) end} + ; _ -> emit(incomplete, fun(Stream) -> comment(<>, Resume) end) end. @@ -758,6 +699,5 @@ maybe_comment_done(<<_/?encoding, Rest/binary>>, Resume) -> maybe_comment_done(Bin, Resume) -> case byte_size(Bin) >= ?symbol_size of true -> {error, badjson} - ; _ -> - {incomplete, fun(Stream) -> maybe_comment_done(<>, Resume) end} + ; _ -> emit(incomplete, fun(Stream) -> maybe_comment_done(<>, Resume) end) end. \ No newline at end of file