Advertisement
fuxoft

Erlang JSON parser

May 27th, 2021
3,952
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Erlang 3.95 KB | None | 0 0
  1. -module(json).
  2. -export([parse/1,test/1]).
  3. %-compile(export_all)
  4.  
  5. test(String) ->
  6.     io:format("Parsing ~p:~n",[String]),
  7.     Parsed = parse(String),
  8.     io:format("~tp~n-----~n",[Parsed]).
  9.  
  10. parse(String) ->
  11.     {Parsed, []} = parse_thing(String),
  12.     Parsed.
  13.  
  14. is_whitespace(Ch) ->
  15.     Ch =< 32.
  16.  
  17. skip_whitespaces([]) -> [];
  18. skip_whitespaces(String) ->
  19.     [H | T] = String,
  20.     case is_whitespace(H) of
  21.         true -> skip_whitespaces(T);
  22.         false -> String
  23.     end.
  24.  
  25. % Returns {Got, Rest}
  26. take_chars_while(Predicate, String) ->
  27.     take_chars_while(Predicate, String, []).
  28.  
  29. take_chars_while(_Pred, [], RevGot) ->
  30.     {lists:reverse(RevGot), []};
  31. take_chars_while(Predicate, Remains, RevGot) ->
  32.     [RemH | RemT] = Remains,
  33.     case Predicate(RemH) of
  34.         false ->
  35.             {lists:reverse(RevGot), Remains};
  36.         true ->
  37.             take_chars_while(Predicate, RemT, [RemH | RevGot])
  38.     end.
  39.  
  40. % we are now past "[" and past whitespaces
  41. parse_array_after_paren(Rest, RevItems) ->
  42.     case Rest of
  43.         [$]|AfterEnd] ->
  44.             {lists:reverse(RevItems), AfterEnd};
  45.         _ ->
  46.             {ArrayItem, AfterArrItem} = parse_thing(Rest),
  47.             case AfterArrItem of
  48.                 [$]|RestAfterArray] ->
  49.                     {lists:reverse([ArrayItem | RevItems]), RestAfterArray};
  50.                 [$,|RestAfterComma] ->
  51.                     parse_array_after_paren(skip_whitespaces(RestAfterComma), [ArrayItem | RevItems])
  52.             end
  53.     end.
  54.                
  55. % we are now past "{" and past whitespaces
  56. parse_object_after_paren(Rest, Map) ->
  57.     case Rest of
  58.         [$}|AfterEnd] ->
  59.             {Map, AfterEnd};
  60.         _ ->
  61.             {MapKey, AfterMapKey} = parse_thing(Rest),
  62.             [$:|AfterColon] = AfterMapKey,
  63.             {MapValue, AfterMapValue} = parse_thing(AfterColon),
  64.             NewMap = Map#{ binary_to_atom(MapKey) => MapValue },
  65.             case AfterMapValue of
  66.                 [$}|RestAfterMap] ->
  67.                     {NewMap, RestAfterMap};
  68.                 [$,|RestAfterComma] ->
  69.                     parse_object_after_paren(skip_whitespaces(RestAfterComma), NewMap)
  70.                 end
  71.     end.
  72.  
  73. parse_string_after_quote(Rest) ->
  74.     parse_string_after_quote(Rest, []).
  75.  
  76. %todo more control characters, \r, \n etc...
  77. parse_string_after_quote([$"|AfterString], RevString) ->
  78.    {unicode:characters_to_binary(lists:reverse(RevString)), AfterString};
  79. parse_string_after_quote([$\\,$"|Rest], RevString) ->
  80.     parse_string_after_quote(Rest, [$"|RevString]);
  81. parse_string_after_quote([Char|Rest], RevString) ->
  82.    parse_string_after_quote(Rest,[Char|RevString]).
  83.  
  84. parse_number([$- | NumberAndRest]) ->
  85.    {Parsed, Rest} = parse_number(NumberAndRest),
  86.    {0 - Parsed, Rest};
  87. parse_number(NumberAndRest) ->
  88.    {NumString, Rest} = take_chars_while(fun(Ch) -> not (is_whitespace(Ch) orelse lists:any(fun(X) -> X == Ch end,[$,, $], $}])) end, NumberAndRest),
  89.    case string:to_integer(NumString) of
  90.        {Integer, []} ->
  91.            {Integer, Rest};
  92.        _ ->
  93.            {Float, []} = string:to_float(NumString),
  94.            {Float, Rest}
  95.    end.
  96.  
  97. parse_thing(String) ->
  98.    BeforeThing = skip_whitespaces(String),
  99.    {Parsed, AfterThing} = parse_thing_without_whitespaces(BeforeThing),
  100.    {Parsed, skip_whitespaces(AfterThing)}.
  101.    
  102. % returns {ParsedThing, Rest}
  103. parse_thing_without_whitespaces("true" ++ Rest) ->
  104.    {true, Rest};
  105. parse_thing_without_whitespaces("false" ++ Rest) ->
  106.    {false, Rest};
  107. parse_thing_without_whitespaces("null" ++ Rest) ->
  108.    {null, Rest};
  109. parse_thing_without_whitespaces([$[|Rest]) ->
  110.    parse_array_after_paren(skip_whitespaces(Rest), []);
  111. parse_thing_without_whitespaces([${|Rest]) ->
  112.    parse_object_after_paren(skip_whitespaces(Rest), #{});
  113. parse_thing_without_whitespaces([$"|AfterQuote]) ->
  114.     parse_string_after_quote(AfterQuote);
  115. parse_thing_without_whitespaces (NumberAndRest) ->
  116.     parse_number(NumberAndRest).
  117.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement