-module(glance). -compile([no_auto_import, nowarn_unused_vars]). -export([module/1]). -export_type([module_/0, import/0, constant_expression/0, constant/0, unqualified_import/0, publicity/0, type_alias/0, custom_type/0, variant/0, field/1, type/0, error/0]). -type module_() :: {module, list(import()), list(custom_type()), list(type_alias()), list(constant())}. -type import() :: {import, binary(), gleam@option:option(binary()), list(unqualified_import())}. -type constant_expression() :: constant_bit_string | {constant_int, binary()} | {constant_float, binary()} | {constant_string, binary()} | {constant_variable, binary()} | {constant_tuple, list(constant_expression())} | {constant_list, list(constant_expression())} | {constant_constructor, binary(), gleam@option:option(binary()), list(field(constant_expression()))}. -type constant() :: {constant, binary(), publicity(), gleam@option:option(type()), constant_expression()}. -type unqualified_import() :: {unqualified_import, binary(), gleam@option:option(binary())}. -type publicity() :: public | private. -type type_alias() :: {type_alias, binary(), publicity(), list(binary()), type()}. -type custom_type() :: {custom_type, binary(), publicity(), list(binary()), list(variant())}. -type variant() :: {variant, binary(), list(field(type()))}. -type field(EZM) :: {field, gleam@option:option(binary()), EZM}. -type type() :: {named_type, binary(), gleam@option:option(binary()), list(type())} | {tuple_type, list(type())} | {function_type, list(type()), type()} | {variable_type, binary()}. -type error() :: unexpected_end_of_input | {unexpected_token, glexer@token:token(), glexer:position()}. -spec push_constant(module_(), constant()) -> module_(). push_constant(Module, Constant) -> erlang:setelement(5, Module, [Constant | erlang:element(5, Module)]). -spec push_custom_type(module_(), custom_type()) -> module_(). push_custom_type(Module, Custom_type) -> Custom_type@1 = erlang:setelement( 5, Custom_type, gleam@list:reverse(erlang:element(5, Custom_type)) ), erlang:setelement(3, Module, [Custom_type@1 | erlang:element(3, Module)]). -spec push_type_alias(module_(), type_alias()) -> module_(). push_type_alias(Module, Type_alias) -> erlang:setelement(4, Module, [Type_alias | erlang:element(4, Module)]). -spec push_variant(custom_type(), variant()) -> custom_type(). push_variant(Custom_type, Variant) -> erlang:setelement( 5, Custom_type, [Variant | erlang:element(5, Custom_type)] ). -spec expect( glexer@token:token(), list({glexer@token:token(), glexer:position()}), fun((glexer:position(), list({glexer@token:token(), glexer:position()})) -> {ok, EZQ} | {error, error()}) ) -> {ok, EZQ} | {error, error()}. expect(Expected, Tokens, Next) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{Token, Position} | Tokens@1] when Token =:= Expected -> Next(Position, Tokens@1); [{Other, Position@1} | _] -> {error, {unexpected_token, Other, Position@1}} end. -spec expect_upper_name( list({glexer@token:token(), glexer:position()}), fun((binary(), list({glexer@token:token(), glexer:position()})) -> {ok, EZV} | {error, error()}) ) -> {ok, EZV} | {error, error()}. expect_upper_name(Tokens, Next) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{{upper_name, Name}, _} | Tokens@1] -> Next(Name, Tokens@1); [{Other, Position} | _] -> {error, {unexpected_token, Other, Position}} end. -spec expect_name( list({glexer@token:token(), glexer:position()}), fun((binary(), list({glexer@token:token(), glexer:position()})) -> {ok, FAA} | {error, error()}) ) -> {ok, FAA} | {error, error()}. expect_name(Tokens, Next) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{{name, Name}, _} | Tokens@1] -> Next(Name, Tokens@1); [{Other, Position} | _] -> {error, {unexpected_token, Other, Position}} end. -spec optional_module_alias(list({glexer@token:token(), glexer:position()})) -> {gleam@option:option(binary()), list({glexer@token:token(), glexer:position()})}. optional_module_alias(Tokens) -> case Tokens of [{as, _}, {{name, Alias}, _} | Tokens@1] -> {{some, Alias}, Tokens@1}; _ -> {none, Tokens} end. -spec name(list({glexer@token:token(), glexer:position()})) -> {ok, {binary(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. name(Tokens) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{{name, Name}, _} | Tokens@1] -> {ok, {Name, Tokens@1}}; [{Token, Position} | _] -> {error, {unexpected_token, Token, Position}} end. -spec field( list({glexer@token:token(), glexer:position()}), fun((list({glexer@token:token(), glexer:position()})) -> {ok, {FCU, list({glexer@token:token(), glexer:position()})}} | {error, error()}) ) -> {ok, {field(FCU), list({glexer@token:token(), glexer:position()})}} | {error, error()}. field(Tokens, Parser) -> case Tokens of [{{name, Name}, _}, {colon, _} | Tokens@1] -> gleam@result:'try'( Parser(Tokens@1), fun(_use0) -> {T, Tokens@2} = _use0, {ok, {{field, {some, Name}, T}, Tokens@2}} end ); _ -> gleam@result:'try'( Parser(Tokens), fun(_use0@1) -> {T@1, Tokens@3} = _use0@1, {ok, {{field, none, T@1}, Tokens@3}} end ) end. -spec slurp(module_(), list({glexer@token:token(), glexer:position()})) -> {ok, module_()} | {error, error()}. slurp(Module, Tokens) -> case Tokens of [] -> {ok, Module}; [{import, _} | Tokens@1] -> import_statement(Module, Tokens@1); [{pub, _}, {type, _} | Tokens@2] -> type_definition(Module, public, Tokens@2); [{type, _} | Tokens@3] -> type_definition(Module, private, Tokens@3); [{pub, _}, {const, _} | Tokens@4] -> const_definition(Module, public, Tokens@4); [{const, _} | Tokens@5] -> const_definition(Module, private, Tokens@5); [_ | Tokens@6] -> slurp(Module, Tokens@6) end. -spec module(binary()) -> {ok, module_()} | {error, error()}. module(Src) -> _pipe = glexer:new(Src), _pipe@1 = glexer:lex(_pipe), _pipe@2 = gleam@list:filter( _pipe@1, fun(Pair) -> erlang:element(1, Pair) /= comment_normal end ), slurp({module, [], [], [], []}, _pipe@2). -spec until( glexer@token:token(), FAF, list({glexer@token:token(), glexer:position()}), fun((FAF, list({glexer@token:token(), glexer:position()})) -> {ok, {FAF, list({glexer@token:token(), glexer:position()})}} | {error, error()}) ) -> {ok, {FAF, list({glexer@token:token(), glexer:position()})}} | {error, error()}. until(Limit, Acc, Tokens, Callback) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{Token, _} | Tokens@1] when Token =:= Limit -> {ok, {Acc, Tokens@1}}; [_ | _] -> case Callback(Acc, Tokens) of {ok, {Acc@1, Tokens@2}} -> until(Limit, Acc@1, Tokens@2, Callback); {error, Error} -> {error, Error} end end. -spec module_name(binary(), list({glexer@token:token(), glexer:position()})) -> {ok, {binary(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. module_name(Name, Tokens) -> case Tokens of [{slash, _}, {{name, S}, _} | Tokens@1] when Name =/= <<""/utf8>> -> module_name( <<<>/binary, S/binary>>, Tokens@1 ); [{{name, S@1}, _} | Tokens@2] when Name =:= <<""/utf8>> -> module_name(S@1, Tokens@2); [] when Name =:= <<""/utf8>> -> {error, unexpected_end_of_input}; [{Other, Position} | _] when Name =:= <<""/utf8>> -> {error, {unexpected_token, Other, Position}}; _ -> {ok, {Name, Tokens}} end. -spec unqualified_imports( list(unqualified_import()), list({glexer@token:token(), glexer:position()}) ) -> {ok, {list(unqualified_import()), list({glexer@token:token(), glexer:position()})}} | {error, error()}. unqualified_imports(Items, Tokens) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{right_brace, _} | Tokens@1] -> {ok, {gleam@list:reverse(Items), Tokens@1}}; [{{upper_name, Name}, _}, {as, _}, {{upper_name, Alias}, _}, {comma, _} | Tokens@2] -> Import_ = {unqualified_import, Name, {some, Alias}}, unqualified_imports([Import_ | Items], Tokens@2); [{{name, Name}, _}, {as, _}, {{name, Alias}, _}, {comma, _} | Tokens@2] -> Import_ = {unqualified_import, Name, {some, Alias}}, unqualified_imports([Import_ | Items], Tokens@2); [{{upper_name, Name@1}, _}, {as, _}, {{upper_name, Alias@1}, _}, {right_brace, _} | Tokens@3] -> Import_@1 = {unqualified_import, Name@1, {some, Alias@1}}, {ok, {gleam@list:reverse([Import_@1 | Items]), Tokens@3}}; [{{name, Name@1}, _}, {as, _}, {{name, Alias@1}, _}, {right_brace, _} | Tokens@3] -> Import_@1 = {unqualified_import, Name@1, {some, Alias@1}}, {ok, {gleam@list:reverse([Import_@1 | Items]), Tokens@3}}; [{{upper_name, Name@2}, _}, {comma, _} | Tokens@4] -> Import_@2 = {unqualified_import, Name@2, none}, unqualified_imports([Import_@2 | Items], Tokens@4); [{{name, Name@2}, _}, {comma, _} | Tokens@4] -> Import_@2 = {unqualified_import, Name@2, none}, unqualified_imports([Import_@2 | Items], Tokens@4); [{{upper_name, Name@3}, _}, {right_brace, _} | Tokens@5] -> Import_@3 = {unqualified_import, Name@3, none}, {ok, {gleam@list:reverse([Import_@3 | Items]), Tokens@5}}; [{{name, Name@3}, _}, {right_brace, _} | Tokens@5] -> Import_@3 = {unqualified_import, Name@3, none}, {ok, {gleam@list:reverse([Import_@3 | Items]), Tokens@5}}; [{Other, Position} | _] -> {error, {unexpected_token, Other, Position}} end. -spec optional_unqualified_imports( list({glexer@token:token(), glexer:position()}) ) -> {ok, {list(unqualified_import()), list({glexer@token:token(), glexer:position()})}} | {error, error()}. optional_unqualified_imports(Tokens) -> case Tokens of [{dot, _}, {left_brace, _} | Tokens@1] -> unqualified_imports([], Tokens@1); _ -> {ok, {[], Tokens}} end. -spec import_statement( module_(), list({glexer@token:token(), glexer:position()}) ) -> {ok, module_()} | {error, error()}. import_statement(Module, Tokens) -> gleam@result:'try'( module_name(<<""/utf8>>, Tokens), fun(_use0) -> {Module_name, Tokens@1} = _use0, gleam@result:'try'( optional_unqualified_imports(Tokens@1), fun(_use0@1) -> {Unqualified, Tokens@2} = _use0@1, {Alias, Tokens@3} = optional_module_alias(Tokens@2), Import_ = {import, Module_name, Alias, Unqualified}, slurp( erlang:setelement( 2, Module, [Import_ | erlang:element(2, Module)] ), Tokens@3 ) end ) end ). -spec comma_delimited( list(FBH), list({glexer@token:token(), glexer:position()}), fun((list({glexer@token:token(), glexer:position()})) -> {ok, {FBH, list({glexer@token:token(), glexer:position()})}} | {error, error()}), glexer@token:token() ) -> {ok, {list(FBH), list({glexer@token:token(), glexer:position()})}} | {error, error()}. comma_delimited(Items, Tokens, Parser, Final) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{Token, _} | Tokens@1] when Token =:= Final -> {ok, {gleam@list:reverse(Items), Tokens@1}}; _ -> gleam@result:'try'( Parser(Tokens), fun(_use0) -> {Element, Tokens@2} = _use0, case Tokens@2 of [{comma, _} | Tokens@3] -> comma_delimited( [Element | Items], Tokens@3, Parser, Final ); [{Token@1, _} | Tokens@4] when Token@1 =:= Final -> {ok, {gleam@list:reverse([Element | Items]), Tokens@4}}; [{Other, Position} | _] -> {error, {unexpected_token, Other, Position}} end end ) end. -spec optional_type_parameters(list({glexer@token:token(), glexer:position()})) -> {ok, {list(binary()), list({glexer@token:token(), glexer:position()})}} | {error, error()}. optional_type_parameters(Tokens) -> case Tokens of [{left_paren, _} | Tokens@1] -> comma_delimited([], Tokens@1, fun name/1, right_paren); _ -> {ok, {[], Tokens}} end. -spec constant_bit_string(list({glexer@token:token(), glexer:position()})) -> {ok, {constant_expression(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. constant_bit_string(Tokens) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{greater_greater, _} | Tokens@1] -> {ok, {constant_bit_string, Tokens@1}}; [_ | Tokens@2] -> constant_bit_string(Tokens@2) end. -spec constant_constructor( binary(), gleam@option:option(binary()), list({glexer@token:token(), glexer:position()}) ) -> {ok, {constant_expression(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. constant_constructor(Name, Module, Tokens) -> case Tokens of [{left_paren, _} | Tokens@1] -> Parser = fun(_capture) -> field(_capture, fun constant_expression/1) end, Result = comma_delimited([], Tokens@1, Parser, right_paren), gleam@result:'try'( Result, fun(_use0) -> {Arguments, Tokens@2} = _use0, {ok, {{constant_constructor, Name, Module, Arguments}, Tokens@2}} end ); _ -> {ok, {{constant_constructor, Name, Module, []}, Tokens}} end. -spec constant_expression(list({glexer@token:token(), glexer:position()})) -> {ok, {constant_expression(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. constant_expression(Tokens) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{{name, Module}, _}, {dot, _}, {{upper_name, Name}, _} | Tokens@1] -> constant_constructor(Name, {some, Module}, Tokens@1); [{{upper_name, Name@1}, _} | Tokens@2] -> constant_constructor(Name@1, none, Tokens@2); [{{int, I}, _} | Tokens@3] -> {ok, {{constant_int, I}, Tokens@3}}; [{{name, N}, _} | Tokens@4] -> {ok, {{constant_variable, N}, Tokens@4}}; [{{float, I@1}, _} | Tokens@5] -> {ok, {{constant_float, I@1}, Tokens@5}}; [{{string, I@2}, _} | Tokens@6] -> {ok, {{constant_string, I@2}, Tokens@6}}; [{left_square, _} | Tokens@7] -> constant_list(Tokens@7); [{hash, _}, {left_paren, _} | Tokens@8] -> constant_tuple(Tokens@8); [{less_less, _} | Tokens@9] -> constant_bit_string(Tokens@9); [{Token, Position} | _] -> {error, {unexpected_token, Token, Position}} end. -spec constant_tuple(list({glexer@token:token(), glexer:position()})) -> {ok, {constant_expression(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. constant_tuple(Tokens) -> Result = comma_delimited([], Tokens, fun constant_expression/1, right_paren), gleam@result:'try'( Result, fun(_use0) -> {Elements, Tokens@1} = _use0, {ok, {{constant_tuple, Elements}, Tokens@1}} end ). -spec constant_list(list({glexer@token:token(), glexer:position()})) -> {ok, {constant_expression(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. constant_list(Tokens) -> Result = comma_delimited( [], Tokens, fun constant_expression/1, right_square ), gleam@result:'try'( Result, fun(_use0) -> {Elements, Tokens@1} = _use0, {ok, {{constant_list, Elements}, Tokens@1}} end ). -spec named_type( binary(), gleam@option:option(binary()), list({glexer@token:token(), glexer:position()}) ) -> {ok, {type(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. named_type(Name, Module, Tokens) -> gleam@result:'try'(case Tokens of [{left_paren, _} | Tokens@1] -> comma_delimited([], Tokens@1, fun type_/1, right_paren); _ -> {ok, {[], Tokens}} end, fun(_use0) -> {Parameters, Tokens@2} = _use0, T = {named_type, Name, Module, Parameters}, {ok, {T, Tokens@2}} end). -spec type_(list({glexer@token:token(), glexer:position()})) -> {ok, {type(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. type_(Tokens) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{fn, _}, {left_paren, _} | Tokens@1] -> fn_type(Tokens@1); [{hash, _}, {left_paren, _} | Tokens@2] -> tuple_type(Tokens@2); [{{name, Module}, _}, {dot, _}, {{upper_name, Name}, _} | Tokens@3] -> named_type(Name, {some, Module}, Tokens@3); [{{upper_name, Name@1}, _} | Tokens@4] -> named_type(Name@1, none, Tokens@4); [{{name, Name@2}, _} | Tokens@5] -> {ok, {{variable_type, Name@2}, Tokens@5}}; [{Token, Position} | _] -> {error, {unexpected_token, Token, Position}} end. -spec const_definition( module_(), publicity(), list({glexer@token:token(), glexer:position()}) ) -> {ok, module_()} | {error, error()}. const_definition(Module, Publicity, Tokens) -> expect_name( Tokens, fun(Name, Tokens@1) -> gleam@result:'try'(case Tokens@1 of [{colon, _} | Tokens@2] -> gleam@result:map( type_(Tokens@2), fun(_use0) -> {Annotation, Tokens@3} = _use0, {{some, Annotation}, Tokens@3} end ); _ -> {ok, {none, Tokens@1}} end, fun(_use0@1) -> {Annotation@1, Tokens@4} = _use0@1, expect( equal, Tokens@4, fun(_, Tokens@5) -> gleam@result:'try'( constant_expression(Tokens@5), fun(_use0@2) -> {Expression, Tokens@6} = _use0@2, _pipe = Module, _pipe@1 = push_constant( _pipe, {constant, Name, Publicity, Annotation@1, Expression} ), slurp(_pipe@1, Tokens@6) end ) end ) end) end ). -spec type_alias( module_(), binary(), list(binary()), publicity(), list({glexer@token:token(), glexer:position()}) ) -> {ok, module_()} | {error, error()}. type_alias(Module, Name, Parameters, Publicity, Tokens) -> gleam@result:'try'( type_(Tokens), fun(_use0) -> {Type_, Tokens@1} = _use0, _pipe = Module, _pipe@1 = push_type_alias( _pipe, {type_alias, Name, Publicity, Parameters, Type_} ), slurp(_pipe@1, Tokens@1) end ). -spec fn_type(list({glexer@token:token(), glexer:position()})) -> {ok, {type(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. fn_type(Tokens) -> Result = comma_delimited([], Tokens, fun type_/1, right_paren), gleam@result:'try'( Result, fun(_use0) -> {Parameters, Tokens@1} = _use0, expect( right_arrow, Tokens@1, fun(_, Tokens@2) -> gleam@result:'try'( type_(Tokens@2), fun(_use0@1) -> {Return, Tokens@3} = _use0@1, {ok, {{function_type, Parameters, Return}, Tokens@3}} end ) end ) end ). -spec tuple_type(list({glexer@token:token(), glexer:position()})) -> {ok, {type(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. tuple_type(Tokens) -> Result = comma_delimited([], Tokens, fun type_/1, right_paren), gleam@result:'try'( Result, fun(_use0) -> {Types, Tokens@1} = _use0, {ok, {{tuple_type, Types}, Tokens@1}} end ). -spec optional_variant_fields(list({glexer@token:token(), glexer:position()})) -> {ok, {list(field(type())), list({glexer@token:token(), glexer:position()})}} | {error, error()}. optional_variant_fields(Tokens) -> case Tokens of [{left_paren, _}, {right_paren, _} | Tokens@1] -> {ok, {[], Tokens@1}}; [{left_paren, _} | Tokens@2] -> comma_delimited( [], Tokens@2, fun(_capture) -> field(_capture, fun type_/1) end, right_paren ); _ -> {ok, {[], Tokens}} end. -spec variants(custom_type(), list({glexer@token:token(), glexer:position()})) -> {ok, {custom_type(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. variants(Ct, Tokens) -> until( right_brace, Ct, Tokens, fun(Ct@1, Tokens@1) -> expect_upper_name( Tokens@1, fun(Name, Tokens@2) -> gleam@result:'try'( optional_variant_fields(Tokens@2), fun(_use0) -> {Parameters, Tokens@3} = _use0, Ct@2 = push_variant( Ct@1, {variant, Name, Parameters} ), {ok, {Ct@2, Tokens@3}} end ) end ) end ). -spec custom_type( module_(), binary(), list(binary()), publicity(), list({glexer@token:token(), glexer:position()}) ) -> {ok, module_()} | {error, error()}. custom_type(Module, Name, Parameters, Publicity, Tokens) -> Ct = {custom_type, Name, Publicity, Parameters, []}, gleam@result:'try'( variants(Ct, Tokens), fun(_use0) -> {Ct@1, Tokens@1} = _use0, Module@1 = push_custom_type(Module, Ct@1), slurp(Module@1, Tokens@1) end ). -spec type_definition( module_(), publicity(), list({glexer@token:token(), glexer:position()}) ) -> {ok, module_()} | {error, error()}. type_definition(Module, Publicity, Tokens) -> expect_upper_name( Tokens, fun(Name, Tokens@1) -> gleam@result:'try'( optional_type_parameters(Tokens@1), fun(_use0) -> {Parameters, Tokens@2} = _use0, case Tokens@2 of [] -> {error, unexpected_end_of_input}; [{equal, _} | Tokens@3] -> type_alias( Module, Name, Parameters, Publicity, Tokens@3 ); [{left_brace, _} | Tokens@4] -> custom_type( Module, Name, Parameters, Publicity, Tokens@4 ) end end ) end ).