Packages

A pure gleam json decoder, encoder, and json pointer resolver

Current section

Files

Jump to
jackson src jackson@internal@parser.erl
Raw

src/jackson@internal@parser.erl

-module(jackson@internal@parser).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([parse/1]).
-spec unexpected_character(binary()) -> binary().
unexpected_character(String) ->
First_char = gleam@string:slice(String, 0, 1),
<<<<"Unexpected character: \""/utf8, First_char/binary>>/binary, "\""/utf8>>.
-spec parse_bool(binary()) -> {ok, {jackson@internal@json:json(), binary()}} |
{error, binary()}.
parse_bool(Input) ->
case Input of
<<"true"/utf8, Rest/binary>> ->
{ok, {{bool, true}, Rest}};
<<"false"/utf8, Rest@1/binary>> ->
{ok, {{bool, false}, Rest@1}};
_ ->
{error, <<"Expected one of: \"true\", \"false\""/utf8>>}
end.
-spec parse_null(binary()) -> {ok, {jackson@internal@json:json(), binary()}} |
{error, binary()}.
parse_null(Input) ->
case Input of
<<"null"/utf8, Rest/binary>> ->
{ok, {null, Rest}};
_ ->
{error, <<"Expected: \"null\""/utf8>>}
end.
-spec parse_exponent(binary(), binary()) -> {ok, jackson@internal@json:json()} |
{error, nil}.
parse_exponent(First, Second) ->
First@1 = case {gleam@int:parse(First), gleam@float:parse(First)} of
{_, {ok, Floatval}} ->
{ok, Floatval};
{{ok, Intval}, _} ->
{ok, gleam@int:to_float(Intval)};
{_, _} ->
{error, nil}
end,
gleam@result:'try'(
First@1,
fun(First@2) ->
Second@1 = case {gleam@int:parse(Second), gleam@float:parse(Second)} of
{_, {ok, Floatval@1}} ->
{ok, Floatval@1};
{{ok, Intval@1}, _} ->
{ok, gleam@int:to_float(Intval@1)};
{_, _} ->
{error, nil}
end,
gleam@result:'try'(
Second@1,
fun(Second@2) ->
Multiplicand = gleam@float:power(10.0, Second@2),
gleam@result:'try'(
Multiplicand,
fun(Mult) -> {ok, {float, First@2 * Mult}} end
)
end
)
end
).
-spec parse_number(binary()) -> {ok, {jackson@internal@json:json(), binary()}} |
{error, binary()}.
parse_number(Input) ->
_assert_subject = gleam@regex:from_string(
<<"^-?[0-9]+(\\.[0-9]+)?((e|E)(\\+|-)?[0-9]+)?"/utf8>>
),
{ok, Re} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"jackson/internal/parser"/utf8>>,
function => <<"parse_number"/utf8>>,
line => 124})
end,
Res = gleam@regex:scan(Re, Input),
case {Res, Input} of
{_, <<""/utf8>>} ->
{error, <<"Unexpected end of input"/utf8>>};
{[], _} ->
{error, unexpected_character(Input)};
{[{match, Content, _} | _], _} ->
Rest = gleam@string:slice(
Input,
gleam@string:length(Content),
gleam@string:length(Input)
),
case gleam@string:split(
gleam@string:lowercase(Content),
<<"e"/utf8>>
) of
[] ->
erlang:error(#{gleam_error => panic,
message => <<"string cannot be empty"/utf8>>,
module => <<"jackson/internal/parser"/utf8>>,
function => <<"parse_number"/utf8>>,
line => 140});
[First] ->
case {gleam@int:parse(First), gleam@float:parse(First)} of
{{ok, Intval}, _} ->
{ok, {{integer, Intval}, Rest}};
{_, {ok, Floatval}} ->
{ok, {{float, Floatval}, Rest}};
{_, _} ->
{error, unexpected_character(Content)}
end;
[First@1, Second] ->
case parse_exponent(First@1, Second) of
{ok, Val} ->
{ok, {Val, Rest}};
{error, _} ->
{error, <<"Invalid number "/utf8, Content/binary>>}
end;
_ ->
{error, <<"Invalid number "/utf8, Content/binary>>}
end
end.
-spec parse_string(binary()) -> {ok, {jackson@internal@json:json(), binary()}} |
{error, binary()}.
parse_string(Input) ->
_assert_subject = gleam@regex:from_string(
<<"^\"([^\\\\\"\\\\u0000-\\\\u001F\\\\u007F-\\\\u009F\\\\u061C\\\\u200E\\\\u200F\\\\u202A-\\\\u202E\\\\u2066-\\\\u2069]|(\\\\(\"|\\\\|\\/|b|f|n|r|t|(u[0-9a-fA-F]{4}))))*?\""/utf8>>
),
{ok, Re} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"jackson/internal/parser"/utf8>>,
function => <<"parse_string"/utf8>>,
line => 184})
end,
Res = gleam@regex:scan(Re, Input),
case Res of
[] ->
{error, unexpected_character(Input)};
[{match, _, _} = Match | _] ->
Inner = gleam@string:slice(
erlang:element(2, Match),
1,
gleam@string:length(erlang:element(2, Match)) - 2
),
Rest = gleam@string:slice(
Input,
gleam@string:length(erlang:element(2, Match)),
gleam@string:length(Input)
),
{ok,
{{string, jackson@internal@escaping:descape_string(Inner)},
Rest}}
end.
-spec parse_object_entry(binary()) -> {ok,
{list({binary(), jackson@internal@json:json()}), binary()}} |
{error, binary()}.
parse_object_entry(Input) ->
Input@1 = gleam@string:trim_left(Input),
gleam@result:'try'(
parse_loop(Input@1),
fun(_use0) ->
{Key, Rest} = _use0,
Rest@1 = gleam@string:trim_left(Rest),
case Key of
{string, Key@1} ->
gleam@bool:guard(
not gleam@string:starts_with(Rest@1, <<":"/utf8>>),
{error, unexpected_character(Rest@1)},
fun() ->
gleam@result:'try'(
parse_loop(
gleam@string:slice(
Rest@1,
1,
gleam@string:length(Rest@1)
)
),
fun(_use0@1) ->
{Value, Rest@2} = _use0@1,
Rest@3 = gleam@string:trim_left(Rest@2),
case Rest@3 of
<<","/utf8, Rest@4/binary>> ->
gleam@result:'try'(
parse_object_entry(Rest@4),
fun(_use0@2) ->
{_, Rest@5} = _use0@2,
{ok,
{[{Key@1, Value}],
Rest@5}}
end
);
<<"}"/utf8, Rest@6/binary>> ->
{ok, {[{Key@1, Value}], Rest@6}};
_ ->
{error,
unexpected_character(Rest@3)}
end
end
)
end
);
_ ->
{error, <<"Only strings are valid keys in json"/utf8>>}
end
end
).
-spec parse_loop(binary()) -> {ok, {jackson@internal@json:json(), binary()}} |
{error, binary()}.
parse_loop(Input) ->
Input@1 = gleam@string:trim_left(Input),
gleam@result:try_recover(
parse_array(Input@1),
fun(_) ->
gleam@result:try_recover(
parse_object(Input@1),
fun(_) ->
gleam@result:try_recover(
parse_bool(Input@1),
fun(_) ->
gleam@result:try_recover(
parse_null(Input@1),
fun(_) ->
gleam@result:try_recover(
parse_number(Input@1),
fun(_) ->
gleam@result:try_recover(
parse_string(Input@1),
fun(Err) -> {error, Err} end
)
end
)
end
)
end
)
end
)
end
).
-spec parse_object(binary()) -> {ok, {jackson@internal@json:json(), binary()}} |
{error, binary()}.
parse_object(Input) ->
Input@1 = gleam@string:trim_left(Input),
case Input@1 of
<<"{"/utf8, Rest/binary>> ->
gleam@result:'try'(
parse_object_entry(Rest),
fun(_use0) ->
{Entries, Rest@1} = _use0,
{ok, {{object, Entries}, Rest@1}}
end
);
_ ->
{error, unexpected_character(Input@1)}
end.
-spec parse(binary()) -> {ok, jackson@internal@json:json()} | {error, binary()}.
parse(Input) ->
Input@1 = gleam@string:trim(Input),
case parse_loop(Input@1) of
{error, Err} ->
{error, Err};
{ok, {Val, <<""/utf8>>}} ->
{ok, Val};
{ok, {_, Rest}} ->
{error, unexpected_character(Rest)}
end.
-spec parse_array_value(binary()) -> {ok,
{jackson@internal@json:json(), binary()}} |
{error, binary()}.
parse_array_value(Input) ->
gleam@result:'try'(
parse_loop(Input),
fun(_use0) ->
{Element, Rest} = _use0,
Rest@1 = gleam@string:trim_left(Rest),
case Rest@1 of
<<","/utf8, Rest@2/binary>> ->
case parse_array_value(Rest@2) of
{ok, {{array, Inner}, Rest@3}} ->
{ok, {{array, [Element | Inner]}, Rest@3}};
_ ->
{error, unexpected_character(Rest@2)}
end;
<<"]"/utf8, Rest@4/binary>> ->
{ok, {{array, [Element]}, Rest@4}};
_ ->
{error, unexpected_character(Rest@1)}
end
end
).
-spec parse_array(binary()) -> {ok, {jackson@internal@json:json(), binary()}} |
{error, binary()}.
parse_array(Input) ->
Input@1 = gleam@string:trim_left(Input),
case Input@1 of
<<"["/utf8, Rest/binary>> ->
parse_array_value(Rest);
_ ->
{error, unexpected_character(Input@1)}
end.