explicit termination is no longer an option. the decoder now terminates whenever it reaches the end of an unambiguous json term and returns a function that accepts either more input to continue decoding or the empty binary to force termination.

examples are currently broken, but the test suite has been updated to reflect the new api.
This commit is contained in:
alisdair sullivan 2010-05-27 06:42:58 -07:00
parent 06ded56939
commit 7e6124fc88
4 changed files with 32 additions and 40 deletions

View file

@ -62,13 +62,10 @@ parse_opts([{escaped_unicode, Value}|Rest], Opts) ->
parse_opts(Rest, Opts#opts{escaped_unicode = Value}); parse_opts(Rest, Opts#opts{escaped_unicode = Value});
parse_opts([{naked_values, Value}|Rest], Opts) -> parse_opts([{naked_values, Value}|Rest], Opts) ->
true = lists:member(Value, [true, false]), true = lists:member(Value, [true, false]),
parse_opts(Rest, Opts#opts{naked_values = Value, explicit_termination = true}); parse_opts(Rest, Opts#opts{naked_values = Value});
parse_opts([{encoding, Value}|Rest], Opts) -> parse_opts([{encoding, Value}|Rest], Opts) ->
true = lists:member(Value, [utf8]), true = lists:member(Value, [utf8]),
parse_opts(Rest, Opts#opts{encoding = Value}); parse_opts(Rest, Opts#opts{encoding = Value}).
parse_opts([{explicit_termination, Value}|Rest], Opts) ->
true = lists:member(Value, [true, false]),
parse_opts(Rest, Opts#opts{explicit_termination = Value}).
%% ensures there's no invalid characters left in the stream upon completion of parsing %% ensures there's no invalid characters left in the stream upon completion of parsing

View file

@ -25,8 +25,7 @@
comments = false, comments = false,
escaped_unicode = ascii, escaped_unicode = ascii,
naked_values = false, naked_values = false,
encoding = utf8, encoding = utf8
explicit_termination = false
}). }).
%% whitespace %% whitespace

View file

@ -59,10 +59,16 @@ start(<<S, Rest/binary>>, Stack, Callbacks, Opts) when ?is_nonzero(S), Opts#opts
integer(Rest, Stack, Callbacks, Opts, [S]); integer(Rest, Stack, Callbacks, Opts, [S]);
start(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts) when Opts#opts.comments == true -> start(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts) when Opts#opts.comments == true ->
maybe_comment(Rest, fun(Resume) -> start(Resume, Stack, Callbacks, Opts) end); maybe_comment(Rest, fun(Resume) -> start(Resume, Stack, Callbacks, Opts) end);
start(<<>>, [], Callbacks, Opts) ->
fun(<<>>) -> {fold(completed_parse, Callbacks), <<>>}
; (Stream) -> start(Stream, [], Callbacks, Opts)
end;
start(<<>>, Stack, Callbacks, Opts) -> start(<<>>, Stack, Callbacks, Opts) ->
fun(Stream) -> start(Stream, Stack, Callbacks, Opts) end. fun(Stream) -> start(Stream, Stack, Callbacks, Opts) end.
maybe_done(<<Rest/binary>>, [], Callbacks, _Opts) ->
{fold(completed_parse, Callbacks), Rest};
maybe_done(<<S, Rest/binary>>, Stack, Callbacks, Opts) when ?is_whitespace(S) -> maybe_done(<<S, Rest/binary>>, Stack, Callbacks, Opts) when ?is_whitespace(S) ->
maybe_done(Rest, Stack, Callbacks, Opts); maybe_done(Rest, Stack, Callbacks, Opts);
maybe_done(<<?end_object, Rest/binary>>, [object|Stack], Callbacks, Opts) -> maybe_done(<<?end_object, Rest/binary>>, [object|Stack], Callbacks, Opts) ->
@ -75,12 +81,6 @@ maybe_done(<<?comma, Rest/binary>>, [array|_] = Stack, Callbacks, Opts) ->
value(Rest, Stack, Callbacks, Opts); value(Rest, Stack, Callbacks, Opts);
maybe_done(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts) when Opts#opts.comments == true -> maybe_done(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts) when Opts#opts.comments == true ->
maybe_comment(Rest, fun(Resume) -> maybe_done(Resume, Stack, Callbacks, Opts) end); maybe_comment(Rest, fun(Resume) -> maybe_done(Resume, Stack, Callbacks, Opts) end);
maybe_done(<<>>, [], Callbacks, Opts) when Opts#opts.explicit_termination == true ->
fun(<<>>) -> {fold(completed_parse, Callbacks), <<>>}
;(Stream) -> maybe_done(Stream, [], Callbacks, Opts)
end;
maybe_done(<<Rest/binary>>, [], Callbacks, _Opts) ->
{fold(completed_parse, Callbacks), Rest};
maybe_done(<<>>, Stack, Callbacks, Opts) -> maybe_done(<<>>, Stack, Callbacks, Opts) ->
fun(Stream) -> maybe_done(Stream, Stack, Callbacks, Opts) end. fun(Stream) -> maybe_done(Stream, Stack, Callbacks, Opts) end.
@ -264,12 +264,10 @@ zero(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when ?is_whitespace(S) ->
maybe_done(Rest, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts); maybe_done(Rest, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts);
zero(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts, Acc) when Opts#opts.comments == true -> zero(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts, Acc) when Opts#opts.comments == true ->
maybe_comment(Rest, fun(Resume) -> zero(Resume, Stack, Callbacks, Opts, Acc) end); maybe_comment(Rest, fun(Resume) -> zero(Resume, Stack, Callbacks, Opts, Acc) end);
zero(<<Rest/binary>>, [], Callbacks, Opts, Acc) when Opts#opts.explicit_termination == true ->
fun(<<>>) -> {fold(completed_parse, fold({number, lists:reverse(Acc)}, Callbacks)), Rest}
;(Stream) -> zero(Stream, [], Callbacks, Opts, Acc)
end;
zero(<<>>, Stack, Callbacks, Opts, Acc) -> zero(<<>>, Stack, Callbacks, Opts, Acc) ->
fun(Stream) -> zero(Stream, Stack, Callbacks, Opts, Acc) end. fun(<<>>) -> maybe_done(<<>>, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts)
; (Stream) -> zero(Stream, Stack, Callbacks, Opts, Acc)
end.
integer(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when ?is_nonzero(S) -> integer(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when ?is_nonzero(S) ->
@ -294,12 +292,10 @@ integer(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when ?is_whitespace(S)
maybe_done(Rest, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts); maybe_done(Rest, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts);
integer(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts, Acc) when Opts#opts.comments == true -> integer(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts, Acc) when Opts#opts.comments == true ->
maybe_comment(Rest, fun(Resume) -> integer(Resume, Stack, Callbacks, Opts, Acc) end); maybe_comment(Rest, fun(Resume) -> integer(Resume, Stack, Callbacks, Opts, Acc) end);
integer(<<>> = Rest, [], Callbacks, Opts, Acc) when Opts#opts.explicit_termination == true ->
fun(<<>>) -> {fold(completed_parse, fold({number, lists:reverse(Acc)}, Callbacks)), Rest}
;(Stream) -> integer(Stream, [], Callbacks, Opts, Acc)
end;
integer(<<>>, Stack, Callbacks, Opts, Acc) -> integer(<<>>, Stack, Callbacks, Opts, Acc) ->
fun(Stream) -> integer(Stream, Stack, Callbacks, Opts, Acc) end. fun(<<>>) -> maybe_done(<<>>, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts)
; (Stream) -> integer(Stream, Stack, Callbacks, Opts, Acc)
end.
fraction(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when ?is_nonzero(S) -> fraction(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when ?is_nonzero(S) ->
fraction(Rest, Stack, Callbacks, Opts, [S] ++ Acc); fraction(Rest, Stack, Callbacks, Opts, [S] ++ Acc);
@ -321,12 +317,10 @@ fraction(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when ?is_whitespace(S)
maybe_done(Rest, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts); maybe_done(Rest, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts);
fraction(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts, Acc) when Opts#opts.comments == true -> fraction(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts, Acc) when Opts#opts.comments == true ->
maybe_comment(Rest, fun(Resume) -> fraction(Resume, Stack, Callbacks, Opts, Acc) end); maybe_comment(Rest, fun(Resume) -> fraction(Resume, Stack, Callbacks, Opts, Acc) end);
fraction(<<Rest/binary>>, [], Callbacks, Opts, Acc) when Opts#opts.explicit_termination == true ->
fun(<<>>) -> {fold(completed_parse, fold({number, lists:reverse(Acc)}, Callbacks)), Rest}
;(Stream) -> fraction(Stream, [], Callbacks, Opts, Acc)
end;
fraction(<<>>, Stack, Callbacks, Opts, Acc) -> fraction(<<>>, Stack, Callbacks, Opts, Acc) ->
fun(Stream) -> fraction(Stream, Stack, Callbacks, Opts, Acc) end. fun(<<>>) -> maybe_done(<<>>, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts)
; (Stream) -> fraction(Stream, Stack, Callbacks, Opts, Acc)
end.
e(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when S =:= ?zero; ?is_nonzero(S) -> e(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when S =:= ?zero; ?is_nonzero(S) ->
@ -359,12 +353,10 @@ exp(<<?solidus, Rest/binary>>, Stack, Callbacks, Opts, Acc) when Opts#opts.comme
maybe_comment(Rest, fun(Resume) -> exp(Resume, Stack, Callbacks, Opts, Acc) end); maybe_comment(Rest, fun(Resume) -> exp(Resume, Stack, Callbacks, Opts, Acc) end);
exp(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when ?is_whitespace(S) -> exp(<<S, Rest/binary>>, Stack, Callbacks, Opts, Acc) when ?is_whitespace(S) ->
maybe_done(Rest, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts); maybe_done(Rest, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts);
exp(<<Rest/binary>>, [], Callbacks, Opts, Acc) when Opts#opts.explicit_termination == true ->
fun(<<>>) -> {fold(completed_parse, fold({number, lists:reverse(Acc)}, Callbacks)), Rest}
;(Stream) -> exp(Stream, [], Callbacks, Opts, Acc)
end;
exp(<<>>, Stack, Callbacks, Opts, Acc) -> exp(<<>>, Stack, Callbacks, Opts, Acc) ->
fun(Stream) -> exp(Stream, Stack, Callbacks, Opts, Acc) end. fun(<<>>) -> maybe_done(<<>>, Stack, fold({number, lists:reverse(Acc)}, Callbacks), Opts)
; (Stream) -> exp(Stream, Stack, Callbacks, Opts, Acc)
end.
tr(<<$r, Rest/binary>>, Stack, Callbacks, Opts) -> tr(<<$r, Rest/binary>>, Stack, Callbacks, Opts) ->

View file

@ -50,24 +50,28 @@ test_body(TestSpec, Dir) ->
case file:consult(Dir ++ "/" ++ TestSpec) of case file:consult(Dir ++ "/" ++ TestSpec) of
{ok, [Events]} -> {ok, [Events]} ->
Decoder = jsx:decoder(), Decoder = jsx:decoder(),
[{TestName, ?_assertEqual(incremental_decode(Decoder, JSON), Events)}] ++ [{TestName ++ "_incremental", ?_assertEqual(incremental_decode(Decoder, JSON), Events)}] ++
[{TestName, ?_assertEqual(decode(Decoder, JSON), Events)}] [{TestName, ?_assertEqual(decode(Decoder, JSON), Events)}]
; {ok, [Events, Flags]} -> ; {ok, [Events, Flags]} ->
Decoder = jsx:decoder(Flags), Decoder = jsx:decoder(Flags),
[{TestName, ?_assertEqual(incremental_decode(Decoder, JSON), Events)}] ++ [{TestName ++ "_incremental", ?_assertEqual(incremental_decode(Decoder, JSON), Events)}] ++
[{TestName, ?_assertEqual(decode(Decoder, JSON), Events)}] [{TestName, ?_assertEqual(decode(Decoder, JSON), Events)}]
end end
catch _:_ -> [] catch _:_ -> []
end. end.
incremental_decode(F, <<>>) -> incremental_decode(F, <<>>) ->
{Result, Rest} = F(<<>>), case F(<<>>) of
true = jsx:tail_clean(Rest), G when is_function(G) ->
Result; {Result, <<>>} = G(<<>>),
Result
; {Result, Rest} ->
Result
end;
incremental_decode(F, <<A/utf8, Rest/binary>>) -> incremental_decode(F, <<A/utf8, Rest/binary>>) ->
case F(<<A/utf8>>) of case F(<<A/utf8>>) of
G when is_function(G) -> G when is_function(G) ->
decode(G, Rest) incremental_decode(G, Rest)
; {Result, _} -> ; {Result, _} ->
Result Result
end. end.
@ -78,7 +82,7 @@ decode(F, JSON) ->
{Result, <<>>} = G(<<>>), {Result, <<>>} = G(<<>>),
Result Result
; {Result, Rest} -> ; {Result, Rest} ->
true = jsx:tail_clean(Rest), {_, <<>>} = (F(Rest))(<<>>),
Result Result
end. end.