-module(jasper). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([parse_json/1, query_json/2]). -export_type([json_value/0, json_parse_error/0, json_query/0, json_query_error/0]). -type json_value() :: {object, gleam@dict:dict(binary(), json_value())} | {array, list(json_value())} | {string, binary()} | {number, float()} | {boolean, boolean()} | null. -type json_parse_error() :: {unexpected_token, binary()} | unexpected_end_of_input. -type json_query() :: root | {key, json_query(), binary()} | {index, json_query(), integer()}. -type json_query_error() :: {unexpected_type, json_value()} | {missing_object_key, json_value(), binary()} | {index_out_of_bounds, json_value(), integer()}. -spec whitespace0() -> fun((pears@input:input(binary())) -> {ok, pears:parsed(binary(), list(binary()))} | {error, pears:parse_error(binary())}). whitespace0() -> _pipe = pears@combinators:one_of( [<<" "/utf8>>, <<"\n"/utf8>>, <<"\r"/utf8>>, <<"\t"/utf8>>] ), pears@combinators:many0(_pipe). -spec value_parser() -> fun((pears@input:input(binary())) -> {ok, pears:parsed(binary(), json_value())} | {error, pears:parse_error(binary())}). value_parser() -> Padded = fun(Parser) -> pears@combinators:left(Parser, whitespace0()) end, Symbol = fun(S) -> Padded(pears@chars:string(S)) end, Hex_digit = pears@combinators:one_of( [<<"0"/utf8>>, <<"1"/utf8>>, <<"2"/utf8>>, <<"3"/utf8>>, <<"4"/utf8>>, <<"5"/utf8>>, <<"6"/utf8>>, <<"7"/utf8>>, <<"8"/utf8>>, <<"9"/utf8>>, <<"a"/utf8>>, <<"b"/utf8>>, <<"c"/utf8>>, <<"d"/utf8>>, <<"e"/utf8>>, <<"f"/utf8>>, <<"A"/utf8>>, <<"B"/utf8>>, <<"C"/utf8>>, <<"D"/utf8>>, <<"E"/utf8>>, <<"F"/utf8>>] ), Unicode_escape_digits = pears@combinators:recognize( pears@combinators:seq([Hex_digit, Hex_digit, Hex_digit, Hex_digit]) ), Escape = begin _pipe = pears@combinators:just(<<"\\"/utf8>>), pears@combinators:right( _pipe, pears@combinators:choice( [pears@combinators:just(<<"\\"/utf8>>), pears@combinators:just(<<"/"/utf8>>), pears@combinators:just(<<"\""/utf8>>), pears@combinators:to( pears@combinators:just(<<"b"/utf8>>), <<"\x{0008}"/utf8>> ), pears@combinators:to( pears@combinators:just(<<"f"/utf8>>), <<"\x{000C}"/utf8>> ), pears@combinators:to( pears@combinators:just(<<"n"/utf8>>), <<"\n"/utf8>> ), pears@combinators:to( pears@combinators:just(<<"r"/utf8>>), <<"\r"/utf8>> ), pears@combinators:to( pears@combinators:just(<<"t"/utf8>>), <<"\t"/utf8>> ), pears@combinators:map( pears@combinators:right( pears@combinators:just(<<"u"/utf8>>), Unicode_escape_digits ), fun(Value) -> _assert_subject = gleam@int:base_parse( gleam@string:concat(Value), 16 ), {ok, Number} = 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 => <<"jasper"/utf8>>, function => <<"value_parser"/utf8>>, line => 61}) end, _assert_subject@1 = gleam@string:utf_codepoint( Number ), {ok, Codepoint} = case _assert_subject@1 of {ok, _} -> _assert_subject@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"jasper"/utf8>>, function => <<"value_parser"/utf8>>, line => 62}) end, gleam_stdlib:utf_codepoint_list_to_string( [Codepoint] ) end )] ) ) end, Str = begin _pipe@1 = pears@combinators:none_of([<<"\""/utf8>>]), _pipe@2 = pears@combinators:alt(_pipe@1, Escape), _pipe@3 = pears@combinators:many0(_pipe@2), _pipe@4 = pears@combinators:map(_pipe@3, fun gleam@string:concat/1), pears@combinators:between( _pipe@4, pears@combinators:just(<<"\""/utf8>>), pears@combinators:just(<<"\""/utf8>>) ) end, Value@1 = pears@combinators:lazy(fun value_parser/0), Num = begin _pipe@5 = pears@combinators:maybe(pears@combinators:just(<<"-"/utf8>>)), _pipe@7 = pears@combinators:pair( _pipe@5, begin _pipe@6 = pears@combinators:alt( pears@combinators:to( pears@combinators:just(<<"0"/utf8>>), [<<"0"/utf8>>] ), pears@combinators:recognize( pears@combinators:pair( pears@combinators:one_of( [<<"1"/utf8>>, <<"2"/utf8>>, <<"3"/utf8>>, <<"4"/utf8>>, <<"5"/utf8>>, <<"6"/utf8>>, <<"7"/utf8>>, <<"8"/utf8>>, <<"9"/utf8>>] ), pears@combinators:many0(pears@chars:digit()) ) ) ), pears@combinators:map(_pipe@6, fun gleam@string:concat/1) end ), _pipe@10 = pears@combinators:pair( _pipe@7, pears@combinators:maybe( begin _pipe@8 = pears@combinators:just(<<"."/utf8>>), _pipe@9 = pears@combinators:right( _pipe@8, pears@combinators:many1(pears@chars:digit()) ), pears@combinators:map(_pipe@9, fun gleam@string:concat/1) end ) ), _pipe@14 = pears@combinators:pair( _pipe@10, begin _pipe@13 = pears@combinators:recognize( pears@combinators:maybe( begin _pipe@11 = pears@combinators:alt( pears@combinators:just(<<"e"/utf8>>), pears@combinators:just(<<"E"/utf8>>) ), _pipe@12 = pears@combinators:pair( _pipe@11, pears@combinators:maybe( pears@combinators:one_of( [<<"+"/utf8>>, <<"-"/utf8>>] ) ) ), pears@combinators:pair( _pipe@12, pears@combinators:many1(pears@chars:digit()) ) end ) ), pears@combinators:map(_pipe@13, fun gleam@string:concat/1) end ), pears@combinators:map(_pipe@14, fun(P) -> case P of {{{Neg, Ns}, Ds}, Ex} -> _pipe@15 = (<<<<<<<<(gleam@option:unwrap( Neg, <<""/utf8>> ))/binary, Ns/binary>>/binary, "."/utf8>>/binary, (gleam@option:unwrap(Ds, <<"0"/utf8>>))/binary>>/binary, Ex/binary>>), _pipe@16 = gleam@float:parse(_pipe@15), _pipe@17 = gleam@result:unwrap(_pipe@16, case Neg of {some, _} -> -1.7976931348623158e308; none -> 1.7976931348623158e308 end), {number, _pipe@17} end end) end, Bool = pears@combinators:alt( pears@combinators:to( pears@chars:string(<<"true"/utf8>>), {boolean, true} ), pears@combinators:to( pears@chars:string(<<"false"/utf8>>), {boolean, false} ) ), Null = pears@combinators:to(pears@chars:string(<<"null"/utf8>>), null), Array = begin _pipe@18 = pears@combinators:sep_by0(Value@1, Symbol(<<","/utf8>>)), _pipe@19 = pears@combinators:between( _pipe@18, Symbol(<<"["/utf8>>), Symbol(<<"]"/utf8>>) ), pears@combinators:map(_pipe@19, fun(Field@0) -> {array, Field@0} end) end, Obj = begin _pipe@20 = Str, _pipe@21 = pears@combinators:left(_pipe@20, Symbol(<<":"/utf8>>)), _pipe@22 = pears@combinators:pair(_pipe@21, Value@1), _pipe@23 = pears@combinators:sep_by0(_pipe@22, Symbol(<<","/utf8>>)), _pipe@24 = pears@combinators:map(_pipe@23, fun maps:from_list/1), _pipe@25 = pears@combinators:between( _pipe@24, Symbol(<<"{"/utf8>>), Symbol(<<"}"/utf8>>) ), pears@combinators:map(_pipe@25, fun(Field@0) -> {object, Field@0} end) end, _pipe@26 = pears@combinators:choice( [Num, Bool, Null, pears@combinators:map(Str, fun(Field@0) -> {string, Field@0} end), Array, Obj] ), Padded(_pipe@26). -spec json_parser() -> fun((pears@input:input(binary())) -> {ok, pears:parsed(binary(), json_value())} | {error, pears:parse_error(binary())}). json_parser() -> _pipe = value_parser(), pears@combinators:between(_pipe, whitespace0(), pears@combinators:eof()). -spec parse_json(binary()) -> {ok, json_value()} | {error, json_parse_error()}. parse_json(Value) -> case (json_parser())(pears@chars:input(Value)) of {ok, {parsed, _, J}} -> {ok, J}; {error, E} -> {error, case E of {unexpected_token, _, _, F} -> {unexpected_token, F}; {unexpected_end_of_input, _, _} -> unexpected_end_of_input end} end. -spec query_json(json_value(), json_query()) -> {ok, json_value()} | {error, json_query_error()}. query_json(Json, Query) -> case Query of root -> {ok, Json}; {key, Q, K} -> case query_json(Json, Q) of {ok, {object, O} = J} -> _pipe = gleam@dict:get(O, K), gleam@result:replace_error( _pipe, {missing_object_key, J, K} ); {ok, J@1} -> {error, {unexpected_type, J@1}}; X -> X end; {index, Q@1, I} -> case query_json(Json, Q@1) of {ok, {array, A} = J@2} -> _pipe@1 = gleam@list:at(A, I), gleam@result:replace_error( _pipe@1, {index_out_of_bounds, J@2, I} ); {ok, J@3} -> {error, {unexpected_type, J@3}}; X@1 -> X@1 end end.