new interruptable api, examples broken for the hojillionth time

This commit is contained in:
alisdair sullivan 2010-06-11 21:57:42 -07:00
parent e690d7723a
commit 4286892861
3 changed files with 186 additions and 199 deletions

View file

@ -21,15 +21,17 @@
%% THE SOFTWARE.
-module(jsx).
-author("alisdairsullivan@yahoo.ca").
-export([decode/1, decode/2, parser/0, parser/1, parser/2]).
-export([decode/1, decode/2, parser/0, parser/1]).
-include("jsx_types.hrl").
%% decode takes a json binary (and optionally, a proplist of options) and returns a list
%% of events corresponding to the json structure/contents. it converts incompletes into
%% errors
-spec decode(JSON::json()) -> {ok, [jsx_event(),...]} | {error, badjson}.
-spec decode(JSON::json(), Opts::jsx_opts()) -> {ok, [jsx_event(),...]} | {error, badjson}.
@ -39,47 +41,39 @@ decode(JSON) ->
decode(JSON, Opts) ->
F = parser(Opts),
case F(JSON) of
{incomplete, _} -> {error, badjson}
; {error, badjson} -> {error, badjson}
; {Result, _} -> {ok, Result}
end.
decode_loop(F(JSON), []).
decode_loop({incomplete, _}, _) -> {error, badjson};
decode_loop({error, badjson}, _) -> {error, badjson};
decode_loop({end_json, _}, State) -> {ok, lists:reverse(State)};
decode_loop({Event, F}, State) -> decode_loop(F(), [Event] ++ State).
%% parser returns an anonymous function of arity 1 that takes a json binary as it's
%% argument and returns a tuple containing an error, incomplete and a new parser that
%% can be handed more input to resume parsing or a single event and a function that
%% can be called to get the next result
-spec parser() -> jsx_parser().
-spec parser(Opts::jsx_opts()) -> jsx_parser().
-spec parser(Callbacks::{fun((jsx_event(), any()) -> any())}, Opts::jsx_opts()) -> jsx_parser()
; (Callbacks::{atom(), atom(), any()}, Opts::jsx_opts()) -> jsx_parser().
parser() ->
parser([]).
parser(Opts) ->
F = fun(end_json, State) -> lists:reverse(State)
; (Event, State) -> [Event] ++ State
end,
parser({F, []}, Opts).
parser({F, S} = Callback, OptsList) when is_list(OptsList), is_function(F) ->
start(Callback, OptsList);
parser({Mod, Fun, State}, OptsList) when is_list(OptsList), is_atom(Mod), is_atom(Fun) ->
start({fun(E, S) -> Mod:Fun(E, S) end, State}, OptsList).
start(Callback, OptsList) ->
parser(OptsList) ->
F = case proplists:get_value(encoding, OptsList, auto) of
utf8 -> fun jsx_utf8:parse/3
; utf16 -> fun jsx_utf16:parse/3
; utf32 -> fun jsx_utf32:parse/3
; {utf16, little} -> fun jsx_utf16le:parse/3
; {utf32, little} -> fun jsx_utf32le:parse/3
; auto -> fun detect_encoding/3
utf8 -> fun jsx_utf8:parse/2
; utf16 -> fun jsx_utf16:parse/2
; utf32 -> fun jsx_utf32:parse/2
; {utf16, little} -> fun jsx_utf16le:parse/2
; {utf32, little} -> fun jsx_utf32le:parse/2
; auto -> fun detect_encoding/2
end,
start(F, Callback, OptsList).
start(F, OptsList).
start(F, Callback, OptsList) ->
start(F, OptsList) ->
Opts = parse_opts(OptsList),
fun(Stream) -> F(Stream, Callback, Opts) end.
fun(Stream) -> F(Stream, Opts) end.
parse_opts(Opts) ->
@ -95,84 +89,87 @@ parse_opts([{escaped_unicode, Value}|Rest], {Comments, _EscapedUnicode, Stream})
parse_opts(Rest, {Comments, Value, Stream});
parse_opts([{stream_mode, Value}|Rest], {Comments, EscapedUnicode, _Stream}) ->
true = lists:member(Value, [true, false]),
parse_opts(Rest, {Comments, EscapedUnicode, Value}).
parse_opts(Rest, {Comments, EscapedUnicode, Value});
parse_opts([{encoding, _}|Rest], Opts) ->
parse_opts(Rest, Opts).
%% first check to see if there's a bom, if not, use the rfc4627 method for determining
%% encoding. this function makes some assumptions about the validity of the stream
%% which may delay failure later than if an encoding is explicitly provided.
%% which may delay failure later than if an encoding is explicitly provided
%% utf8 bom detection
detect_encoding(<<16#ef, 16#bb, 16#bf, Rest/binary>>, Callback, Opts) ->
jsx_utf8:parse(Rest, Callback, Opts);
detect_encoding(<<16#ef, 16#bb, 16#bf, Rest/binary>>, Opts) ->
jsx_utf8:parse(Rest, Opts);
%% utf32-little bom detection (this has to come before utf16-little)
detect_encoding(<<16#ff, 16#fe, 0, 0, Rest/binary>>, Callback, Opts) ->
jsx_utf32le:parse(Rest, Callback, Opts);
detect_encoding(<<16#ff, 16#fe, 0, 0, Rest/binary>>, Opts) ->
jsx_utf32le:parse(Rest, Opts);
%% utf16-big bom detection
detect_encoding(<<16#fe, 16#ff, Rest/binary>>, Callback, Opts) ->
jsx_utf16:parse(Rest, Callback, Opts);
detect_encoding(<<16#fe, 16#ff, Rest/binary>>, Opts) ->
jsx_utf16:parse(Rest, Opts);
%% utf16-little bom detection
detect_encoding(<<16#ff, 16#fe, Rest/binary>>, Callback, Opts) ->
jsx_utf16le:parse(Rest, Callback, Opts);
detect_encoding(<<16#ff, 16#fe, Rest/binary>>, Opts) ->
jsx_utf16le:parse(Rest, Opts);
%% utf32-big bom detection
detect_encoding(<<0, 0, 16#fe, 16#ff, Rest/binary>>, Callback, Opts) ->
jsx_utf32:parse(Rest, Callback, Opts);
detect_encoding(<<0, 0, 16#fe, 16#ff, Rest/binary>>, Opts) ->
jsx_utf32:parse(Rest, Opts);
%% utf32-little null order detection
detect_encoding(<<X, 0, 0, 0, _Rest/binary>> = JSON, Callback, Opts) when X =/= 0 ->
jsx_utf32le:parse(JSON, Callback, Opts);
detect_encoding(<<X, 0, 0, 0, _Rest/binary>> = JSON, Opts) when X =/= 0 ->
jsx_utf32le:parse(JSON, Opts);
%% utf16-big null order detection
detect_encoding(<<0, X, 0, Y, _Rest/binary>> = JSON, Callback, Opts) when X =/= 0, Y =/= 0 ->
jsx_utf16:parse(JSON, Callback, Opts);
detect_encoding(<<0, X, 0, Y, _Rest/binary>> = JSON, Opts) when X =/= 0, Y =/= 0 ->
jsx_utf16:parse(JSON, Opts);
%% utf16-little null order detection
detect_encoding(<<X, 0, Y, 0, _Rest/binary>> = JSON, Callback, Opts) when X =/= 0, Y =/= 0 ->
jsx_utf16le:parse(JSON, Callback, Opts);
detect_encoding(<<X, 0, Y, 0, _Rest/binary>> = JSON, Opts) when X =/= 0, Y =/= 0 ->
jsx_utf16le:parse(JSON, Opts);
%% utf32-big null order detection
detect_encoding(<<0, 0, 0, X, _Rest/binary>> = JSON, Callback, Opts) when X =/= 0 ->
jsx_utf32:parse(JSON, Callback, Opts);
detect_encoding(<<0, 0, 0, X, _Rest/binary>> = JSON, Opts) when X =/= 0 ->
jsx_utf32:parse(JSON, Opts);
%% utf8 null order detection
detect_encoding(<<X, Y, _Rest/binary>> = JSON, Callback, Opts) when X =/= 0, Y =/= 0 ->
jsx_utf8:parse(JSON, Callback, Opts);
detect_encoding(<<X, Y, _Rest/binary>> = JSON, Opts) when X =/= 0, Y =/= 0 ->
jsx_utf8:parse(JSON, Opts);
%% a problem, to autodetect naked single digits' encoding, there is not enough data
%% to conclusively determine the encoding correctly. below is an attempt to solve
%% the problem
detect_encoding(<<X>>, Callback, Opts) when X =/= 0 ->
{try {Result, _} = jsx_utf8:parse(<<X>>, Callback, Opts), Result
detect_encoding(<<X>>, Opts) when X =/= 0 ->
{try {Result, _} = jsx_utf8:parse(<<X>>, Opts), Result
catch error:function_clause -> incomplete end,
fun(Stream) ->
detect_encoding(<<X, Stream/binary>>, Callback, Opts)
detect_encoding(<<X, Stream/binary>>, Opts)
end
};
detect_encoding(<<0, X>>, Callback, Opts) when X =/= 0 ->
{try {Result, _} = jsx_utf16:parse(<<0, X>>, Callback, Opts), Result
detect_encoding(<<0, X>>, Opts) when X =/= 0 ->
{try {Result, _} = jsx_utf16:parse(<<0, X>>, Opts), Result
catch error:function_clause -> incomplete end,
fun(Stream) ->
detect_encoding(<<0, X, Stream/binary>>, Callback, Opts)
detect_encoding(<<0, X, Stream/binary>>, Opts)
end
};
detect_encoding(<<X, 0>>, Callback, Opts) when X =/= 0 ->
{try {Result, _} = jsx_utf16le:parse(<<X, 0>>, Callback, Opts), Result
detect_encoding(<<X, 0>>, Opts) when X =/= 0 ->
{try {Result, _} = jsx_utf16le:parse(<<X, 0>>, Opts), Result
catch error:function_clause -> incomplete end,
fun(Stream) ->
detect_encoding(<<X, 0, Stream/binary>>, Callback, Opts)
detect_encoding(<<X, 0, Stream/binary>>, Opts)
end
};
%% not enough input, request more
detect_encoding(Bin, Callback, Opts) ->
detect_encoding(Bin, Opts) ->
{incomplete,
fun(Stream) ->
detect_encoding(<<Bin/binary, Stream/binary>>, Callback, Opts)
detect_encoding(<<Bin/binary, Stream/binary>>, Opts)
end
}.