From 986b60af37277727182dbe5e6c2bd31488f2a86c Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Tue, 9 Aug 2011 16:02:14 -0700 Subject: [PATCH 1/7] groundwork for eventual inline ops --- src/jsx_decoder.hrl | 739 ++++++++++++++++++++++---------------------- 1 file changed, 371 insertions(+), 368 deletions(-) diff --git a/src/jsx_decoder.hrl b/src/jsx_decoder.hrl index 537fb84..0e60ea0 100644 --- a/src/jsx_decoder.hrl +++ b/src/jsx_decoder.hrl @@ -26,6 +26,18 @@ %% this file should take that into account +%% 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 +]). + + -export([decoder/1]). -spec decoder(OptsList::jsx_opts()) -> jsx_decoder(). @@ -33,7 +45,7 @@ decoder(OptsList) -> case parse_opts(OptsList) of {error, badopt} -> {error, badopt} - ; Opts -> fun(JSON) -> start(JSON, [], Opts) end + ; Opts -> fun(JSON) -> start(JSON, iterate, [], Opts) end end. @@ -219,140 +231,148 @@ partial_utf(_) -> false. %% 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([incomplete], {State, Rest, iterate, Args}) -> + {jsx, incomplete, fun(end_stream) -> + {error, {badjson, <<>>}} + ; (Stream) -> + erlang:apply(?MODULE, + State, + [<>, iterate] ++ Args + ) + end}; +emit([Event], {State, Rest, iterate, Args}) -> + {jsx, Event, fun() -> + erlang:apply(?MODULE, State, [Rest, iterate] ++ Args) + end}; +emit([Event|Rest], {_State, _Rest, iterate, _Args} = Next) -> + {jsx, Event, fun() -> emit(Rest, Next) end}. -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)). + +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(<>, T, Stack, 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(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)). +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{multi_term=true}=Opts) -> + emit([end_json], {start, Rest, T, [[], Opts]}); +maybe_done(Rest, T, [], Opts) -> + done(Rest, T, Opts); +maybe_done(Bin, T, Stack, Opts) -> + ?incomplete(maybe_done(<>, 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)). +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(<>, T, 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)). +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(<>, T, Stack, 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)). +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(<>, 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)). +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(<>, 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)). +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(<>, 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)). +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(<>, T, Stack, Opts)). %% string has an additional parameter, an accumulator (Acc) used to hold the @@ -362,28 +382,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 +419,18 @@ 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) + string(<>, T, Stack, Opts, Acc) end} ; 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(<>, 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,49 @@ 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) -> + escaped_unicode(Rest, T, Stack, Opts, String, [S] ++ Acc); +escaped_unicode(Bin, T, Stack, Opts, String, Acc) -> ?incomplete( - escaped_unicode(<>, Stack, Opts, String, Acc) + escaped_unicode(<>, 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) -> +low_surrogate(Bin, T, Stack, Opts, String, High) -> ?incomplete( - low_surrogate(<>, Stack, Opts, String, High) + low_surrogate(<>, 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 +617,13 @@ low_surrogate_u(<> = Bin, Stack, Opts, String, _) -> ; false -> {error, {badjson, <>}} end; -low_surrogate_u(Bin, Stack, Opts, String, H) -> +low_surrogate_u(Bin, T, Stack, Opts, String, H) -> ?incomplete( - low_surrogate_u(<>, Stack, Opts, String, H) + low_surrogate_u(<>, 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,29 +631,29 @@ 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) -> + low_surrogate(Rest, T, Stack, Opts, String, [S] ++ Acc, H); +low_surrogate(Bin, T, Stack, Opts, String, Acc, H) -> ?incomplete( low_surrogate( - <>, Stack, Opts, String, Acc, H + <>, T, Stack, Opts, String, Acc, H ) ). @@ -646,153 +665,137 @@ 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(<>, 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) + {decimal, <<>>, T, [[], Opts, Acc]}) + ; (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(<>, 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) + {decimal, <<>>, T, [[], Opts, Acc]}) + ; (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(<>, 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) -> + decimal(Rest, T, Stack, Opts, {Int, [S] ++ Frac}); +initial_decimal(Bin, T, Stack, Opts, Acc) -> ?incomplete( - initial_decimal(<>, Stack, Opts, Acc) + initial_decimal(<>, 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) + {decimal, <<>>, T, [[], Opts, Acc]}) + ; (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(<>, 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(<>, 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(<>, 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) + {exp, <<>>, T, [[], Opts, Acc]}) + ; (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(<>, T, Stack, Opts, Acc)). format_number(Int) when is_list(Int) -> @@ -805,64 +808,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(<>, 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(<>, 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(<>, 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(<>, 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(<>, 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(<>, 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(<>, 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(<>, 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(<>, 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(<>, T, Stack, Opts)). From 8bb0f66470d12ef34e5f53b1807df5b2401640c4 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Tue, 9 Aug 2011 17:49:42 -0700 Subject: [PATCH 2/7] unless iterate option is specified, jsx now buffers events internally before returning --- src/jsx.erl | 47 +++++++---- src/jsx_decoder.hrl | 190 +++++++++++++++++++++++++------------------- src/jsx_format.erl | 2 +- src/jsx_terms.erl | 2 +- src/jsx_verify.erl | 2 +- 5 files changed, 143 insertions(+), 100 deletions(-) diff --git a/src/jsx.erl b/src/jsx.erl index 6abaff3..5140437 100644 --- a/src/jsx.erl +++ b/src/jsx.erl @@ -174,13 +174,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 +225,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) -> @@ -257,7 +276,7 @@ multi_decode_test_() -> multi_decode(JSON, Flags) -> - P = jsx:decoder(Flags ++ [multi_term]), + P = jsx:decoder([multi_term, iterate] ++ Flags), multi_decode_loop(P(JSON), [[]]). multi_decode_loop({jsx, incomplete, _Next}, [[]|Acc]) -> diff --git a/src/jsx_decoder.hrl b/src/jsx_decoder.hrl index 0e60ea0..2b8fdde 100644 --- a/src/jsx_decoder.hrl +++ b/src/jsx_decoder.hrl @@ -34,10 +34,47 @@ array/4, value/4, colon/4, - key/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 ]). + +%% opts record for decoder +-record(opts, { + multi_term = false, + loose_unicode = false, + encoding = auto, + escape_forward_slash = false, %% does nothing, used by encoder + iterate = false +}). + + + -export([decoder/1]). -spec decoder(OptsList::jsx_opts()) -> jsx_decoder(). @@ -45,17 +82,13 @@ decoder(OptsList) -> case parse_opts(OptsList) of {error, badopt} -> {error, badopt} - ; Opts -> fun(JSON) -> start(JSON, iterate, [], 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 @@ -68,6 +101,8 @@ 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([iterate|Rest], Opts) -> + parse_opts(Rest, Opts#opts{iterate=true}); parse_opts([{encoding, _}|Rest], Opts) -> parse_opts(Rest, Opts); parse_opts(_, _) -> @@ -217,35 +252,36 @@ 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([incomplete], {State, Rest, iterate, Args}) -> +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, - [<>, iterate] ++ Args + [<>, T] ++ Args ) end}; -emit([Event], {State, Rest, iterate, Args}) -> - {jsx, Event, fun() -> - erlang:apply(?MODULE, State, [Rest, iterate] ++ Args) - end}; -emit([Event|Rest], {_State, _Rest, iterate, _Args} = Next) -> - {jsx, Event, fun() -> emit(Rest, Next) 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}). + + + +bad_json(Stream, _) -> {error, {badjson, Stream}}. @@ -270,7 +306,7 @@ start(<>, T, Stack, Opts) -> start(<>, T, Stack, Opts) when ?is_nonzero(S) -> integer(Rest, T, Stack, Opts, [S]); start(Bin, T, Stack, Opts) -> - ?incomplete(start(<>, T, Stack, Opts)). + incomplete(start, Bin, T, [Stack, Opts]). maybe_done(<>, T, Stack, Opts) when ?is_whitespace(S) -> @@ -288,7 +324,7 @@ maybe_done(Rest, T, [], #opts{multi_term=true}=Opts) -> maybe_done(Rest, T, [], Opts) -> done(Rest, T, Opts); maybe_done(Bin, T, Stack, Opts) -> - ?incomplete(maybe_done(<>, T, Stack, Opts)). + incomplete(maybe_done, Bin, T, [Stack, Opts]). done(<>, T, Opts) when ?is_whitespace(S) -> @@ -296,7 +332,7 @@ done(<>, T, Opts) when ?is_whitespace(S) -> done(<<>>, T, Opts) -> emit([end_json, incomplete], {done, <<>>, T, [Opts]}); done(Bin, T, Opts) -> - ?incomplete(done(<>, T, Opts)). + incomplete(done, Bin, T, [Opts]). object(<>, T, Stack, Opts) when ?is_whitespace(S) -> @@ -306,7 +342,7 @@ object(<>, T, Stack, Opts) -> object(<>, T, [key|Stack], Opts) -> emit([end_object], {maybe_done, Rest, T, [Stack, Opts]}); object(Bin, T, Stack, Opts) -> - ?incomplete(object(<>, T, Stack, Opts)). + incomplete(object, Bin, T, [Stack, Opts]). array(<>, T, Stack, Opts) when ?is_whitespace(S) -> @@ -332,7 +368,7 @@ array(<>, T, Stack, Opts) -> array(<>, T, [array|Stack], Opts) -> emit([end_array], {maybe_done, Rest, T, [Stack, Opts]}); array(Bin, T, Stack, Opts) -> - ?incomplete(array(<>, T, Stack, Opts)). + incomplete(array, Bin, T, [Stack, Opts]). value(<>, T, Stack, Opts) when ?is_whitespace(S) -> @@ -356,7 +392,7 @@ value(<>, T, Stack, Opts) -> value(<>, T, Stack, Opts) -> emit([start_array], {array, Rest, T, [[array|Stack], Opts]}); value(Bin, T, Stack, Opts) -> - ?incomplete(value(<>, T, Stack, Opts)). + incomplete(value, Bin, T, [Stack, Opts]). colon(<>, T, Stack, Opts) when ?is_whitespace(S) -> @@ -364,7 +400,7 @@ colon(<>, T, Stack, Opts) when ?is_whitespace(S) -> colon(<>, T, [key|Stack], Opts) -> value(Rest, T, [object|Stack], Opts); colon(Bin, T, Stack, Opts) -> - ?incomplete(colon(<>, T, Stack, Opts)). + incomplete(colon, Bin, T, [Stack, Opts]). key(<>, T, Stack, Opts) when ?is_whitespace(S) -> @@ -372,7 +408,7 @@ key(<>, T, Stack, Opts) when ?is_whitespace(S) -> key(<>, T, Stack, Opts) -> string(Rest, T, Stack, Opts); key(Bin, T, Stack, Opts) -> - ?incomplete(key(<>, T, Stack, Opts)). + incomplete(key, Bin, T, [Stack, Opts]). %% string has an additional parameter, an accumulator (Acc) used to hold the @@ -423,11 +459,7 @@ string(<>, T, Stack, Opts, Acc) string(Bin, T, Stack, Opts, Acc) -> case partial_utf(Bin) of true -> - {jsx, incomplete, fun(end_stream) -> - {error, {badjson, Bin}} - ; (Stream) -> - string(<>, T, Stack, Opts, Acc) - end} + emit([incomplete], {string, Bin, T, [Stack, Opts, Acc]}) ; false -> case Opts#opts.loose_unicode of true -> noncharacter(Bin, T, Stack, Opts, Acc) @@ -543,7 +575,7 @@ escape(<>, T, Stack, Opts, Acc) when S =:= ?quote; S =:= ?solidus; S =:= ?rsolidus -> string(Rest, T, Stack, Opts, [S] ++ Acc); escape(Bin, T, Stack, Opts, Acc) -> - ?incomplete(escape(<>, 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 @@ -579,9 +611,7 @@ escaped_unicode(<>, T, Stack, Opts, String, Acc) when ?is_hex(S) -> escaped_unicode(Rest, T, Stack, Opts, String, [S] ++ Acc); escaped_unicode(Bin, T, Stack, Opts, String, Acc) -> - ?incomplete( - escaped_unicode(<>, T, Stack, Opts, String, Acc) - ). + incomplete(escaped_unicode, Bin, T, [Stack, Opts, String, Acc]). low_surrogate(<>, T, Stack, Opts, String, High) -> @@ -596,9 +626,7 @@ low_surrogate(<> = Bin, T, Stack, Opts, String, _) -> {error, {badjson, <>}} end; low_surrogate(Bin, T, Stack, Opts, String, High) -> - ?incomplete( - low_surrogate(<>, T, Stack, Opts, String, High) - ). + incomplete(low_surrogate, Bin, T, [Stack, Opts, String, High]). low_surrogate_u(<<$u/?utfx, Rest/binary>>, T, Stack, Opts, String, H) -> @@ -618,9 +646,7 @@ low_surrogate_u(<> = Bin, T, Stack, Opts, String, _) -> {error, {badjson, <>}} end; low_surrogate_u(Bin, T, Stack, Opts, String, H) -> - ?incomplete( - low_surrogate_u(<>, T, Stack, Opts, String, H) - ). + incomplete(low_surrogate_u, Bin, T, [Stack, Opts, String, H]). low_surrogate(<>, T, Stack, Opts, String, [C, B, A], H) @@ -651,11 +677,7 @@ low_surrogate(<>, T, Stack, Opts, String, Acc, H) when ?is_hex(S) -> low_surrogate(Rest, T, Stack, Opts, String, [S] ++ Acc, H); low_surrogate(Bin, T, Stack, Opts, String, Acc, H) -> - ?incomplete( - low_surrogate( - <>, T, Stack, Opts, String, Acc, H - ) - ). + incomplete(low_surrogate, Bin, T, [Stack, Opts, String, Acc, H]). %% stole this from the unicode spec @@ -670,7 +692,7 @@ negative(<<$0/?utfx, Rest/binary>>, T, Stack, Opts, 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(<>, T, Stack, Opts, Acc)). + incomplete(negative, Bin, T, [Stack, Opts, Acc]). zero(<>, T, [object|Stack], Opts, Acc) -> @@ -687,12 +709,13 @@ 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], - {decimal, <<>>, T, [[], Opts, Acc]}) + emit([format_number(Acc), end_json, incomplete], + {bad_json, <<>>, T, []} + ) ; (Stream) -> zero(Stream, T, [], Opts, Acc) end}; zero(Bin, T, Stack, Opts, Acc) -> - ?incomplete(zero(<>, T, Stack, Opts, Acc)). + incomplete(zero, Bin, T, [Stack, Opts, Acc]). integer(<>, T, Stack, Opts, Acc) when ?is_nonzero(S) -> @@ -715,21 +738,20 @@ 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], - {decimal, <<>>, T, [[], Opts, Acc]}) + emit([format_number(Acc), end_json, incomplete], + {bad_json, <<>>, T, []} + ) ; (Stream) -> integer(Stream, T, [], Opts, Acc) end}; integer(Bin, T, Stack, Opts, Acc) -> - ?incomplete(integer(<>, T, Stack, Opts, Acc)). + incomplete(integer, Bin, T, [Stack, Opts, Acc]). initial_decimal(<>, T, Stack, Opts, {Int, Frac}) when S =:= ?zero; ?is_nonzero(S) -> decimal(Rest, T, Stack, Opts, {Int, [S] ++ Frac}); initial_decimal(Bin, T, Stack, Opts, Acc) -> - ?incomplete( - initial_decimal(<>, T, Stack, Opts, Acc) - ). + incomplete(initial_decimal, Bin, T, [Stack, Opts, Acc]). decimal(<>, T, Stack, Opts, {Int, Frac}) @@ -750,12 +772,13 @@ 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], - {decimal, <<>>, T, [[], Opts, Acc]}) + emit([format_number(Acc), end_json, incomplete], + {bad_json, <<>>, T, []} + ) ; (Stream) -> decimal(Stream, T, [], Opts, Acc) end}; decimal(Bin, T, Stack, Opts, Acc) -> - ?incomplete(decimal(<>, T, Stack, Opts, Acc)). + incomplete(decimal, Bin, T, [Stack, Opts, Acc]). e(<>, T, Stack, Opts, {Int, Frac, Exp}) @@ -765,14 +788,14 @@ e(<>, T, Stack, Opts, {Int, Frac, Exp}) when S =:= ?positive; S =:= ?negative -> ex(Rest, T, Stack, Opts, {Int, Frac, [S] ++ Exp}); e(Bin, T, Stack, Opts, Acc) -> - ?incomplete(e(<>, T, Stack, Opts, Acc)). + incomplete(e, Bin, T, [Stack, Opts, Acc]). ex(<>, T, Stack, Opts, {Int, Frac, Exp}) when S =:= ?zero; ?is_nonzero(S) -> exp(Rest, T, Stack, Opts, {Int, Frac, [S] ++ Exp}); ex(Bin, T, Stack, Opts, Acc) -> - ?incomplete(ex(<>, T, Stack, Opts, Acc)). + incomplete(ex, Bin, T, [Stack, Opts, Acc]). exp(<>, T, Stack, Opts, {Int, Frac, Exp}) @@ -790,12 +813,13 @@ 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], - {exp, <<>>, T, [[], Opts, Acc]}) + emit([format_number(Acc), end_json, incomplete], + {bad_json, <<>>, T, []} + ) ; (Stream) -> exp(Stream, T, [], Opts, Acc) end}; exp(Bin, T, Stack, Opts, Acc) -> - ?incomplete(exp(<>, T, Stack, Opts, Acc)). + incomplete(exp, Bin, T, [Stack, Opts, Acc]). format_number(Int) when is_list(Int) -> @@ -811,61 +835,61 @@ format_number({Int, Frac, Exp}) -> tr(<<$r/?utfx, Rest/binary>>, T, Stack, Opts) -> tru(Rest, T, Stack, Opts); tr(Bin, T, Stack, Opts) -> - ?incomplete(tr(<>, T, Stack, Opts)). + incomplete(tr, Bin, T, [Stack, Opts]). tru(<<$u/?utfx, Rest/binary>>, T, Stack, Opts) -> true(Rest, T, Stack, Opts); tru(Bin, T, Stack, Opts) -> - ?incomplete(tru(<>, T, Stack, Opts)). + incomplete(tru, Bin, T, [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(<>, T, Stack, Opts)). + incomplete(true, Bin, T, [Stack, Opts]). fa(<<$a/?utfx, Rest/binary>>, T, Stack, Opts) -> fal(Rest, T, Stack, Opts); fa(Bin, T, Stack, Opts) -> - ?incomplete(fa(<>, T, Stack, Opts)). + incomplete(fa, Bin, T, [Stack, Opts]). fal(<<$l/?utfx, Rest/binary>>, T, Stack, Opts) -> fals(Rest, T, Stack, Opts); fal(Bin, T, Stack, Opts) -> - ?incomplete(fal(<>, T, Stack, Opts)). + incomplete(fal, Bin, T, [Stack, Opts]). fals(<<$s/?utfx, Rest/binary>>, T, Stack, Opts) -> false(Rest, T, Stack, Opts); fals(Bin, T, Stack, Opts) -> - ?incomplete(fals(<>, T, Stack, Opts)). + incomplete(fals, Bin, T, [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(<>, T, Stack, Opts)). + incomplete(false, Bin, T, [Stack, Opts]). nu(<<$u/?utfx, Rest/binary>>, T, Stack, Opts) -> nul(Rest, T, Stack, Opts); nu(Bin, T, Stack, Opts) -> - ?incomplete(nu(<>, T, Stack, Opts)). + incomplete(nu, Bin, T, [Stack, Opts]). nul(<<$l/?utfx, Rest/binary>>, T, Stack, Opts) -> null(Rest, T, Stack, Opts); nul(Bin, T, Stack, Opts) -> - ?incomplete(nul(<>, T, Stack, Opts)). + incomplete(nul, Bin, T, [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(<>, T, Stack, Opts)). + incomplete(null, Bin, T, [Stack, Opts]). @@ -964,7 +988,7 @@ check([H|T], Opts, Acc) -> decode(JSON, Opts) -> - F = decoder(Opts), + F = decoder([iterate] ++ Opts), loop(F(JSON), []). diff --git a/src/jsx_format.erl b/src/jsx_format.erl index a00f845..561ef50 100644 --- a/src/jsx_format.erl +++ b/src/jsx_format.erl @@ -41,7 +41,7 @@ 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(), 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..de88499 100644 --- a/src/jsx_verify.erl +++ b/src/jsx_verify.erl @@ -38,7 +38,7 @@ ; (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(), From ea764162c007d4bde9933eca3b6fb6c5b4deab29 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Tue, 9 Aug 2011 21:02:01 -0700 Subject: [PATCH 3/7] moves opts and parse_opts to external header --- src/jsx_decoder.hrl | 36 +++------------------------------ src/jsx_opts.hrl | 49 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 33 deletions(-) create mode 100644 src/jsx_opts.hrl diff --git a/src/jsx_decoder.hrl b/src/jsx_decoder.hrl index 2b8fdde..1b8b9fb 100644 --- a/src/jsx_decoder.hrl +++ b/src/jsx_decoder.hrl @@ -26,6 +26,8 @@ %% this file should take that into account +-export([decoder/1]). + %% exported solely to facilitate stupid trick i shouldn't be using -export([start/4, maybe_done/4, @@ -63,22 +65,11 @@ ]). - -%% opts record for decoder --record(opts, { - multi_term = false, - loose_unicode = false, - encoding = auto, - escape_forward_slash = false, %% does nothing, used by encoder - iterate = false -}). +-include("jsx_opts.hrl"). - --export([decoder/1]). -spec decoder(OptsList::jsx_opts()) -> jsx_decoder(). - decoder(OptsList) -> case parse_opts(OptsList) of {error, badopt} -> {error, badopt} @@ -90,25 +81,6 @@ decoder(OptsList) -> end. - -%% 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([iterate|Rest], Opts) -> - parse_opts(Rest, Opts#opts{iterate=true}); -parse_opts([{encoding, _}|Rest], Opts) -> - parse_opts(Rest, Opts); -parse_opts(_, _) -> - {error, badarg}. - - %% whitespace -define(space, 16#20). -define(tab, 16#09). @@ -280,11 +252,9 @@ emit([Event|Events], {State, Rest, T, Args}) -> emit(Events, {State, Rest, [Event] ++ T, Args}). - bad_json(Stream, _) -> {error, {badjson, Stream}}. - start(<>, T, Stack, Opts) when ?is_whitespace(S) -> start(Rest, T, Stack, Opts); start(<>, T, Stack, Opts) -> diff --git a/src/jsx_opts.hrl b/src/jsx_opts.hrl new file mode 100644 index 0000000..5ca7076 --- /dev/null +++ b/src/jsx_opts.hrl @@ -0,0 +1,49 @@ +%% 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, { + multi_term = false, + 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([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([iterate|Rest], Opts) -> + parse_opts(Rest, Opts#opts{iterate=true}); +parse_opts([{encoding, _}|Rest], Opts) -> + parse_opts(Rest, Opts); +parse_opts(_, _) -> + {error, badarg}. \ No newline at end of file From 3485ca54139ec62765d1b7c4f0d5e02395600e2f Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Tue, 9 Aug 2011 21:02:20 -0700 Subject: [PATCH 4/7] whitespace --- src/jsx.erl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/jsx.erl b/src/jsx.erl index 5140437..d16db41 100644 --- a/src/jsx.erl +++ b/src/jsx.erl @@ -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([]). @@ -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)). From 0c3cf2b87b29cae8a5c8be14274e2f32b17eea26 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Wed, 10 Aug 2011 00:33:14 -0700 Subject: [PATCH 5/7] encoder updated to use same api as decoder, format and verify updated to be aware of new api --- src/jsx_encoder.erl | 274 ++++++++++++++++++++++---------------------- src/jsx_format.erl | 2 +- src/jsx_opts.hrl | 2 + src/jsx_verify.erl | 2 +- 4 files changed, 144 insertions(+), 136 deletions(-) diff --git a/src/jsx_encoder.erl b/src/jsx_encoder.erl index d5cc49d..eab5a3d 100644 --- a/src/jsx_encoder.erl +++ b/src/jsx_encoder.erl @@ -23,116 +23,127 @@ -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_json|Forms], T, [], #opts{multi_term=true}=Opts) -> + emit([end_json], {start, Forms, T, [Opts]}); +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 +210,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 +260,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 +280,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 +331,4 @@ escape_test_() -> } ]. --endif. - +-endif. \ No newline at end of file diff --git a/src/jsx_format.erl b/src/jsx_format.erl index 561ef50..918deb3 100644 --- a/src/jsx_format.erl +++ b/src/jsx_format.erl @@ -44,7 +44,7 @@ format(JSON, OptsList) when is_binary(JSON) -> 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 index 5ca7076..80a51ec 100644 --- a/src/jsx_opts.hrl +++ b/src/jsx_opts.hrl @@ -43,6 +43,8 @@ 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(_, _) -> diff --git a/src/jsx_verify.erl b/src/jsx_verify.erl index de88499..6c0024f 100644 --- a/src/jsx_verify.erl +++ b/src/jsx_verify.erl @@ -41,7 +41,7 @@ is_json(JSON, OptsList) when is_binary(JSON) -> 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{}), From ba057d13ccfd6aaf255e296aa95fca69ac30a1f7 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Wed, 10 Aug 2011 00:33:52 -0700 Subject: [PATCH 6/7] encoder exported correctly --- src/jsx.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jsx.erl b/src/jsx.erl index d16db41..9a2c890 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]). @@ -71,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). From aca326b42e14b0a3b82a087c84e2372f11030a11 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Wed, 10 Aug 2011 00:35:51 -0700 Subject: [PATCH 7/7] multi_term mode removed until sane interface can be developed --- src/jsx.erl | 42 ------------------------------------------ src/jsx_decoder.hrl | 2 -- src/jsx_encoder.erl | 2 -- src/jsx_opts.hrl | 3 --- 4 files changed, 49 deletions(-) diff --git a/src/jsx.erl b/src/jsx.erl index 9a2c890..261409c 100644 --- a/src/jsx.erl +++ b/src/jsx.erl @@ -261,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([multi_term, iterate] ++ Flags), - 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 1b8b9fb..00bc8c0 100644 --- a/src/jsx_decoder.hrl +++ b/src/jsx_decoder.hrl @@ -289,8 +289,6 @@ 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{multi_term=true}=Opts) -> - emit([end_json], {start, Rest, T, [[], Opts]}); maybe_done(Rest, T, [], Opts) -> done(Rest, T, Opts); maybe_done(Bin, T, Stack, Opts) -> diff --git a/src/jsx_encoder.erl b/src/jsx_encoder.erl index eab5a3d..f27e649 100644 --- a/src/jsx_encoder.erl +++ b/src/jsx_encoder.erl @@ -133,8 +133,6 @@ 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_json|Forms], T, [], #opts{multi_term=true}=Opts) -> - emit([end_json], {start, Forms, T, [Opts]}); 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) -> diff --git a/src/jsx_opts.hrl b/src/jsx_opts.hrl index 80a51ec..ee301be 100644 --- a/src/jsx_opts.hrl +++ b/src/jsx_opts.hrl @@ -23,7 +23,6 @@ %% opts record for decoder/encoder -record(opts, { - multi_term = false, loose_unicode = false, encoding = auto, escape_forward_slash = false, %% does nothing, used by encoder @@ -37,8 +36,6 @@ parse_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([iterate|Rest], Opts) ->