Просмотр исходного кода

Merge branch 'master' into hipe

Conflicts:
	rebar.config
Takeru Ohta 11 лет назад
Родитель
Сommit
06396a0fde
9 измененных файлов с 256 добавлено и 194 удалено
  1. 2 2
      README.md
  2. 29 3
      doc/jsone.md
  3. 1 2
      rebar.config
  4. 1 1
      src/jsone.app.src
  5. 26 4
      src/jsone.erl
  6. 39 35
      src/jsone_decode.erl
  7. 75 64
      src/jsone_encode.erl
  8. 56 56
      test/jsone_decode_tests.erl
  9. 27 27
      test/jsone_encode_tests.erl

+ 2 - 2
README.md

@@ -1,4 +1,4 @@
-jsone (0.1.2)
+jsone (0.2.1)
 =============
 
 Erlangで実装されたJSONのエンコード/デコードライブラリ。
@@ -44,7 +44,7 @@ API
 {[1,2,3],<<>>}                  % 返り値は {デコード結果, 未使用バイナリ} 形式
 
 > json:decode(<<"{\"1\":2}">>).
-{{object,[{<<"1">>,2}]},<<>>}   % オブジェクトは {object, [Key, Value]} 形式にデコードされる
+{{[{<<"1">>,2}]},<<>>}   % オブジェクトは {object, [Key, Value]} 形式にデコードされる
 
 %% エンコード
 > jsone:encode([1,2,3]).

+ 29 - 3
doc/jsone.md

@@ -58,7 +58,7 @@ json_number() = number()
 
 
 <pre><code>
-json_object() = {object, <a href="#type-json_object_members">json_object_members()</a>}
+json_object() = {<a href="#type-json_object_members">json_object_members()</a>}
 </code></pre>
 
 
@@ -103,7 +103,7 @@ json_value() = <a href="#type-json_number">json_number()</a> | <a href="#type-js
 ## Function Index ##
 
 
-<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#decode-1">decode/1</a></td><td>JSONバイナリをデコードする.</td></tr><tr><td valign="top"><a href="#encode-1">encode/1</a></td><td>JSON値をバイナリ形式にエンコードする.</td></tr></table>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#decode-1">decode/1</a></td><td>JSONバイナリをデコードする.</td></tr><tr><td valign="top"><a href="#encode-1">encode/1</a></td><td>JSON値をバイナリ形式にエンコードする.</td></tr><tr><td valign="top"><a href="#try_decode-1">try_decode/1</a></td><td>JSONバイナリをデコードする.</td></tr><tr><td valign="top"><a href="#try_encode-1">try_encode/1</a></td><td>JSON値をバイナリ形式にエンコードする.</td></tr></table>
 
 
 <a name="functions"></a>
@@ -116,7 +116,7 @@ json_value() = <a href="#type-json_number">json_number()</a> | <a href="#type-js
 
 
 <pre><code>
-decode(Json::binary()) -&gt; {<a href="#type-json_value">json_value()</a>, RestJson::binary()}
+decode(Json::binary()) -&gt; <a href="#type-json_value">json_value()</a>
 </code></pre>
 
 <br></br>
@@ -144,3 +144,29 @@ JSON値をバイナリ形式にエンコードする.
 
 
 エンコードに失敗した場合はエラーが送出される
+<a name="try_decode-1"></a>
+
+### try_decode/1 ###
+
+
+<pre><code>
+try_decode(Json::binary()) -&gt; {ok, <a href="#type-json_value">json_value()</a>, Rest::binary()} | {error, {Reason::term(), [<a href="erlang.md#type-stack_item">erlang:stack_item()</a>]}}
+</code></pre>
+
+<br></br>
+
+
+JSONバイナリをデコードする.
+<a name="try_encode-1"></a>
+
+### try_encode/1 ###
+
+
+<pre><code>
+try_encode(JsonValue::<a href="#type-json_value">json_value()</a>) -&gt; {ok, binary()} | {error, {Reason::term(), [<a href="erlang.md#type-stack_item">erlang:stack_item()</a>]}}
+</code></pre>
+
+<br></br>
+
+
+JSON値をバイナリ形式にエンコードする

+ 1 - 2
rebar.config

@@ -4,7 +4,6 @@
             warn_export_all,
             warn_untyped_record,
             native,
-            {hipe, [o3]},
             inline
            ]}. 
 
@@ -29,5 +28,5 @@
  
 {deps,
   [
-%   {edown, ".*", {git, "git://github.com/sile/edown.git", {branch, "master"}}}
+%%   {edown, ".*", {git, "git://github.com/sile/edown.git", {branch, "master"}}}
   ]}.

+ 1 - 1
src/jsone.app.src

