diff --git a/src/jsx.erl b/src/jsx.erl index 6abaff3..261409c 100644 --- a/src/jsx.erl +++ b/src/jsx.erl @@ -27,7 +27,7 @@ %% the core parser api -export([parser/0, parser/1]). -export([decoder/0, decoder/1]). --export([encoder/0]). +-export([encoder/0, encoder/1]). -export([term_to_json/1, term_to_json/2]). -export([json_to_term/1, json_to_term/2]). -export([is_json/1, is_json/2]). @@ -37,11 +37,6 @@ -include("jsx_common.hrl"). --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. - - -spec parser() -> jsx_decoder(). parser() -> decoder([]). @@ -52,7 +47,6 @@ parser() -> decoder([]). parser(OptsList) -> decoder(OptsList). - -spec decoder() -> jsx_decoder(). decoder() -> decoder([]). @@ -77,7 +71,7 @@ decoder(OptsList) -> encoder() -> encoder([]). --spec encoder(OptsList::jsx_opts()) -> jsx_encoder(). +-spec encoder(Opts::jsx_opts()) -> jsx_encoder(). encoder(Opts) -> jsx_encoder:encoder(Opts). @@ -146,6 +140,8 @@ format(JSON, Opts) -> -ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + jsx_decoder_test_() -> jsx_decoder_gen(load_tests(?eunit_test_path)). @@ -174,13 +170,19 @@ jsx_decoder_gen([Test|_] = Tests, [Encoding|Encodings]) -> Flags = proplists:get_value(jsx_flags, Test, []), {generator, fun() -> - [{Name, ?_assertEqual(decode(JSON, Flags), JSX)} - | {generator, + [{Name ++ " iterative", + ?_assertEqual(iterative_decode(JSON, Flags), JSX)} + | {generator, fun() -> [{Name ++ " incremental", ?_assertEqual( incremental_decode(JSON, Flags), JSX) - } | jsx_decoder_gen(Tests, Encodings)] - end - } + } | {generator, + fun() -> + [{Name, ?_assertEqual( + decode(JSON, Flags), JSX) + } | jsx_decoder_gen(Tests, Encodings)] + end} + ] + end} ] end }. @@ -219,20 +221,33 @@ parse_tests([], _Dir, Acc) -> decode(JSON, Flags) -> P = jsx:decoder(Flags), - decode_loop(P(JSON), []). + case P(JSON) of + {error, {badjson, _}} -> {error, badjson} + ; {jsx, incomplete, More} -> + case More(end_stream) of + {error, {badjson, _}} -> {error, badjson} + ; {jsx, T, _} -> T + end + ; {jsx, T, _} -> T + end. -decode_loop({jsx, end_json, _Next}, Acc) -> + +iterative_decode(JSON, Flags) -> + P = jsx:decoder([iterate] ++ Flags), + iterative_decode_loop(P(JSON), []). + +iterative_decode_loop({jsx, end_json, _Next}, Acc) -> lists:reverse([end_json] ++ Acc); -decode_loop({jsx, incomplete, More}, Acc) -> - decode_loop(More(end_stream), Acc); -decode_loop({jsx, E, Next}, Acc) -> - decode_loop(Next(), [E] ++ Acc); -decode_loop({error, {badjson, _Error}}, _Acc) -> +iterative_decode_loop({jsx, incomplete, More}, Acc) -> + iterative_decode_loop(More(end_stream), Acc); +iterative_decode_loop({jsx, E, Next}, Acc) -> + iterative_decode_loop(Next(), [E] ++ Acc); +iterative_decode_loop({error, {badjson, _Error}}, _Acc) -> {error, badjson}. incremental_decode(<>, Flags) -> - P = jsx:decoder(Flags), + P = jsx:decoder([iterate] ++ Flags), incremental_decode_loop(P(C), Rest, []). incremental_decode_loop({jsx, incomplete, Next}, <<>>, Acc) -> @@ -246,47 +261,5 @@ incremental_decode_loop({jsx, Event, Next}, Rest, Acc) -> incremental_decode_loop({error, {badjson, _Error}}, _Rest, _Acc) -> {error, badjson}. - - -multi_decode_test_() -> - [ - {"multiple values in a single stream", ?_assert( - multi_decode(multi_json_body(), []) =:= multi_test_result() - )} - ]. - - -multi_decode(JSON, Flags) -> - P = jsx:decoder(Flags ++ [multi_term]), - multi_decode_loop(P(JSON), [[]]). - -multi_decode_loop({jsx, incomplete, _Next}, [[]|Acc]) -> - lists:reverse(Acc); -multi_decode_loop({jsx, end_json, Next}, [S|Acc]) -> - multi_decode_loop(Next(), [[]|[lists:reverse(S)] ++ Acc]); -multi_decode_loop({jsx, E, Next}, [S|Acc]) -> - multi_decode_loop(Next(), [[E] ++ S] ++ Acc). - - -multi_json_body() -> - <<"0 1 -1 1e1 0.7 0.7e-1 truefalsenull {} {\"key\": \"value\"}[] [1, 2, 3]\"hope this works\"">>. - -multi_test_result() -> - [[{integer, 0}], - [{integer, 1}], - [{integer, -1}], - [{float, 1.0e1}], - [{float, 0.7}], - [{float, 0.7e-1}], - [{literal, true}], - [{literal, false}], - [{literal, null}], - [start_object, end_object], - [start_object, {key, "key"}, {string, "value"}, end_object], - [start_array, end_array], - [start_array, {integer, 1}, {integer, 2}, {integer, 3}, end_array], - [{string, "hope this works"}] - ]. - -endif. \ No newline at end of file diff --git a/src/jsx_decoder.hrl b/src/jsx_decoder.hrl index 537fb84..00bc8c0 100644 --- a/src/jsx_decoder.hrl +++ b/src/jsx_decoder.hrl @@ -27,40 +27,59 @@ -export([decoder/1]). --spec decoder(OptsList::jsx_opts()) -> jsx_decoder(). +%% exported solely to facilitate stupid trick i shouldn't be using +-export([start/4, + maybe_done/4, + done/3, + object/4, + array/4, + value/4, + colon/4, + key/4, + string/5, + escape/5, + escaped_unicode/6, + low_surrogate/6, + low_surrogate_u/6, + low_surrogate/7, + negative/5, + zero/5, + integer/5, + initial_decimal/5, + decimal/5, + e/5, + ex/5, + exp/5, + tr/4, + tru/4, + true/4, + fa/4, + fal/4, + fals/4, + false/4, + nu/4, + nul/4, + null/4, + bad_json/2 +]). + + +-include("jsx_opts.hrl"). + + +-spec decoder(OptsList::jsx_opts()) -> jsx_decoder(). decoder(OptsList) -> case parse_opts(OptsList) of {error, badopt} -> {error, badopt} - ; Opts -> fun(JSON) -> start(JSON, [], Opts) end + ; Opts -> + case Opts#opts.iterate of + true -> fun(JSON) -> start(JSON, iterate, [], Opts) end + ; false -> fun(JSON) -> start(JSON, [], [], Opts) end + end end. - -%% opts record for decoder --record(opts, { - multi_term = false, - loose_unicode = false, - encoding = auto, - escape_forward_slash = false %% does nothing, used by encoder -}). - - -%% converts a proplist into a tuple -parse_opts(Opts) -> - parse_opts(Opts, #opts{}). - -parse_opts([], Opts) -> - Opts; -parse_opts([multi_term|Rest], Opts) -> - parse_opts(Rest, Opts#opts{multi_term=true}); -parse_opts([loose_unicode|Rest], Opts) -> - parse_opts(Rest, Opts#opts{loose_unicode=true}); -parse_opts([{encoding, _}|Rest], Opts) -> - parse_opts(Rest, Opts); -parse_opts(_, _) -> - {error, badarg}. - %% whitespace -define(space, 16#20). @@ -205,154 +224,159 @@ partial_utf(_) -> false. -endif. --define(incomplete(Next), +incomplete(State, Bin, T, Args) -> case ?partial_codepoint(Bin) of true -> - {jsx, incomplete, fun(end_stream) -> - {error, {badjson, Bin}} - ; (Stream) -> - Next - end} + emit([incomplete], {State, Bin, T, Args}) ; false -> {error, {badjson, Bin}} - end -). + end. %% emit takes a list of `events` to present to client code and formats them %% appropriately -emit([Event], F) -> {jsx, Event, F}; -emit([Event|Rest], F) -> {jsx, Event, fun() -> emit(Rest, F) end}. +emit([], {State, Rest, T, Args}) -> + erlang:apply(?MODULE, State, [Rest, T] ++ Args); +emit([incomplete], {State, Rest, T, Args}) -> + {jsx, incomplete, fun(end_stream) -> + {error, {badjson, <<>>}} + ; (Stream) -> + erlang:apply(?MODULE, + State, + [<>, T] ++ Args + ) + end}; +emit([Event|Events], {_State, _Rest, iterate, _Args} = Next) -> + {jsx, Event, fun() -> emit(Events, Next) end}; +emit([end_json|Events], {_State, _Rest, T, _Args} = Next) -> + {jsx, lists:reverse([end_json] ++ T), fun() -> emit(Events, Next) end}; +emit([Event|Events], {State, Rest, T, Args}) -> + emit(Events, {State, Rest, [Event] ++ T, Args}). -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/?utfx, Rest/binary>>, Stack, Opts) -> - tr(Rest, Stack, Opts); -start(<<$f/?utfx, Rest/binary>>, Stack, Opts) -> - fa(Rest, Stack, Opts); -start(<<$n/?utfx, 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(Bin, Stack, Opts) -> - ?incomplete(start(<>, Stack, Opts)). +bad_json(Stream, _) -> {error, {badjson, Stream}}. -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(Rest, [], #opts{multi_term=true}=Opts) -> - emit([end_json], fun() -> start(Rest, [], Opts) end); -maybe_done(Rest, [], Opts) -> - done(Rest, Opts); -maybe_done(Bin, Stack, Opts) -> - ?incomplete(maybe_done(<>, Stack, Opts)). +start(<>, T, Stack, Opts) when ?is_whitespace(S) -> + start(Rest, T, Stack, Opts); +start(<>, T, Stack, Opts) -> + emit([start_object], {object, Rest, T, [[key|Stack], Opts]}); +start(<>, T, Stack, Opts) -> + emit([start_array], {array, Rest, T, [[array|Stack], Opts]}); +start(<>, T, Stack, Opts) -> + string(Rest, T, Stack, Opts); +start(<<$t/?utfx, Rest/binary>>, T, Stack, Opts) -> + tr(Rest, T, Stack, Opts); +start(<<$f/?utfx, Rest/binary>>, T, Stack, Opts) -> + fa(Rest, T, Stack, Opts); +start(<<$n/?utfx, Rest/binary>>, T, Stack, Opts) -> + nu(Rest, T, Stack, Opts); +start(<>, T, Stack, Opts) -> + negative(Rest, T, Stack, Opts, "-"); +start(<>, T, Stack, Opts) -> + zero(Rest, T, Stack, Opts, "0"); +start(<>, T, Stack, Opts) when ?is_nonzero(S) -> + integer(Rest, T, Stack, Opts, [S]); +start(Bin, T, Stack, Opts) -> + incomplete(start, Bin, T, [Stack, Opts]). -done(<>, Opts) when ?is_whitespace(S) -> - done(Rest, Opts); -done(<<>>, Opts) -> - emit([end_json], fun() -> - {jsx, incomplete, fun(end_stream) -> - {error, {badjson, <<>>}} - ; (Stream) -> - done(Stream, Opts) - end} - end); -done(Bin, Opts) -> - ?incomplete(done(<>, Opts)). +maybe_done(<>, T, Stack, Opts) when ?is_whitespace(S) -> + maybe_done(Rest, T, Stack, Opts); +maybe_done(<>, T, [object|Stack], Opts) -> + emit([end_object], {maybe_done, Rest, T, [Stack, Opts]}); +maybe_done(<>, T, [array|Stack], Opts) -> + emit([end_array], {maybe_done, Rest, T, [Stack, Opts]}); +maybe_done(<>, T, [object|Stack], Opts) -> + key(Rest, T, [key|Stack], Opts); +maybe_done(<>, T, [array|_] = Stack, Opts) -> + value(Rest, T, Stack, Opts); +maybe_done(Rest, T, [], Opts) -> + done(Rest, T, Opts); +maybe_done(Bin, T, Stack, Opts) -> + incomplete(maybe_done, Bin, T, [Stack, 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(Bin, Stack, Opts) -> - ?incomplete(object(<>, Stack, Opts)). +done(<>, T, Opts) when ?is_whitespace(S) -> + done(Rest, T, Opts); +done(<<>>, T, Opts) -> + emit([end_json, incomplete], {done, <<>>, T, [Opts]}); +done(Bin, T, Opts) -> + incomplete(done, Bin, T, [Opts]). -array(<>, Stack, Opts) when ?is_whitespace(S) -> - array(Rest, Stack, Opts); -array(<>, Stack, Opts) -> - string(Rest, Stack, Opts); -array(<<$t/?utfx, Rest/binary>>, Stack, Opts) -> - tr(Rest, Stack, Opts); -array(<<$f/?utfx, Rest/binary>>, Stack, Opts) -> - fa(Rest, Stack, Opts); -array(<<$n/?utfx, 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(Bin, Stack, Opts) -> - ?incomplete(array(<>, Stack, Opts)). +object(<>, T, Stack, Opts) when ?is_whitespace(S) -> + object(Rest, T, Stack, Opts); +object(<>, T, Stack, Opts) -> + string(Rest, T, Stack, Opts); +object(<>, T, [key|Stack], Opts) -> + emit([end_object], {maybe_done, Rest, T, [Stack, Opts]}); +object(Bin, T, Stack, Opts) -> + incomplete(object, Bin, T, [Stack, Opts]). -value(<>, Stack, Opts) when ?is_whitespace(S) -> - value(Rest, Stack, Opts); -value(<>, Stack, Opts) -> - string(Rest, Stack, Opts); -value(<<$t/?utfx, Rest/binary>>, Stack, Opts) -> - tr(Rest, Stack, Opts); -value(<<$f/?utfx, Rest/binary>>, Stack, Opts) -> - fa(Rest, Stack, Opts); -value(<<$n/?utfx, 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(Bin, Stack, Opts) -> - ?incomplete(value(<>, Stack, Opts)). +array(<>, T, Stack, Opts) when ?is_whitespace(S) -> + array(Rest, T, Stack, Opts); +array(<>, T, Stack, Opts) -> + string(Rest, T, Stack, Opts); +array(<<$t/?utfx, Rest/binary>>, T, Stack, Opts) -> + tr(Rest, T, Stack, Opts); +array(<<$f/?utfx, Rest/binary>>, T, Stack, Opts) -> + fa(Rest, T, Stack, Opts); +array(<<$n/?utfx, Rest/binary>>, T, Stack, Opts) -> + nu(Rest, T, Stack, Opts); +array(<>, T, Stack, Opts) -> + negative(Rest, T, Stack, Opts, "-"); +array(<>, T, Stack, Opts) -> + zero(Rest, T, Stack, Opts, "0"); +array(<>, T, Stack, Opts) when ?is_nonzero(S) -> + integer(Rest, T, Stack, Opts, [S]); +array(<>, T, Stack, Opts) -> + emit([start_object], {object, Rest, T, [[key|Stack], Opts]}); +array(<>, T, Stack, Opts) -> + emit([start_array], {array, Rest, T, [[array|Stack], Opts]}); +array(<>, T, [array|Stack], Opts) -> + emit([end_array], {maybe_done, Rest, T, [Stack, Opts]}); +array(Bin, T, Stack, Opts) -> + incomplete(array, Bin, T, [Stack, Opts]). -colon(<>, Stack, Opts) when ?is_whitespace(S) -> - colon(Rest, Stack, Opts); -colon(<>, [key|Stack], Opts) -> - value(Rest, [object|Stack], Opts); -colon(Bin, Stack, Opts) -> - ?incomplete(colon(<>, Stack, Opts)). +value(<>, T, Stack, Opts) when ?is_whitespace(S) -> + value(Rest, T, Stack, Opts); +value(<>, T, Stack, Opts) -> + string(Rest, T, Stack, Opts); +value(<<$t/?utfx, Rest/binary>>, T, Stack, Opts) -> + tr(Rest, T, Stack, Opts); +value(<<$f/?utfx, Rest/binary>>, T, Stack, Opts) -> + fa(Rest, T, Stack, Opts); +value(<<$n/?utfx, Rest/binary>>, T, Stack, Opts) -> + nu(Rest, T, Stack, Opts); +value(<>, T, Stack, Opts) -> + negative(Rest, T, Stack, Opts, "-"); +value(<>, T, Stack, Opts) -> + zero(Rest, T, Stack, Opts, "0"); +value(<>, T, Stack, Opts) when ?is_nonzero(S) -> + integer(Rest, T, Stack, Opts, [S]); +value(<>, T, Stack, Opts) -> + emit([start_object], {object, Rest, T, [[key|Stack], Opts]}); +value(<>, T, Stack, Opts) -> + emit([start_array], {array, Rest, T, [[array|Stack], Opts]}); +value(Bin, T, Stack, Opts) -> + incomplete(value, Bin, T, [Stack, Opts]). -key(<>, Stack, Opts) when ?is_whitespace(S) -> - key(Rest, Stack, Opts); -key(<>, Stack, Opts) -> - string(Rest, Stack, Opts); -key(Bin, Stack, Opts) -> - ?incomplete(key(<>, Stack, Opts)). +colon(<>, T, Stack, Opts) when ?is_whitespace(S) -> + colon(Rest, T, Stack, Opts); +colon(<>, T, [key|Stack], Opts) -> + value(Rest, T, [object|Stack], Opts); +colon(Bin, T, Stack, Opts) -> + incomplete(colon, Bin, T, [Stack, Opts]). + + +key(<>, T, Stack, Opts) when ?is_whitespace(S) -> + key(Rest, T, Stack, Opts); +key(<>, T, Stack, Opts) -> + string(Rest, T, Stack, Opts); +key(Bin, T, Stack, Opts) -> + incomplete(key, Bin, T, [Stack, Opts]). %% string has an additional parameter, an accumulator (Acc) used to hold the @@ -362,28 +386,26 @@ key(Bin, Stack, Opts) -> %% string uses partial_utf/1 to cease parsing when invalid encodings are %% encountered rather than just checking remaining binary size like other %% states to eliminate certain incomplete states -string(Bin, Stack, Opts) -> string(Bin, Stack, Opts, []). +string(Bin, T, Stack, Opts) -> string(Bin, T, Stack, Opts, []). -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(<>, T, [key|_] = Stack, Opts, Acc) -> + emit([{key, lists:reverse(Acc)}], {colon, Rest, T, [Stack, Opts]}); +string(<>, T, Stack, Opts, Acc) -> + emit([{string, lists:reverse(Acc)}], {maybe_done, Rest, T, [Stack, Opts]}); +string(<>, T, Stack, Opts, Acc) -> + escape(Rest, T, Stack, Opts, Acc); %% things get dumb here. erlang doesn't properly restrict unicode non-characters %% so you can't trust the codepoints it returns always %% the range 32..16#fdcf is safe, so allow that -string(<>, Stack, Opts, Acc) +string(<>, T, Stack, Opts, Acc) when ?is_noncontrol(S), S < 16#fdd0 -> - string(Rest, Stack, Opts, [S] ++ Acc); + string(Rest, T, Stack, Opts, [S] ++ Acc); %% the range 16#fdf0..16#fffd is also safe -string(<>, Stack, Opts, Acc) +string(<>, T, Stack, Opts, Acc) when S > 16#fdef, S < 16#fffe -> - string(Rest, Stack, Opts, [S] ++ Acc); + string(Rest, T, Stack, Opts, [S] ++ Acc); %% yes, i think it's insane too -string(<>, Stack, Opts, Acc) +string(<>, T, Stack, Opts, Acc) when S > 16#ffff andalso S =/= 16#1fffe andalso S =/= 16#1ffff andalso S =/= 16#2fffe andalso S =/= 16#2ffff andalso @@ -401,18 +423,14 @@ string(<>, Stack, Opts, Acc) S =/= 16#efffe andalso S =/= 16#effff andalso S =/= 16#ffffe andalso S =/= 16#fffff andalso S =/= 16#10fffe andalso S =/= 16#10ffff -> - string(Rest, Stack, Opts, [S] ++ Acc); -string(Bin, Stack, Opts, Acc) -> + string(Rest, T, Stack, Opts, [S] ++ Acc); +string(Bin, T, Stack, Opts, Acc) -> case partial_utf(Bin) of true -> - {jsx, incomplete, fun(end_stream) -> - {error, {badjson, Bin}} - ; (Stream) -> - string(<>, Stack, Opts, Acc) - end} + emit([incomplete], {string, Bin, T, [Stack, Opts, Acc]}) ; false -> case Opts#opts.loose_unicode of - true -> noncharacter(Bin, Stack, Opts, Acc) + true -> noncharacter(Bin, T, Stack, Opts, Acc) ; false -> {error, {badjson, Bin}} end end. @@ -423,85 +441,85 @@ string(Bin, Stack, Opts, Acc) -> %% unreachable -ifdef(utf8). %% non-characters erlang doesn't recognize as non-characters, idiotically -noncharacter(<>, Stack, Opts, Acc) +noncharacter(<>, T, Stack, Opts, Acc) when ?is_noncontrol(S) -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); %% u+fffe and u+ffff -noncharacter(<<239, 191, X, Rest/binary>>, Stack, Opts, Acc) +noncharacter(<<239, 191, X, Rest/binary>>, T, Stack, Opts, Acc) when X == 190; X == 191 -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); %% surrogates -noncharacter(<<237, X, _, Rest/binary>>, Stack, Opts, Acc) when X >= 160 -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); -noncharacter(Bin, _Stack, _Opts, _Acc) -> +noncharacter(<<237, X, _, Rest/binary>>, T, Stack, Opts, Acc) when X >= 160 -> + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); +noncharacter(Bin, _T, _Stack, _Opts, _Acc) -> {error, {badjson, Bin}}. -endif. -ifdef(utf16). %% non-characters blah blah -noncharacter(<>, Stack, Opts, Acc) +noncharacter(<>, T, Stack, Opts, Acc) when ?is_noncontrol(S) -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); %% u+ffff and u+fffe -noncharacter(<<255, X, Rest/binary>>, Stack, Opts, Acc) +noncharacter(<<255, X, Rest/binary>>, T, Stack, Opts, Acc) when X == 254; X == 255 -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); %% surrogates -noncharacter(<>, Stack, Opts, Acc) +noncharacter(<>, T, Stack, Opts, Acc) when X >= 216, X =< 223 -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); -noncharacter(Bin, _Stack, _Opts, _Acc) -> + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); +noncharacter(Bin, _T, _Stack, _Opts, _Acc) -> {error, {badjson, Bin}}. -endif. -ifdef(utf16le). %% non-characters blah blah -noncharacter(<>, Stack, Opts, Acc) +noncharacter(<>, T, Stack, Opts, Acc) when ?is_noncontrol(S) -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); %% u+ffff and u+fffe -noncharacter(<>, Stack, Opts, Acc) +noncharacter(<>, T, Stack, Opts, Acc) when X == 254; X == 255 -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); %% surrogates -noncharacter(<<_, X, Rest/binary>>, Stack, Opts, Acc) +noncharacter(<<_, X, Rest/binary>>, T, Stack, Opts, Acc) when X >= 216, X =< 223 -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); -noncharacter(Bin, _Stack, _Opts, _Acc) -> + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); +noncharacter(Bin, _T, _Stack, _Opts, _Acc) -> {error, {badjson, Bin}}. -endif. -ifdef(utf32). %% non-characters blah blah -noncharacter(<>, Stack, Opts, Acc) +noncharacter(<>, T, Stack, Opts, Acc) when ?is_noncontrol(S) -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); %% u+ffff and u+fffe -noncharacter(<<0, 0, 255, X, Rest/binary>>, Stack, Opts, Acc) +noncharacter(<<0, 0, 255, X, Rest/binary>>, T, Stack, Opts, Acc) when X == 254; X == 255 -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); %% surrogates -noncharacter(<<0, 0, X, _, Rest/binary>>, Stack, Opts, Acc) +noncharacter(<<0, 0, X, _, Rest/binary>>, T, Stack, Opts, Acc) when X >= 216, X =< 223 -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); -noncharacter(Bin, _Stack, _Opts, _Acc) -> + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); +noncharacter(Bin, _T, _Stack, _Opts, _Acc) -> {error, {badjson, Bin}}. -endif. -ifdef(utf32le). %% non-characters blah blah -noncharacter(<>, Stack, Opts, Acc) +noncharacter(<>, T, Stack, Opts, Acc) when ?is_noncontrol(S) -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); %% u+ffff and u+fffe -noncharacter(<>, Stack, Opts, Acc) +noncharacter(<>, T, Stack, Opts, Acc) when X == 254; X == 255 -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); %% surrogates -noncharacter(<<_, X, 0, 0, Rest/binary>>, Stack, Opts, Acc) +noncharacter(<<_, X, 0, 0, Rest/binary>>, T, Stack, Opts, Acc) when X >= 216, X =< 223 -> - string(Rest, Stack, Opts, [16#fffd] ++ Acc); -noncharacter(Bin, _Stack, _Opts, _Acc) -> + string(Rest, T, Stack, Opts, [16#fffd] ++ Acc); +noncharacter(Bin, _T, _Stack, _Opts, _Acc) -> {error, {badjson, Bin}}. -endif. @@ -509,38 +527,38 @@ noncharacter(Bin, _Stack, _Opts, _Acc) -> %% only thing to note here is the additional accumulator passed to %% escaped_unicode used to hold the codepoint sequence. unescessary, but nicer %% than using the string accumulator -escape(<<$b/?utfx, Rest/binary>>, Stack, Opts, Acc) -> - string(Rest, Stack, Opts, "\b" ++ Acc); -escape(<<$f/?utfx, Rest/binary>>, Stack, Opts, Acc) -> - string(Rest, Stack, Opts, "\f" ++ Acc); -escape(<<$n/?utfx, Rest/binary>>, Stack, Opts, Acc) -> - string(Rest, Stack, Opts, "\n" ++ Acc); -escape(<<$r/?utfx, Rest/binary>>, Stack, Opts, Acc) -> - string(Rest, Stack, Opts, "\r" ++ Acc); -escape(<<$t/?utfx, Rest/binary>>, Stack, Opts, Acc) -> - string(Rest, Stack, Opts, "\t" ++ Acc); -escape(<<$u/?utfx, Rest/binary>>, Stack, Opts, Acc) -> - escaped_unicode(Rest, Stack, Opts, Acc, []); -escape(<>, Stack, Opts, Acc) +escape(<<$b/?utfx, Rest/binary>>, T, Stack, Opts, Acc) -> + string(Rest, T, Stack, Opts, "\b" ++ Acc); +escape(<<$f/?utfx, Rest/binary>>, T, Stack, Opts, Acc) -> + string(Rest, T, Stack, Opts, "\f" ++ Acc); +escape(<<$n/?utfx, Rest/binary>>, T, Stack, Opts, Acc) -> + string(Rest, T, Stack, Opts, "\n" ++ Acc); +escape(<<$r/?utfx, Rest/binary>>, T, Stack, Opts, Acc) -> + string(Rest, T, Stack, Opts, "\r" ++ Acc); +escape(<<$t/?utfx, Rest/binary>>, T, Stack, Opts, Acc) -> + string(Rest, T, Stack, Opts, "\t" ++ Acc); +escape(<<$u/?utfx, Rest/binary>>, T, Stack, Opts, Acc) -> + escaped_unicode(Rest, T, Stack, Opts, Acc, []); +escape(<>, T, Stack, Opts, Acc) when S =:= ?quote; S =:= ?solidus; S =:= ?rsolidus -> - string(Rest, Stack, Opts, [S] ++ Acc); -escape(Bin, Stack, Opts, Acc) -> - ?incomplete(escape(<>, Stack, Opts, Acc)). + string(Rest, T, Stack, Opts, [S] ++ Acc); +escape(Bin, T, Stack, Opts, Acc) -> + incomplete(escape, Bin, T, [Stack, Opts, Acc]). %% this code is ugly and unfortunate, but so is json's handling of escaped %% unicode codepoint sequences. -escaped_unicode(<>, Stack, Opts, String, [C, B, A]) +escaped_unicode(<>, T, Stack, Opts, String, [C, B, A]) when ?is_hex(D) -> case erlang:list_to_integer([A, B, C, D], 16) of %% high surrogate, we need a low surrogate next X when X >= 16#d800, X =< 16#dbff -> - low_surrogate(Rest, Stack, Opts, String, X) + low_surrogate(Rest, T, Stack, Opts, String, X) %% non-characters, you're not allowed to exchange these ; X when X == 16#fffe; X == 16#ffff; X >= 16#fdd0, X =< 16#fdef -> case Opts#opts.loose_unicode of true -> - string(Rest, Stack, Opts, [16#fffd] ++ String) + string(Rest, T, Stack, Opts, [16#fffd] ++ String) ; false -> {error, {badjson, <>}} end @@ -549,48 +567,45 @@ escaped_unicode(<>, Stack, Opts, String, [C, B, A]) ; X when X == 16#0000 -> case Opts#opts.loose_unicode of true -> - string(Rest, Stack, Opts, [16#fffd] ++ String) + string(Rest, T, Stack, Opts, [16#fffd] ++ String) ; false -> {error, {badjson, <>}} end %% anything else ; X -> - string(Rest, Stack, Opts, [X] ++ String) + string(Rest, T, Stack, Opts, [X] ++ String) end; -escaped_unicode(<>, Stack, Opts, String, Acc) +escaped_unicode(<>, T, Stack, Opts, String, Acc) when ?is_hex(S) -> - escaped_unicode(Rest, Stack, Opts, String, [S] ++ Acc); -escaped_unicode(Bin, Stack, Opts, String, Acc) -> - ?incomplete( - escaped_unicode(<>, Stack, Opts, String, Acc) - ). + escaped_unicode(Rest, T, Stack, Opts, String, [S] ++ Acc); +escaped_unicode(Bin, T, Stack, Opts, String, Acc) -> + incomplete(escaped_unicode, Bin, T, [Stack, Opts, String, Acc]). -low_surrogate(<>, Stack, Opts, String, High) -> - low_surrogate_u(Rest, Stack, Opts, String, High); +low_surrogate(<>, T, Stack, Opts, String, High) -> + low_surrogate_u(Rest, T, Stack, Opts, String, High); %% not an escaped codepoint, our high codepoint is illegal. dispatch back to %% string to handle -low_surrogate(<> = Bin, Stack, Opts, String, _) -> +low_surrogate(<> = Bin, T, Stack, Opts, String, _) -> case Opts#opts.loose_unicode of true -> - string(Bin, Stack, Opts, [16#fffd] ++ String) + string(Bin, T, Stack, Opts, [16#fffd] ++ String) ; false -> {error, {badjson, <>}} end; -low_surrogate(Bin, Stack, Opts, String, High) -> - ?incomplete( - low_surrogate(<>, Stack, Opts, String, High) - ). +low_surrogate(Bin, T, Stack, Opts, String, High) -> + incomplete(low_surrogate, Bin, T, [Stack, Opts, String, High]). -low_surrogate_u(<<$u/?utfx, Rest/binary>>, Stack, Opts, String, H) -> - low_surrogate(Rest, Stack, Opts, String, [], H); +low_surrogate_u(<<$u/?utfx, Rest/binary>>, T, Stack, Opts, String, H) -> + low_surrogate(Rest, T, Stack, Opts, String, [], H); %% not a low surrogate, dispatch back to string to handle, including the %% rsolidus we parsed previously -low_surrogate_u(<> = Bin, Stack, Opts, String, _) -> +low_surrogate_u(<> = Bin, T, Stack, Opts, String, _) -> case Opts#opts.loose_unicode of true -> string(<>, + T, Stack, Opts, [16#fffd] ++ String @@ -598,13 +613,11 @@ low_surrogate_u(<> = Bin, Stack, Opts, String, _) -> ; false -> {error, {badjson, <>}} end; -low_surrogate_u(Bin, Stack, Opts, String, H) -> - ?incomplete( - low_surrogate_u(<>, Stack, Opts, String, H) - ). +low_surrogate_u(Bin, T, Stack, Opts, String, H) -> + incomplete(low_surrogate_u, Bin, T, [Stack, Opts, String, H]). -low_surrogate(<>, Stack, Opts, String, [C, B, A], H) +low_surrogate(<>, T, Stack, Opts, String, [C, B, A], H) when ?is_hex(D) -> case erlang:list_to_integer([A, B, C, D], 16) of X when X >= 16#dc00, X =< 16#dfff -> @@ -612,31 +625,27 @@ low_surrogate(<>, Stack, Opts, String, [C, B, A], H) case V rem 16#10000 of Y when Y == 16#fffe; Y == 16#ffff -> case Opts#opts.loose_unicode of true -> - string(Rest, Stack, Opts, [16#fffd] ++ String) + string(Rest, T, Stack, Opts, [16#fffd] ++ String) ; false -> {error, {badjson, <>}} end ; _ -> - string(Rest, Stack, Opts, [V] ++ String) + string(Rest, T, Stack, Opts, [V] ++ String) end %% not a low surrogate, bad bad bad ; _ -> case Opts#opts.loose_unicode of true -> - string(Rest, Stack, Opts, [16#fffd, 16#fffd] ++ String) + string(Rest, T, Stack, Opts, [16#fffd, 16#fffd] ++ String) ; false -> {error, {badjson, <>}} end end; -low_surrogate(<>, Stack, Opts, String, Acc, H) +low_surrogate(<>, T, Stack, Opts, String, Acc, H) when ?is_hex(S) -> - low_surrogate(Rest, Stack, Opts, String, [S] ++ Acc, H); -low_surrogate(Bin, Stack, Opts, String, Acc, H) -> - ?incomplete( - low_surrogate( - <>, Stack, Opts, String, Acc, H - ) - ). + low_surrogate(Rest, T, Stack, Opts, String, [S] ++ Acc, H); +low_surrogate(Bin, T, Stack, Opts, String, Acc, H) -> + incomplete(low_surrogate, Bin, T, [Stack, Opts, String, Acc, H]). %% stole this from the unicode spec @@ -646,153 +655,139 @@ surrogate_to_codepoint(High, Low) -> %% like strings, numbers are collected in an intermediate accumulator before %% being emitted to the callback handler -negative(<<$0/?utfx, 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) -> - ?incomplete(negative(<>, Stack, Opts, Acc)). +negative(<<$0/?utfx, Rest/binary>>, T, Stack, Opts, Acc) -> + zero(Rest, T, Stack, Opts, "0" ++ Acc); +negative(<>, T, Stack, Opts, Acc) when ?is_nonzero(S) -> + integer(Rest, T, Stack, Opts, [S] ++ Acc); +negative(Bin, T, Stack, Opts, Acc) -> + incomplete(negative, Bin, T, [Stack, Opts, Acc]). -zero(<>, [object|Stack], Opts, Acc) -> - emit([format_number(Acc), end_object], fun() -> - maybe_done(Rest, Stack, Opts) - end); -zero(<>, [array|Stack], Opts, Acc) -> - emit([format_number(Acc), end_array], fun() -> - maybe_done(Rest, Stack, Opts) - end); -zero(<>, [object|Stack], Opts, Acc) -> - emit([format_number(Acc)], fun() -> key(Rest, [key|Stack], Opts) end); -zero(<>, [array|_] = Stack, Opts, Acc) -> - emit([format_number(Acc)], fun() -> value(Rest, Stack, Opts) end); -zero(<>, Stack, Opts, Acc) -> - initial_decimal(Rest, Stack, Opts, {Acc, []}); -zero(<>, Stack, Opts, Acc) when ?is_whitespace(S) -> - emit([format_number(Acc)], fun() -> maybe_done(Rest, Stack, Opts) end); -zero(<<>>, [], Opts, Acc) -> +zero(<>, T, [object|Stack], Opts, Acc) -> + emit([format_number(Acc), end_object], {maybe_done, Rest, T, [Stack, Opts]}); +zero(<>, T, [array|Stack], Opts, Acc) -> + emit([format_number(Acc), end_array], {maybe_done, Rest, T, [Stack, Opts]}); +zero(<>, T, [object|Stack], Opts, Acc) -> + emit([format_number(Acc)], {key, Rest, T, [[key|Stack], Opts]}); +zero(<>, T, [array|_] = Stack, Opts, Acc) -> + emit([format_number(Acc)], {value, Rest, T, [Stack, Opts]}); +zero(<>, T, Stack, Opts, Acc) -> + initial_decimal(Rest, T, Stack, Opts, {Acc, []}); +zero(<>, T, Stack, Opts, Acc) when ?is_whitespace(S) -> + emit([format_number(Acc)], {maybe_done, Rest, T, [Stack, Opts]}); +zero(<<>>, T, [], Opts, Acc) -> {jsx, incomplete, fun(end_stream) -> - emit([format_number(Acc), end_json], - fun() -> decimal(<<>>, [], Opts, Acc) end) - ; (Stream) -> zero(Stream, [], Opts, Acc) + emit([format_number(Acc), end_json, incomplete], + {bad_json, <<>>, T, []} + ) + ; (Stream) -> zero(Stream, T, [], Opts, Acc) end}; -zero(Bin, Stack, Opts, Acc) -> - ?incomplete(zero(<>, Stack, Opts, Acc)). +zero(Bin, T, Stack, Opts, Acc) -> + incomplete(zero, Bin, T, [Stack, Opts, Acc]). -integer(<>, Stack, Opts, Acc) when ?is_nonzero(S) -> - integer(Rest, Stack, Opts, [S] ++ Acc); -integer(<>, [object|Stack], Opts, Acc) -> - emit([format_number(Acc), end_object], fun() -> - maybe_done(Rest, Stack, Opts) - end); -integer(<>, [array|Stack], Opts, Acc) -> - emit([format_number(Acc), end_array], fun() -> - maybe_done(Rest, Stack, Opts) - end); -integer(<>, [object|Stack], Opts, Acc) -> - emit([format_number(Acc)], fun() -> key(Rest, [key|Stack], Opts) end); -integer(<>, [array|_] = Stack, Opts, Acc) -> - emit([format_number(Acc)], fun() -> value(Rest, Stack, Opts) end); -integer(<>, Stack, Opts, Acc) -> - initial_decimal(Rest, Stack, Opts, {Acc, []}); -integer(<>, Stack, Opts, Acc) -> - integer(Rest, Stack, Opts, [?zero] ++ Acc); -integer(<>, Stack, Opts, Acc) when S =:= $e; S =:= $E -> - e(Rest, Stack, Opts, {Acc, [], []}); -integer(<>, Stack, Opts, Acc) when ?is_whitespace(S) -> - emit([format_number(Acc)], fun() -> maybe_done(Rest, Stack, Opts) end); -integer(<<>>, [], Opts, Acc) -> +integer(<>, T, Stack, Opts, Acc) when ?is_nonzero(S) -> + integer(Rest, T, Stack, Opts, [S] ++ Acc); +integer(<>, T, [object|Stack], Opts, Acc) -> + emit([format_number(Acc), end_object], {maybe_done, Rest, T, [Stack, Opts]}); +integer(<>, T, [array|Stack], Opts, Acc) -> + emit([format_number(Acc), end_array], {maybe_done, Rest, T, [Stack, Opts]}); +integer(<>, T, [object|Stack], Opts, Acc) -> + emit([format_number(Acc)], {key, Rest, T, [[key|Stack], Opts]}); +integer(<>, T, [array|_] = Stack, Opts, Acc) -> + emit([format_number(Acc)], {value, Rest, T, [Stack, Opts]}); +integer(<>, T, Stack, Opts, Acc) -> + initial_decimal(Rest, T, Stack, Opts, {Acc, []}); +integer(<>, T, Stack, Opts, Acc) -> + integer(Rest, T, Stack, Opts, [?zero] ++ Acc); +integer(<>, T, Stack, Opts, Acc) when S =:= $e; S =:= $E -> + e(Rest, T, Stack, Opts, {Acc, [], []}); +integer(<>, T, Stack, Opts, Acc) when ?is_whitespace(S) -> + emit([format_number(Acc)], {maybe_done, Rest, T, [Stack, Opts]}); +integer(<<>>, T, [], Opts, Acc) -> {jsx, incomplete, fun(end_stream) -> - emit([format_number(Acc), end_json], - fun() -> decimal(<<>>, [], Opts, Acc) end) - ; (Stream) -> integer(Stream, [], Opts, Acc) + emit([format_number(Acc), end_json, incomplete], + {bad_json, <<>>, T, []} + ) + ; (Stream) -> integer(Stream, T, [], Opts, Acc) end}; -integer(Bin, Stack, Opts, Acc) -> - ?incomplete(integer(<>, Stack, Opts, Acc)). +integer(Bin, T, Stack, Opts, Acc) -> + incomplete(integer, Bin, T, [Stack, Opts, Acc]). -initial_decimal(<>, Stack, Opts, {Int, Frac}) +initial_decimal(<>, T, Stack, Opts, {Int, Frac}) when S =:= ?zero; ?is_nonzero(S) -> - decimal(Rest, Stack, Opts, {Int, [S] ++ Frac}); -initial_decimal(Bin, Stack, Opts, Acc) -> - ?incomplete( - initial_decimal(<>, Stack, Opts, Acc) - ). + decimal(Rest, T, Stack, Opts, {Int, [S] ++ Frac}); +initial_decimal(Bin, T, Stack, Opts, Acc) -> + incomplete(initial_decimal, Bin, T, [Stack, Opts, Acc]). -decimal(<>, Stack, Opts, {Int, Frac}) +decimal(<>, T, Stack, Opts, {Int, Frac}) when S=:= ?zero; ?is_nonzero(S) -> - decimal(Rest, Stack, Opts, {Int, [S] ++ Frac}); -decimal(<>, [object|Stack], Opts, Acc) -> - emit([format_number(Acc), end_object], fun() -> - maybe_done(Rest, Stack, Opts) - end); -decimal(<>, [array|Stack], Opts, Acc) -> - emit([format_number(Acc), end_array], fun() -> - maybe_done(Rest, Stack, Opts) - end); -decimal(<>, [object|Stack], Opts, Acc) -> - emit([format_number(Acc)], fun() -> key(Rest, [key|Stack], Opts) end); -decimal(<>, [array|_] = Stack, Opts, Acc) -> - emit([format_number(Acc)], fun() -> value(Rest, Stack, Opts) end); -decimal(<>, Stack, Opts, {Int, Frac}) + decimal(Rest, T, Stack, Opts, {Int, [S] ++ Frac}); +decimal(<>, T, [object|Stack], Opts, Acc) -> + emit([format_number(Acc), end_object], {maybe_done, Rest, T, [Stack, Opts]}); +decimal(<>, T, [array|Stack], Opts, Acc) -> + emit([format_number(Acc), end_array], {maybe_done, Rest, T, [Stack, Opts]}); +decimal(<>, T, [object|Stack], Opts, Acc) -> + emit([format_number(Acc)], {key, Rest, T, [[key|Stack], Opts]}); +decimal(<>, T, [array|_] = Stack, Opts, Acc) -> + emit([format_number(Acc)], {value, Rest, T, [Stack, Opts]}); +decimal(<>, T, Stack, Opts, {Int, Frac}) when S =:= $e; S =:= $E -> - e(Rest, Stack, Opts, {Int, Frac, []}); -decimal(<>, Stack, Opts, Acc) when ?is_whitespace(S) -> - emit([format_number(Acc)], fun() -> maybe_done(Rest, Stack, Opts) end); -decimal(<<>>, [], Opts, Acc) -> + e(Rest, T, Stack, Opts, {Int, Frac, []}); +decimal(<>, T, Stack, Opts, Acc) when ?is_whitespace(S) -> + emit([format_number(Acc)], {maybe_done, Rest, T, [Stack, Opts]}); +decimal(<<>>, T, [], Opts, Acc) -> {jsx, incomplete, fun(end_stream) -> - emit([format_number(Acc), end_json], - fun() -> decimal(<<>>, [], Opts, Acc) end) - ; (Stream) -> decimal(Stream, [], Opts, Acc) + emit([format_number(Acc), end_json, incomplete], + {bad_json, <<>>, T, []} + ) + ; (Stream) -> decimal(Stream, T, [], Opts, Acc) end}; -decimal(Bin, Stack, Opts, Acc) -> - ?incomplete(decimal(<>, Stack, Opts, Acc)). +decimal(Bin, T, Stack, Opts, Acc) -> + incomplete(decimal, Bin, T, [Stack, Opts, Acc]). -e(<>, Stack, Opts, {Int, Frac, Exp}) +e(<>, T, Stack, Opts, {Int, Frac, Exp}) when S =:= ?zero; ?is_nonzero(S) -> - exp(Rest, Stack, Opts, {Int, Frac, [S] ++ Exp}); -e(<>, Stack, Opts, {Int, Frac, Exp}) + exp(Rest, T, Stack, Opts, {Int, Frac, [S] ++ Exp}); +e(<>, T, Stack, Opts, {Int, Frac, Exp}) when S =:= ?positive; S =:= ?negative -> - ex(Rest, Stack, Opts, {Int, Frac, [S] ++ Exp}); -e(Bin, Stack, Opts, Acc) -> - ?incomplete(e(<>, Stack, Opts, Acc)). + ex(Rest, T, Stack, Opts, {Int, Frac, [S] ++ Exp}); +e(Bin, T, Stack, Opts, Acc) -> + incomplete(e, Bin, T, [Stack, Opts, Acc]). -ex(<>, Stack, Opts, {Int, Frac, Exp}) +ex(<>, T, Stack, Opts, {Int, Frac, Exp}) when S =:= ?zero; ?is_nonzero(S) -> - exp(Rest, Stack, Opts, {Int, Frac, [S] ++ Exp}); -ex(Bin, Stack, Opts, Acc) -> - ?incomplete(ex(<>, Stack, Opts, Acc)). + exp(Rest, T, Stack, Opts, {Int, Frac, [S] ++ Exp}); +ex(Bin, T, Stack, Opts, Acc) -> + incomplete(ex, Bin, T, [Stack, Opts, Acc]). -exp(<>, Stack, Opts, {Int, Frac, Exp}) +exp(<>, T, Stack, Opts, {Int, Frac, Exp}) when S =:= ?zero; ?is_nonzero(S) -> - exp(Rest, Stack, Opts, {Int, Frac, [S] ++ Exp}); -exp(<>, [object|Stack], Opts, Acc) -> - emit([format_number(Acc), end_object], fun() -> - maybe_done(Rest, Stack, Opts) - end); -exp(<>, [array|Stack], Opts, Acc) -> - emit([format_number(Acc), end_array], fun() -> - maybe_done(Rest, Stack, Opts) - end); -exp(<>, [object|Stack], Opts, Acc) -> - emit([format_number(Acc)], fun() -> key(Rest, [key|Stack], Opts) end); -exp(<>, [array|_] = Stack, Opts, Acc) -> - emit([format_number(Acc)], fun() -> value(Rest, Stack, Opts) end); -exp(<>, Stack, Opts, Acc) when ?is_whitespace(S) -> - emit([format_number(Acc)], fun() -> maybe_done(Rest, Stack, Opts) end); -exp(<<>>, [], Opts, Acc) -> + exp(Rest, T, Stack, Opts, {Int, Frac, [S] ++ Exp}); +exp(<>, T, [object|Stack], Opts, Acc) -> + emit([format_number(Acc), end_object], {maybe_done, Rest, T, [Stack, Opts]}); +exp(<>, T, [array|Stack], Opts, Acc) -> + emit([format_number(Acc), end_array], {maybe_done, Rest, T, [Stack, Opts]}); +exp(<>, T, [object|Stack], Opts, Acc) -> + emit([format_number(Acc)], {key, Rest, T, [[key|Stack], Opts]}); +exp(<>, T, [array|_] = Stack, Opts, Acc) -> + emit([format_number(Acc)], {value, Rest, T, [Stack, Opts]}); +exp(<>, T, Stack, Opts, Acc) when ?is_whitespace(S) -> + emit([format_number(Acc)], {maybe_done, Rest, T, [Stack, Opts]}); +exp(<<>>, T, [], Opts, Acc) -> {jsx, incomplete, fun(end_stream) -> - emit([format_number(Acc), end_json], - fun() -> exp(<<>>, [], Opts, Acc) end) - ; (Stream) -> exp(Stream, [], Opts, Acc) + emit([format_number(Acc), end_json, incomplete], + {bad_json, <<>>, T, []} + ) + ; (Stream) -> exp(Stream, T, [], Opts, Acc) end}; -exp(Bin, Stack, Opts, Acc) -> - ?incomplete(exp(<>, Stack, Opts, Acc)). +exp(Bin, T, Stack, Opts, Acc) -> + incomplete(exp, Bin, T, [Stack, Opts, Acc]). format_number(Int) when is_list(Int) -> @@ -805,64 +800,64 @@ format_number({Int, Frac, Exp}) -> {float, list_to_float(lists:reverse(Exp ++ "e" ++ Frac ++ "." ++ Int))}. -tr(<<$r/?utfx, Rest/binary>>, Stack, Opts) -> - tru(Rest, Stack, Opts); -tr(Bin, Stack, Opts) -> - ?incomplete(tr(<>, Stack, Opts)). +tr(<<$r/?utfx, Rest/binary>>, T, Stack, Opts) -> + tru(Rest, T, Stack, Opts); +tr(Bin, T, Stack, Opts) -> + incomplete(tr, Bin, T, [Stack, Opts]). -tru(<<$u/?utfx, Rest/binary>>, Stack, Opts) -> - true(Rest, Stack, Opts); -tru(Bin, Stack, Opts) -> - ?incomplete(tru(<>, Stack, Opts)). +tru(<<$u/?utfx, Rest/binary>>, T, Stack, Opts) -> + true(Rest, T, Stack, Opts); +tru(Bin, T, Stack, Opts) -> + incomplete(tru, Bin, T, [Stack, Opts]). -true(<<$e/?utfx, Rest/binary>>, Stack, Opts) -> - emit([{literal, true}], fun() -> maybe_done(Rest, Stack, Opts) end); -true(Bin, Stack, Opts) -> - ?incomplete(true(<>, Stack, Opts)). +true(<<$e/?utfx, Rest/binary>>, T, Stack, Opts) -> + emit([{literal, true}], {maybe_done, Rest, T, [Stack, Opts]}); +true(Bin, T, Stack, Opts) -> + incomplete(true, Bin, T, [Stack, Opts]). -fa(<<$a/?utfx, Rest/binary>>, Stack, Opts) -> - fal(Rest, Stack, Opts); -fa(Bin, Stack, Opts) -> - ?incomplete(fa(<>, Stack, Opts)). +fa(<<$a/?utfx, Rest/binary>>, T, Stack, Opts) -> + fal(Rest, T, Stack, Opts); +fa(Bin, T, Stack, Opts) -> + incomplete(fa, Bin, T, [Stack, Opts]). -fal(<<$l/?utfx, Rest/binary>>, Stack, Opts) -> - fals(Rest, Stack, Opts); -fal(Bin, Stack, Opts) -> - ?incomplete(fal(<>, Stack, Opts)). +fal(<<$l/?utfx, Rest/binary>>, T, Stack, Opts) -> + fals(Rest, T, Stack, Opts); +fal(Bin, T, Stack, Opts) -> + incomplete(fal, Bin, T, [Stack, Opts]). -fals(<<$s/?utfx, Rest/binary>>, Stack, Opts) -> - false(Rest, Stack, Opts); -fals(Bin, Stack, Opts) -> - ?incomplete(fals(<>, Stack, Opts)). +fals(<<$s/?utfx, Rest/binary>>, T, Stack, Opts) -> + false(Rest, T, Stack, Opts); +fals(Bin, T, Stack, Opts) -> + incomplete(fals, Bin, T, [Stack, Opts]). -false(<<$e/?utfx, Rest/binary>>, Stack, Opts) -> - emit([{literal, false}], fun() -> maybe_done(Rest, Stack, Opts) end); -false(Bin, Stack, Opts) -> - ?incomplete(false(<>, Stack, Opts)). +false(<<$e/?utfx, Rest/binary>>, T, Stack, Opts) -> + emit([{literal, false}], {maybe_done, Rest, T, [Stack, Opts]}); +false(Bin, T, Stack, Opts) -> + incomplete(false, Bin, T, [Stack, Opts]). -nu(<<$u/?utfx, Rest/binary>>, Stack, Opts) -> - nul(Rest, Stack, Opts); -nu(Bin, Stack, Opts) -> - ?incomplete(nu(<>, Stack, Opts)). +nu(<<$u/?utfx, Rest/binary>>, T, Stack, Opts) -> + nul(Rest, T, Stack, Opts); +nu(Bin, T, Stack, Opts) -> + incomplete(nu, Bin, T, [Stack, Opts]). -nul(<<$l/?utfx, Rest/binary>>, Stack, Opts) -> - null(Rest, Stack, Opts); -nul(Bin, Stack, Opts) -> - ?incomplete(nul(<>, Stack, Opts)). +nul(<<$l/?utfx, Rest/binary>>, T, Stack, Opts) -> + null(Rest, T, Stack, Opts); +nul(Bin, T, Stack, Opts) -> + incomplete(nul, Bin, T, [Stack, Opts]). -null(<<$l/?utfx, Rest/binary>>, Stack, Opts) -> - emit([{literal, null}], fun() -> maybe_done(Rest, Stack, Opts) end); -null(Bin, Stack, Opts) -> - ?incomplete(null(<>, Stack, Opts)). +null(<<$l/?utfx, Rest/binary>>, T, Stack, Opts) -> + emit([{literal, null}], {maybe_done, Rest, T, [Stack, Opts]}); +null(Bin, T, Stack, Opts) -> + incomplete(null, Bin, T, [Stack, Opts]). @@ -961,7 +956,7 @@ check([H|T], Opts, Acc) -> decode(JSON, Opts) -> - F = decoder(Opts), + F = decoder([iterate] ++ Opts), loop(F(JSON), []). diff --git a/src/jsx_encoder.erl b/src/jsx_encoder.erl index d5cc49d..f27e649 100644 --- a/src/jsx_encoder.erl +++ b/src/jsx_encoder.erl @@ -23,116 +23,125 @@ -module(jsx_encoder). +-export([start/3, + list_or_object/4, + key/4, + value/4, + maybe_done/4, + bad_json/2 +]). -export([encoder/1]). -include("jsx_common.hrl"). +-include("jsx_opts.hrl"). --record(opts, { - multi_term = false, - encoding = auto, - loose_unicode = false, %% does nothing, used by decoder - escape_forward_slash = false -}). +-spec encoder(OptsList::jsx_opts()) -> jsx_encoder(). + +encoder(OptsList) -> + Opts = parse_opts(OptsList), + case Opts#opts.iterate of + true -> + fun(Forms) -> start(Forms, iterate, Opts) end + ; false -> + fun(Forms) -> start(Forms, [], Opts) end + end. --spec encoder(Opts::jsx_opts()) -> jsx_encoder(). -encoder(Opts) -> fun(Forms) -> start(Forms, Opts) end. - - --define(ENDJSON, - {jsx, end_json, fun() -> - {jsx, incomplete, fun(Forms) -> {error, {badjson, Forms}} end} - end} -). - - -start({string, String}, Opts) when is_binary(String) -> - {jsx, {string, json_escape(String, Opts)}, fun() -> ?ENDJSON end}; -start({float, Float}, _Opts) when is_float(Float) -> - {jsx, {float, Float}, fun() -> ?ENDJSON end}; -start({integer, Int}, _Opts) when is_integer(Int) -> - {jsx, {integer, Int}, fun() -> ?ENDJSON end}; -start({literal, Atom}, _Opts) when Atom == true; Atom == false; Atom == null -> - {jsx, {literal, Atom}, fun() -> ?ENDJSON end}; -%% second parameter is a stack to match end_foos to start_foos -start(Forms, Opts) -> list_or_object(Forms, [], Opts). - - -list_or_object([start_object|Forms], Stack, Opts) -> - {jsx, start_object, fun() -> key(Forms, [object] ++ Stack, Opts) end}; -list_or_object([start_array|Forms], Stack, Opts) -> - {jsx, start_array, fun() -> value(Forms, [array] ++ Stack, Opts) end}; -list_or_object([], Stack, Opts) -> - {jsx, incomplete, fun(end_stream) -> +%% emit takes a list of `events` to present to client code and formats them +%% appropriately +emit([], {State, Rest, T, Args}) -> + erlang:apply(?MODULE, State, [Rest, T] ++ Args); +emit([incomplete], {State, Rest, T, Args}) -> + {jsx, incomplete, fun(end_stream) -> {error, {badjson, []}} - ; (Stream) -> - list_or_object(Stream, Stack, Opts) + ; (Stream) -> + erlang:apply(?MODULE, + State, + [Rest ++ Stream, T] ++ Args + ) end}; -list_or_object(Forms, _, _) -> {error, {badjson, Forms}}. +emit([Event|Events], {_State, _Rest, iterate, _Args} = Next) -> + {jsx, Event, fun() -> emit(Events, Next) end}; +emit([end_json|Events], {_State, _Rest, T, _Args} = Next) -> + {jsx, lists:reverse([end_json] ++ T), fun() -> emit(Events, Next) end}; +emit([Event|Events], {State, Rest, T, Args}) -> + emit(Events, {State, Rest, [Event] ++ T, Args}). + + +bad_json(Stream, _) -> {error, {badjson, Stream}}. + + +start({string, String}, T, Opts) when is_binary(String) -> + emit([{string, json_escape(String, Opts)}, end_json, incomplete], + {bad_json, [], T, []} + ); +start({float, Float}, T, _Opts) when is_float(Float) -> + emit([{float, Float}, end_json, incomplete], {bad_json, [], T, []}); +start({integer, Int}, T, _Opts) when is_integer(Int) -> + emit([{integer, Int}, end_json, incomplete], {bad_json, [], T, []}); +start({literal, Atom}, T, _Opts) when Atom == true; Atom == false; Atom == null -> + emit([{literal, Atom}, end_json, incomplete], {bad_json, [], T, []}); +%% third parameter is a stack to match end_foos to start_foos +start(Forms, T, Opts) -> list_or_object(Forms, T, [], Opts). + + +list_or_object([start_object|Forms], T, Stack, Opts) -> + emit([start_object], {key, Forms, T, [[object] ++ Stack, Opts]}); +list_or_object([start_array|Forms], T, Stack, Opts) -> + emit([start_array], {value, Forms, T, [[array] ++ Stack, Opts]}); +list_or_object([], T, Stack, Opts) -> + emit([incomplete], {list_or_object, [], T, [Stack, Opts]}); +list_or_object(Forms, _, _, _) -> {error, {badjson, Forms}}. -key([{key, Key}|Forms], Stack, Opts) when is_binary(Key) -> - {jsx, {key, json_escape(Key, Opts)}, fun() -> - value(Forms, Stack, Opts) - end}; -key([end_object|Forms], [object|Stack], Opts) -> - {jsx, end_object, fun() -> maybe_done(Forms, Stack, Opts) end}; -key([], Stack, Opts) -> - {jsx, incomplete, fun(end_stream) -> - {error, {badjson, []}} - ; (Stream) -> - key(Stream, Stack, Opts) - end}; -key(Forms, _, _) -> {error, {badjson, Forms}}. +key([{key, Key}|Forms], T, Stack, Opts) when is_binary(Key) -> + emit([{key, json_escape(Key, Opts)}], {value, Forms, T, [Stack, Opts]}); +key([end_object|Forms], T, [object|Stack], Opts) -> + emit([end_object], {maybe_done, Forms, T, [Stack, Opts]}); +key([], T, Stack, Opts) -> + emit([incomplete], {key, [], T, [Stack, Opts]}); +key(Forms, _, _, _) -> {error, {badjson, Forms}}. -value([{string, S}|Forms], Stack, Opts) when is_binary(S) -> - {jsx, {string, json_escape(S, Opts)}, fun() -> - maybe_done(Forms, Stack, Opts) - end}; -value([{float, F}|Forms], Stack, Opts) when is_float(F) -> - {jsx, {float, F}, fun() -> maybe_done(Forms, Stack, Opts) end}; -value([{integer, I}|Forms], Stack, Opts) when is_integer(I) -> - {jsx, {integer, I}, fun() -> maybe_done(Forms, Stack, Opts) end}; -value([{literal, L}|Forms], Stack, Opts) +value([{string, S}|Forms], T, Stack, Opts) when is_binary(S) -> + emit([{string, json_escape(S, Opts)}], + {maybe_done, Forms, T, [Stack, Opts]} + ); +value([{float, F}|Forms], T, Stack, Opts) when is_float(F) -> + emit([{float, F}], {maybe_done, Forms, T, [Stack, Opts]}); +value([{integer, I}|Forms], T, Stack, Opts) when is_integer(I) -> + emit([{integer, I}], {maybe_done, Forms, T, [Stack, Opts]}); +value([{literal, L}|Forms], T, Stack, Opts) when L == true; L == false; L == null -> - {jsx, {literal, L}, fun() -> maybe_done(Forms, Stack, Opts) end}; -value([start_object|Forms], Stack, Opts) -> - {jsx, start_object, fun() -> key(Forms, [object] ++ Stack, Opts) end}; -value([start_array|Forms], Stack, Opts) -> - {jsx, start_array, fun() -> value(Forms, [array] ++ Stack, Opts) end}; -value([end_array|Forms], [array|Stack], Opts) -> - {jsx, end_array, fun() -> maybe_done(Forms, Stack, Opts) end}; -value([], Stack, Opts) -> - {jsx, incomplete, fun(end_stream) -> - {error, {badjson, []}} - ; (Stream) -> - value(Stream, Stack, Opts) - end}; -value(Forms, _, _) -> {error, {badjson, Forms}}. + emit([{literal, L}], {maybe_done, Forms, T, [Stack, Opts]}); +value([start_object|Forms], T, Stack, Opts) -> + emit([start_object], {key, Forms, T, [[object] ++ Stack, Opts]}); +value([start_array|Forms], T, Stack, Opts) -> + emit([start_array], {value, Forms, T, [[array] ++ Stack, Opts]}); +value([end_array|Forms], T, [array|Stack], Opts) -> + emit([end_array], {maybe_done, Forms, T, [Stack, Opts]}); +value([], T, Stack, Opts) -> + emit([incomplete], {value, [], T, [Stack, Opts]}); +value(Forms, _, _, _) -> {error, {badjson, Forms}}. -maybe_done([], [], _) -> ?ENDJSON; -maybe_done([end_json], [], _) -> ?ENDJSON; -maybe_done([end_json|Forms], [], #opts{multi_term=true}=Opts) -> - {jsx, end_json, fun() -> start(Forms, Opts) end}; -maybe_done([end_object|Forms], [object|Stack], Opts) -> - {jsx, end_object, fun() -> maybe_done(Forms, Stack, Opts) end}; -maybe_done([end_array|Forms], [array|Stack], Opts) -> - {jsx, end_array, fun() -> maybe_done(Forms, Stack, Opts) end}; -maybe_done(Forms, [object|_] = Stack, Opts) -> key(Forms, Stack, Opts); -maybe_done(Forms, [array|_] = Stack, Opts) -> value(Forms, Stack, Opts); -maybe_done([], Stack, Opts) -> - {jsx, incomplete, fun(end_stream) -> - {error, {badjson, []}} - ; (Stream) -> - maybe_done(Stream, Stack, Opts) - end}; -maybe_done(Forms, _, _) -> {error, {badjson, Forms}}. +maybe_done([], T, [], _Opts) -> + emit([end_json, incomplete], {bad_json, [], T, []}); +maybe_done([end_json], T, [], _Opts) -> + emit([end_json, incomplete], {bad_json, [], T, []}); +maybe_done([end_object|Forms], T, [object|Stack], Opts) -> + emit([end_object], {maybe_done, Forms, T, [Stack, Opts]}); +maybe_done([end_array|Forms], T, [array|Stack], Opts) -> + emit([end_array], {maybe_done, Forms, T, [Stack, Opts]}); +maybe_done(Forms, T, [object|_] = Stack, Opts) -> key(Forms, T, Stack, Opts); +maybe_done(Forms, T, [array|_] = Stack, Opts) -> value(Forms, T, Stack, Opts); +maybe_done([], T, Stack, Opts) -> + emit([incomplete], {maybe_done, [], T, [Stack, Opts]}); +maybe_done(Forms, _, _, _) -> {error, {badjson, Forms}}. @@ -199,58 +208,49 @@ to_hex(X) -> X + $0. - -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). - -encode(Terms) -> encode_whole(Terms) andalso encode_incremental(Terms). +encode(Terms) -> + encode_simple(Terms) andalso encode_iterative(Terms). -encode_whole(Terms) -> - case loop((encoder([]))(Terms), []) of - %% unwrap naked values - {ok, [Terms]} -> true - ; {ok, Terms} -> true - ; _ -> false - end. - - -encode_incremental(Terms) when is_list(Terms) -> - encode_incremental(Terms, encoder([]), Terms, []); -%% we could feed naked terms to the regular encoder, but we already do that, so -%% cheat instead -encode_incremental(_) -> true. - -encode_incremental([Term], F, Expected, Acc) -> - case loop(F([Term]), []) of - {ok, R} -> Expected =:= Acc ++ R - ; _ -> false - end; -encode_incremental([Term|Terms], F, Expected, Acc) -> - case loop(F([Term]), []) of - {jsx, incomplete, Next, R} -> - encode_incremental(Terms, Next, Expected, Acc ++ R) - ; _ -> +encode_simple(Terms) -> + case (encoder([]))(Terms) of + {jsx, Terms, _} -> + true + %% matches [foo, end_json], aka naked terms + ; {jsx, [Terms, end_json], _} -> + true + ; {error, _} -> false end. -loop({error, _}, _) -> error; -loop({jsx, incomplete, Next}, Acc) -> - {jsx, incomplete, Next, lists:reverse(Acc)}; +encode_iterative(Terms) -> + case loop((encoder([iterate]))(Terms), []) of + {ok, Terms} -> + true + %% matches naked terms + ; {ok, [Terms, end_json]} -> + true + ; {error, _} -> + false + end. + loop({jsx, end_json, Next}, Acc) -> {jsx, incomplete, F} = Next(), - {error, {badjson, []}} = F([]), - {ok, lists:reverse(Acc)}; -loop({jsx, Event, Next}, Acc) -> loop(Next(), [Event] ++ Acc). + {error, _} = F([]), + {ok, lists:reverse([end_json] ++ Acc)}; +loop({jsx, Event, Next}, Acc) -> + loop(Next(), [Event] ++ Acc). encode_test_() -> [ - {"empty object", ?_assert(encode([start_object, end_object]))}, - {"empty array", ?_assert(encode([start_array, end_array]) =:= true)}, + {"empty object", ?_assert(encode([start_object, end_object, end_json]))}, + {"empty array", ?_assert(encode([start_array, end_array, end_json]))}, {"nested empty objects", ?_assert(encode([start_object, {key, <<"empty object">>}, start_object, @@ -258,14 +258,16 @@ encode_test_() -> start_object, end_object, end_object, - end_object + end_object, + end_json ]))}, {"nested empty arrays", ?_assert(encode([start_array, start_array, start_array, end_array, end_array, - end_array + end_array, + end_json ]))}, {"simple object", ?_assert(encode([start_object, {key, <<"a">>}, @@ -276,18 +278,21 @@ encode_test_() -> {float, 1.0}, {key, <<"d">>}, {literal, true}, - end_object + end_object, + end_json ]))}, {"simple array", ?_assert(encode([start_array, {string, <<"hello">>}, {integer, 1}, {float, 1.0}, {literal, true}, - end_array + end_array, + end_json ]))}, {"unbalanced array", ?_assertNot(encode([start_array, end_array, - end_array + end_array, + end_json ]))}, {"naked string", ?_assert(encode({string, <<"hello">>}))}, {"naked literal", ?_assert(encode({literal, true}))}, @@ -324,5 +329,4 @@ escape_test_() -> } ]. --endif. - +-endif. \ No newline at end of file diff --git a/src/jsx_format.erl b/src/jsx_format.erl index a00f845..918deb3 100644 --- a/src/jsx_format.erl +++ b/src/jsx_format.erl @@ -41,10 +41,10 @@ binary() | iolist(). format(JSON, OptsList) when is_binary(JSON) -> - P = jsx:decoder(extract_parser_opts(OptsList)), + P = jsx:decoder([iterate] ++ extract_parser_opts(OptsList)), format(fun() -> P(JSON) end, OptsList); format(Terms, OptsList) when is_list(Terms); is_tuple(Terms) -> - P = jsx:encoder(), + P = jsx:encoder([iterate]), format(fun() -> P(Terms) end, OptsList); format(F, OptsList) when is_function(F) -> Opts = parse_opts(OptsList, #format_opts{}), diff --git a/src/jsx_opts.hrl b/src/jsx_opts.hrl new file mode 100644 index 0000000..ee301be --- /dev/null +++ b/src/jsx_opts.hrl @@ -0,0 +1,48 @@ +%% The MIT License + +%% Copyright (c) 2010 Alisdair Sullivan + +%% Permission is hereby granted, free of charge, to any person obtaining a copy +%% of this software and associated documentation files (the "Software"), to deal +%% in the Software without restriction, including without limitation the rights +%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +%% copies of the Software, and to permit persons to whom the Software is +%% furnished to do so, subject to the following conditions: + +%% The above copyright notice and this permission notice shall be included in +%% all copies or substantial portions of the Software. + +%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +%% THE SOFTWARE. + + +%% opts record for decoder/encoder +-record(opts, { + loose_unicode = false, + encoding = auto, + escape_forward_slash = false, %% does nothing, used by encoder + iterate = false +}). + + + +parse_opts(Opts) -> + parse_opts(Opts, #opts{}). + +parse_opts([], Opts) -> + Opts; +parse_opts([loose_unicode|Rest], Opts) -> + parse_opts(Rest, Opts#opts{loose_unicode=true}); +parse_opts([iterate|Rest], Opts) -> + parse_opts(Rest, Opts#opts{iterate=true}); +parse_opts([escape_forward_slash|Rest], Opts) -> + parse_opts(Rest, Opts#opts{escape_forward_slash=true}); +parse_opts([{encoding, _}|Rest], Opts) -> + parse_opts(Rest, Opts); +parse_opts(_, _) -> + {error, badarg}. \ No newline at end of file diff --git a/src/jsx_terms.erl b/src/jsx_terms.erl index 3d61312..0e6d93a 100644 --- a/src/jsx_terms.erl +++ b/src/jsx_terms.erl @@ -39,7 +39,7 @@ jsx_term() | {jsx, incomplete, fun()}. json_to_term(JSON, Opts) -> - P = jsx:decoder(extract_parser_opts(Opts)), + P = jsx:decoder([iterate] ++ extract_parser_opts(Opts)), case proplists:get_value(strict, Opts, false) of true -> collect_strict(P(JSON), [[]], Opts) ; false -> collect(P(JSON), [[]], Opts) diff --git a/src/jsx_verify.erl b/src/jsx_verify.erl index 0f463d2..6c0024f 100644 --- a/src/jsx_verify.erl +++ b/src/jsx_verify.erl @@ -38,10 +38,10 @@ ; (F::jsx_iterator(), Opts::verify_opts()) -> true | false. is_json(JSON, OptsList) when is_binary(JSON) -> - P = jsx:decoder(extract_parser_opts(OptsList)), + P = jsx:decoder([iterate] ++ extract_parser_opts(OptsList)), is_json(fun() -> P(JSON) end, OptsList); is_json(Terms, OptsList) when is_list(Terms) -> - P = jsx:encoder(), + P = jsx:encoder([iterate]), is_json(fun() -> P(Terms) end, OptsList); is_json(F, OptsList) when is_function(F) -> Opts = parse_opts(OptsList, #verify_opts{}),