From a86dec09efcf22db658ea0a0f1b3825bd5af3656 Mon Sep 17 00:00:00 2001
From: alisdair sullivan
Date: Wed, 25 Aug 2010 23:17:10 -0700
Subject: [PATCH] major whitespace updates to get line lengths down to < 80
characters whenever possible (a few tests ignore this). srx/jsx_test.erl was
not touched because it's shortly to be replaced
---
include/jsx_common.hrl | 6 +-
include/jsx_decoder.hrl | 672 +++++++++++++++++++++++++++-------------
src/jsx.erl | 211 ++++++++-----
src/jsx_eep0018.erl | 344 ++++++++++++++------
src/jsx_format.erl | 93 +++++-
src/jsx_verify.erl | 86 ++++-
6 files changed, 998 insertions(+), 414 deletions(-)
diff --git a/include/jsx_common.hrl b/include/jsx_common.hrl
index fdc6748..aa31871 100644
--- a/include/jsx_common.hrl
+++ b/include/jsx_common.hrl
@@ -23,5 +23,9 @@
-define(is_utf_encoding(X),
- X == utf8; X == utf16; X == utf32; X == {utf16, little}; X == {utf32, little}
+ X == utf8
+ ; X == utf16
+ ; X == utf32
+ ; X == {utf16, little}
+ ; X == {utf32, little}
).
\ No newline at end of file
diff --git a/include/jsx_decoder.hrl b/include/jsx_decoder.hrl
index b2a20c0..6feb245 100644
--- a/include/jsx_decoder.hrl
+++ b/include/jsx_decoder.hrl
@@ -21,9 +21,9 @@
%% THE SOFTWARE.
-%% this is the implementation of the utf backends for the jsx decoder. it's included
-%% by the various jsx_utfxx.erl frontends and all modifications to this file
-%% should take that into account
+%% this is the implementation of the utf backends for the jsx decoder. it's
+%% included by the various jsx_utfxx.erl frontends and all modifications to
+%% this file ?utfxshould take that into account
%% opts record for decoder
@@ -93,27 +93,27 @@
%% partial codepoint max size differs across encodings
-ifdef(utf8).
--define(encoding, utf8).
+-define(utfx, utf8).
-define(partial_codepoint(Bin), byte_size(Bin) < 1).
-endif.
-ifdef(utf16).
--define(encoding, utf16).
+-define(utfx, utf16).
-define(partial_codepoint(Bin), byte_size(Bin) < 2).
-endif.
-ifdef(utf16le).
--define(encoding, utf16-little).
+-define(utfx, utf16-little).
-define(partial_codepoint(Bin), byte_size(Bin) < 2).
-endif.
-ifdef(utf32).
--define(encoding, utf32).
+-define(utfx, utf32).
-define(partial_codepoint(Bin), byte_size(Bin) < 4).
-endif.
-ifdef(utf32le).
--define(encoding, utf32-little).
+-define(utfx, utf32-little).
-define(partial_codepoint(Bin), byte_size(Bin) < 4).
-endif.
@@ -137,13 +137,13 @@ parse_opts([], Opts) ->
Opts;
parse_opts([{comments, Value}|Rest], Opts) ->
true = lists:member(Value, [true, false]),
- parse_opts(Rest, Opts#opts{comments = Value});
+ parse_opts(Rest, Opts#opts{comments=Value});
parse_opts([{escaped_unicode, Value}|Rest], Opts) ->
true = lists:member(Value, [ascii, codepoint, none]),
- parse_opts(Rest, Opts#opts{escaped_unicode = Value});
+ parse_opts(Rest, Opts#opts{escaped_unicode=Value});
parse_opts([{multi_term, Value}|Rest], Opts) ->
true = lists:member(Value, [true, false]),
- parse_opts(Rest, Opts#opts{multi_term = Value});
+ parse_opts(Rest, Opts#opts{multi_term=Value});
parse_opts([{encoding, _}|Rest], Opts) ->
parse_opts(Rest, Opts);
parse_opts(_, _) ->
@@ -151,189 +151,243 @@ parse_opts(_, _) ->
-start(<>, Stack, Opts) when ?is_whitespace(S) ->
+start(<>, Stack, Opts) when ?is_whitespace(S) ->
start(Rest, Stack, Opts);
-start(<>, Stack, Opts) ->
+start(<>, Stack, Opts) ->
{event, start_object, fun() -> object(Rest, [key|Stack], Opts) end};
-start(<>, Stack, Opts) ->
+start(<>, Stack, Opts) ->
{event, start_array, fun() -> array(Rest, [array|Stack], Opts) end};
-start(<>, Stack, Opts) ->
+start(<>, Stack, Opts) ->
string(Rest, Stack, Opts, []);
-start(<<$t/?encoding, Rest/binary>>, Stack, Opts) ->
+start(<<$t/?utfx, Rest/binary>>, Stack, Opts) ->
tr(Rest, Stack, Opts);
-start(<<$f/?encoding, Rest/binary>>, Stack, Opts) ->
+start(<<$f/?utfx, Rest/binary>>, Stack, Opts) ->
fa(Rest, Stack, Opts);
-start(<<$n/?encoding, Rest/binary>>, Stack, Opts) ->
+start(<<$n/?utfx, Rest/binary>>, Stack, Opts) ->
nu(Rest, Stack, Opts);
-start(<>, Stack, Opts) ->
+start(<>, Stack, Opts) ->
negative(Rest, Stack, Opts, "-");
-start(<>, Stack, Opts) ->
+start(<>, Stack, Opts) ->
zero(Rest, Stack, Opts, "0");
-start(<>, Stack, Opts) when ?is_nonzero(S) ->
+start(<>, Stack, Opts) when ?is_nonzero(S) ->
integer(Rest, Stack, Opts, [S]);
-start(<>, Stack, #opts{comments = true} = Opts) ->
+start(<>, Stack, #opts{comments=true}=Opts) ->
maybe_comment(Rest, fun(Resume) -> start(Resume, Stack, Opts) end);
start(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> start(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ start(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-maybe_done(<>, Stack, Opts) when ?is_whitespace(S) ->
+maybe_done(<>, Stack, Opts) when ?is_whitespace(S) ->
maybe_done(Rest, Stack, Opts);
-maybe_done(<>, [object|Stack], Opts) ->
+maybe_done(<>, [object|Stack], Opts) ->
{event, end_object, fun() -> maybe_done(Rest, Stack, Opts) end};
-maybe_done(<>, [array|Stack], Opts) ->
+maybe_done(<>, [array|Stack], Opts) ->
{event, end_array, fun() -> maybe_done(Rest, Stack, Opts) end};
-maybe_done(<>, [object|Stack], Opts) ->
+maybe_done(<>, [object|Stack], Opts) ->
key(Rest, [key|Stack], Opts);
-maybe_done(<>, [array|_] = Stack, Opts) ->
+maybe_done(<>, [array|_] = Stack, Opts) ->
value(Rest, Stack, Opts);
-maybe_done(<>, Stack, #opts{comments = true} = Opts) ->
+maybe_done(<>, Stack, #opts{comments=true}=Opts) ->
maybe_comment(Rest, fun(Resume) -> maybe_done(Resume, Stack, Opts) end);
-maybe_done(Rest, [], #opts{multi_term = true} = Opts) ->
+maybe_done(Rest, [], #opts{multi_term=true}=Opts) ->
{event, end_json, fun() -> start(Rest, [], Opts) end};
maybe_done(Rest, [], Opts) ->
done(Rest, Opts);
maybe_done(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> maybe_done(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ maybe_done(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-done(<>, Opts) when ?is_whitespace(S) ->
+done(<>, Opts) when ?is_whitespace(S) ->
done(Rest, Opts);
-done(<>, #opts{comments = true} = Opts) ->
+done(<>, #opts{comments=true}=Opts) ->
maybe_comment(Rest, fun(Resume) -> done(Resume, Opts) end);
done(<<>>, Opts) ->
- {event, end_json, fun() -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> done(Stream, Opts) end} end};
+ {event, end_json, fun() ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ done(Stream, Opts)
+ end}
+ end};
done(Bin, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> done(<>, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ done(<>, Opts)
+ end}
; false -> {error, badjson}
end.
-object(<>, Stack, Opts) when ?is_whitespace(S) ->
+object(<>, Stack, Opts) when ?is_whitespace(S) ->
object(Rest, Stack, Opts);
-object(<>, Stack, Opts) ->
+object(<>, Stack, Opts) ->
string(Rest, Stack, Opts, []);
-object(<>, [key|Stack], Opts) ->
+object(<>, [key|Stack], Opts) ->
{event, end_object, fun() -> maybe_done(Rest, Stack, Opts) end};
-object(<>, Stack, #opts{comments = true} = Opts) ->
+object(<>, Stack, #opts{comments=true}=Opts) ->
maybe_comment(Rest, fun(Resume) -> object(Resume, Stack, Opts) end);
object(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> object(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ object(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-array(<>, Stack, Opts) when ?is_whitespace(S) ->
+array(<>, Stack, Opts) when ?is_whitespace(S) ->
array(Rest, Stack, Opts);
-array(<>, Stack, Opts) ->
+array(<>, Stack, Opts) ->
string(Rest, Stack, Opts, []);
-array(<<$t/?encoding, Rest/binary>>, Stack, Opts) ->
+array(<<$t/?utfx, Rest/binary>>, Stack, Opts) ->
tr(Rest, Stack, Opts);
-array(<<$f/?encoding, Rest/binary>>, Stack, Opts) ->
+array(<<$f/?utfx, Rest/binary>>, Stack, Opts) ->
fa(Rest, Stack, Opts);
-array(<<$n/?encoding, Rest/binary>>, Stack, Opts) ->
+array(<<$n/?utfx, Rest/binary>>, Stack, Opts) ->
nu(Rest, Stack, Opts);
-array(<>, Stack, Opts) ->
+array(<>, Stack, Opts) ->
negative(Rest, Stack, Opts, "-");
-array(<>, Stack, Opts) ->
+array(<>, Stack, Opts) ->
zero(Rest, Stack, Opts, "0");
-array(<>, Stack, Opts) when ?is_nonzero(S) ->
+array(<>, Stack, Opts) when ?is_nonzero(S) ->
integer(Rest, Stack, Opts, [S]);
-array(<>, Stack, Opts) ->
+array(<>, Stack, Opts) ->
{event, start_object, fun() -> object(Rest, [key|Stack], Opts) end};
-array(<>, Stack, Opts) ->
+array(<>, Stack, Opts) ->
{event, start_array, fun() -> array(Rest, [array|Stack], Opts) end};
-array(<>, [array|Stack], Opts) ->
+array(<>, [array|Stack], Opts) ->
{event, end_array, fun() -> maybe_done(Rest, Stack, Opts) end};
-array(<>, Stack, #opts{comments = true} = Opts) ->
+array(<>, Stack, #opts{comments=true}=Opts) ->
maybe_comment(Rest, fun(Resume) -> array(Resume, Stack, Opts) end);
array(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> array(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ array(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-value(<>, Stack, Opts) when ?is_whitespace(S) ->
+value(<>, Stack, Opts) when ?is_whitespace(S) ->
value(Rest, Stack, Opts);
-value(<>, Stack, Opts) ->
+value(<>, Stack, Opts) ->
string(Rest, Stack, Opts, []);
-value(<<$t/?encoding, Rest/binary>>, Stack, Opts) ->
+value(<<$t/?utfx, Rest/binary>>, Stack, Opts) ->
tr(Rest, Stack, Opts);
-value(<<$f/?encoding, Rest/binary>>, Stack, Opts) ->
+value(<<$f/?utfx, Rest/binary>>, Stack, Opts) ->
fa(Rest, Stack, Opts);
-value(<<$n/?encoding, Rest/binary>>, Stack, Opts) ->
+value(<<$n/?utfx, Rest/binary>>, Stack, Opts) ->
nu(Rest, Stack, Opts);
-value(<>, Stack, Opts) ->
+value(<>, Stack, Opts) ->
negative(Rest, Stack, Opts, "-");
-value(<>, Stack, Opts) ->
+value(<>, Stack, Opts) ->
zero(Rest, Stack, Opts, "0");
-value(<>, Stack, Opts) when ?is_nonzero(S) ->
+value(<>, Stack, Opts) when ?is_nonzero(S) ->
integer(Rest, Stack, Opts, [S]);
-value(<>, Stack, Opts) ->
+value(<>, Stack, Opts) ->
{event, start_object, fun() -> object(Rest, [key|Stack], Opts) end};
-value(<>, Stack, Opts) ->
+value(<>, Stack, Opts) ->
{event, start_array, fun() -> array(Rest, [array|Stack], Opts) end};
-value(<>, Stack, #opts{comments = true} = Opts) ->
+value(<>, Stack, #opts{comments=true}=Opts) ->
maybe_comment(Rest, fun(Resume) -> value(Resume, Stack, Opts) end);
value(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> value(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ value(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-colon(<>, Stack, Opts) when ?is_whitespace(S) ->
+colon(<>, Stack, Opts) when ?is_whitespace(S) ->
colon(Rest, Stack, Opts);
-colon(<>, [key|Stack], Opts) ->
+colon(<>, [key|Stack], Opts) ->
value(Rest, [object|Stack], Opts);
-colon(<>, Stack, #opts{comments = true} = Opts) ->
+colon(<>, Stack, #opts{comments=true}=Opts) ->
maybe_comment(Rest, fun(Resume) -> colon(Resume, Stack, Opts) end);
colon(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> colon(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ colon(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-key(<>, Stack, Opts) when ?is_whitespace(S) ->
+key(<>, Stack, Opts) when ?is_whitespace(S) ->
key(Rest, Stack, Opts);
-key(<>, Stack, Opts) ->
+key(<>, Stack, Opts) ->
string(Rest, Stack, Opts, []);
-key(<>, Stack, #opts{comments = true} = Opts) ->
+key(<>, Stack, #opts{comments=true}=Opts) ->
maybe_comment(Rest, fun(Resume) -> key(Resume, Stack, Opts) end);
key(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> key(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ key(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-%% string has an additional parameter, an accumulator (Acc) used to hold the intermediate
-%% representation of the string being parsed. using a list of integers representing
-%% unicode codepoints is faster than constructing binaries, many of which will be
-%% converted back to lists by the user anyways
-%% string uses partial_utf/1 to cease parsing when invalid encodings are encountered
-%% rather than just checking remaining binary size like other states
-string(<>, [key|_] = Stack, Opts, Acc) ->
+%% string has an additional parameter, an accumulator (Acc) used to hold the
+%% intermediate representation of the string being parsed. using a list of
+%% integers representing unicode codepoints is faster than constructing
+%% binaries, many of which will be converted back to lists by the user anyways
+%% string uses partial_utf/1 to cease parsing when invalid encodings are
+%% encountered rather than just checking remaining binary size like other
+%% states
+string(<>, [key|_] = Stack, Opts, Acc) ->
{event, {key, lists:reverse(Acc)}, fun() -> colon(Rest, Stack, Opts) end};
-string(<>, Stack, Opts, Acc) ->
- {event, {string, lists:reverse(Acc)}, fun() -> maybe_done(Rest, Stack, Opts) end};
-string(<>, Stack, Opts, Acc) ->
+string(<>, Stack, Opts, Acc) ->
+ {event, {string, lists:reverse(Acc)}, fun() ->
+ maybe_done(Rest, Stack, Opts)
+ end};
+string(<>, Stack, Opts, Acc) ->
escape(Rest, Stack, Opts, Acc);
-string(<>, Stack, Opts, Acc) when ?is_noncontrol(S) ->
+string(<>, Stack, Opts, Acc) when ?is_noncontrol(S) ->
string(Rest, Stack, Opts, [S] ++ Acc);
string(Bin, Stack, Opts, Acc) ->
case partial_utf(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> string(<>, Stack, Opts, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ string(<>, Stack, Opts, Acc)
+ end}
; false -> {error, badjson}
end.
@@ -356,19 +410,24 @@ partial_utf(_) -> false.
-endif.
-ifdef(utf16).
-partial_utf(<<>>) -> true;
-%% this case is not strictly true, there are single bytes that should be rejected, but
-%% they're rare enough they can be ignored
-partial_utf(<<_X>>) -> true;
-partial_utf(<>) when X >= 16#d8, X =< 16#df -> true;
-partial_utf(<>) when X >= 16#d8, X =< 16#df, Z >= 16#dc, Z =< 16#df -> true;
-partial_utf(_) -> false.
+partial_utf(<<>>) ->
+ true;
+%% this case is not strictly true, there are single bytes that should be
+%% rejected, but they're rare enough they can be ignored
+partial_utf(<<_X>>) ->
+ true;
+partial_utf(<>) when X >= 16#d8, X =< 16#df ->
+ true;
+partial_utf(<>) when X >= 16#d8, X =< 16#df, Z >= 16#dc, Z =< 16#df ->
+ true;
+partial_utf(_) ->
+ false.
-endif.
-ifdef(utf16le).
partial_utf(<<>>) -> true;
-%% this case is not strictly true, there are single bytes that should be rejected, but
-%% they're rare enough they can be ignored
+%% this case is not strictly true, there are single bytes that should be
+%% rejected, but they're rare enough they can be ignored
partial_utf(<<_X>>) -> true;
partial_utf(<<_Y, X>>) when X >= 16#d8, X =< 16#df -> true;
partial_utf(<<_Y, X, _Z>>) when X >= 16#d8, X =< 16#df -> true;
@@ -386,82 +445,108 @@ partial_utf(_) -> true.
-endif.
-%% 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/?encoding, Rest/binary>>, 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/?encoding, Rest/binary>>, Stack, Opts, Acc) ->
+escape(<<$f/?utfx, Rest/binary>>, Stack, Opts, Acc) ->
string(Rest, Stack, Opts, "\f" ++ Acc);
-escape(<<$n/?encoding, Rest/binary>>, Stack, Opts, Acc) ->
+escape(<<$n/?utfx, Rest/binary>>, Stack, Opts, Acc) ->
string(Rest, Stack, Opts, "\n" ++ Acc);
-escape(<<$r/?encoding, Rest/binary>>, Stack, Opts, Acc) ->
+escape(<<$r/?utfx, Rest/binary>>, Stack, Opts, Acc) ->
string(Rest, Stack, Opts, "\r" ++ Acc);
-escape(<<$t/?encoding, Rest/binary>>, Stack, Opts, Acc) ->
+escape(<<$t/?utfx, Rest/binary>>, Stack, Opts, Acc) ->
string(Rest, Stack, Opts, "\t" ++ Acc);
-escape(<<$u/?encoding, Rest/binary>>, Stack, Opts, Acc) ->
+escape(<<$u/?utfx, Rest/binary>>, Stack, Opts, Acc) ->
escaped_unicode(Rest, Stack, Opts, Acc, []);
-escape(<>, Stack, Opts, Acc)
+escape(<>, Stack, Opts, Acc)
when S =:= ?quote; S =:= ?solidus; S =:= ?rsolidus ->
string(Rest, Stack, Opts, [S] ++ Acc);
escape(Bin, Stack, Opts, Acc) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> escape(<>, Stack, Opts, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ escape(<>, Stack, Opts, Acc)
+ end}
; false -> {error, badjson}
end.
-%% this code is ugly and unfortunate, but so is json's handling of escaped unicode
-%% codepoint sequences. if the ascii option is present, the sequence is converted
-%% to a codepoint and inserted into the string if it represents an ascii value. if
-%% the codepoint option is present the sequence is converted and inserted as long
-%% as it represents a valid unicode codepoint. this means non-characters
-%% representable in 16 bits are not converted (the utf16 surrogates and the two
-%% special non-characters). any other option and no conversion is done
-escaped_unicode(<>,
+%% this code is ugly and unfortunate, but so is json's handling of escaped
+%% unicode codepoint sequences. if the ascii option is present, the sequence
+%% is converted to a codepoint and inserted into the string if it represents
+%% an ascii value. if the codepoint option is present the sequence is
+%% converted and inserted as long as it represents a valid unicode codepoint.
+%% this means non-characters representable in 16 bits are not converted (the
+%5 utf16 surrogates and the two special non-characters). any other option and
+%% no conversion is done
+escaped_unicode(<>,
Stack,
- #opts{escaped_unicode = ascii} = Opts,
+ #opts{escaped_unicode=ascii}=Opts,
String,
[C, B, A])
- when ?is_hex(D) ->
+ when ?is_hex(D) ->
case erlang:list_to_integer([A, B, C, D], 16) of
X when X < 128 ->
string(Rest, Stack, Opts, [X] ++ String)
; _ ->
string(Rest, Stack, Opts, [D, C, B, A, $u, ?rsolidus] ++ String)
end;
-escaped_unicode(<>,
+escaped_unicode(<>,
Stack,
- #opts{escaped_unicode = codepoint} = Opts,
+ #opts{escaped_unicode=codepoint}=Opts,
String,
[C, B, A])
- when ?is_hex(D) ->
+ when ?is_hex(D) ->
case erlang:list_to_integer([A, B, C, D], 16) of
X when X >= 16#dc00, X =< 16#dfff ->
case check_acc_for_surrogate(String) of
false ->
- string(Rest, Stack, Opts, [D, C, B, A, $u, ?rsolidus] ++ String)
+ string(Rest,
+ Stack,
+ Opts,
+ [D, C, B, A, $u, ?rsolidus] ++ String
+ )
; {Y, NewString} ->
- string(Rest, Stack, Opts, [surrogate_to_codepoint(Y, X)] ++ NewString)
+ string(Rest,
+ Stack,
+ Opts,
+ [surrogate_to_codepoint(Y, X)] ++ NewString
+ )
end
; X when X < 16#d800; X > 16#dfff, X < 16#fffe ->
string(Rest, Stack, Opts, [X] ++ String)
; _ ->
string(Rest, Stack, Opts, [D, C, B, A, $u, ?rsolidus] ++ String)
end;
-escaped_unicode(<>, Stack, Opts, String, [C, B, A]) when ?is_hex(D) ->
+escaped_unicode(<>, Stack, Opts, String, [C, B, A])
+ when ?is_hex(D) ->
string(Rest, Stack, Opts, [D, C, B, A, $u, ?rsolidus] ++ String);
-escaped_unicode(<>, Stack, Opts, String, Acc) when ?is_hex(S) ->
+escaped_unicode(<>, Stack, Opts, String, Acc)
+ when ?is_hex(S) ->
escaped_unicode(Rest, Stack, Opts, String, [S] ++ Acc);
escaped_unicode(Bin, Stack, Opts, String, Acc) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> escaped_unicode(<>, Stack, Opts, String, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ escaped_unicode(<>,
+ Stack,
+ Opts,
+ String,
+ Acc
+ )
+ end}
; false -> {error, badjson}
end.
-%% upon encountering a low pair json/hex encoded value, check to see if there's a high
-%% value already in the accumulator
+%% upon encountering a low pair json/hex encoded value, check to see if there's
+%% a high value already in the accumulator
check_acc_for_surrogate([D, C, B, A, $u, ?rsolidus|Rest])
when ?is_hex(D), ?is_hex(C), ?is_hex(B), ?is_hex(A) ->
case erlang:list_to_integer([A, B, C, D], 16) of
@@ -481,34 +566,45 @@ surrogate_to_codepoint(High, Low) ->
%% like strings, numbers are collected in an intermediate accumulator before
%% being emitted to the callback handler
-negative(<<$0/?encoding, Rest/binary>>, Stack, Opts, Acc) ->
+negative(<<$0/?utfx, Rest/binary>>, Stack, Opts, Acc) ->
zero(Rest, Stack, Opts, "0" ++ Acc);
-negative(<>, Stack, Opts, Acc) when ?is_nonzero(S) ->
+negative(<>, Stack, Opts, Acc) when ?is_nonzero(S) ->
integer(Rest, Stack, Opts, [S] ++ Acc);
negative(Bin, Stack, Opts, Acc) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> negative(<>, Stack, Opts, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ negative(<>, Stack, Opts, Acc)
+ end}
; false -> {error, badjson}
end.
-zero(<>, [object|Stack], Opts, Acc) ->
+zero(<>, [object|Stack], Opts, Acc) ->
{event, {integer, lists:reverse(Acc)}, fun() ->
{event, end_object, fun() -> maybe_done(Rest, Stack, Opts) end}
end};
-zero(<>, [array|Stack], Opts, Acc) ->
+zero(<>, [array|Stack], Opts, Acc) ->
{event, {integer, lists:reverse(Acc)}, fun() ->
{event, end_array, fun() -> maybe_done(Rest, Stack, Opts) end}
end};
-zero(<>, [object|Stack], Opts, Acc) ->
- {event, {integer, lists:reverse(Acc)}, fun() -> key(Rest, [key|Stack], Opts) end};
-zero(<>, [array|_] = Stack, Opts, Acc) ->
- {event, {integer, lists:reverse(Acc)}, fun() -> value(Rest, Stack, Opts) end};
-zero(<>, Stack, Opts, Acc) ->
+zero(<>, [object|Stack], Opts, Acc) ->
+ {event, {integer, lists:reverse(Acc)}, fun() ->
+ key(Rest, [key|Stack], Opts)
+ end};
+zero(<>, [array|_] = Stack, Opts, Acc) ->
+ {event, {integer, lists:reverse(Acc)}, fun() ->
+ value(Rest, Stack, Opts)
+ end};
+zero(<>, Stack, Opts, Acc) ->
initial_decimal(Rest, Stack, Opts, [?decimalpoint] ++ Acc);
-zero(<>, Stack, Opts, Acc) when ?is_whitespace(S) ->
- {event, {integer, lists:reverse(Acc)}, fun() -> maybe_done(Rest, Stack, Opts) end};
-zero(<>, Stack, #opts{comments = true} = Opts, Acc) ->
+zero(<>, Stack, Opts, Acc) when ?is_whitespace(S) ->
+ {event, {integer, lists:reverse(Acc)}, fun() ->
+ maybe_done(Rest, Stack, Opts)
+ end};
+zero(<>, Stack, #opts{comments=true}=Opts, Acc) ->
maybe_comment(Rest, fun(Resume) -> zero(Resume, Stack, Opts, Acc) end);
zero(<<>>, [], Opts, Acc) ->
{incomplete, fun(end_stream) ->
@@ -519,36 +615,51 @@ zero(<<>>, [], Opts, Acc) ->
end};
zero(Bin, Stack, Opts, Acc) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> zero(<>, Stack, Opts, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ zero(<>, Stack, Opts, Acc)
+ end}
; false -> {error, badjson}
end.
-integer(<>, Stack, Opts, Acc) when ?is_nonzero(S) ->
+integer(<>, Stack, Opts, Acc) when ?is_nonzero(S) ->
integer(Rest, Stack, Opts, [S] ++ Acc);
-integer(<>, [object|Stack], Opts, Acc) ->
+integer(<>, [object|Stack], Opts, Acc) ->
{event, {integer, lists:reverse(Acc)}, fun() ->
{event, end_object, fun() -> maybe_done(Rest, Stack, Opts) end}
end};
-integer(<>, [array|Stack], Opts, Acc) ->
+integer(<>, [array|Stack], Opts, Acc) ->
{event, {integer, lists:reverse(Acc)}, fun() ->
{event, end_array, fun() -> maybe_done(Rest, Stack, Opts) end}
end};
-integer(<>, [object|Stack], Opts, Acc) ->
- {event, {integer, lists:reverse(Acc)}, fun() -> key(Rest, [key|Stack], Opts) end};
-integer(<>, [array|_] = Stack, Opts, Acc) ->
- {event, {integer, lists:reverse(Acc)}, fun() -> value(Rest, Stack, Opts) end};
-integer(<>, Stack, Opts, Acc) ->
+integer(<>, [object|Stack], Opts, Acc) ->
+ {event, {integer, lists:reverse(Acc)}, fun() ->
+ key(Rest, [key|Stack], Opts)
+ end};
+integer(<>, [array|_] = Stack, Opts, Acc) ->
+ {event, {integer, lists:reverse(Acc)}, fun() ->
+ value(Rest, Stack, Opts)
+ end};
+integer(<>, Stack, Opts, Acc) ->
initial_decimal(Rest, Stack, Opts, [?decimalpoint] ++ Acc);
-integer(<>, Stack, Opts, Acc) ->
+integer(<>, Stack, Opts, Acc) ->
integer(Rest, Stack, Opts, [?zero] ++ Acc);
-integer(<<$e/?encoding, Rest/binary>>, Stack, Opts, Acc) ->
+integer(<<$e/?utfx, Rest/binary>>, Stack, Opts, Acc) ->
e(Rest, Stack, Opts, "e0." ++ Acc);
-integer(<<$E/?encoding, Rest/binary>>, Stack, Opts, Acc) ->
+integer(<<$E/?utfx, Rest/binary>>, Stack, Opts, Acc) ->
e(Rest, Stack, Opts, "e0." ++ Acc);
-integer(<>, Stack, Opts, Acc) when ?is_whitespace(S) ->
- {event, {integer, lists:reverse(Acc)}, fun() -> maybe_done(Rest, Stack, Opts) end};
-integer(<>, Stack, #opts{comments = true} = Opts, Acc) ->
+integer(<>, Stack, Opts, Acc) when ?is_whitespace(S) ->
+ {event, {integer, lists:reverse(Acc)}, fun() ->
+ maybe_done(Rest, Stack, Opts)
+ end};
+integer(<>,
+ Stack,
+ #opts{comments=true}=Opts,
+ Acc
+) ->
maybe_comment(Rest, fun(Resume) -> integer(Resume, Stack, Opts, Acc) end);
integer(<<>>, [], Opts, Acc) ->
{incomplete, fun(end_stream) ->
@@ -559,45 +670,70 @@ integer(<<>>, [], Opts, Acc) ->
end};
integer(Bin, Stack, Opts, Acc) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> integer(<>, Stack, Opts, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ integer(<>, Stack, Opts, Acc)
+ end}
; false -> {error, badjson}
end.
-initial_decimal(<>, Stack, Opts, Acc) when ?is_nonzero(S) ->
+initial_decimal(<>, Stack, Opts, Acc)
+ when ?is_nonzero(S) ->
decimal(Rest, Stack, Opts, [S] ++ Acc);
-initial_decimal(<>, Stack, Opts, Acc) ->
+initial_decimal(<>, Stack, Opts, Acc) ->
decimal(Rest, Stack, Opts, [?zero] ++ Acc);
-initial_decimal(Bin, Stack, Opts, Acc) ->
+initial_decimal(Bin, Stack, Opts, Acc) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> initial_decimal(<>, Stack, Opts, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ initial_decimal(<>,
+ Stack,
+ Opts,
+ Acc
+ )
+ end}
; false -> {error, badjson}
end.
-decimal(<>, Stack, Opts, Acc) when ?is_nonzero(S) ->
+decimal(<>, Stack, Opts, Acc) when ?is_nonzero(S) ->
decimal(Rest, Stack, Opts, [S] ++ Acc);
-decimal(<>, [object|Stack], Opts, Acc) ->
+decimal(<>, [object|Stack], Opts, Acc) ->
{event, {float, lists:reverse(Acc)}, fun() ->
{event, end_object, fun() -> maybe_done(Rest, Stack, Opts) end}
end};
-decimal(<>, [array|Stack], Opts, Acc) ->
+decimal(<>, [array|Stack], Opts, Acc) ->
{event, {float, lists:reverse(Acc)}, fun() ->
{event, end_array, fun() -> maybe_done(Rest, Stack, Opts) end}
end};
-decimal(<>, [object|Stack], Opts, Acc) ->
- {event, {float, lists:reverse(Acc)}, fun() -> key(Rest, [key|Stack], Opts) end};
-decimal(<>, [array|_] = Stack, Opts, Acc) ->
- {event, {float, lists:reverse(Acc)}, fun() -> value(Rest, Stack, Opts) end};
-decimal(<>, Stack, Opts, Acc) ->
+decimal(<>, [object|Stack], Opts, Acc) ->
+ {event, {float, lists:reverse(Acc)}, fun() ->
+ key(Rest, [key|Stack], Opts)
+ end};
+decimal(<>, [array|_] = Stack, Opts, Acc) ->
+ {event, {float, lists:reverse(Acc)}, fun() ->
+ value(Rest, Stack, Opts)
+ end};
+decimal(<>, Stack, Opts, Acc) ->
decimal(Rest, Stack, Opts, [?zero] ++ Acc);
-decimal(<<$e/?encoding, Rest/binary>>, Stack, Opts, Acc) ->
+decimal(<<$e/?utfx, Rest/binary>>, Stack, Opts, Acc) ->
e(Rest, Stack, Opts, "e" ++ Acc);
-decimal(<<$E/?encoding, Rest/binary>>, Stack, Opts, Acc) ->
+decimal(<<$E/?utfx, Rest/binary>>, Stack, Opts, Acc) ->
e(Rest, Stack, Opts, "e" ++ Acc);
-decimal(<>, Stack, Opts, Acc) when ?is_whitespace(S) ->
- {event, {float, lists:reverse(Acc)}, fun() -> maybe_done(Rest, Stack, Opts) end};
-decimal(<>, Stack, #opts{comments = true} = Opts, Acc) ->
+decimal(<>, Stack, Opts, Acc) when ?is_whitespace(S) ->
+ {event, {float, lists:reverse(Acc)}, fun() ->
+ maybe_done(Rest, Stack, Opts)
+ end};
+decimal(<>,
+ Stack,
+ #opts{comments=true}=Opts,
+ Acc
+) ->
maybe_comment(Rest, fun(Resume) -> decimal(Resume, Stack, Opts, Acc) end);
decimal(<<>>, [], Opts, Acc) ->
{incomplete, fun(end_stream) ->
@@ -608,50 +744,74 @@ decimal(<<>>, [], Opts, Acc) ->
end};
decimal(Bin, Stack, Opts, Acc) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> decimal(<>, Stack, Opts, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ decimal(<>, Stack, Opts, Acc)
+ end}
; false -> {error, badjson}
end.
-e(<>, Stack, Opts, Acc) when S =:= ?zero; ?is_nonzero(S) ->
+e(<>, Stack, Opts, Acc)
+ when S =:= ?zero; ?is_nonzero(S) ->
exp(Rest, Stack, Opts, [S] ++ Acc);
-e(<>, Stack, Opts, Acc) when S =:= ?positive; S =:= ?negative ->
+e(<>, Stack, Opts, Acc)
+ when S =:= ?positive; S =:= ?negative ->
ex(Rest, Stack, Opts, [S] ++ Acc);
e(Bin, Stack, Opts, Acc) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> e(<>, Stack, Opts, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ e(<>, Stack, Opts, Acc)
+ end}
; false -> {error, badjson}
end.
-ex(<>, Stack, Opts, Acc) when S =:= ?zero; ?is_nonzero(S) ->
+ex(<>, Stack, Opts, Acc)
+ when S =:= ?zero; ?is_nonzero(S) ->
exp(Rest, Stack, Opts, [S] ++ Acc);
ex(Bin, Stack, Opts, Acc) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> ex(<>, Stack, Opts, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ ex(<>, Stack, Opts, Acc)
+ end}
; false -> {error, badjson}
end.
-exp(<>, Stack, Opts, Acc) when ?is_nonzero(S) ->
+exp(<>, Stack, Opts, Acc) when ?is_nonzero(S) ->
exp(Rest, Stack, Opts, [S] ++ Acc);
-exp(<>, [object|Stack], Opts, Acc) ->
+exp(<>, [object|Stack], Opts, Acc) ->
{event, {float, lists:reverse(Acc)}, fun() ->
{event, end_object, fun() -> maybe_done(Rest, Stack, Opts) end}
end};
-exp(<>, [array|Stack], Opts, Acc) ->
+exp(<>, [array|Stack], Opts, Acc) ->
{event, {float, lists:reverse(Acc)}, fun() ->
{event, end_array, fun() -> maybe_done(Rest, Stack, Opts) end}
end};
-exp(<>, [object|Stack], Opts, Acc) ->
- {event, {float, lists:reverse(Acc)}, fun() -> key(Rest, [key|Stack], Opts) end};
-exp(<>, [array|_] = Stack, Opts, Acc) ->
- {event, {float, lists:reverse(Acc)}, fun() -> value(Rest, Stack, Opts) end};
-exp(<>, Stack, Opts, Acc) ->
+exp(<>, [object|Stack], Opts, Acc) ->
+ {event, {float, lists:reverse(Acc)}, fun() ->
+ key(Rest, [key|Stack], Opts)
+ end};
+exp(<>, [array|_] = Stack, Opts, Acc) ->
+ {event, {float, lists:reverse(Acc)}, fun() ->
+ value(Rest, Stack, Opts)
+ end};
+exp(<>, Stack, Opts, Acc) ->
exp(Rest, Stack, Opts, [?zero] ++ Acc);
-exp(<>, Stack, Opts, Acc) when ?is_whitespace(S) ->
- {event, {float, lists:reverse(Acc)}, fun() -> maybe_done(Rest, Stack, Opts) end};
-exp(<>, Stack, #opts{comments = true} = Opts, Acc) ->
+exp(<>, Stack, Opts, Acc) when ?is_whitespace(S) ->
+ {event, {float, lists:reverse(Acc)}, fun() ->
+ maybe_done(Rest, Stack, Opts)
+ end};
+exp(<>, Stack, #opts{comments=true}=Opts, Acc) ->
maybe_comment(Rest, fun(Resume) -> exp(Resume, Stack, Opts, Acc) end);
exp(<<>>, [], Opts, Acc) ->
{incomplete, fun(end_stream) ->
@@ -662,132 +822,202 @@ exp(<<>>, [], Opts, Acc) ->
end};
exp(Bin, Stack, Opts, Acc) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> exp(<>, Stack, Opts, Acc) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ exp(<>, Stack, Opts, Acc)
+ end}
; false -> {error, badjson}
end.
-tr(<<$r/?encoding, Rest/binary>>, Stack, Opts) ->
+tr(<<$r/?utfx, Rest/binary>>, Stack, Opts) ->
tru(Rest, Stack, Opts);
tr(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> tr(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ tr(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-tru(<<$u/?encoding, Rest/binary>>, Stack, Opts) ->
+tru(<<$u/?utfx, Rest/binary>>, Stack, Opts) ->
true(Rest, Stack, Opts);
tru(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> tru(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ tru(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-true(<<$e/?encoding, Rest/binary>>, Stack, Opts) ->
+true(<<$e/?utfx, Rest/binary>>, Stack, Opts) ->
{event, {literal, true}, fun() -> maybe_done(Rest, Stack, Opts) end};
true(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> true(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ true(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-fa(<<$a/?encoding, Rest/binary>>, Stack, Opts) ->
+fa(<<$a/?utfx, Rest/binary>>, Stack, Opts) ->
fal(Rest, Stack, Opts);
fa(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> fa(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ fa(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-fal(<<$l/?encoding, Rest/binary>>, Stack, Opts) ->
+fal(<<$l/?utfx, Rest/binary>>, Stack, Opts) ->
fals(Rest, Stack, Opts);
fal(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> fal(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ fal(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-fals(<<$s/?encoding, Rest/binary>>, Stack, Opts) ->
+fals(<<$s/?utfx, Rest/binary>>, Stack, Opts) ->
false(Rest, Stack, Opts);
fals(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> fals(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ fals(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-false(<<$e/?encoding, Rest/binary>>, Stack, Opts) ->
+false(<<$e/?utfx, Rest/binary>>, Stack, Opts) ->
{event, {literal, false}, fun() -> maybe_done(Rest, Stack, Opts) end};
false(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> false(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ false(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-nu(<<$u/?encoding, Rest/binary>>, Stack, Opts) ->
+nu(<<$u/?utfx, Rest/binary>>, Stack, Opts) ->
nul(Rest, Stack, Opts);
nu(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> nu(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ nu(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-nul(<<$l/?encoding, Rest/binary>>, Stack, Opts) ->
+nul(<<$l/?utfx, Rest/binary>>, Stack, Opts) ->
null(Rest, Stack, Opts);
nul(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> nul(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ nul(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
-null(<<$l/?encoding, Rest/binary>>, Stack, Opts) ->
+null(<<$l/?utfx, Rest/binary>>, Stack, Opts) ->
{event, {literal, null}, fun() -> maybe_done(Rest, Stack, Opts) end};
null(Bin, Stack, Opts) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> null(<>, Stack, Opts) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ null(<>, Stack, Opts)
+ end}
; false -> {error, badjson}
end.
%% comments are c style, ex: /* blah blah */
-%% any unicode character is valid in a comment except the */ sequence which ends
-%% the comment. they're implemented as a closure called when the comment ends that
-%% returns execution to the point where the comment began. comments are not
-%% reported in any way, simply parsed.
-maybe_comment(<>, Resume) ->
+%% any unicode character is valid in a comment except the */ sequence which
+%% ends the comment. they're implemented as a closure called when the comment
+%% ends that returns execution to the point where the comment began. comments
+%% are not reported in any way, simply parsed.
+maybe_comment(<>, Resume) ->
comment(Rest, Resume);
maybe_comment(Bin, Resume) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> maybe_comment(<>, Resume) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ maybe_comment(<>, Resume)
+ end}
; false -> {error, badjson}
end.
-comment(<>, Resume) ->
+comment(<>, Resume) ->
maybe_comment_done(Rest, Resume);
-comment(<<_/?encoding, Rest/binary>>, Resume) ->
+comment(<<_/?utfx, Rest/binary>>, Resume) ->
comment(Rest, Resume);
comment(Bin, Resume) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> comment(<>, Resume) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ comment(<>, Resume)
+ end}
; false -> {error, badjson}
end.
-maybe_comment_done(<>, Resume) ->
+maybe_comment_done(<>, Resume) ->
Resume(Rest);
-maybe_comment_done(<<_/?encoding, Rest/binary>>, Resume) ->
+maybe_comment_done(<<_/?utfx, Rest/binary>>, Resume) ->
comment(Rest, Resume);
maybe_comment_done(Bin, Resume) ->
case ?partial_codepoint(Bin) of
- true -> {incomplete, fun(end_stream) -> {error, badjson}; (Stream) -> maybe_comment_done(<>, Resume) end}
+ true ->
+ {incomplete, fun(end_stream) ->
+ {error, badjson}
+ ; (Stream) ->
+ maybe_comment_done(<>, Resume)
+ end}
; false -> {error, badjson}
end.
\ No newline at end of file
diff --git a/src/jsx.erl b/src/jsx.erl
index 3fb380e..649ef58 100644
--- a/src/jsx.erl
+++ b/src/jsx.erl
@@ -26,7 +26,6 @@
%% @version really, really beta
%% @doc this module defines the interface to the jsx json parsing library
-
-module(jsx).
@@ -70,7 +69,11 @@
%% | {multi_term, true | false}
%% | {encoding, auto | supported_utf()}.
-%% @type supported_utf() = utf8 | utf16 | {utf16, little} | utf32 | {utf32, little}.
+%% @type supported_utf() = utf8
+%% | utf16
+%% | {utf16, little}
+%% | utf32
+%% | {utf32, little}.
%% @type eep0018() = eep0018_object() | eep0018_array().
@@ -79,7 +82,13 @@
%% @type eep0018_key() = binary() | atom().
-%% @type eep0018_term() = eep0018_array() | eep0018_object() | eep0018_string() | eep0018_number() | true | false | null.
+%% @type eep0018_term() = eep0018_array()
+%% | eep0018_object()
+%% | eep0018_string()
+%% | eep0018_number()
+%% | true
+%% | false
+%% | null.
%% @type eep0018_string() = binary().
@@ -127,7 +136,8 @@ parser() ->
%% @spec parser(Opts::jsx_opts()) -> jsx_parser()
%% @doc
-%% produces a function which takes a binary which may or may not represent an encoded json document and returns a generator
+%% produces a function which takes a binary which may or may not represent an
+%% encoded json document and returns a generator
%%
%% options:
%%
@@ -137,17 +147,23 @@ parser() ->
%% false
%%
%% {encoded_unicode, ascii | codepoint | none}
-%% if a \uXXXX escape sequence is encountered within a key or string,
-%% this option controls how it is interpreted. none makes no attempt
-%% to interpret the value, leaving it unconverted. ascii will convert
-%% any value that falls within the ascii range. codepoint will convert
-%% any value that is a valid unicode codepoint. note that unicode
-%% non-characters (including badly formed surrogates) will never be
-%% converted. codepoint is the default
+%% if a \uXXXX escape sequence is encountered within a key or
+%% string, this option controls how it is interpreted. none makes no
+%% attempt to interpret the value, leaving it unconverted. ascii will
+%% convert any value that falls within the ascii range. codepoint will
+%% convert any value that is a valid unicode codepoint. note that
+%% unicode non-characters (including badly formed surrogates) will
+%% never be converted. codepoint is the default
%%
-%% {encoding, auto | utf8 | utf16 | {utf16, little} | utf32 | {utf32, little} }
-%% attempt to parse the binary using the specified encoding. auto will
-%% auto detect any supported encoding and is the default
+%% {encoding, auto
+%% | utf8
+%% | utf16
+%% | {utf16, little}
+%% | utf32
+%% | {utf32, little}
+%% }
+%% attempt to parse the binary using the specified encoding. auto
+%% will auto detect any supported encoding and is the default
%%
%% {multi_term, true | false}
%% usually, documents will be parsed in full before the end_json
@@ -183,25 +199,32 @@ json_to_term(JSON) ->
%% options:
%%
%% - {strict, true | false}
-%%
by default, attempting to convert unwrapped json values (numbers, strings and
-%% the atoms true, false and null) result in a badarg exception. if strict equals
-%% false, these are instead decoded to their equivalent eep0018 value. default is
-%% false
+%% by default, attempting to convert unwrapped json values (numbers,
+%% strings and the atoms true, false and null) result in a badarg
+%% exception. if strict equals false, these are instead decoded to
+%% their equivalent eep0018 value. default is false
%%
-%% note that there is a problem of ambiguity when parsing unwrapped json
-%% numbers that requires special handling
+%% note that there is a problem of ambiguity when parsing unwrapped
+%% json numbers that requires special handling
%%
-%% an unwrapped json number has no unambiguous end marker like a json object,
-%% array or string. `1', `12' and `123' may all represent either a complete json
-%% number or just the beginning of one. in this case, the parser will always
-%% return `{incomplete, More}' rather than potentially terminate before input
-%% is exhausted. to force termination, `More/1' may be called with the atom
-%% `end_stream' as it's argument. note also that numbers followed by whitespace
-%% will be parsed correctly
+%% an unwrapped json number has no unambiguous end marker like a
+%% json object, array or string. `1', `12' and `123' may all represent
+%% either a complete json number or just the beginning of one. in this
+%% case, the parser will always return `{incomplete, More}' rather than
+%% potentially terminate before input is exhausted. to force
+%% termination, `More/1' may be called with the atom `end_stream' as
+%% it's argument. note also that numbers followed by whitespace will be
+%% parsed correctly
%%
-%% {encoding, auto | utf8 | utf16 | {utf16, little} | utf32 | {utf32, little} }
-%% assume the binary is encoded using the specified binary. default is auto, which
-%% attempts to autodetect the encoding
+%% {encoding, auto
+%% | utf8
+%% | utf16
+%% | {utf16, little}
+%% | utf32
+%% | {utf32, little}
+%% }
+%% assume the binary is encoded using the specified binary. default
+%% is auto, which attempts to autodetect the encoding
%%
%% {comments, true | false}
%% if true, json documents that contain c style (/* ... */) comments
@@ -230,29 +253,38 @@ term_to_json(JSON) ->
%% @spec term_to_json(JSON::eep0018(), Opts::encoder_opts()) -> binary()
%% @doc
-%% takes the erlang representation of a json object (as defined in eep0018) and returns a (binary encoded) json string
+%% takes the erlang representation of a json object (as defined in eep0018) and
+%% returns a (binary encoded) json string
%%
%% options:
%%
%% - {strict, true | false}
%%
by default, attempting to convert unwrapped json values (numbers,
-%% strings and the atoms true, false and null) result in a badarg exception.
-%% if strict equals false, these are instead json encoded. default is false
+%% strings and the atoms true, false and null) result in a badarg
+%% exception. if strict equals false, these are instead json encoded.
+%% default is false
%%
-%% {encoding, utf8 | utf16 | {utf16, little} | utf32 | {utf32, little} }
+%% {encoding, utf8
+%% | utf16
+%% | {utf16, little}
+%% | utf32
+%% | {utf32, little}
+%% }
%% the encoding of the resulting binary. default is utf8
%%
%% space
%% space is equivalent to {space, 1}
%%
%% {space, N}
-%% place N spaces after each colon and comma in the resulting binary. default is zero
+%% place N spaces after each colon and comma in the resulting
+%% binary. default is zero
%%
%% indent
%% indent is equivalent to {indent, 1}
%%
%% {indent, N}
-%% indent each 'level' of the json structure by N spaces. default is zero
+%% indent each 'level' of the json structure by N spaces. default is
+%% zero
%%
%% @end
@@ -273,13 +305,19 @@ is_json(JSON) ->
%% options:
%%
%% - {strict, true | false}
-%%
by default, unwrapped json values (numbers, strings and the atoms
-%% true, false and null) return false. if strict equals true, is_json
-%% returns true. default is false
+%% by default, unwrapped json values (numbers, strings and the
+%% atoms true, false and null) return false. if strict equals true,
+%% is_json returns true. default is false
%%
-%% - {encoding, auto | utf8 | utf16 | {utf16, little} | utf32 | {utf32, little} }
-%%
assume the binary is encoded using the specified binary. default is auto,
-%% which attempts to autodetect the encoding
+%% - {encoding, auto
+%% | utf8
+%% | utf16
+%% | {utf16, little}
+%% | utf32
+%% | {utf32, little}
+%% }
+%%
assume the binary is encoded using the specified binary. default
+%% is auto, which attempts to autodetect the encoding
%%
%% - {comments, true | false}
%%
if true, json documents that contain c style (/* ... */) comments
@@ -300,20 +338,32 @@ format(JSON) ->
%% @spec format(JSON::binary(), Opts::format_opts()) -> binary()
%% @doc
-%% formats a binary encoded json string according to the options chose. the defaults will produced a string stripped of all whitespace
+%% formats a binary encoded json string according to the options chose. the
+%% defaults will produced a string stripped of all whitespace
%%
%% options:
%%
%% - {strict, true | false}
-%%
by default, unwrapped json values (numbers, strings and the atoms
-%% true, false and null) result in an error. if strict equals true, they
-%% are treated as valid json. default is false
+%% by default, unwrapped json values (numbers, strings and the
+%% atoms true, false and null) result in an error. if strict equals
+%% true, they are treated as valid json. default is false
%%
-%% - {encoding, auto | utf8 | utf16 | {utf16, little} | utf32 | {utf32, little} }
-%%
assume the binary is encoded using the specified binary. default is auto,
-%% which attempts to autodetect the encoding
+%% - {encoding, auto
+%% | utf8
+%% | utf16
+%% | {utf16, little}
+%% | utf32
+%% | {utf32, little}
+%% }
+%%
assume the binary is encoded using the specified binary. default
+%% is auto, which attempts to autodetect the encoding
%%
-%% - {output_encoding, utf8 | utf16 | {utf16, little} | utf32 | {utf32, little} }
+%%
- {encoding, utf8
+%% | utf16
+%% | {utf16, little}
+%% | utf32
+%% | {utf32, little}
+%% }
%%
the encoding of the resulting binary. default is utf8
%%
%% - {comments, true | false}
@@ -325,13 +375,15 @@ format(JSON) ->
%%
space is equivalent to {space, 1}
%%
%% - {space, N}
-%%
place N spaces after each colon and comma in the resulting binary. default is zero
+%% place N spaces after each colon and comma in the resulting
+%% binary. default is zero
%%
%% - indent
%%
indent is equivalent to {indent, 1}
%%
%% - {indent, N}
-%%
indent each 'level' of the json structure by N spaces. default is zero
+%% indent each 'level' of the json structure by N spaces. default is
+%% zero
%%
%% @end
@@ -340,9 +392,17 @@ format(JSON, Opts) ->
%% @spec eventify(List::list()) -> jsx_parser_result()
-%% @doc fake the jsx api for any list. useful if you want to serialize a structure to json using the pretty printer, or verify a sequence could be valid json
+%% @doc fake the jsx api for any list. useful if you want to serialize a
+%% structure to json using the pretty printer, or verify a sequence could be
+%% valid json
eventify([]) ->
- fun() -> {incomplete, fun(List) when is_list(List) -> eventify(List); (_) -> erlang:error(badarg) end} end;
+ fun() ->
+ {incomplete, fun(List) when is_list(List) ->
+ eventify(List)
+ ; (_) ->
+ erlang:error(badarg)
+ end}
+ end;
eventify([Next|Rest]) ->
fun() -> {event, Next, eventify(Rest)} end.
@@ -352,43 +412,50 @@ eventify([Next|Rest]) ->
%% encoding detection
-%% 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
+%% 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
detect_encoding(OptsList) ->
fun(Stream) -> detect_encoding(Stream, OptsList) end.
%% utf8 bom detection
-detect_encoding(<<16#ef, 16#bb, 16#bf, Rest/binary>>, Opts) -> (jsx_utf8:parser(Opts))(Rest);
-%% utf32-little bom detection (this has to come before utf16-little or it'll match that)
-detect_encoding(<<16#ff, 16#fe, 0, 0, Rest/binary>>, Opts) -> (jsx_utf32le:parser(Opts))(Rest);
+detect_encoding(<<16#ef, 16#bb, 16#bf, Rest/binary>>, Opts) ->
+ (jsx_utf8:parser(Opts))(Rest);
+%% utf32-little bom detection (this has to come before utf16-little or it'll
+%% match that)
+detect_encoding(<<16#ff, 16#fe, 0, 0, Rest/binary>>, Opts) ->
+ (jsx_utf32le:parser(Opts))(Rest);
%% utf16-big bom detection
-detect_encoding(<<16#fe, 16#ff, Rest/binary>>, Opts) -> (jsx_utf16:parser(Opts))(Rest);
+detect_encoding(<<16#fe, 16#ff, Rest/binary>>, Opts) ->
+ (jsx_utf16:parser(Opts))(Rest);
%% utf16-little bom detection
-detect_encoding(<<16#ff, 16#fe, Rest/binary>>, Opts) -> (jsx_utf16le:parser(Opts))(Rest);
+detect_encoding(<<16#ff, 16#fe, Rest/binary>>, Opts) ->
+ (jsx_utf16le:parser(Opts))(Rest);
%% utf32-big bom detection
-detect_encoding(<<0, 0, 16#fe, 16#ff, Rest/binary>>, Opts) -> (jsx_utf32:parser(Opts))(Rest);
+detect_encoding(<<0, 0, 16#fe, 16#ff, Rest/binary>>, Opts) ->
+ (jsx_utf32:parser(Opts))(Rest);
%% utf32-little null order detection
detect_encoding(<> = JSON, Opts) when X =/= 0 ->
(jsx_utf32le:parser(Opts))(JSON);
-%% utf16-big null order detection
-detect_encoding(<<0, X, 0, Y, _Rest/binary>> = JSON, Opts) when X =/= 0, Y =/= 0 ->
- (jsx_utf16:parser(Opts))(JSON);
-%% utf16-little null order detection
-detect_encoding(<> = JSON, Opts) when X =/= 0, Y =/= 0 ->
- (jsx_utf16le:parser(Opts))(JSON);
%% utf32-big null order detection
detect_encoding(<<0, 0, 0, X, _Rest/binary>> = JSON, Opts) when X =/= 0 ->
(jsx_utf32:parser(Opts))(JSON);
+%% utf16-little null order detection
+detect_encoding(<> = JSON, Opts) when X =/= 0 ->
+ (jsx_utf16le:parser(Opts))(JSON);
+%% utf16-big null order detection
+detect_encoding(<<0, X, 0, _, _Rest/binary>> = JSON, Opts) when X =/= 0 ->
+ (jsx_utf16:parser(Opts))(JSON);
%% utf8 null order detection
detect_encoding(<> = JSON, Opts) when X =/= 0, Y =/= 0 ->
(jsx_utf8:parser(Opts))(JSON);
-%% 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
+%% 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(<>, Opts) when X =/= 0 ->
{incomplete,
fun(end_stream) ->
diff --git a/src/jsx_eep0018.erl b/src/jsx_eep0018.erl
index 465907d..fdbe1af 100644
--- a/src/jsx_eep0018.erl
+++ b/src/jsx_eep0018.erl
@@ -33,7 +33,6 @@
-include("./include/jsx_common.hrl").
-
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
@@ -48,10 +47,10 @@ json_to_term(JSON, Opts) ->
end.
-%% the jsx formatter (pretty printer) can do most of the heavy lifting in converting erlang
-%% terms to json strings, but it expects a jsx event iterator. luckily, the mapping from
-%% erlang terms to jsx events is straightforward and the iterator can be faked with an
-%% anonymous function
+%% the jsx formatter (pretty printer) can do most of the heavy lifting in
+%% converting erlang terms to json strings, but it expects a jsx event
+%% iterator. luckily, the mapping from erlang terms to jsx events is
+%% straightforward and the iterator can be faked with an anonymous function
term_to_json(List, Opts) ->
case proplists:get_value(strict, Opts, true) of
true when is_list(List) -> continue
@@ -59,7 +58,9 @@ term_to_json(List, Opts) ->
; false -> continue
end,
Encoding = proplists:get_value(encoding, Opts, utf8),
- jsx:format(jsx:eventify(lists:reverse([end_json] ++ term_to_events(List))), [{output_encoding, Encoding}] ++ Opts).
+ jsx:format(jsx:eventify(lists:reverse([end_json] ++ term_to_events(List))),
+ [{output_encoding, Encoding}] ++ Opts
+ ).
%% parse opts for the decoder
@@ -67,7 +68,9 @@ opts_to_jsx_opts(Opts) ->
opts_to_jsx_opts(Opts, []).
opts_to_jsx_opts([{encoding, Val}|Rest], Acc) ->
- case lists:member(Val, [auto, utf8, utf16, {utf16, little}, utf32, {utf32, little}]) of
+ case lists:member(Val,
+ [auto, utf8, utf16, {utf16, little}, utf32, {utf32, little}]
+ ) of
true -> opts_to_jsx_opts(Rest, [{encoding, Val}] ++ Acc)
; false -> opts_to_jsx_opts(Rest, Acc)
end;
@@ -85,49 +88,55 @@ opts_to_jsx_opts([], Acc) ->
%% ensure the first jsx event we get is start_object or start_array when running
%% in strict mode
-collect_strict({event, Start, Next}, Acc, Opts) when Start =:= start_object; Start =:= start_array ->
+collect_strict({event, Start, Next}, Acc, Opts)
+ when Start =:= start_object; Start =:= start_array ->
collect(Next(), [[]|Acc], Opts);
collect_strict(_, _, _) ->
erlang:error(badarg).
%% collect decoder events and convert to eep0018 format
-collect({event, Start, Next}, Acc, Opts) when Start =:= start_object; Start =:= start_array ->
+collect({event, Start, Next}, Acc, Opts)
+ when Start =:= start_object; Start =:= start_array ->
collect(Next(), [[]|Acc], Opts);
%% special case for empty object
-collect({event, end_object, Next}, [[], Parent|Rest], Opts) when is_list(Parent) ->
+collect({event, end_object, Next}, [[], Parent|Rest], Opts)
+ when is_list(Parent) ->
collect(Next(), [[[{}]] ++ Parent] ++ Rest, Opts);
%% reverse the array/object accumulator before prepending it to it's parent
-collect({event, end_object, Next}, [Current, Parent|Rest], Opts) when is_list(Parent) ->
+collect({event, end_object, Next}, [Current, Parent|Rest], Opts)
+ when is_list(Parent) ->
collect(Next(), [[lists:reverse(Current)] ++ Parent] ++ Rest, Opts);
-collect({event, end_array, Next}, [Current, Parent|Rest], Opts) when is_list(Parent) ->
+collect({event, end_array, Next}, [Current, Parent|Rest], Opts)
+ when is_list(Parent) ->
collect(Next(), [[lists:reverse(Current)] ++ Parent] ++ Rest, Opts);
%% special case for empty object
collect({event, end_object, Next}, [[], Key, Parent|Rest], Opts) ->
collect(Next(), [[{Key, [{}]}] ++ Parent] ++ Rest, Opts);
collect({event, End, Next}, [Current, Key, Parent|Rest], Opts)
- when End =:= end_object; End =:= end_array ->
+ when End =:= end_object; End =:= end_array ->
collect(Next(), [[{Key, lists:reverse(Current)}] ++ Parent] ++ Rest, Opts);
collect({event, end_json, _Next}, [[Acc]], _Opts) ->
Acc;
-%% key can only be emitted inside of a json object, so just insert it directly into
-%% the head of the accumulator and deal with it when we receive it's paired value
+%% key can only be emitted inside of a json object, so just insert it directly
+%% into the head of the accumulator and deal with it when we receive it's
+%% paired value
collect({event, {key, _} = PreKey, Next}, [Current|_] = Acc, Opts) ->
Key = event(PreKey, Opts),
case decode_key_repeats(Key, Current) of
true -> erlang:error(badarg)
; false -> collect(Next(), [Key] ++ Acc, Opts)
end;
-%% check acc to see if we're inside an object or an array. because inside an object
-%% context the events that fall this far are always preceded by a key (which are
-%% binaries or atoms), if Current is a list, we're inside an array, else, an
-%% object
+%% check acc to see if we're inside an object or an array. because inside an
+%% object context the events that fall this far are always preceded by a key
+%% (which are binaries or atoms), if Current is a list, we're inside an array,
+%% else, an object
collect({event, Event, Next}, [Current|Rest], Opts) when is_list(Current) ->
collect(Next(), [[event(Event, Opts)] ++ Current] ++ Rest, Opts);
collect({event, Event, Next}, [Key, Current|Rest], Opts) ->
collect(Next(), [[{Key, event(Event, Opts)}] ++ Current] ++ Rest, Opts);
-%% if our first returned event is {incomplete, ...} try to force end and return the
-%% Event if one is returned
+%% if our first returned event is {incomplete, ...} try to force end and return
+%% the Event if one is returned
collect({incomplete, More}, [[]], Opts) ->
case More(end_stream) of
{event, Event, _Next} -> event(Event, Opts)
@@ -170,7 +179,8 @@ decode_key_repeats(_Key, []) -> false.
-%% convert eep0018 representation to jsx events. note special casing for the empty object
+%% convert eep0018 representation to jsx events. note special casing for the
+%% empty object
term_to_events([{}]) ->
[end_object, start_object];
term_to_events([First|_] = List) when is_tuple(First) ->
@@ -203,7 +213,7 @@ list_to_events([], Acc) ->
term_to_event(List) when is_list(List) ->
term_to_events(List);
term_to_event(Float) when is_float(Float) ->
- [{float, float_to_decimal(Float)}];
+ [{float, nice_decimal(Float)}];
term_to_event(Integer) when is_integer(Integer) ->
[{integer, erlang:integer_to_list(Integer)}];
term_to_event(String) when is_binary(String) ->
@@ -222,24 +232,32 @@ key_to_event(Key) when is_binary(Key) ->
encode_key_repeats([Key], SoFar) -> encode_key_repeats(Key, SoFar, 0).
-encode_key_repeats(Key, [Key|_], 0) -> true;
-encode_key_repeats(Key, [end_object|Rest], Level) -> encode_key_repeats(Key, Rest, Level + 1);
-encode_key_repeats(_, [start_object|_], 0) -> false;
-encode_key_repeats(Key, [start_object|Rest], Level) -> encode_key_repeats(Key, Rest, Level - 1);
-encode_key_repeats(Key, [_|Rest], Level) -> encode_key_repeats(Key, Rest, Level);
-encode_key_repeats(_, [], 0) -> false.
+encode_key_repeats(Key, [Key|_], 0) ->
+ true;
+encode_key_repeats(Key, [end_object|Rest], Level) ->
+ encode_key_repeats(Key, Rest, Level + 1);
+encode_key_repeats(_, [start_object|_], 0) ->
+ false;
+encode_key_repeats(Key, [start_object|Rest], Level) ->
+ encode_key_repeats(Key, Rest, Level - 1);
+encode_key_repeats(Key, [_|Rest], Level) ->
+ encode_key_repeats(Key, Rest, Level);
+encode_key_repeats(_, [], 0) ->
+ false.
-%% conversion of floats to 'nice' decimal output. erlang's float implementation is almost
-%% but not quite ieee 754. it converts negative zero to plain zero silently, and throws
-%% exceptions for any operations that would produce NaN or infinity. as far as I can
-%% tell that is. trying to match against NaN or infinity binary patterns produces nomatch
-%% exceptions, and arithmetic operations produce badarg exceptions. with that in mind, this
-%% function makes no attempt to handle special values (except for zero)
+%% conversion of floats to 'nice' decimal output. erlang's float implementation
+%% is almost but not quite ieee 754. it converts negative zero to plain zero
+%% silently, and throws exceptions for any operations that would produce NaN
+%% or infinity. as far as I can tell that is. trying to match against NaN or
+%% infinity binary patterns produces nomatch exceptions, and arithmetic
+%% operations produce badarg exceptions. with that in mind, this function
+%% makes no attempt to handle special values (except for zero)
-%% algorithm from "Printing FLoating-Point Numbers Quickly and Accurately" by Burger & Dybvig
-float_to_decimal(0.0) -> "0.0";
-float_to_decimal(Num) when is_float(Num) ->
+%% algorithm from "Printing FLoating-Point Numbers Quickly and Accurately" by
+%% Burger & Dybvig
+nice_decimal(0.0) -> "0.0";
+nice_decimal(Num) when is_float(Num) ->
{F, E} = extract(<>),
{R, S, MP, MM} = initial_vals(F, E),
K = ceiling(math:log10(abs(Num)) - 1.0e-10),
@@ -315,7 +333,8 @@ generate(RT, S, MP, MM, Round) ->
end.
-%% this is not efficient at all and should be replaced with a lookup table probably
+%% this is not efficient at all and should be replaced with a lookup table
+%% probably
pow(_B, 0) -> 1;
pow(B, E) when E > 0 -> pow(B, E, 1).
@@ -331,8 +350,10 @@ format(Dpoint, Digits) when Dpoint =< length(Digits), Dpoint > 0 ->
format(Dpoint, Digits) when Dpoint > 0 ->
Pad = Dpoint - length(Digits),
case Pad of
- X when X > 6 -> format(Digits, 1, []) ++ "e" ++ integer_to_list(Dpoint - 1)
- ; _ -> format(Digits ++ [ 0 || _ <- lists:seq(1, Pad)], Dpoint, [])
+ X when X > 6 ->
+ format(Digits, 1, []) ++ "e" ++ integer_to_list(Dpoint - 1)
+ ; _ ->
+ format(Digits ++ [ 0 || _ <- lists:seq(1, Pad)], Dpoint, [])
end;
format(Dpoint, Digits) when Dpoint < 0 ->
format(Digits, 1, []) ++ "e" ++ integer_to_list(Dpoint - 1).
@@ -344,32 +365,41 @@ format([], ignore, Acc) ->
format(Digits, 0, Acc) ->
format(Digits, ignore, "." ++ Acc);
format([Digit|Digits], Dpoint, Acc) ->
- format(Digits, case Dpoint of ignore -> ignore; X -> X - 1 end, to_ascii(Digit) ++ Acc).
+ format(Digits,
+ case Dpoint of ignore -> ignore; X -> X - 1 end, to_ascii(Digit) ++ Acc
+ ).
to_ascii(X) -> [X + 48]. %% ascii "1" is [49], "2" is [50], etc...
-%% json string escaping, for utf8 binaries. escape the json control sequences to their
-%% json equivalent, escape other control characters to \uXXXX sequences, everything
-%% else should be a legal json string component
+%% json string escaping, for utf8 binaries. escape the json control sequences to
+%% their json equivalent, escape other control characters to \uXXXX sequences,
+%% everything else should be a legal json string component
json_escape(String) ->
json_escape(String, <<>>).
%% double quote
-json_escape(<<$\", Rest/binary>>, Acc) -> json_escape(Rest, <>);
+json_escape(<<$\", Rest/binary>>, Acc) ->
+ json_escape(Rest, <>);
%% backslash \ reverse solidus
-json_escape(<<$\\, Rest/binary>>, Acc) -> json_escape(Rest, <>);
+json_escape(<<$\\, Rest/binary>>, Acc) ->
+ json_escape(Rest, <>);
%% backspace
-json_escape(<<$\b, Rest/binary>>, Acc) -> json_escape(Rest, <>);
+json_escape(<<$\b, Rest/binary>>, Acc) ->
+ json_escape(Rest, <>);
%% form feed
-json_escape(<<$\f, Rest/binary>>, Acc) -> json_escape(Rest, <>);
+json_escape(<<$\f, Rest/binary>>, Acc) ->
+ json_escape(Rest, <>);
%% newline
-json_escape(<<$\n, Rest/binary>>, Acc) -> json_escape(Rest, <>);
+json_escape(<<$\n, Rest/binary>>, Acc) ->
+ json_escape(Rest, <>);
%% cr
-json_escape(<<$\r, Rest/binary>>, Acc) -> json_escape(Rest, <>);
+json_escape(<<$\r, Rest/binary>>, Acc) ->
+ json_escape(Rest, <>);
%% tab
-json_escape(<<$\t, Rest/binary>>, Acc) -> json_escape(Rest, <>);
+json_escape(<<$\t, Rest/binary>>, Acc) ->
+ json_escape(Rest, <>);
%% other control characters
json_escape(<>, Acc) when C >= 0, C < $\s ->
json_escape(Rest, <>);
@@ -382,8 +412,8 @@ json_escape(_, _) ->
erlang:error(badarg).
-%% convert a codepoint to it's \uXXXX equiv. for laziness, this only handles codepoints
-%% this module might escape, ie, control characters
+%% convert a codepoint to it's \uXXXX equiv. for laziness, this only handles
+%% codepoints this module might escape, ie, control characters
json_escape_sequence(C) when C < 16#20 ->
<<_:8, A:4, B:4>> = <>, % first two hex digits are always zero
<<$\\, $u, $0, $0, (to_hex(A)), (to_hex(B))>>.
@@ -405,64 +435,194 @@ decode_test_() ->
[
{"empty object", ?_assert(json_to_term(<<"{}">>, []) =:= [{}])},
{"empty array", ?_assert(json_to_term(<<"[]">>, []) =:= [])},
- {"simple object", ?_assert(json_to_term(<<"{\"a\": true, \"b\": true, \"c\": true}">>, [{label, atom}]) =:= [{a, true}, {b, true}, {c, true}])},
- {"simple array", ?_assert(json_to_term(<<"[true,true,true]">>, []) =:= [true, true, true])},
- {"nested structures", ?_assert(json_to_term(<<"{\"list\":[{\"list\":[{}, {}],\"object\":{}}, []],\"object\":{}}">>, [{label, atom}]) =:= [{list, [[{list, [[{}], [{}]]}, {object, [{}]}],[]]}, {object, [{}]}])},
- {"numbers", ?_assert(json_to_term(<<"[-10000000000.0, -1, 0.0, 0, 1, 10000000000, 1000000000.0]">>, []) =:= [-10000000000.0, -1, 0.0, 0, 1, 10000000000, 1000000000.0])},
- {"numbers (all floats)", ?_assert(json_to_term(<<"[-10000000000.0, -1, 0.0, 0, 1, 10000000000, 1000000000.0]">>, [{float, true}]) =:= [-10000000000.0, -1.0, 0.0, 0.0, 1.0, 10000000000.0, 1000000000.0])},
- {"strings", ?_assert(json_to_term(<<"[\"a string\"]">>, []) =:= [<<"a string">>])},
- {"literals", ?_assert(json_to_term(<<"[true,false,null]">>, []) =:= [true,false,null])},
- {"naked true", ?_assert(json_to_term(<<"true">>, [{strict, false}]) =:= true)},
- {"naked short number", ?_assert(json_to_term(<<"1">>, [{strict, false}]) =:= 1)},
+ {"simple object",
+ ?_assert(json_to_term(
+ <<"{\"a\": true, \"b\": true, \"c\": true}">>,
+ [{label, atom}]
+ ) =:= [{a, true}, {b, true}, {c, true}]
+ )
+ },
+ {"simple array",
+ ?_assert(json_to_term(<<"[true,true,true]">>,
+ []
+ ) =:= [true, true, true]
+ )
+ },
+ {"nested structures",
+ ?_assert(json_to_term(
+ <<"{\"x\":[{\"x\":[{}, {}],\"y\":{}}, []],\"y\":{}}">>,
+ [{label, atom}]
+ ) =:= [{x, [[{x, [[{}], [{}]]}, {y, [{}]}],[]]}, {y, [{}]}]
+ )
+ },
+ {"numbers",
+ ?_assert(json_to_term(
+ <<"[-100000000.0, -1, 0.0, 0, 1, 100000000, 10000000.0]">>,
+ []
+ ) =:= [-100000000.0, -1, 0.0, 0, 1, 100000000, 10000000.0]
+ )
+ },
+ {"numbers (all floats)",
+ ?_assert(json_to_term(
+ <<"[-100000000.0, -1, 0.0, 0, 1, 1000, 10000000.0]">>,
+ [{float, true}]
+ ) =:= [-100000000.0, -1.0, 0.0, 0.0, 1.0, 1000.0, 10000000.0]
+ )
+ },
+ {"strings",
+ ?_assert(json_to_term(<<"[\"a string\"]">>,
+ []
+ ) =:= [<<"a string">>])
+ },
+ {"literals",
+ ?_assert(json_to_term(<<"[true,false,null]">>,
+ []
+ ) =:= [true,false,null]
+ )
+ },
+ {"naked true",
+ ?_assert(json_to_term(<<"true">>, [{strict, false}]) =:= true)
+ },
+ {"naked short number",
+ ?_assert(json_to_term(<<"1">>, [{strict, false}]) =:= 1)
+ },
{"float", ?_assert(json_to_term(<<"1.0">>, [{strict, false}]) =:= 1.0)},
- {"naked string", ?_assert(json_to_term(<<"\"hello world\"">>, [{strict, false}]) =:= <<"hello world">>)},
- {"comments", ?_assert(json_to_term(<<"[ /* a comment in an empty array */ ]">>, [{comments, true}]) =:= [])}
+ {"naked string",
+ ?_assert(json_to_term(<<"\"hello world\"">>,
+ [{strict, false}]
+ ) =:= <<"hello world">>
+ )
+ },
+ {"comments",
+ ?_assert(json_to_term(<<"[ /* a comment in an empty array */ ]">>,
+ [{comments, true}]
+ ) =:= []
+ )
+ }
].
encode_test_() ->
[
{"empty object", ?_assert(term_to_json([{}], []) =:= <<"{}">>)},
{"empty array", ?_assert(term_to_json([], []) =:= <<"[]">>)},
- {"simple object", ?_assert(term_to_json([{a, true}, {b, true}, {c, true}], []) =:= <<"{\"a\":true,\"b\":true,\"c\":true}">>)},
- {"simple array", ?_assert(term_to_json([true, true, true], []) =:= <<"[true,true,true]">>)},
- {"nested structures", ?_assert(term_to_json([{list, [[{list, [[{}], [{}]]}, {object, [{}]}],[]]}, {object, [{}]}], []) =:= <<"{\"list\":[{\"list\":[{},{}],\"object\":{}},[]],\"object\":{}}">>)},
- {"numbers", ?_assert(term_to_json([-10000000000.0, -1, 0.0, 0, 1, 10000000000, 1000000000.0], []) =:= <<"[-1.0e10,-1,0.0,0,1,10000000000,1.0e9]">>)},
- {"strings", ?_assert(term_to_json([<<"a string">>], []) =:= <<"[\"a string\"]">>)},
- {"literals", ?_assert(term_to_json([true,false,null], []) =:= <<"[true,false,null]">>)},
- {"naked true", ?_assert(term_to_json(true, [{strict, false}]) =:= <<"true">>)},
- {"naked number", ?_assert(term_to_json(1, [{strict, false}]) =:= <<"1">>)},
+ {"simple object",
+ ?_assert(term_to_json([{a, true}, {b, true}, {c, true}],
+ []
+ ) =:= <<"{\"a\":true,\"b\":true,\"c\":true}">>
+ )
+ },
+ {"simple array",
+ ?_assert(term_to_json([true, true, true],
+ []
+ ) =:= <<"[true,true,true]">>
+ )
+ },
+ {"nested structures",
+ ?_assert(term_to_json(
+ [{x, [[{x, [[{}], [{}]]}, {y, [{}]}],[]]}, {y, [{}]}],
+ []
+ ) =:= <<"{\"x\":[{\"x\":[{},{}],\"y\":{}},[]],\"y\":{}}">>
+ )
+ },
+ {"numbers",
+ ?_assert(term_to_json(
+ [-10000000000.0, -1, 0.0, 0, 1, 10000000, 1000000000.0],
+ []
+ ) =:= <<"[-1.0e10,-1,0.0,0,1,10000000,1.0e9]">>
+ )
+ },
+ {"strings",
+ ?_assert(term_to_json([<<"a string">>],
+ []
+ ) =:= <<"[\"a string\"]">>
+ )
+ },
+ {"literals",
+ ?_assert(term_to_json([true,false,null],
+ []
+ ) =:= <<"[true,false,null]">>
+ )
+ },
+ {"naked true",
+ ?_assert(term_to_json(true, [{strict, false}]) =:= <<"true">>)
+ },
+ {"naked number",
+ ?_assert(term_to_json(1, [{strict, false}]) =:= <<"1">>)
+ },
{"float", ?_assert(term_to_json(1.0, [{strict, false}]) =:= <<"1.0">>)},
- {"naked string", ?_assert(term_to_json(<<"hello world">>, [{strict, false}]) =:= <<"\"hello world\"">>)}
+ {"naked string",
+ ?_assert(term_to_json(<<"hello world">>,
+ [{strict, false}]
+ ) =:= <<"\"hello world\"">>
+ )
+ }
].
repeated_keys_test_() ->
[
- {"encode", ?_assertError(badarg, term_to_json([{k, true}, {k, false}], []))},
- {"decode", ?_assertError(badarg, json_to_term(<<"{\"k\": true, \"k\": false}">>, []))}
+ {"encode",
+ ?_assertError(badarg, term_to_json([{k, true}, {k, false}], []))
+ },
+ {"decode",
+ ?_assertError(badarg, json_to_term(
+ <<"{\"k\": true, \"k\": false}">>,
+ []
+ )
+ )
+ }
].
escape_test_() ->
[
- {"json string escaping", ?_assert(json_escape(<<"\"\\\b\f\n\r\t">>) =:= <<"\\\"\\\\\\b\\f\\n\\r\\t">>)},
- {"json string hex escape", ?_assert(json_escape(<<1, 2, 3, 11, 26, 30, 31>>) =:= <<"\\u0001\\u0002\\u0003\\u000b\\u001a\\u001e\\u001f">>)}
+ {"json string escaping",
+ ?_assert(json_escape(
+ <<"\"\\\b\f\n\r\t">>
+ ) =:= <<"\\\"\\\\\\b\\f\\n\\r\\t">>
+ )
+ },
+ {"json string hex escape",
+ ?_assert(json_escape(
+ <<1, 2, 3, 11, 26, 30, 31>>
+ ) =:= <<"\\u0001\\u0002\\u0003\\u000b\\u001a\\u001e\\u001f">>
+ )
+ }
].
nice_decimal_test_() ->
[
- {"0.0", ?_assert(float_to_decimal(0.0) =:= "0.0")},
- {"1.0", ?_assert(float_to_decimal(1.0) =:= "1.0")},
- {"-1.0", ?_assert(float_to_decimal(-1.0) =:= "-1.0")},
- {"3.1234567890987654321", ?_assert(float_to_decimal(3.1234567890987654321) =:= "3.1234567890987655")},
- {"1.0e23", ?_assert(float_to_decimal(1.0e23) =:= "1.0e23")},
- {"0.3", ?_assert(float_to_decimal(3.0/10.0) =:= "0.3")},
- {"0.0001", ?_assert(float_to_decimal(0.0001) =:= "1.0e-4")},
- {"0.00000001", ?_assert(float_to_decimal(0.00000001) =:= "1.0e-8")},
- {"1.0e-323", ?_assert(float_to_decimal(1.0e-323) =:= "1.0e-323")},
- {"1.0e308", ?_assert(float_to_decimal(1.0e308) =:= "1.0e308")},
- {"min normalized float", ?_assert(float_to_decimal(math:pow(2, -1022)) =:= "2.2250738585072014e-308")},
- {"max normalized float", ?_assert(float_to_decimal((2 - math:pow(2, -52)) * math:pow(2, 1023)) =:= "1.7976931348623157e308")},
- {"min denormalized float", ?_assert(float_to_decimal(math:pow(2, -1074)) =:= "5.0e-324")},
- {"max denormalized float", ?_assert(float_to_decimal((1 - math:pow(2, -52)) * math:pow(2, -1022)) =:= "2.225073858507201e-308")}
+ {"0.0", ?_assert(nice_decimal(0.0) =:= "0.0")},
+ {"1.0", ?_assert(nice_decimal(1.0) =:= "1.0")},
+ {"-1.0", ?_assert(nice_decimal(-1.0) =:= "-1.0")},
+ {"3.1234567890987654321",
+ ?_assert(
+ nice_decimal(3.1234567890987654321) =:= "3.1234567890987655")
+ },
+ {"1.0e23", ?_assert(nice_decimal(1.0e23) =:= "1.0e23")},
+ {"0.3", ?_assert(nice_decimal(3.0/10.0) =:= "0.3")},
+ {"0.0001", ?_assert(nice_decimal(0.0001) =:= "1.0e-4")},
+ {"0.00000001", ?_assert(nice_decimal(0.00000001) =:= "1.0e-8")},
+ {"1.0e-323", ?_assert(nice_decimal(1.0e-323) =:= "1.0e-323")},
+ {"1.0e308", ?_assert(nice_decimal(1.0e308) =:= "1.0e308")},
+ {"min normalized float",
+ ?_assert(
+ nice_decimal(math:pow(2, -1022)) =:= "2.2250738585072014e-308"
+ )
+ },
+ {"max normalized float",
+ ?_assert(
+ nice_decimal((2 - math:pow(2, -52)) * math:pow(2, 1023))
+ =:= "1.7976931348623157e308"
+ )
+ },
+ {"min denormalized float",
+ ?_assert(nice_decimal(math:pow(2, -1074)) =:= "5.0e-324")
+ },
+ {"max denormalized float",
+ ?_assert(
+ nice_decimal((1 - math:pow(2, -52)) * math:pow(2, -1022))
+ =:= "2.225073858507201e-308"
+ )
+ }
].
-endif.
\ No newline at end of file
diff --git a/src/jsx_format.erl b/src/jsx_format.erl
index 9ecab6f..970aa83 100644
--- a/src/jsx_format.erl
+++ b/src/jsx_format.erl
@@ -79,7 +79,11 @@ format_something({event, start_object, Next}, Opts, Level) ->
{Continue, [?start_object, ?end_object]}
; Event ->
{Continue, Object} = format_object(Event, [], Opts, Level + 1),
- {Continue, [?start_object, Object, indent(Opts, Level), ?end_object]}
+ {Continue, [?start_object,
+ Object,
+ indent(Opts, Level),
+ ?end_object
+ ]}
end;
format_something({event, start_array, Next}, Opts, Level) ->
case Next() of
@@ -99,10 +103,24 @@ format_object({event, {key, Key}, Next}, Acc, Opts, Level) ->
{Continue, Value} = format_something(Next(), Opts, Level),
case Continue() of
{event, end_object, NextNext} ->
- {NextNext, [Acc, indent(Opts, Level), encode(string, Key), ?colon, space(Opts), Value]}
+ {NextNext, [Acc,
+ indent(Opts, Level),
+ encode(string, Key),
+ ?colon,
+ space(Opts),
+ Value
+ ]}
; Else ->
format_object(Else,
- [Acc, indent(Opts, Level), encode(string, Key), ?colon, space(Opts), Value, ?comma, space(Opts)],
+ [Acc,
+ indent(Opts, Level),
+ encode(string, Key),
+ ?colon,
+ space(Opts),
+ Value,
+ ?comma,
+ space(Opts)
+ ],
Opts,
Level
)
@@ -117,14 +135,24 @@ format_array(Event, Acc, Opts, Level) ->
{event, end_array, NextNext} ->
{NextNext, [Acc, indent(Opts, Level), Value]}
; Else ->
- format_array(Else, [Acc, indent(Opts, Level), Value, ?comma, space(Opts)], Opts, Level)
+ format_array(Else,
+ [Acc,
+ indent(Opts, Level),
+ Value,
+ ?comma,
+ space(Opts)
+ ],
+ Opts,
+ Level
+ )
end.
encode(Acc, Opts) when is_list(Acc) ->
case Opts#format_opts.output_encoding of
iolist -> Acc
- ; UTF when ?is_utf_encoding(UTF) -> unicode:characters_to_binary(Acc, utf8, UTF)
+ ; UTF when ?is_utf_encoding(UTF) ->
+ unicode:characters_to_binary(Acc, utf8, UTF)
; _ -> erlang:throw(badarg)
end;
encode(string, String) ->
@@ -162,17 +190,58 @@ space(Opts) ->
minify_test_() ->
[
- {"minify object", ?_assert(format(<<" { \"key\" :\n\t \"value\"\r\r\r\n } ">>, []) =:= <<"{\"key\":\"value\"}">>)},
- {"minify array", ?_assert(format(<<" [\n\ttrue,\n\tfalse,\n\tnull\n] ">>, []) =:= <<"[true,false,null]">>)}
+ {"minify object",
+ ?_assert(format(<<" { \"key\" :\n\t \"value\"\r\r\r\n } ">>,
+ []
+ ) =:= <<"{\"key\":\"value\"}">>
+ )
+ },
+ {"minify array",
+ ?_assert(format(<<" [\n\ttrue,\n\tfalse,\n\tnull\n] ">>,
+ []
+ ) =:= <<"[true,false,null]">>
+ )
+ }
].
opts_test_() ->
[
- {"unspecified indent/space", ?_assert(format(<<" [\n\ttrue,\n\tfalse,\n\tnull\n] ">>, [space, indent]) =:= <<"[\n true, \n false, \n null\n]">>)},
- {"specific indent/space", ?_assert(format(<<"\n{\n\"key\" : [],\n\"another key\" : true\n}\n">>, [{space, 2}, {indent, 4}]) =:= <<"{\n \"key\": [], \n \"another key\": true\n}">>)},
- {"nested structures", ?_assert(format(<<"[{\"key\":\"value\", \"another key\": \"another value\"}, [[true, false, null]]]">>, [{space, 2}, {indent, 2}]) =:= <<"[\n {\n \"key\": \"value\", \n \"another key\": \"another value\"\n }, \n [\n [\n true, \n false, \n null\n ]\n ]\n]">>)},
- {"just spaces", ?_assert(format(<<"[1,2,3]">>, [{space, 2}]) =:= <<"[1, 2, 3]">>)},
- {"just indent", ?_assert(format(<<"[1.0, 2.0, 3.0]">>, [{indent, 2}]) =:= <<"[\n 1.0,\n 2.0,\n 3.0\n]">>)}
+ {"unspecified indent/space",
+ ?_assert(format(<<" [\n\ttrue,\n\tfalse,\n\tnull\n] ">>,
+ [space, indent]
+ ) =:= <<"[\n true, \n false, \n null\n]">>
+ )
+ },
+ {"specific indent/space",
+ ?_assert(format(
+ <<"\n{\n\"key\" : [],\n\"another key\" : true\n}\n">>,
+ [{space, 2}, {indent, 3}]
+ ) =:= <<"{\n \"key\": [], \n \"another key\": true\n}">>
+ )
+ },
+ {"nested structures",
+ ?_assert(format(
+ <<"[{\"key\":\"value\",
+ \"another key\": \"another value\"
+ },
+ [[true, false, null]]
+ ]">>,
+ [{space, 2}, {indent, 2}]
+ ) =:= <<"[\n {\n \"key\": \"value\", \n \"another key\": \"another value\"\n }, \n [\n [\n true, \n false, \n null\n ]\n ]\n]">>
+ )
+ },
+ {"just spaces",
+ ?_assert(format(<<"[1,2,3]">>,
+ [{space, 2}]
+ ) =:= <<"[1, 2, 3]">>
+ )
+ },
+ {"just indent",
+ ?_assert(format(<<"[1.0, 2.0, 3.0]">>,
+ [{indent, 2}]
+ ) =:= <<"[\n 1.0,\n 2.0,\n 3.0\n]">>
+ )
+ }
].
-endif.
\ No newline at end of file
diff --git a/src/jsx_verify.erl b/src/jsx_verify.erl
index 848112b..05097c9 100644
--- a/src/jsx_verify.erl
+++ b/src/jsx_verify.erl
@@ -68,8 +68,8 @@ collect({event, start_object, Next}, Keys) -> collect(Next(), [[]|Keys]);
collect({event, end_object, Next}, [_|Keys]) -> collect(Next(), [Keys]);
-%% check to see if key has already been encountered, if not add it to the key accumulator
-%% and continue, else return false
+%% check to see if key has already been encountered, if not add it to the key
+%% accumulator and continue, else return false
collect({event, {key, Key}, Next}, [Current|Keys]) ->
case lists:member(Key, Current) of
true -> false
@@ -81,7 +81,8 @@ collect({event, _, Next}, Keys) ->
collect(Next(), Keys);
-%% needed to parse numbers that don't have trailing whitespace in less strict mode
+%% needed to parse numbers that don't have trailing whitespace in less strict
+%% mode
collect({incomplete, More}, Keys) ->
collect(More(end_stream), Keys);
@@ -98,32 +99,85 @@ true_test_() ->
[
{"empty object", ?_assert(is_json(<<"{}">>, []) =:= true)},
{"empty array", ?_assert(is_json(<<"[]">>, []) =:= true)},
- {"whitespace", ?_assert(is_json(<<" \n \t \r [true] \t \n\r ">>, []) =:= true)},
- {"nested terms", ?_assert(is_json(<<"[ { \"key\": [ {}, {}, {} ], \"more key\": [{}] }, {}, [[[]]] ]">>, []) =:= true)},
- {"numbers", ?_assert(is_json(<<"[ -1.0, -1, -0, 0, 1e-1, 1, 1.0, 1e1 ]">>, []) =:= true)},
- {"strings", ?_assert(is_json(<<"[ \"a\", \"string\", \"in\", \"multiple\", \"acts\" ]">>, []) =:= true)},
- {"literals", ?_assert(is_json(<<"[ true, false, null ]">>, []) =:= true)},
- {"nested objects", ?_assert(is_json(<<"{\"key\": { \"key\": true}}">>, []) =:= true)}
+ {"whitespace",
+ ?_assert(is_json(<<" \n \t \r [true] \t \n\r ">>,
+ []
+ ) =:= true
+ )
+ },
+ {"nested terms",
+ ?_assert(is_json(
+ <<"[{ \"x\": [ {}, {}, {} ], \"y\": [{}] }, {}, [[[]]]]">>,
+ []
+ ) =:= true
+ )
+ },
+ {"numbers",
+ ?_assert(is_json(
+ <<"[ -1.0, -1, -0, 0, 1e-1, 1, 1.0, 1e1 ]">>,
+ []
+ ) =:= true
+ )
+ },
+ {"strings",
+ ?_assert(is_json(
+ <<"[ \"a\", \"string\", \"in\", \"multiple\", \"acts\" ]">>,
+ []
+ ) =:= true
+ )
+ },
+ {"literals",
+ ?_assert(is_json(<<"[ true, false, null ]">>, []) =:= true)
+ },
+ {"nested objects",
+ ?_assert(is_json(<<"{\"key\": { \"key\": true}}">>, []) =:= true)
+ }
].
false_test_() ->
[
{"naked true", ?_assert(is_json(<<"true">>, []) =:= false)},
{"naked number", ?_assert(is_json(<<"1">>, []) =:= false)},
- {"naked string", ?_assert(is_json(<<"\"i am not json\"">>, []) =:= false)},
+ {"naked string",
+ ?_assert(is_json(<<"\"i am not json\"">>, []) =:= false)
+ },
{"unbalanced list", ?_assert(is_json(<<"[[[]]">>, []) =:= false)},
- {"trailing comma", ?_assert(is_json(<<"[ true, false, null, ]">>, []) =:= false)},
+ {"trailing comma",
+ ?_assert(is_json(<<"[ true, false, null, ]">>, []) =:= false)
+ },
{"unquoted key", ?_assert(is_json(<<"{ key: false }">>, []) =:= false)},
- {"repeated key", ?_assert(is_json(<<"{\"key\": true, \"key\": true}">>, []) =:= false)},
+ {"repeated key",
+ ?_assert(is_json(
+ <<"{\"key\": true, \"key\": true}">>,
+ []
+ ) =:= false
+ )
+ },
{"comments", ?_assert(is_json(<<"[ /* a comment */ ]">>, []) =:= false)}
].
less_strict_test_() ->
[
- {"naked true", ?_assert(is_json(<<"true">>, [{strict, false}]) =:= true)},
- {"naked number", ?_assert(is_json(<<"1">>, [{strict, false}]) =:= true)},
- {"naked string", ?_assert(is_json(<<"\"i am not json\"">>, [{strict, false}]) =:= true)},
- {"comments", ?_assert(is_json(<<"[ /* a comment */ ]">>, [{comments, true}]) =:= true)}
+ {"naked true",
+ ?_assert(is_json(<<"true">>, [{strict, false}]) =:= true)
+ },
+ {"naked number",
+ ?_assert(is_json(<<"1">>, [{strict, false}]) =:= true)
+ },
+ {"naked string",
+ ?_assert(is_json(
+ <<"\"i am not json\"">>,
+ [{strict, false}]
+ ) =:= true
+ )
+ },
+ {"comments",
+ ?_assert(is_json(
+ <<"[ /* a comment */ ]">>,
+ [{comments, true}]
+ ) =:= true
+ )
+ }
].