@@ -2,7 +2,7 @@
 {application, jsone,
  [
   {description, "Erlang JSON Library"},
-  {vsn, "0.1.2"},
+  {vsn, "0.2.1"},
   {registered, []},
   {applications, [
                   kernel,

+ 26 - 4
src/jsone.erl

@@ -30,8 +30,8 @@
 %% Exported API
 %%--------------------------------------------------------------------------------
 -export([
-         decode/1,
-         encode/1
+         decode/1, try_decode/1,
+         encode/1, try_encode/1
         ]).
 
 -export_type([
@@ -52,7 +52,7 @@
 -type json_number()         :: number().
 -type json_string()         :: binary().
 -type json_array()          :: [json_value()].
--type json_object()         :: {object, json_object_members()}.
+-type json_object()         :: {json_object_members()}.
 -type json_object_members() :: [{json_string(), json_value()}].
 
 %%--------------------------------------------------------------------------------
@@ -61,8 +61,19 @@
 %% @doc JSONバイナリをデコードする.
 %%
 %% デコードに失敗した場合はエラーが送出される
--spec decode(binary()) -> {json_value(), RestJson::binary()}.
+-spec decode(binary()) -> json_value().
 decode(Json) ->
+    try
+        {ok, Value, _} = try_decode(Json),
+        Value
+    catch
+        error:{badmatch, {error, {Reason, [StackItem]}}} ->
+            erlang:raise(error, Reason, [StackItem | erlang:get_stacktrace()])
+    end.
+
+%% @doc JSONバイナリをデコードする.
+-spec try_decode(binary()) -> {ok, json_value(), Rest::binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
+try_decode(Json) ->
     jsone_decode:decode(Json).
 
 %% @doc JSON値をバイナリ形式にエンコードする.
@@ -70,4 +81,15 @@ decode(Json) ->
 %% エンコードに失敗した場合はエラーが送出される
 -spec encode(json_value()) -> binary().
 encode(JsonValue) ->
+    try
+        {ok, Binary} = try_encode(JsonValue),
+        Binary
+    catch
+        error:{badmatch, {error, {Reason, [StackItem]}}} ->
+            erlang:raise(error, Reason, [StackItem | erlang:get_stacktrace()])
+    end.
+
+%% @doc JSON値をバイナリ形式にエンコードする
+-spec try_encode(json_value()) -> {ok, binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
+try_encode(JsonValue) ->
     jsone_encode:encode(JsonValue).

+ 39 - 35
src/jsone_decode.erl

@@ -27,6 +27,8 @@
 %%%---------------------------------------------------------------------------------------
 -module(jsone_decode).
 
+-compile([native, {hipe, [o3]}]).
+
 %%--------------------------------------------------------------------------------
 %% Exported API
 %%--------------------------------------------------------------------------------
@@ -35,6 +37,8 @@
 %%--------------------------------------------------------------------------------
 %% Macros & Types
 %%--------------------------------------------------------------------------------
+-define(ERROR(Function, Args), {error, {badarg, [{?MODULE, Function, Args, [{line, ?LINE}]}]}}).
+
 -type next() :: {array_next, [jsone:json_value()]}
               | {object_value, jsone:json_object_members()}
               | {object_next, jsone:json_string(), jsone:json_object_members()}.
@@ -47,22 +51,22 @@
                          | {object_value, jsone:json_string(), jsone:json_object_members()}
                          | {object_next, jsone:json_string(), jsone:json_value(), jsone:json_object_members()}.
 
+-type decode_result() :: {ok, jsone:json_value(), Rest::binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
+
 %%--------------------------------------------------------------------------------
 %% Exported Functions
 %%--------------------------------------------------------------------------------
 %% @doc JSONバイナリをデコードする.
-%%
-%% デコードに失敗した場合は`{invalid_json, 失敗位置より後のJSON::binary()}'形式のエラーが送出される.
--spec decode(binary()) -> {jsone:json_value(), RestJson::binary()}.
+-spec decode(binary()) -> decode_result().
 decode(<<Json/binary>>) ->
     whitespace(Json, value, [], <<"">>).
 
 %%--------------------------------------------------------------------------------
 %% Internal Functions
 %%--------------------------------------------------------------------------------
--spec next(binary(), jsone:json_value(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec next(binary(), jsone:json_value(), [next()], binary()) -> decode_result().
 next(<<Bin/binary>>, Value, [], _Buf) ->
-    {Value, Bin};
+    {ok, Value, Bin};
 next(<<Bin/binary>>, Value, [Next | Nexts], Buf) ->
     case Next of
         {array_next, Values}        -> whitespace(Bin, {array_next, [Value | Values]}, Nexts, Buf);
@@ -70,7 +74,7 @@ next(<<Bin/binary>>, Value, [Next | Nexts], Buf) ->
         {object_next, Key, Members} -> whitespace(Bin, {object_next, Key, Value, Members}, Nexts, Buf)
     end.
 
--spec whitespace(binary(), whitespace_next(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec whitespace(binary(), whitespace_next(), [next()], binary()) -> decode_result().
 whitespace(<<$  , Bin/binary>>, Next, Nexts, Buf) -> whitespace(Bin, Next, Nexts, Buf);
 whitespace(<<$\t, Bin/binary>>, Next, Nexts, Buf) -> whitespace(Bin, Next, Nexts, Buf);
 whitespace(<<$\r, Bin/binary>>, Next, Nexts, Buf) -> whitespace(Bin, Next, Nexts, Buf);
@@ -82,14 +86,14 @@ whitespace(<<Bin/binary>>,      Next, Nexts, Buf) ->
         object -> object(Bin, Nexts, Buf);
         string -> case Bin of
                       <<$", Bin2/binary>> -> string(Bin2, byte_size(Buf), Nexts, Buf);
-                      _                   -> error(badarg, [Bin, Next, Nexts, Buf])
+                      _                   -> ?ERROR(whitespace, [Bin, Next, Nexts, Buf])
                   end;
         {array_next, Values}               -> array_next(Bin, Values, Nexts, Buf);
         {object_value, Key, Members}       -> object_value(Bin, Key, Members, Nexts, Buf);
         {object_next, Key, Value, Members} -> object_next(Bin, [{Key, Value} | Members], Nexts, Buf)
     end.
 
--spec value(binary(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec value(binary(), [next()], binary()) -> decode_result().
 value(<<"false", Bin/binary>>, Nexts, Buf) -> next(Bin, false, Nexts, Buf);
 value(<<"true", Bin/binary>>, Nexts, Buf)  -> next(Bin, true, Nexts, Buf);
 value(<<"null", Bin/binary>>, Nexts, Buf)  -> next(Bin, null, Nexts, Buf);
@@ -98,29 +102,29 @@ value(<<${, Bin/binary>>, Nexts, Buf)      -> whitespace(Bin, object, Nexts, Buf
 value(<<$", Bin/binary>>, Nexts, Buf)      -> string(Bin, byte_size(Buf), Nexts, Buf);
 value(<<Bin/binary>>, Nexts, Buf)          -> number(Bin, Nexts, Buf).
 
--spec array(binary(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec array(binary(), [next()], binary()) -> decode_result().
 array(<<$], Bin/binary>>, Nexts, Buf) -> next(Bin, [], Nexts, Buf);
 array(<<Bin/binary>>, Nexts, Buf)     -> whitespace(Bin, value, [{array_next, []} | Nexts], Buf).
 
--spec array_next(binary(), [jsone:json_value()], [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec array_next(binary(), [jsone:json_value()], [next()], binary()) -> decode_result().
 array_next(<<$], Bin/binary>>, Values, Nexts, Buf) -> next(Bin, lists:reverse(Values), Nexts, Buf);
 array_next(<<$,, Bin/binary>>, Values, Nexts, Buf) -> whitespace(Bin, value, [{array_next, Values} | Nexts], Buf);
-array_next(Bin,                Values, Nexts, Buf) -> error(badarg, [Bin, Values, Nexts, Buf]).
+array_next(Bin,                Values, Nexts, Buf) -> ?ERROR(array_next, [Bin, Values, Nexts, Buf]).
 
--spec object(binary(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
-object(<<$}, Bin/binary>>, Nexts, Buf) -> next(Bin, {object, []}, Nexts, Buf);
+-spec object(binary(), [next()], binary()) -> decode_result().
+object(<<$}, Bin/binary>>, Nexts, Buf) -> next(Bin, {[]}, Nexts, Buf);
 object(<<Bin/binary>>, Nexts, Buf) -> whitespace(Bin, string, [{object_value, []} | Nexts], Buf).
 
--spec object_value(binary(), jsone:json_string(), jsone:json_object_members(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec object_value(binary(), jsone:json_string(), jsone:json_object_members(), [next()], binary()) -> decode_result().
 object_value(<<$:, Bin/binary>>, Key, Members, Nexts, Buf) -> whitespace(Bin, value, [{object_next, Key, Members} | Nexts], Buf);
-object_value(Bin,                Key, Members, Nexts, Buf) -> error(badarg, [Bin, Key, Members, Nexts, Buf]).
+object_value(Bin,                Key, Members, Nexts, Buf) -> ?ERROR(object_value, [Bin, Key, Members, Nexts, Buf]).
 
--spec object_next(binary(), jsone:json_object_members(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
-object_next(<<$}, Bin/binary>>, Members, Nexts, Buf) -> next(Bin, {object, Members}, Nexts, Buf);
+-spec object_next(binary(), jsone:json_object_members(), [next()], binary()) -> decode_result().
+object_next(<<$}, Bin/binary>>, Members, Nexts, Buf) -> next(Bin, {Members}, Nexts, Buf);
 object_next(<<$,, Bin/binary>>, Members, Nexts, Buf) -> whitespace(Bin, string, [{object_value, Members} | Nexts], Buf);
-object_next(Bin,                Members, Nexts, Buf) -> error(badarg, [Bin, Members, Nexts, Buf]).
+object_next(Bin,                Members, Nexts, Buf) -> ?ERROR(object_next, [Bin, Members, Nexts, Buf]).
 
--spec string(binary(), non_neg_integer(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec string(binary(), non_neg_integer(), [next()], binary()) -> decode_result().
 string(<<$", Bin/binary>>, Start, Nexts, Buf) -> next(Bin, binary:part(Buf, Start, byte_size(Buf) - Start), Nexts, Buf);
 string(<<$\\, B/binary>>,  Start, Nexts, Buf) ->
     case B of
@@ -133,12 +137,12 @@ string(<<$\\, B/binary>>,  Start, Nexts, Buf) ->
         <<$r, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, $\r>>);
         <<$t, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, $\t>>);
         <<$u, Bin/binary>> -> unicode_string(Bin, Start, Nexts, Buf);
-        _                  -> error(badarg, [<<$\\, B/binary>>, Start, Nexts, Buf])
+        _                  -> ?ERROR(string, [<<$\\, B/binary>>, Start, Nexts, Buf])
     end;
 string(<<C, Bin/binary>>, Start, Nexts, Buf) when 16#20 =< C ->
     string(Bin, Start, Nexts, <<Buf/binary, C>>).
 
--spec unicode_string(binary(), non_neg_integer(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec unicode_string(binary(), non_neg_integer(), [next()], binary()) -> decode_result().
 unicode_string(<<N:4/binary, Bin/binary>>, Start, Nexts, Buf) ->
     case binary_to_integer(N, 16) of
         High when 16#D800 =< High, High =< 16#DBFF ->
@@ -149,17 +153,17 @@ unicode_string(<<N:4/binary, Bin/binary>>, Start, Nexts, Buf) ->
                         Low when 16#DC00 =< Low, Low =< 16#DFFF ->
                             Unicode = 16#10000 + (High - 16#D800) * 16#400 + (Low - 16#DC00),
                             string(Bin2, Start, Nexts, unicode_to_utf8(Unicode, Buf));
-                        _ -> error(badarg, [<<N/binary, Bin/binary>>, Start, Nexts, Buf])
+                        _ -> ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf])
                     end;
-                _ -> error(badarg, [<<N/binary, Bin/binary>>, Start, Nexts, Buf])
+                _ -> ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf])
             end;
         Unicode when 16#DC00 =< Unicode, Unicode =< 16#DFFF ->  % サロゲートペアの後半部分
-            error(badarg, [<<N/binary, Bin/binary>>, Start, Nexts, Buf]);
+            ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf]);
         Unicode -> 
             string(Bin, Start, Nexts, unicode_to_utf8(Unicode, Buf))
     end;
 unicode_string(Bin, Acc, Nexts, Buf) ->
-    error(badarg, [Bin, Acc, Nexts, Buf]).
+    ?ERROR(unicode_string, [Bin, Acc, Nexts, Buf]).
 
 -spec unicode_to_utf8(0..1114111, binary()) -> binary().
 unicode_to_utf8(Code, Buf) when Code < 16#80 ->
@@ -180,39 +184,39 @@ unicode_to_utf8(Code, Buf) -> % NOTE: サロゲートペアの仕組み上、コ
     D = 2#10000000 bor (Code band 2#111111),
     <<Buf/binary, A, B, C, D>>.
 
--spec number(binary(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec number(binary(), [next()], binary()) -> decode_result().
 number(<<$-, Bin/binary>>, Nexts, Buf) -> number_integer_part(Bin, -1, Nexts, Buf);
 number(<<Bin/binary>>,     Nexts, Buf) -> number_integer_part(Bin,  1, Nexts, Buf).
 
--spec number_integer_part(binary(), 1|-1, [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec number_integer_part(binary(), 1|-1, [next()], binary()) -> decode_result().
 number_integer_part(<<$0, Bin/binary>>, Sign, Nexts, Buf) ->
     number_fraction_part(Bin, Sign, 0, Nexts, Buf);
 number_integer_part(<<C, Bin/binary>>, Sign, Nexts, Buf) when $1 =< C, C =< $9 ->
     number_integer_part_rest(Bin, C - $0, Sign, Nexts, Buf);
 number_integer_part(Bin, Sign, Nexts, Buf) ->
-    error(badarg, [Bin, Sign, Nexts, Buf]).
+    ?ERROR(number_integer_part, [Bin, Sign, Nexts, Buf]).
 
--spec number_integer_part_rest(binary(), non_neg_integer(), 1|-1, [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec number_integer_part_rest(binary(), non_neg_integer(), 1|-1, [next()], binary()) -> decode_result().
 number_integer_part_rest(<<C, Bin/binary>>, N, Sign, Nexts, Buf) when $0 =< C, C =< $9 ->
     number_integer_part_rest(Bin, N * 10 + C - $0, Sign, Nexts, Buf);
 number_integer_part_rest(<<Bin/binary>>, N, Sign, Nexts, Buf) ->
     number_fraction_part(Bin, Sign, N, Nexts, Buf).
 
--spec number_fraction_part(binary(), 1|-1, non_neg_integer(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec number_fraction_part(binary(), 1|-1, non_neg_integer(), [next()], binary()) -> decode_result().
 number_fraction_part(<<$., Bin/binary>>, Sign, Int, Nexts, Buf) ->
     number_fraction_part_rest(Bin, Sign, Int, 0, Nexts, Buf);
 number_fraction_part(<<Bin/binary>>, Sign, Int, Nexts, Buf) ->
     number_exponation_part(Bin, Sign * Int, 0, Nexts, Buf).
 
--spec number_fraction_part_rest(binary(), 1|-1, non_neg_integer(), non_neg_integer(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec number_fraction_part_rest(binary(), 1|-1, non_neg_integer(), non_neg_integer(), [next()], binary()) -> decode_result().
 number_fraction_part_rest(<<C, Bin/binary>>, Sign, N, DecimalOffset, Nexts, Buf) when $0 =< C, C =< $9 ->
     number_fraction_part_rest(Bin, Sign, N * 10 + C - $0, DecimalOffset + 1, Nexts, Buf);
 number_fraction_part_rest(<<Bin/binary>>, Sign, N, DecimalOffset, Nexts, Buf) when DecimalOffset > 0 ->
     number_exponation_part(Bin, Sign * N, DecimalOffset, Nexts, Buf);
 number_fraction_part_rest(Bin, Sign, N, DecimalOffset, Nexts, Buf) ->
-    error(badarg, [Bin, Sign, N, DecimalOffset, Nexts, Buf]).
+    ?ERROR(number_fraction_part_rest, [Bin, Sign, N, DecimalOffset, Nexts, Buf]).
 
--spec number_exponation_part(binary(), integer(), non_neg_integer(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec number_exponation_part(binary(), integer(), non_neg_integer(), [next()], binary()) -> decode_result().
 number_exponation_part(<<$e, $+, Bin/binary>>, N, DecimalOffset, Nexts, Buf) ->
     number_exponation_part(Bin, N, DecimalOffset, 1, 0, true, Nexts, Buf);
 number_exponation_part(<<$E, $+, Bin/binary>>, N, DecimalOffset, Nexts, Buf) ->
@@ -231,11 +235,11 @@ number_exponation_part(<<Bin/binary>>, N, DecimalOffset, Nexts, Buf) ->
         _ -> next(Bin, N / math:pow(10, DecimalOffset), Nexts, Buf)
     end.
 
--spec number_exponation_part(binary(), integer(), non_neg_integer(), 1|-1, non_neg_integer(), boolean(), [next()], binary()) -> {jsone:json_value(), Rest::binary()}.
+-spec number_exponation_part(binary(), integer(), non_neg_integer(), 1|-1, non_neg_integer(), boolean(), [next()], binary()) -> decode_result().
 number_exponation_part(<<C, Bin/binary>>, N, DecimalOffset, ExpSign, Exp, _, Nexts, Buf) when $0 =< C, C =< $9 ->
     number_exponation_part(Bin, N, DecimalOffset, ExpSign, Exp * 10 + C - $0, false, Nexts, Buf);
 number_exponation_part(<<Bin/binary>>, N, DecimalOffset, ExpSign, Exp, false, Nexts, Buf) ->
     Pos = ExpSign * Exp - DecimalOffset,
     next(Bin, N * math:pow(10, Pos), Nexts, Buf);
 number_exponation_part(Bin, N, DecimalOffset, ExpSign, Exp, IsFirst, Nexts, Buf) ->
-    error(badarg, [Bin, N, DecimalOffset, ExpSign, Exp, IsFirst, Nexts, Buf]).
+    ?ERROR(number_exponation_part, [Bin, N, DecimalOffset, ExpSign, Exp, IsFirst, Nexts, Buf]).

+ 75 - 64
src/jsone_encode.erl

@@ -27,6 +27,8 @@
 %%%---------------------------------------------------------------------------------------
 -module(jsone_encode).
 
+-compile([native, {hipe, [o3]}]).
+
 %%--------------------------------------------------------------------------------
 %% Exported API
 %%--------------------------------------------------------------------------------
@@ -35,6 +37,7 @@
 %%--------------------------------------------------------------------------------
 %% Macros
 %%--------------------------------------------------------------------------------
+-define(ERROR(Function, Args), {error, {badarg, [{?MODULE, Function, Args, [{line, ?LINE}]}]}}).
 -define(IS_REDUNDANT_UTF8(B1, B2, FirstBitN), (B1 =:= 0 andalso B2 < (1 bsl (FirstBitN + 1)))).
 -define(HEX(N, I), (binary:at(<<"0123456789abcdef">>, (N bsr (I * 4)) band 2#1111))).
 -define(UNICODE_TO_HEX(Code), ?HEX(Code, 3), ?HEX(Code, 2), ?HEX(Code, 1), ?HEX(Code, 0)).
@@ -43,84 +46,92 @@
 %% Exported Functions
 %%--------------------------------------------------------------------------------
 %% @doc JSON値をバイナリ形式にエンコードする.
--spec encode(jsone:json_value()) -> binary().
+% -spec encode(jsone:json_value()) -> binary().
 encode(Value) ->
-    value(Value, <<"">>).
+    value(Value, [], <<"">>).
 
 %%--------------------------------------------------------------------------------
 %% Internal Functions
 %%--------------------------------------------------------------------------------
--spec value(jsone:json_value(), binary()) -> binary().
-value(null, Buf)                         -> <<Buf/binary, "null">>;
-value(false, Buf)                        -> <<Buf/binary, "false">>;
-value(true, Buf)                         -> <<Buf/binary, "true">>;
-value(Value, Buf) when is_integer(Value) -> <<Buf/binary, (integer_to_binary(Value))/binary>>;
-value(Value, Buf) when is_float(Value)   -> <<Buf/binary, (float_to_binary(Value))/binary>>;
-value(Value, Buf) when is_binary(Value)  -> string(Value, Buf);
-value(Value, Buf) when is_list(Value)    -> array(Value, Buf);
-value({object, _} = Value, Buf)          -> object(Value, Buf);
-value(Value, Buf)                        -> error(badarg, [Value, Buf]).
+next([], Buf)             -> {ok, Buf};
+next([Next | Nexts], Buf) ->
+    case Next of
+        {array_values, Values} ->
+            case Values of
+                [] -> array_values(Values, Nexts, Buf);
+                _  -> array_values(Values, Nexts, <<Buf/binary, $,>>)
+            end;
+        {object_value, Value, Members} ->
+            object_value(Value, Members, Nexts, Buf);
+        {object_members, Members} ->
+            case Members of
+                [] -> object_members(Members, Nexts, Buf);
+                _  -> object_members(Members, Nexts, <<Buf/binary, $,>>)
+            end
+    end.
+
+% -spec value(jsone:json_value(), binary()) -> binary().
+value(null, Nexts, Buf)                         -> next(Nexts, <<Buf/binary, "null">>);
+value(false, Nexts, Buf)                        -> next(Nexts, <<Buf/binary, "false">>);
+value(true, Nexts, Buf)                         -> next(Nexts, <<Buf/binary, "true">>);
+value(Value, Nexts, Buf) when is_integer(Value) -> next(Nexts, <<Buf/binary, (integer_to_binary(Value))/binary>>);
+value(Value, Nexts, Buf) when is_float(Value)   -> next(Nexts, <<Buf/binary, (float_to_binary(Value))/binary>>);
+value(Value, Nexts, Buf) when is_binary(Value)  -> string(Value, Nexts, Buf);
+value(Value, Nexts, Buf) when is_list(Value)    -> array(Value, Nexts, Buf);
+value({_} = Value, Nexts, Buf)                  -> object(Value, Nexts, Buf);
+value(Value, Nexts, Buf)                        -> ?ERROR(value, [Value, Nexts, Buf]).
 
--spec string(jsone:json_string(), binary()) -> binary().
-string(<<Str/binary>>, Buf) ->
-    escape_string(Str, <<Buf/binary, $">>).
+% -spec string(jsone:json_string(), binary()) -> binary().
+string(<<Str/binary>>, Nexts, Buf) ->
+    escape_string(Str, Nexts, <<Buf/binary, $">>).
 
--spec escape_string(binary(), binary()) -> binary().
-escape_string(<<"">>,                   Buf) -> <<Buf/binary, $">>;
-escape_string(<<$", Str/binary>>,       Buf) -> escape_string(Str, <<Buf/binary, $\\, $">>);
-escape_string(<<$\/, Str/binary>>,      Buf) -> escape_string(Str, <<Buf/binary, $\\, $\/>>);
-escape_string(<<$\\, Str/binary>>,      Buf) -> escape_string(Str, <<Buf/binary, $\\, $\\>>);
-escape_string(<<$\b, Str/binary>>,      Buf) -> escape_string(Str, <<Buf/binary, $\\, $b>>);
-escape_string(<<$\f, Str/binary>>,      Buf) -> escape_string(Str, <<Buf/binary, $\\, $f>>);
-escape_string(<<$\n, Str/binary>>,      Buf) -> escape_string(Str, <<Buf/binary, $\\, $n>>);
-escape_string(<<$\r, Str/binary>>,      Buf) -> escape_string(Str, <<Buf/binary, $\\, $r>>);
-escape_string(<<$\t, Str/binary>>,      Buf) -> escape_string(Str, <<Buf/binary, $\\, $t>>);
-escape_string(<<0:1, C:7, Str/binary>>, Buf) -> escape_string(Str, <<Buf/binary, C>>);
-escape_string(<<2#110:3, B1:5, 2#10:2, B2:6, Str/binary>>, Buf) when not ?IS_REDUNDANT_UTF8(B1, B2, 5) ->
+% -spec escape_string(binary(), binary()) -> binary().
+escape_string(<<"">>,                   Nexts, Buf) -> next(Nexts, <<Buf/binary, $">>);
+escape_string(<<$", Str/binary>>,       Nexts, Buf) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $">>);
+escape_string(<<$\/, Str/binary>>,      Nexts, Buf) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $\/>>);
+escape_string(<<$\\, Str/binary>>,      Nexts, Buf) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $\\>>);
+escape_string(<<$\b, Str/binary>>,      Nexts, Buf) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $b>>);
+escape_string(<<$\f, Str/binary>>,      Nexts, Buf) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $f>>);
+escape_string(<<$\n, Str/binary>>,      Nexts, Buf) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $n>>);
+escape_string(<<$\r, Str/binary>>,      Nexts, Buf) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $r>>);
+escape_string(<<$\t, Str/binary>>,      Nexts, Buf) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $t>>);
+escape_string(<<0:1, C:7, Str/binary>>, Nexts, Buf) -> escape_string(Str, Nexts, <<Buf/binary, C>>);
+escape_string(<<2#110:3, B1:5, 2#10:2, B2:6, Str/binary>>, Nexts, Buf) when not ?IS_REDUNDANT_UTF8(B1, B2, 5) ->
     Unicode = (B1 bsl 6) + B2,
-    escape_unicode_char(Str, Unicode, Buf);
-escape_string(<<2#1110:4, B1:4, 2#10:2, B2:6, 2#10:2, B3:6, Str/binary>>, Buf) when not ?IS_REDUNDANT_UTF8(B1, B2, 4) ->
+    escape_unicode_char(Str, Unicode, Nexts, Buf);
+escape_string(<<2#1110:4, B1:4, 2#10:2, B2:6, 2#10:2, B3:6, Str/binary>>, Nexts, Buf) when not ?IS_REDUNDANT_UTF8(B1, B2, 4) ->
     Unicode = (B1 bsl 12) + (B2 bsl 6) + B3,
-    escape_unicode_char(Str, Unicode, Buf);
-escape_string(<<2#11110:5, B1:3, 2#10:2, B2:6, 2#10:2, B3:6, 2#10:2, B4:6, Str/binary>>, Buf) when not ?IS_REDUNDANT_UTF8(B1, B2, 3) ->
+    escape_unicode_char(Str, Unicode, Nexts, Buf);
+escape_string(<<2#11110:5, B1:3, 2#10:2, B2:6, 2#10:2, B3:6, 2#10:2, B4:6, Str/binary>>, Nexts, Buf) when not ?IS_REDUNDANT_UTF8(B1, B2, 3) ->
     Unicode = (B1 bsl 18) + (B2 bsl 12) + (B3 bsl 6) + B4,
-    escape_unicode_char(Str, Unicode, Buf);
-escape_string(Str, Buf) ->
-    error(badarg, [Str, Buf]).
+    escape_unicode_char(Str, Unicode, Nexts, Buf);
+escape_string(Str, Nexts, Buf) ->
+    ?ERROR(escape_string, [Str, Nexts, Buf]).
 
--spec escape_unicode_char(binary(), char(), binary()) -> binary().
-escape_unicode_char(<<Str/binary>>, Unicode, Buf) when Unicode =< 16#FFFF ->
-    escape_string(Str, <<Buf/binary, $\\, $u, ?UNICODE_TO_HEX(Unicode)>>);
-escape_unicode_char(<<Str/binary>>, Unicode, Buf) ->
+% -spec escape_unicode_char(binary(), char(), binary()) -> binary().
+escape_unicode_char(<<Str/binary>>, Unicode, Nexts, Buf) when Unicode =< 16#FFFF ->
+    escape_string(Str, Nexts, <<Buf/binary, $\\, $u, ?UNICODE_TO_HEX(Unicode)>>);
+escape_unicode_char(<<Str/binary>>, Unicode, Nexts, Buf) ->
     %% サロゲートペア
     <<High:10, Low:10>> = <<(Unicode - 16#10000):20>>, % 非効率
-    escape_string(Str, <<Buf/binary, $\\, $u, ?UNICODE_TO_HEX(High + 16#D800), $\\, $u, ?UNICODE_TO_HEX(Low + 16#DC00)>>).
+    escape_string(Str, Nexts, <<Buf/binary, $\\, $u, ?UNICODE_TO_HEX(High + 16#D800), $\\, $u, ?UNICODE_TO_HEX(Low + 16#DC00)>>).
 
--spec array(jsone:json_array(), binary()) -> binary().
-array(List, Buf) ->
-    array_values(List, <<Buf/binary, $[>>).
+% -spec array(jsone:json_array(), binary()) -> binary().
+array(List, Nexts, Buf) ->
+    array_values(List, Nexts, <<Buf/binary, $[>>).
 
--spec array_values(jsone:json_array(), binary()) -> binary().
-array_values([],       Buf) -> <<Buf/binary, $]>>;
-array_values([X | Xs], Buf) ->
-    Buf2 = value(X, Buf),
-    case Xs of
-        [] -> <<Buf2/binary, $]>>;
-        _  -> array_values(Xs, <<Buf2/binary, $,>>)
-    end.
+% -spec array_values(jsone:json_array(), binary()) -> binary().
+array_values([],       Nexts, Buf) -> next(Nexts, <<Buf/binary, $]>>);
+array_values([X | Xs], Nexts, Buf) -> value(X, [{array_values, Xs} | Nexts], Buf).
+
+% -spec object(jsone:json_object(), binary()) -> binary().
+object({Members}, Nexts, Buf) ->
+    object_members(Members, Nexts, <<Buf/binary, ${>>).
 
--spec object(jsone:json_object(), binary()) -> binary().
-object({object, Members}, Buf) ->
-    object_members(Members, <<Buf/binary, ${>>).
+% -spec object_members(jsone:json_object_members(), binary()) -> binary().
+object_members([],                             Nexts, Buf) -> next(Nexts, <<Buf/binary, $}>>);
+object_members([{<<Key/binary>>, Value} | Xs], Nexts, Buf) -> string(Key, [{object_value, Value, Xs} | Nexts], Buf);
+object_members(Arg, Nexts, Buf)                            -> ?ERROR(object_members, [Arg, Nexts, Buf]).
 
--spec object_members(jsone:json_object_members(), binary()) -> binary().
-object_members([],                             Buf) -> <<Buf/binary, $}>>;
-object_members([{<<Key/binary>>, Value} | Xs], Buf) ->
-    Buf2 = string(Key, Buf),
-    Buf3 = value(Value, <<Buf2/binary, $:>>),
-    case Xs of
-        [] -> <<Buf3/binary, $}>>;
-        _  -> object_members(Xs, <<Buf3/binary, $,>>)
-    end;
-object_members(Arg, Buf) ->
-    error(badarg, [Arg, Buf]).
+object_value(Value, Members, Nexts, Buf) ->
+    value(Value, [{object_members, Members} | Nexts], <<Buf/binary, $:>>).

+ 56 - 56
test/jsone_decode_tests.erl

@@ -8,114 +8,114 @@ decode_test_() ->
      %% シンボル系
      {"'false'がデコード可能",
       fun () ->
-              ?assertEqual({false, <<"">>}, jsone_decode:decode(<<"false">>))
+              ?assertEqual({ok, false, <<"">>}, jsone_decode:decode(<<"false">>))
       end},
      {"'true'がデコード可能",
       fun () ->
-              ?assertEqual({true, <<"">>}, jsone_decode:decode(<<"true">>))
+              ?assertEqual({ok, true, <<"">>}, jsone_decode:decode(<<"true">>))
       end},
      {"'null'がデコード可能",
       fun () ->
-              ?assertEqual({null, <<"">>}, jsone_decode:decode(<<"null">>))
+              ?assertEqual({ok, null, <<"">>}, jsone_decode:decode(<<"null">>))
       end},
      {"正の整数がデコード可能",
       fun () ->
-              ?assertEqual({1, <<"">>}, jsone_decode:decode(<<"1">>))
+              ?assertEqual({ok, 1, <<"">>}, jsone_decode:decode(<<"1">>))
       end},
 
      %% 数値系: 整数
      {"0がデコード可能",
       fun () ->
-              ?assertEqual({0, <<"">>}, jsone_decode:decode(<<"0">>))
+              ?assertEqual({ok, 0, <<"">>}, jsone_decode:decode(<<"0">>))
       end},
      {"負の整数がデコード可能",
       fun () ->
-              ?assertEqual({-1, <<"">>}, jsone_decode:decode(<<"-1">>))
+              ?assertEqual({ok, -1, <<"">>}, jsone_decode:decode(<<"-1">>))
       end},
      {"整数の値の大きさに制限はなし",
       fun () ->
-              ?assertEqual({111111111111111111111111111111111111111111111111111111111111111111111111111111, <<"">>},
+              ?assertEqual({ok, 111111111111111111111111111111111111111111111111111111111111111111111111111111, <<"">>},
                            jsone_decode:decode(<<"111111111111111111111111111111111111111111111111111111111111111111111111111111">>))
       end},
      {"先頭に余計な0がつく場合は、先頭文字とそれ以降が別々のトークンと判断される",
       fun () ->
-              ?assertEqual({0, <<"0">>}, jsone_decode:decode(<<"00">>)),
-              ?assertEqual({0, <<"1">>}, jsone_decode:decode(<<"01">>)),
-              ?assertEqual({0, <<"1">>}, jsone_decode:decode(<<"-01">>))
+              ?assertEqual({ok, 0, <<"0">>}, jsone_decode:decode(<<"00">>)),
+              ?assertEqual({ok, 0, <<"1">>}, jsone_decode:decode(<<"01">>)),
+              ?assertEqual({ok, 0, <<"1">>}, jsone_decode:decode(<<"-01">>))
       end},
      {"正の整数の前の'+'記号は許可されない",
       fun () ->
-              ?assertError(badarg, jsone_decode:decode(<<"+1">>))
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"+1">>))
       end},
 
      %% 数値系: 小数
      {"小数がデコード可能",
       fun () ->
-              ?assertEqual({1.23, <<"">>}, jsone_decode:decode(<<"1.23">>))
+              ?assertEqual({ok, 1.23, <<"">>}, jsone_decode:decode(<<"1.23">>))
       end},
      {"指数形式の小数がデコード可能",
       fun () ->
-              ?assertEqual({12.345, <<"">>}, jsone_decode:decode(<<"12345e-3">>)),
-              ?assertEqual({12.345, <<"">>}, jsone_decode:decode(<<"12345E-3">>)), % 'e'は大文字でも可
-              ?assertEqual({12.345, <<"">>}, jsone_decode:decode(<<"12345.0e-3">>)),
-              ?assertEqual({12.345, <<"">>}, jsone_decode:decode(<<"0.12345E2">>)),
-              ?assertEqual({12.345, <<"">>}, jsone_decode:decode(<<"0.12345e+2">>)), % 指数部では'+'をつけても良い
-              ?assertEqual({12.345, <<"">>}, jsone_decode:decode(<<"0.12345E+2">>)), % 指数部では'+'をつけても良い
-              ?assertEqual({-12.345, <<"">>}, jsone_decode:decode(<<"-0.012345e3">>))
+              ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"12345e-3">>)),
+              ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"12345E-3">>)), % 'e'は大文字でも可
+              ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"12345.0e-3">>)),
+              ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"0.12345E2">>)),
+              ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"0.12345e+2">>)), % 指数部では'+'をつけても良い
+              ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"0.12345E+2">>)), % 指数部では'+'をつけても良い
+              ?assertEqual({ok, -12.345, <<"">>}, jsone_decode:decode(<<"-0.012345e3">>))
       end},
      {"不正な形式の小数",
       fun () ->
-              ?assertError(badarg, jsone_decode:decode(<<".123">>)),  % 整数部が省略されている
-              ?assertError(badarg, jsone_decode:decode(<<"0.">>)),    % '.'の後ろに小数部が続かない
-              ?assertError(badarg, jsone_decode:decode(<<"0.e+3">>)), % '.'の後ろに指数部が来る
-              ?assertError(badarg, jsone_decode:decode(<<"0.1e">>)),    % 指数部が欠けている
-              ?assertError(badarg, jsone_decode:decode(<<"0.1e-">>)),   % 指数部が欠けている
-              ?assertError(badarg, jsone_decode:decode(<<"0.1ee-1">>)), % 'e'が複数ある
-              ?assertError(badarg, jsone_decode:decode(<<"0.1e--1">>)), % 符号が複数ある
-              ?assertEqual({0.1, <<".2">>}, jsone_decode:decode(<<"0.1.2">>))  % '.'が複数ある => 別々のトークンと判断される
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<".123">>)),  % 整数部が省略されている
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.">>)),    % '.'の後ろに小数部が続かない
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.e+3">>)), % '.'の後ろに指数部が来る
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e">>)),    % 指数部が欠けている
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e-">>)),   % 指数部が欠けている
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1ee-1">>)), % 'e'が複数ある
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e--1">>)), % 符号が複数ある
+              ?assertEqual({ok, 0.1, <<".2">>}, jsone_decode:decode(<<"0.1.2">>))  % '.'が複数ある => 別々のトークンと判断される
       end},
      
      %% 文字列系
      {"文字列がデコード可能",
       fun () ->
-              ?assertEqual({<<"abc">>,  <<"">>}, jsone_decode:decode(<<"\"abc\"">>))
+              ?assertEqual({ok, <<"abc">>,  <<"">>}, jsone_decode:decode(<<"\"abc\"">>))
       end},
      {"各種エスケープ文字がデコード可能",
       fun () ->
               Input    = list_to_binary([$", [[$\\, C] || C <- [$", $/, $\\, $b, $f, $n, $r, $t]], $"]),
               Expected = <<"\"\/\\\b\f\n\r\t">>,
-              ?assertEqual({Expected, <<"">>}, jsone_decode:decode(Input))
+              ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
      {"エスケープされたUTF-16文字列がデコード可能",
       fun () ->
               %% 日本語
               Input1    = <<"\"\\u3042\\u3044\\u3046\\u3048\\u304A\"">>,
               Expected1 = <<"あいうえお">>,  % このファイルの文字エンコーディングがUTF-8であることが前提
-              ?assertEqual({Expected1, <<"">>}, jsone_decode:decode(Input1)),
+              ?assertEqual({ok, Expected1, <<"">>}, jsone_decode:decode(Input1)),
 
               %% ascii
               Input2    = <<"\"\\u0061\\u0062\\u0063\"">>,
               Expected2 = <<"abc">>,
-              ?assertEqual({Expected2, <<"">>}, jsone_decode:decode(Input2)),
+              ?assertEqual({ok, Expected2, <<"">>}, jsone_decode:decode(Input2)),
 
               %% 日本語以外のマルチバイト文字
               Input3    = <<"\"\\u06DD\\u06DE\\u10AE\\u10AF\"">>,
               Expected3 = <<"۝۞ႮႯ">>,
-              ?assertEqual({Expected3, <<"">>}, jsone_decode:decode(Input3))
+              ?assertEqual({ok, Expected3, <<"">>}, jsone_decode:decode(Input3))
       end},
      {"サロゲートペアを含む文字列がデコード可能",
       fun () ->
               Input    = <<"\"\\ud848\\udc49\\ud848\\udc9a\\ud848\\udcfc\"">>,
               Expected = <<"𢁉𢂚𢃼">>,
-              ?assertEqual({Expected, <<"">>}, jsone_decode:decode(Input))
+              ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
      {"不正なエスケープ文字",
       fun () ->
-              ?assertError(badarg, jsone_decode:decode(<<"\"\\z\"">>)),    % '\z'は未定義のエスケープ文字
-              ?assertError(badarg, jsone_decode:decode(<<"\"\\uab\"">>)),  % '\u'の後ろに続く数値が足りない
-              ?assertError(badarg, jsone_decode:decode(<<"\"\\ud848\"">>)), % 上位サロゲートが単独で出現
-              ?assertError(badarg, jsone_decode:decode(<<"\"\\udc49\"">>)), % 下位サロゲーが単独で出現
-              ?assertError(badarg, jsone_decode:decode(<<"\"\\ud848\\u0061\"">>)) % 上位サロゲートの後ろに下位サロゲートが続かない
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\z\"">>)),    % '\z'は未定義のエスケープ文字
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\uab\"">>)),  % '\u'の後ろに続く数値が足りない
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\ud848\"">>)), % 上位サロゲートが単独で出現
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\udc49\"">>)), % 下位サロゲーが単独で出現
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\ud848\\u0061\"">>)) % 上位サロゲートの後ろに下位サロゲートが続かない
       end},
 
      %% 配列系
@@ -123,78 +123,78 @@ decode_test_() ->
       fun () ->
               Input    = <<"[1, 2, \"abc\", null]">>,
               Expected = [1, 2, <<"abc">>, null],
-              ?assertEqual({Expected, <<"">>}, jsone_decode:decode(Input))
+              ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
      {"空配列がデコード可能",
       fun () ->
-              ?assertEqual({[], <<"">>}, jsone_decode:decode(<<"[]">>)),
-              ?assertEqual({[], <<"">>}, jsone_decode:decode(<<"[ \t\r\n]">>))
+              ?assertEqual({ok, [], <<"">>}, jsone_decode:decode(<<"[]">>)),
+              ?assertEqual({ok, [], <<"">>}, jsone_decode:decode(<<"[ \t\r\n]">>))
       end},
      {"配列の末尾のカンマは許容されない",
       fun () ->
               Input = <<"[1, 2, \"abc\", null, ]">>,
-              ?assertError(badarg, jsone_decode:decode(Input))
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
      {"区切り文字のカンマが抜けているとエラーとなる",
       fun () ->
               Input = <<"[1 2, \"abc\", null]">>, % 1と2の間にカンマがない
-              ?assertError(badarg, jsone_decode:decode(Input))
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
      {"配列が閉じていないとエラー",
       fun () ->
               Input = <<"[1, 2, \"abc\", null">>,
-              ?assertError(badarg, jsone_decode:decode(Input))
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
 
      %% オブジェクト系
      {"オブジェクトがデコード可能",
       fun () ->
               Input    = <<"{\"1\":2, \"key\":\"value\"}">>,
-              Expected = {object, [{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
-              ?assertEqual({Expected, <<"">>}, jsone_decode:decode(Input))
+              Expected = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
+              ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
      {"空オブジェクトがデコード可能",
       fun () ->
-              ?assertEqual({{object, []}, <<"">>}, jsone_decode:decode(<<"{}">>)),
-              ?assertEqual({{object, []}, <<"">>}, jsone_decode:decode(<<"{ \t\r\n}">>))
+              ?assertEqual({ok, {[]}, <<"">>}, jsone_decode:decode(<<"{}">>)),
+              ?assertEqual({ok, {[]}, <<"">>}, jsone_decode:decode(<<"{ \t\r\n}">>))
       end},
      {"オブジェクトの末尾のカンマは許容されない",
       fun () ->
               Input = <<"{\"1\":2, \"key\":\"value\", }">>,
               io:format("~p\n", [catch jsone_decode:decode(Input)]),
-              ?assertError(badarg, jsone_decode:decode(Input))
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
      {"区切り文字のカンマが抜けているとエラーとなる",
       fun () ->
               Input = <<"{\"1\":2 \"key\":\"value\"}">>,
-              ?assertError(badarg, jsone_decode:decode(Input))
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
      {"メンバのキーがない場合はエラー",
       fun () ->
               Input = <<"{:2, \"key\":\"value\"}">>,
-              ?assertError(badarg, jsone_decode:decode(Input))
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
      {"メンバのキーが文字列以外の場合はエラー",
       fun () ->
               Input = <<"{1:2, \"key\":\"value\"}">>,
-              ?assertError(badarg, jsone_decode:decode(Input))
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
      {"メンバの値がない場合はエラー",
       fun () ->
               Input = <<"{\"1\", \"key\":\"value\"}">>,
-              ?assertError(badarg, jsone_decode:decode(Input))
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
      {"オブジェクトが閉じていないとエラー",
       fun () ->
               Input = <<"{\"1\":2 \"key\":\"value\"">>,
-              ?assertError(badarg, jsone_decode:decode(Input))
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
 
      %% その他
      {"複雑なデータがデコード可能",
       fun () ->
               Input    = <<"  [true, {\"1\" : 2, \"array\":[[[[1]]], {\"ab\":\"cd\"}, false]}, null]   ">>,
-              Expected = [true, {object, [{<<"array">>, [[[[1]]], {object, [{<<"ab">>, <<"cd">>}]}, false]}, {<<"1">>, 2}]}, null],
-              ?assertEqual({Expected, <<"   ">>}, jsone_decode:decode(Input))
+              Expected = [true, {[{<<"array">>, [[[[1]]], {[{<<"ab">>, <<"cd">>}]}, false]}, {<<"1">>, 2}]}, null],
+              ?assertEqual({ok, Expected, <<"   ">>}, jsone_decode:decode(Input))
       end}
     ].

+ 27 - 27
test/jsone_encode_tests.erl

@@ -8,72 +8,72 @@ encode_test_() ->
      %% シンボル系
      {"false がエンコード可能",
       fun () ->
-              ?assertEqual(<<"false">>, jsone_encode:encode(false))
+              ?assertEqual({ok, <<"false">>}, jsone_encode:encode(false))
       end},
      {"true がエンコード可能",
       fun () ->
-              ?assertEqual(<<"true">>, jsone_encode:encode(true))
+              ?assertEqual({ok, <<"true">>}, jsone_encode:encode(true))
       end},
      {"null がエンコード可能",
       fun () ->
-              ?assertEqual(<<"null">>, jsone_encode:encode(null))
+              ?assertEqual({ok, <<"null">>}, jsone_encode:encode(null))
       end},
 
      %% 数値系: 整数
      {"0がエンコード可能",
       fun () ->
-              ?assertEqual(<<"0">>, jsone_encode:encode(0))
+              ?assertEqual({ok, <<"0">>}, jsone_encode:encode(0))
       end},
      {"正の整数がエンコード可能",
       fun () ->
-              ?assertEqual(<<"1">>, jsone_encode:encode(1))
+              ?assertEqual({ok, <<"1">>}, jsone_encode:encode(1))
       end},
      {"負の整数がエンコード可能",
       fun () ->
-              ?assertEqual(<<"-1">>, jsone_encode:encode(-1))
+              ?assertEqual({ok, <<"-1">>}, jsone_encode:encode(-1))
       end},
      {"巨大な整数がエンコード可能",
       fun () ->
-              ?assertEqual(<<"11111111111111111111111111111111111111111111111111111111111111111111111">>,
+              ?assertEqual({ok, <<"11111111111111111111111111111111111111111111111111111111111111111111111">>},
                            jsone_encode:encode(11111111111111111111111111111111111111111111111111111111111111111111111))
       end},
 
      %% 数値系: 小数
      {"小数がエンコード可能",
       fun () ->
-              Input   = 1.234,
-              Encoded = jsone_encode:encode(Input),
-              ?assertEqual(Input, binary_to_float(Encoded))
+              Input = 1.234,
+              ?assertMatch({ok, _}, jsone_encode:encode(Input)),
+              ?assertEqual(Input, binary_to_float(element(2, jsone_encode:encode(Input))))
       end},
 
      %% 文字列系
      {"文字列がエンコード可能",
       fun () ->
-              ?assertEqual(<<"\"abc\"">>, jsone_encode:encode(<<"abc">>))
+              ?assertEqual({ok, <<"\"abc\"">>}, jsone_encode:encode(<<"abc">>))
       end},
      {"各種エスケープ文字を含む文字列をエンコード可能",
       fun () ->
               Input    = <<"\"\/\\\b\f\n\r\t">>,
               Expected = list_to_binary([$", [[$\\, C] || C <- [$", $/, $\\, $b, $f, $n, $r, $t]], $"]),
-              ?assertEqual(Expected, jsone_encode:encode(Input))
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
      {"UTF-8形式のマルチバイト文字列がエンコード可能",
       fun () ->
               %% 日本語
               Input1    = <<"あいうえお">>,  % このファイルの文字エンコーディングがUTF-8であることが前提
               Expected1 = <<"\"\\u3042\\u3044\\u3046\\u3048\\u304a\"">>,
-              ?assertEqual(Expected1, jsone_encode:encode(Input1)),
+              ?assertEqual({ok, Expected1}, jsone_encode:encode(Input1)),
 
               %% 日本語以外のマルチバイト文字
               Input2    = <<"۝۞ႮႯ">>,
               Expected2 = <<"\"\\u06dd\\u06de\\u10ae\\u10af\"">>,
-              ?assertEqual(Expected2, jsone_encode:encode(Input2))
+              ?assertEqual({ok, Expected2}, jsone_encode:encode(Input2))
       end},
      {"サロゲートペアを含む文字列がエンコード可能",
       fun () ->
               Input    = <<"𢁉𢂚𢃼">>,
               Expected = <<"\"\\ud848\\udc49\\ud848\\udc9a\\ud848\\udcfc\"">>,
-              ?assertEqual(Expected, jsone_encode:encode(Input))
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
 
      %% 配列系
@@ -81,44 +81,44 @@ encode_test_() ->
       fun () ->
               Input    = [1, 2, 3],
               Expected = <<"[1,2,3]">>,
-              ?assertEqual(Expected, jsone_encode:encode(Input))
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
      {"空配列がエンコード可能",
       fun () ->
               Input    = [],
               Expected = <<"[]">>,
-              ?assertEqual(Expected, jsone_encode:encode(Input))
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
 
      %% オブジェクト系
      {"オブジェクトがエンコード可能",
       fun () ->
-              Input    = {object, [{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
+              Input    = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
               Expected = <<"{\"key\":\"value\",\"1\":2}">>,
-              ?assertEqual(Expected, jsone_encode:encode(Input))
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
      {"空オブジェクトがエンコード可能",
       fun () ->
-              Input    = {object, []},
+              Input    = {[]},
               Expected = <<"{}">>,
-              ?assertEqual(Expected, jsone_encode:encode(Input))
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
      {"オブジェクトのメンバのキーにはバイナリのみが使用可能",
       fun () ->
-              ?assertError(badarg, jsone_encode:encode({object, [{1, 2}]})),
-              ?assertError(badarg, jsone_encode:encode({object, [{"1", 2}]})),
-              ?assertError(badarg, jsone_encode:encode({object, [{true, 2}]}))
+              ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{1, 2}]})),
+              ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{"1", 2}]})),
+              ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{true, 2}]}))
       end},
 
      %% その他
      {"複雑なデータがエンコード可能",
       fun () ->
-              Input    = [true, {object, [{<<"1">>, 2}, {<<"array">>, [[[[1]]], {object, [{<<"ab">>, <<"cd">>}]}, false]}]}, null],
+              Input    = [true, {[{<<"1">>, 2}, {<<"array">>, [[[[1]]], {[{<<"ab">>, <<"cd">>}]}, false]}]}, null],
               Expected = <<"[true,{\"1\":2,\"array\":[[[[1]]],{\"ab\":\"cd\"},false]},null]">>,
-              ?assertEqual(Expected, jsone_encode:encode(Input))
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
      {"不正な値",
       fun () ->
-              ?assertError(badarg, jsone_encode:encode(self()))
+              ?assertMatch({error, {badarg, _}}, jsone_encode:encode(self()))
       end}
     ].