Created
May 17, 2014 15:45
-
-
Save sile/fa3c77155d61c97205e1 to your computer and use it in GitHub Desktop.
Erlangコード最適化メモ: JSONデコード処理(1): まず基本形 ref: http://qiita.com/sile/items/6df1400ce45f870b4b03
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(json_decode_1). | |
-export([decode/1]). | |
-type json_value() :: null | boolean() | json_number() | | |
json_string() | json_array() | json_object(). | |
-type json_number() :: non_neg_integer(). | |
-type json_string() :: binary(). | |
-type json_array() :: [json_value()]. | |
-type json_object() :: {[json_object_member()]}. | |
-type json_object_member() :: {json_string(), json_value()}. | |
%% @doc JSON文字列をデコードする | |
%% | |
%% 不正なJSON文字列が渡された場合は、badargエラーが送出される | |
-spec decode(binary()) -> json_value(). | |
decode(Json) -> | |
{Value, _RestBin} = value(skip_whitespace(Json)), | |
Value. | |
-spec skip_whitespace(binary()) -> binary(). | |
skip_whitespace(<<$ , Bin/binary>>) -> skip_whitespace(Bin); | |
skip_whitespace(<<$\t, Bin/binary>>) -> skip_whitespace(Bin); | |
skip_whitespace(<<$\r, Bin/binary>>) -> skip_whitespace(Bin); | |
skip_whitespace(<<$\n, Bin/binary>>) -> skip_whitespace(Bin); | |
skip_whitespace(Bin) -> Bin. | |
-spec value(binary()) -> {json_value(), binary()}. | |
value(<<"null", Bin/binary>>) -> {null, Bin}; | |
value(<<"false", Bin/binary>>) -> {false, Bin}; | |
value(<<"true", Bin/binary>>) -> {true, Bin}; | |
value(<<$[, Bin/binary>>) -> array(skip_whitespace(Bin)); | |
value(<<${, Bin/binary>>) -> object(skip_whitespace(Bin)); | |
value(<<$", Bin/binary>>) -> string(Bin, ""); | |
value(<<C, Bin/binary>>) when $0 =< C, C =< $9 -> number(C - $0, Bin); | |
value(Bin) -> error(badarg, [Bin]). | |
-spec array(binary()) -> {json_array(), binary()}. | |
array(<<$], Bin/binary>>) -> {[], Bin}; | |
array(Bin) -> array(Bin, []). | |
-spec array(binary(), [json_value()]) -> {json_array(), binary()}. | |
array(Bin, Values) -> | |
{Value, Bin2} = value(Bin), | |
Values2 = [Value | Values], | |
case skip_whitespace(Bin2) of | |
<<$], Bin3/binary>> -> {lists:reverse(Values2), Bin3}; | |
<<$,, Bin3/binary>> -> array(skip_whitespace(Bin3), Values2); | |
_ -> error(badarg, [Bin, Values]) | |
end. | |
-spec object(binary()) -> {json_object(), binary()}. | |
object(<<$}, Bin/binary>>) -> {{[]}, Bin}; | |
object(Bin) -> object(Bin, []). | |
-spec object(binary(), [json_object_member()]) -> {json_object(), binary()}. | |
object(<<$", Bin/binary>>, Members) -> | |
{Key, Bin2} = string(Bin, ""), | |
case skip_whitespace(Bin2) of | |
<<$:, Bin3/binary>> -> | |
{Value, Bin4} = value(skip_whitespace(Bin3)), | |
Members2 = [{Key, Value} | Members], | |
case skip_whitespace(Bin4) of | |
<<$}, Bin5/binary>> -> {{lists:reverse(Members2)}, Bin5}; | |
<<$,, Bin5/binary>> -> object(skip_whitespace(Bin5), Members2); | |
_ -> error(badarg, [<<$", Bin/binary>>, Members]) | |
end; | |
_ -> error(badarg, [<<$", Bin/binary>>, Members]) | |
end; | |
object(Bin, Members) -> error(badarg, [Bin, Members]). | |
-spec string(binary(), string()) -> {json_string(), binary()}. | |
string(<<$", Bin/binary>>, Acc) -> {list_to_binary(lists:reverse(Acc)), Bin}; | |
string(<<$\\, $", Bin/binary>>, Acc) -> string(Bin, [$" | Acc]); | |
string(<<$\\, $/, Bin/binary>>, Acc) -> string(Bin, [$/ | Acc]); | |
string(<<$\\, $\\, Bin/binary>>, Acc) -> string(Bin, [$\\ | Acc]); | |
string(<<$\\, $b, Bin/binary>>, Acc) -> string(Bin, [$\b | Acc]); | |
string(<<$\\, $f, Bin/binary>>, Acc) -> string(Bin, [$\f | Acc]); | |
string(<<$\\, $n, Bin/binary>>, Acc) -> string(Bin, [$\n | Acc]); | |
string(<<$\\, $r, Bin/binary>>, Acc) -> string(Bin, [$\r | Acc]); | |
string(<<$\\, $t, Bin/binary>>, Acc) -> string(Bin, [$\t | Acc]); | |
string(<<$\\, Bin/binary>>, Acc) -> error(badarg, [<<$\\, Bin/binary>>, Acc]); | |
string(<<0:1, C:7, Bin/binary>>, Acc) -> string(Bin, [C | Acc]); | |
string(Bin, Acc) -> error(badarg, [Bin, Acc]). | |
-spec number(json_number(), binary()) -> {json_number(), binary()}. | |
number(N, <<C, Bin/binary>>) when $0 =< C, C =< $9 -> number(N * 10 + C - $0, Bin); | |
number(N, Bin) -> {N, Bin}. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment