Takeru Ohta 11 лет назад
Родитель
Сommit
62aea81f65
8 измененных файлов с 290 добавлено и 154 удалено
  1. 2 5
      Makefile
  2. 73 32
      README.md
  3. 62 8
      doc/jsone.md
  4. 49 7
      src/jsone.erl
  5. 4 4
      src/jsone_decode.erl
  6. 3 3
      src/jsone_encode.erl
  7. 66 65
      test/jsone_decode_tests.erl
  8. 31 30
      test/jsone_encode_tests.erl

+ 2 - 5
Makefile

@@ -3,8 +3,6 @@ NODE=$(APP)@localhost
 
 DIALYZER_OPTS=-Werror_handling -Wrace_conditions -Wunmatched_returns
 
-DEPENDED_APPS=
-
 all: compile xref eunit dialyze
 
 init:
@@ -27,14 +25,13 @@ edoc:
 
 start: compile
 	erl -sname $(NODE) -pz ebin deps/*/ebin \
-      -eval 'erlang:display({start_depended_app, [{A, application:start(A)} || A <- [$(DEPENDED_APPS)]]}).' \
       -eval 'erlang:display({start_app, $(APP), application:start($(APP))}).'
 
 .dialyzer.plt:
 	touch .dialyzer.plt
-	dialyzer --build_plt --plt .dialyzer.plt --apps erts kernel stdlib $(shell echo $(DEPENDED_APPS) | sed -e 's/,/ /g')
+	dialyzer --build_plt --plt .dialyzer.plt --apps erts kernel stdlib
 
-dialyze: .dialyzer.plt
+dialyze: .dialyzer.plt compile
 	dialyzer --plt .dialyzer.plt -r ebin $(DIALYZER_OPTS)
 
 create_app:

+ 73 - 32
README.md

@@ -1,55 +1,101 @@
-jsone (0.2.2)
+jsone (0.2.3)
 =============
 
-Erlangで実装されたJSONのエンコード/デコードライブラリ。
+An Erlang library for encoding, decoding [JSON](http://json.org/index.html) data.
 
-特徴
-----
-- RFC4627準拠
-- 日本語を含む文字列に対応
-  - ただし文字列はUTF-8のみをサポート
+Features
+--------
+- Provides simple encode/decode function only
+- [RFC4627](http://www.ietf.org/rfc/rfc4627.txt)-compliant
+- Supports UTF-8 encoded binary
 - Pure Erlang
-- デコード処理部を実験的にCPS的に実装
-  - パース途中にサブバイナリが生成されることを極力抑制して最適化
-  - NIFを使わない実装の中ではおそらく最速
+- Highly Efficient
+  - Maybe one of the fastest JSON library (except those which are implemented in NIF)
+      - TODO: benchmark result
+  - Decode function is written in continuation-passing style(CPS)
+      - CPS facilitates application of 'creation of sub binary delayed' optimization
+      - See also [Erlang Efficiency Guide](http://www.erlang.org/doc/efficiency_guide/binaryhandling.html)
 
-ビルド方法
+
+QuickStart
 ----------
-ビルドツールには[rebar](https://github.com/basho/rebar)を使用している。
 
-ビルド手順:
 ```sh
-# ビルド
+# clone
 $ git clone git://github.com/sile/jsone.git
-$ make init
+$ cd jsone
+
+# If you want to use HiPE enabled version, please execute following command.
+# $ git checkout hipe
+
+# compile
+$ make compile
+
+# run tests
+$ make eunit
 
-# テスト & dialyzer 実行
-$ make
+# dialyze
+$ make dialyze
 
-# ロードパスに追加してErlangシェルを起動
+# Erlang shell
 $ make start
 1> jsone:decode(<<"[1,2,3]">>).
 [1,2,3]
 ```
 
-使用例
------
+
+Usage Example
+-------------
+
 ```erlang
-%% デコード
+%% Decode
 > jsone:decode(<<"[1,2,3]">>).
 [1,2,3]
 
-> json:decode(<<"{\"1\":2}">>).
-{[{<<"1">>,2}]}   % オブジェクトは {[Key, Value]} 形式にデコードされる
+> jsone:decode(<<"{\"1\":2}">>).
+{[{<<"1">>,2}]}
+
+> jsone:try_decode(<<"[1,2,3] \"next value\"">>). % try_decode/1 returns remaining (unconsumed binary)
+{ok,[1,2,3],<<" \"next value\"">>}
 
-%% エンコード
+% error: raises exception
+> jsone:decode(<<"1.x">>).
+** exception error: bad argument
+     in function  jsone_decode:number_fraction_part_rest/6
+        called as jsone_decode:number_fraction_part_rest(<<"x">>,1,1,0,[],<<>>)
+     in call from jsone:decode/1 (src/jsone.erl, line 71)
+
+% error: returns {error, Reason}
+> jsone:try_decode(<<"1.x">>).
+{error,{badarg,[{jsone_decode,number_fraction_part_rest,
+                              [<<"x">>,1,1,0,[],<<>>],
+                              [{line,228}]}]}}
+
+
+%% Encode
 > jsone:encode([1,2,3]).
 <<"[1,2,3]">>
 
+> jsone:encode({[{<<"key">>, <<"value">>}]}).
+<<"{\"key\":\"value\"}">>
+
+% error: raises exception
+> jsone:encode({[{key, <<"value">>}]}). % non binary key is not allowed
+** exception error: bad argument
+     in function  jsone_encode:object_members/3
+        called as jsone_encode:object_members([{key,<<"value">>}],[],<<"{">>)
+     in call from jsone:encode/1 (src/jsone.erl, line 97)
+
+% error: returns {error, Reason}
+> jsone:try_encode({[{key, <<"value">>}]}).
+{error,{badarg,[{jsone_encode,object_members,
+                              [[{key,<<"value">>}],[],<<"{">>],
+                              [{line,138}]}]}}
 ```
 
-Erlangの型とJSONの対応
-----------------------
+
+Data Mapping (Erlangの<=> JSON)
+-------------------------------
 
 |         | Erlang                       | JSON            |
 |:-------:|-----------------------------:|----------------:|
@@ -63,9 +109,4 @@ Erlangの型とJSONの対応
 
 API
 ---
-[EDOCドキュメント](doc/jsone.md)
-
-参考
-----
-- [JSON](http://www.json.org/)
-- [RFC4627](http://www.ietf.org/rfc/rfc4627.txt)
+See [EDoc Document](doc/jsone.md)

+ 62 - 8
doc/jsone.md

@@ -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><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>
+<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>Decodes an erlang term from json text (a utf8 encoded binary).</td></tr><tr><td valign="top"><a href="#encode-1">encode/1</a></td><td>Encodes an erlang term into json text (a utf8 encoded binary).</td></tr><tr><td valign="top"><a href="#try_decode-1">try_decode/1</a></td><td>Decodes an erlang term from json text (a utf8 encoded binary).</td></tr><tr><td valign="top"><a href="#try_encode-1">try_encode/1</a></td><td>Encodes an erlang term into json text (a utf8 encoded binary).</td></tr></table>
 
 
 <a name="functions"></a>
@@ -123,10 +123,24 @@ decode(Json::binary()) -&gt; <a href="#type-json_value">json_value()</a>
 
 
 
-JSONバイナリをデコードする.
+Decodes an erlang term from json text (a utf8 encoded binary)
 
 
-デコードに失敗した場合はエラーが送出される
+
+Raises an error exception if input is not valid json
+
+
+
+```
+  > jsone:decode(<<"1">>).
+  1
+  > jsone:decode(<<"wrong json">>).
+  ** exception error: bad argument
+      in function  jsone_decode:number_integer_part/4
+         called as jsone_decode:number_integer_part(<<"wrong json">>,1,[],<<>>)
+      in call from jsone:decode/1 (src/jsone.erl, line 71)
+```
+
 <a name="encode-1"></a>
 
 ### encode/1 ###
@@ -140,23 +154,50 @@ encode(JsonValue::<a href="#type-json_value">json_value()</a>) -&gt; binary()
 
 
 
-JSON値をバイナリ形式にエンコードする.
+Encodes an erlang term into json text (a utf8 encoded binary)
+
+
+
+Raises an error exception if input is not an instance of type `json_value()`
+
+
 
+```
+  > jsone:encode([1, null, 2]).
+  <<"[1,null,2]">>
+  > jsone:encode([1, hoge, 2]).  % 'hoge' atom is not a json value
+  ** exception error: bad argument
+       in function  jsone_encode:value/3
+          called as jsone_encode:value(hoge,[{array_values,[2]}],<<"[1,">>)
+       in call from jsone:encode/1 (src/jsone.erl, line 97)
+```
 
-エンコードに失敗した場合はエラーが送出される
 <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>]}}
+try_decode(Json::binary()) -&gt; {ok, <a href="#type-json_value">json_value()</a>, Remainings::binary()} | {error, {Reason::term(), [<a href="erlang.md#type-stack_item">erlang:stack_item()</a>]}}
 </code></pre>
 
 <br></br>
 
 
-JSONバイナリをデコードする.
+
+Decodes an erlang term from json text (a utf8 encoded binary)
+
+
+
+```
+  > jsone:try_decode(<<"[1,2,3] \"next value\"">>).
+  {ok,[1,2,3],<<" \"next value\"">>}
+  > jsone:try_decode(<<"wrong json">>).
+  {error,{badarg,[{jsone_decode,number_integer_part,
+                                [<<"wrong json">>,1,[],<<>>],
+                                [{line,208}]}]}}
+```
+
 <a name="try_encode-1"></a>
 
 ### try_encode/1 ###
@@ -169,4 +210,17 @@ try_encode(JsonValue::<a href="#type-json_value">json_value()</a>) -&gt; {ok, bi
 <br></br>
 
 
-JSON値をバイナリ形式にエンコードする
+
+Encodes an erlang term into json text (a utf8 encoded binary)
+
+
+
+```
+  > jsone:try_encode([1, null, 2]).
+  {ok,<<"[1,null,2]">>}
+  > jsone:try_encode([1, hoge, 2]).  % 'hoge' atom is not a json value
+  {error,{badarg,[{jsone_encode,value,
+                                [hoge,[{array_values,[2]}],<<"[1,">>],
+                                [{line,86}]}]}}
+```
+

+ 49 - 7
src/jsone.erl

@@ -58,9 +58,20 @@
 %%--------------------------------------------------------------------------------
 %% Exported Functions
 %%--------------------------------------------------------------------------------
-%% @doc JSONバイナリをデコードする.
+%% @doc Decodes an erlang term from json text (a utf8 encoded binary)
 %%
-%% デコードに失敗した場合はエラーが送出される
+%% Raises an error exception if input is not valid json
+%%
+%% ```
+%% > jsone:decode(<<"1">>).
+%% 1
+%%
+%% > jsone:decode(<<"wrong json">>).
+%% ** exception error: bad argument
+%%     in function  jsone_decode:number_integer_part/4
+%%        called as jsone_decode:number_integer_part(<<"wrong json">>,1,[],<<>>)
+%%     in call from jsone:decode/1 (src/jsone.erl, line 71)
+%% '''
 -spec decode(binary()) -> json_value().
 decode(Json) ->
     try
@@ -71,14 +82,35 @@ decode(Json) ->
             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()]}}.
+%% @doc Decodes an erlang term from json text (a utf8 encoded binary)
+%%
+%% ```
+%% > jsone:try_decode(<<"[1,2,3] \"next value\"">>).
+%% {ok,[1,2,3],<<" \"next value\"">>}
+%%
+%% > jsone:try_decode(<<"wrong json">>).
+%% {error,{badarg,[{jsone_decode,number_integer_part,
+%%                               [<<"wrong json">>,1,[],<<>>],
+%%                               [{line,208}]}]}}
+%% '''
+-spec try_decode(binary()) -> {ok, json_value(), Remainings::binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
 try_decode(Json) ->
     jsone_decode:decode(Json).
 
-%% @doc JSON値をバイナリ形式にエンコードする.
+%% @doc Encodes an erlang term into json text (a utf8 encoded binary)
 %%
-%% エンコードに失敗した場合はエラーが送出される
+%% Raises an error exception if input is not an instance of type `json_value()'
+%%
+%% ```
+%% > jsone:encode([1, null, 2]).
+%% <<"[1,null,2]">>
+%%
+%% > jsone:encode([1, hoge, 2]).  % 'hoge' atom is not a json value
+%% ** exception error: bad argument
+%%      in function  jsone_encode:value/3
+%%         called as jsone_encode:value(hoge,[{array_values,[2]}],<<"[1,">>)
+%%      in call from jsone:encode/1 (src/jsone.erl, line 97)
+%% '''
 -spec encode(json_value()) -> binary().
 encode(JsonValue) ->
     try
@@ -89,7 +121,17 @@ encode(JsonValue) ->
             erlang:raise(error, Reason, [StackItem | erlang:get_stacktrace()])
     end.
 
-%% @doc JSON値をバイナリ形式にエンコードする
+%% @doc Encodes an erlang term into json text (a utf8 encoded binary)
+%%
+%% ```
+%% > jsone:try_encode([1, null, 2]).
+%% {ok,<<"[1,null,2]">>}
+%%
+%% > jsone:try_encode([1, hoge, 2]).  % 'hoge' atom is not a json value
+%% {error,{badarg,[{jsone_encode,value,
+%%                               [hoge,[{array_values,[2]}],<<"[1,">>],
+%%                               [{line,86}]}]}}
+%% '''
 -spec try_encode(json_value()) -> {ok, binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
 try_encode(JsonValue) ->
     jsone_encode:encode(JsonValue).

+ 4 - 4
src/jsone_decode.erl

@@ -54,7 +54,7 @@
 %%--------------------------------------------------------------------------------
 %% Exported Functions
 %%--------------------------------------------------------------------------------
-%% @doc JSONバイナリをデコードする.
+%% @doc Decodes an erlang term from json text (a utf8 encoded binary)
 -spec decode(binary()) -> decode_result().
 decode(<<Json/binary>>) ->
     whitespace(Json, value, [], <<"">>).
@@ -157,7 +157,7 @@ string(<<C, Bin/binary>>, Base, Start, Nexts, Buf) when 16#20 =< C ->
 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 ->
-            %% サロゲートペア
+            %% surrogate pair
             case Bin of
                 <<$\\, $u, N2:4/binary, Bin2/binary>> ->
                     case binary_to_integer(N2, 16) of
@@ -168,7 +168,7 @@ unicode_string(<<N:4/binary, Bin/binary>>, Start, Nexts, Buf) ->
                     end;
                 _ -> ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf])
             end;
-        Unicode when 16#DC00 =< Unicode, Unicode =< 16#DFFF ->  % サロゲートペアの後半部分
+        Unicode when 16#DC00 =< Unicode, Unicode =< 16#DFFF ->  % second part of surrogate pair (without first part)
             ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf]);
         Unicode -> 
             string(Bin, Start, Nexts, unicode_to_utf8(Unicode, Buf))
@@ -188,7 +188,7 @@ unicode_to_utf8(Code, Buf) when Code < 16#10000 ->
     B = 2#10000000 bor ((Code bsr 6) band 2#111111),
     C = 2#10000000 bor (Code band 2#111111),
     <<Buf/binary, A, B, C>>;
-unicode_to_utf8(Code, Buf) -> % NOTE: サロゲートペアの仕組み上、コード値が上限を越えることはないので、ここでの範囲チェックは不要
+unicode_to_utf8(Code, Buf) ->
     A = 2#11110000 bor (Code bsr 18),
     B = 2#10000000 bor ((Code bsr 12) band 2#111111),
     C = 2#10000000 bor ((Code bsr  6) band 2#111111),

+ 3 - 3
src/jsone_encode.erl

@@ -48,7 +48,7 @@
 %%--------------------------------------------------------------------------------
 %% Exported Functions
 %%--------------------------------------------------------------------------------
-%% @doc JSON値をバイナリ形式にエンコードする.
+%% @doc Encodes an erlang term into json text (a utf8 encoded binary)
 -spec encode(jsone:json_value()) -> encode_result().
 encode(Value) ->
     value(Value, [], <<"">>).
@@ -116,8 +116,8 @@ escape_string(Str, Nexts, Buf) ->
 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>>, % 非効率
+    %% Surrogate Pair
+    <<High:10, Low:10>> = <<(Unicode - 16#10000):20>>, % XXX: inefficient
     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(), [next()], binary()) -> encode_result().

+ 66 - 65
test/jsone_decode_tests.erl

@@ -1,96 +1,97 @@
-%% Copyright (c) 2013, Takeru Ohta <phjgt308@gmail.com>
+%% Copyright (c) 2013-2014, Takeru Ohta <phjgt308@gmail.com>
+%% coding: latin-1
 -module(jsone_decode_tests).
 
 -include_lib("eunit/include/eunit.hrl").
 
 decode_test_() ->
     [
-     %% シンボル系
-     {"'false'がデコード可能",
+     %% Symbols
+     {"false",
       fun () ->
               ?assertEqual({ok, false, <<"">>}, jsone_decode:decode(<<"false">>))
       end},
-     {"'true'がデコード可能",
+     {"true",
       fun () ->
               ?assertEqual({ok, true, <<"">>}, jsone_decode:decode(<<"true">>))
       end},
-     {"'null'がデコード可能",
+     {"null",
       fun () ->
               ?assertEqual({ok, null, <<"">>}, jsone_decode:decode(<<"null">>))
       end},
-     {"正の整数がデコード可能",
+
+     %% Numbers: Integer
+     {"positive integer",
       fun () ->
               ?assertEqual({ok, 1, <<"">>}, jsone_decode:decode(<<"1">>))
       end},
-
-     %% 数値系: 整数
-     {"0がデコード可能",
+     {"zero",
       fun () ->
               ?assertEqual({ok, 0, <<"">>}, jsone_decode:decode(<<"0">>))
       end},
-     {"負の整数がデコード可能",
+     {"negative integer",
       fun () ->
               ?assertEqual({ok, -1, <<"">>}, jsone_decode:decode(<<"-1">>))
       end},
-     {"整数の値の大きさに制限はなし",
+     {"large integer (no limit on size)",
       fun () ->
               ?assertEqual({ok, 111111111111111111111111111111111111111111111111111111111111111111111111111111, <<"">>},
                            jsone_decode:decode(<<"111111111111111111111111111111111111111111111111111111111111111111111111111111">>))
       end},
-     {"先頭に余計な0がつく場合は、先頭文字とそれ以降が別々のトークンと判断される",
+     {"integer with leading zero (interpreted as zero and remaining binary)",
       fun () ->
               ?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},
-     {"正の整数の前の'+'記号は許可されない",
+     {"integer can't begin with an explicit plus sign",
       fun () ->
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"+1">>))
       end},
 
-     %% 数値系: 小数
-     {"小数がデコード可能",
+     %% Numbers: Floats
+     {"float: decimal notation",
       fun () ->
               ?assertEqual({ok, 1.23, <<"">>}, jsone_decode:decode(<<"1.23">>))
       end},
-     {"指数形式の小数がデコード可能",
+     {"float: exponential notation",
       fun () ->
-              ?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(<<"12345e-3">>)), % lower case 'e'
+              ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"12345E-3">>)), % upper case '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.12345e+2">>)), % exponent part can begin with plus sign
+              ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"0.12345E+2">>)),
               ?assertEqual({ok, -12.345, <<"">>}, jsone_decode:decode(<<"-0.012345e3">>))
       end},
-     {"不正な形式の小数",
+     {"float: invalid format",
       fun () ->
-              ?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">>))  % '.'が複数ある => 別々のトークンと判断される
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<".123">>)),  % omitted integer part
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.">>)),    % omitted fraction part: EOS
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.e+3">>)), % omitted fraction part: with exponent part
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e">>)),    % imcomplete fraction part
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e-">>)),   % imcomplete fraction part
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1ee-1">>)), % duplicated 'e'
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e--1">>)), % duplicated sign
+              ?assertEqual({ok, 0.1, <<".2">>}, jsone_decode:decode(<<"0.1.2">>))     % duplicated '.': interpreted as individual tokens
       end},
      
-     %% 文字列系
-     {"文字列がデコード可能",
+     %% Strings
+     {"simple string",
       fun () ->
               ?assertEqual({ok, <<"abc">>,  <<"">>}, jsone_decode:decode(<<"\"abc\"">>))
       end},
-     {"各種エスケープ文字がデコード可能",
+     {"string: escaped characters",
       fun () ->
               Input    = list_to_binary([$", [[$\\, C] || C <- [$", $/, $\\, $b, $f, $n, $r, $t]], $"]),
               Expected = <<"\"\/\\\b\f\n\r\t">>,
               ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
-     {"エスケープされたUTF-16文字列がデコード可能",
+     {"string: escaped Unicode characters",
       fun () ->
-              %% 日本語
+              %% japanese
               Input1    = <<"\"\\u3042\\u3044\\u3046\\u3048\\u304A\"">>,
-              Expected1 = <<"あいうえお">>,  % このファイルの文字エンコーディングがUTF-8であることが前提
+              Expected1 = <<"あいうえお">>,  % assumed that the encoding of this file is UTF-8
               ?assertEqual({ok, Expected1, <<"">>}, jsone_decode:decode(Input1)),
 
               %% ascii
@@ -98,117 +99,117 @@ decode_test_() ->
               Expected2 = <<"abc">>,
               ?assertEqual({ok, Expected2, <<"">>}, jsone_decode:decode(Input2)),
 
-              %% 日本語以外のマルチバイト文字
+              %% other multi-byte characters
               Input3    = <<"\"\\u06DD\\u06DE\\u10AE\\u10AF\"">>,
               Expected3 = <<"۝۞ႮႯ">>,
               ?assertEqual({ok, Expected3, <<"">>}, jsone_decode:decode(Input3)),
 
-              %% 日本語と英数字が混在
+              %% mixture of ascii and japanese characters
               Input4    = <<"\"a\\u30421\\u3044bb\\u304622\\u3048ccc\\u304A333\"">>,
-              Expected4 = <<"aあ1いbbう22えcccお333">>,  % このファイルの文字エンコーディングがUTF-8であることが前提
+              Expected4 = <<"aあ1いbbう22えcccお333">>,  % assumed that the encoding of this file is UTF-8
               ?assertEqual({ok, Expected4, <<"">>}, jsone_decode:decode(Input4))
       end},
-     {"サロゲートペアを含む文字列がデコード可能",
+     {"string: surrogate pairs",
       fun () ->
               Input    = <<"\"\\ud848\\udc49\\ud848\\udc9a\\ud848\\udcfc\"">>,
               Expected = <<"𢁉𢂚𢃼">>,
               ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
-     {"不正なエスケープ文字",
+     {"string: invalid escape characters",
       fun () ->
-              ?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\"">>)) % 上位サロゲートの後ろに下位サロゲートが続かない
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\z\"">>)),    % '\z' is undefined
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\uab\"">>)),  % too few hex characters
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\ud848\"">>)), % high(first) surrogate only
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\udc49\"">>)), % low(second) surrogate only
+              ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\ud848\\u0061\"">>)) % missing low(second) surrogate
       end},
 
-     %% 配列系
-     {"配列がデコード可能",
+     %% Arrays
+     {"simple array",
       fun () ->
               Input    = <<"[1,2,\"abc\",null]">>,
               Expected = [1, 2, <<"abc">>, null],
               ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
-     {"空白文字を含む配列がデコード可能",
+     {"array: contains whitespaces",
       fun () ->
               Input    = <<"[  1,\t2, \n \"abc\",\r null]">>,
               Expected = [1, 2, <<"abc">>, null],
               ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
-     {"空配列がデコード可能",
+     {"empty array",
       fun () ->
               ?assertEqual({ok, [], <<"">>}, jsone_decode:decode(<<"[]">>)),
               ?assertEqual({ok, [], <<"">>}, jsone_decode:decode(<<"[ \t\r\n]">>))
       end},
-     {"配列の末尾のカンマは許容されない",
+     {"array: trailing comma is disallowed",
       fun () ->
               Input = <<"[1, 2, \"abc\", null, ]">>,
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
-     {"区切り文字のカンマが抜けているとエラーとなる",
+     {"array: missing comma",
       fun () ->
-              Input = <<"[1 2, \"abc\", null]">>, % 1と2の間にカンマがない
+              Input = <<"[1 2, \"abc\", null]">>, % a missing comma between '1' and '2'
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
-     {"配列が閉じていないとエラー",
+     {"array: missing closing bracket",
       fun () ->
               Input = <<"[1, 2, \"abc\", null">>,
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
 
-     %% オブジェクト系
-     {"オブジェクトがデコード可能",
+     %% Objects
+     {"simple object",
       fun () ->
               Input    = <<"{\"1\":2,\"key\":\"value\"}">>,
               Expected = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
               ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
-     {"空白文字を含むオブジェクトがデコード可能",
+     {"object: contains whitespaces",
       fun () ->
               Input    = <<"{  \"1\" :\t 2,\n\r\"key\" :   \n  \"value\"}">>,
               Expected = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
               ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
-     {"空オブジェクトがデコード可能",
+     {"empty object",
       fun () ->
               ?assertEqual({ok, {[]}, <<"">>}, jsone_decode:decode(<<"{}">>)),
               ?assertEqual({ok, {[]}, <<"">>}, jsone_decode:decode(<<"{ \t\r\n}">>))
       end},
-     {"オブジェクトの末尾のカンマは許容されない",
+     {"object: trailing comma is disallowed",
       fun () ->
               Input = <<"{\"1\":2, \"key\":\"value\", }">>,
               io:format("~p\n", [catch jsone_decode:decode(Input)]),
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
-     {"区切り文字のカンマが抜けているとエラーとなる",
+     {"object: missing comma",
       fun () ->
               Input = <<"{\"1\":2 \"key\":\"value\"}">>,
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
-     {"メンバのキーがない場合はエラー",
+     {"object: missing field key",
       fun () ->
               Input = <<"{:2, \"key\":\"value\"}">>,
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
-     {"メンバのキーが文字列以外の場合はエラー",
+     {"object: non string key",
       fun () ->
               Input = <<"{1:2, \"key\":\"value\"}">>,
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
-     {"メンバの値がない場合はエラー",
+     {"object: missing field value",
       fun () ->
               Input = <<"{\"1\", \"key\":\"value\"}">>,
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
-     {"オブジェクトが閉じていないとエラー",
+     {"object: missing closing brace",
       fun () ->
               Input = <<"{\"1\":2 \"key\":\"value\"">>,
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
       end},
 
-     %% その他
-     {"複雑なデータがデコード可能",
+     %% Others
+     {"compound data",
       fun () ->
               Input    = <<"  [true, {\"1\" : 2, \"array\":[[[[1]]], {\"ab\":\"cd\"}, false]}, null]   ">>,
               Expected = [true, {[{<<"array">>, [[[[1]]], {[{<<"ab">>, <<"cd">>}]}, false]}, {<<"1">>, 2}]}, null],

+ 31 - 30
test/jsone_encode_tests.erl

@@ -1,123 +1,124 @@
-%% Copyright (c) 2013, Takeru Ohta <phjgt308@gmail.com>
+%% Copyright (c) 2013-2014, Takeru Ohta <phjgt308@gmail.com>
+%% coding: latin-1
 -module(jsone_encode_tests).
 
 -include_lib("eunit/include/eunit.hrl").
 
 encode_test_() ->
     [
-     %% シンボル系
-     {"false がエンコード可能",
+     %% Symbols
+     {"false",
       fun () ->
               ?assertEqual({ok, <<"false">>}, jsone_encode:encode(false))
       end},
-     {"true がエンコード可能",
+     {"true",
       fun () ->
               ?assertEqual({ok, <<"true">>}, jsone_encode:encode(true))
       end},
-     {"null がエンコード可能",
+     {"null",
       fun () ->
               ?assertEqual({ok, <<"null">>}, jsone_encode:encode(null))
       end},
 
-     %% 数値系: 整数
-     {"0がエンコード可能",
+     %% Numbers: Integer
+     {"zero",
       fun () ->
               ?assertEqual({ok, <<"0">>}, jsone_encode:encode(0))
       end},
-     {"正の整数がエンコード可能",
+     {"positive integer",
       fun () ->
               ?assertEqual({ok, <<"1">>}, jsone_encode:encode(1))
       end},
-     {"負の整数がエンコード可能",
+     {"negative integer",
       fun () ->
               ?assertEqual({ok, <<"-1">>}, jsone_encode:encode(-1))
       end},
-     {"巨大な整数がエンコード可能",
+     {"large number",
       fun () ->
               ?assertEqual({ok, <<"11111111111111111111111111111111111111111111111111111111111111111111111">>},
                            jsone_encode:encode(11111111111111111111111111111111111111111111111111111111111111111111111))
       end},
 
-     %% 数値系: 小数
-     {"小数がエンコード可能",
+     %% Numbers: Float",
+     {"float",
       fun () ->
               Input = 1.234,
               ?assertMatch({ok, _}, jsone_encode:encode(Input)),
               ?assertEqual(Input, binary_to_float(element(2, jsone_encode:encode(Input))))
       end},
 
-     %% 文字列系
-     {"文字列がエンコード可能",
+     %% Strings
+     {"simple string",
       fun () ->
               ?assertEqual({ok, <<"\"abc\"">>}, jsone_encode:encode(<<"abc">>))
       end},
-     {"各種エスケープ文字を含む文字列をエンコード可能",
+     {"string: contains escaped characters",
       fun () ->
               Input    = <<"\"\/\\\b\f\n\r\t">>,
               Expected = list_to_binary([$", [[$\\, C] || C <- [$", $/, $\\, $b, $f, $n, $r, $t]], $"]),
               ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
-     {"UTF-8形式のマルチバイト文字列がエンコード可能",
+     {"string: contains multi-byte (UTF-8 encoded) characters",
       fun () ->
-              %% 日本語
-              Input1    = <<"あいうえお">>,  % このファイルの文字エンコーディングがUTF-8であることが前提
+              %% japanese
+              Input1    = <<"あいうえお">>,  % assumed that the encoding of this file is UTF-8
               Expected1 = <<"\"\\u3042\\u3044\\u3046\\u3048\\u304a\"">>,
               ?assertEqual({ok, Expected1}, jsone_encode:encode(Input1)),
 
-              %% 日本語以外のマルチバイト文字
+              %% other multi-byte characters
               Input2    = <<"۝۞ႮႯ">>,
               Expected2 = <<"\"\\u06dd\\u06de\\u10ae\\u10af\"">>,
               ?assertEqual({ok, Expected2}, jsone_encode:encode(Input2))
       end},
-     {"サロゲートペアを含む文字列がエンコード可能",
+     {"string: containts surrogate pairs",
       fun () ->
               Input    = <<"𢁉𢂚𢃼">>,
               Expected = <<"\"\\ud848\\udc49\\ud848\\udc9a\\ud848\\udcfc\"">>,
               ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
 
-     %% 配列系
-     {"配列(リスト)がエンコード可能",
+     %% Arrays
+     {"simple array",
       fun () ->
               Input    = [1, 2, 3],
               Expected = <<"[1,2,3]">>,
               ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
-     {"空配列がエンコード可能",
+     {"empty array",
       fun () ->
               Input    = [],
               Expected = <<"[]">>,
               ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
 
-     %% オブジェクト系
-     {"オブジェクトがエンコード可能",
+     %% Objects
+     {"simple object",
       fun () ->
               Input    = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
               Expected = <<"{\"key\":\"value\",\"1\":2}">>,
               ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
-     {"空オブジェクトがエンコード可能",
+     {"empty object",
       fun () ->
               Input    = {[]},
               Expected = <<"{}">>,
               ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
-     {"オブジェクトのメンバのキーにはバイナリのみが使用可能",
+     {"non binary object member key is disallowed",
       fun () ->
               ?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},
 
-     %% その他
-     {"複雑なデータがエンコード可能",
+     %% Others
+     {"compound data",
       fun () ->
               Input    = [true, {[{<<"1">>, 2}, {<<"array">>, [[[[1]]], {[{<<"ab">>, <<"cd">>}]}, false]}]}, null],
               Expected = <<"[true,{\"1\":2,\"array\":[[[[1]]],{\"ab\":\"cd\"},false]},null]">>,
               ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
       end},
-     {"不正な値",
+     {"invalid value",
       fun () ->
               ?assertMatch({error, {badarg, _}}, jsone_encode:encode(self()))
       end}