new interruptable api, examples broken for the hojillionth time
This commit is contained in:
parent
e690d7723a
commit
4286892861
3 changed files with 186 additions and 199 deletions
125
src/jsx.erl
125
src/jsx.erl
|
@ -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
|
||||
}.
|
Loading…
Add table
Add a link
Reference in a new issue