-module(glance). -compile([no_auto_import, nowarn_unused_vars]). -export([module/1]). -export_type([module_/0, import/0, unqualified_import/0, publicity/0, type_alias/0, custom_type/0, variant/0, field/0, type/0, error/0]). -type module_() :: {module, list(import()), list(custom_type()), list(type_alias())}. -type import() :: {import, binary(), gleam@option:option(binary()), list(unqualified_import())}. -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 field() :: {field, gleam@option:option(binary()), type()}. -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_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, EZD} | {error, error()}) ) -> {ok, EZD} | {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, EZI} | {error, error()}) ) -> {ok, EZI} | {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 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 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); [_ | Tokens@4] -> slurp(Module, Tokens@4) 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(), EZN, list({glexer@token:token(), glexer:position()}), fun((EZN, list({glexer@token:token(), glexer:position()})) -> {ok, {EZN, list({glexer@token:token(), glexer:position()})}} | {error, error()}) ) -> {ok, {EZN, 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 type_parameters( list(binary()), list({glexer@token:token(), glexer:position()}) ) -> {ok, {list(binary()), list({glexer@token:token(), glexer:position()})}} | {error, error()}. type_parameters(Parameters, Tokens) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{{name, Name}, _}, {right_paren, _} | Tokens@1] -> {ok, {gleam@list:reverse([Name | Parameters]), Tokens@1}}; [{{name, Name}, _}, {comma, _}, {right_paren, _} | Tokens@1] -> {ok, {gleam@list:reverse([Name | Parameters]), Tokens@1}}; [{{name, Name@1}, _}, {comma, _} | Tokens@2] -> type_parameters([Name@1 | Parameters], Tokens@2); [{right_paren, _} | Tokens@3] -> {ok, {gleam@list:reverse(Parameters), Tokens@3}} 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] -> type_parameters([], Tokens@1); _ -> {ok, {[], Tokens}} end. -spec types_then_paren( list(type()), list({glexer@token:token(), glexer:position()}) ) -> {ok, {list(type()), list({glexer@token:token(), glexer:position()})}} | {error, error()}. types_then_paren(Types, Tokens) -> case Tokens of [] -> {error, unexpected_end_of_input}; [{right_paren, _} | Tokens@1] -> {ok, {gleam@list:reverse(Types), Tokens@1}}; _ -> gleam@result:'try'( type_(Tokens), fun(_use0) -> {Type_, Tokens@2} = _use0, case Tokens@2 of [] -> {error, unexpected_end_of_input}; [{right_paren, _} | Tokens@3] -> {ok, {gleam@list:reverse([Type_ | Types]), Tokens@3}}; [{comma, _} | Tokens@4] -> types_then_paren([Type_ | Types], Tokens@4); [{Token, Position} | _] -> {error, {unexpected_token, Token, Position}} end end ) 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] -> types_then_paren([], Tokens@1); _ -> {ok, {[], Tokens}} end, fun(_use0) -> {Parameters, Tokens@2} = _use0, T = {named_type, Name, Module, Parameters}, {ok, {T, Tokens@2}} end). -spec tuple_type(list({glexer@token:token(), glexer:position()})) -> {ok, {type(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. tuple_type(Tokens) -> gleam@result:'try'( types_then_paren([], Tokens), fun(_use0) -> {Types, Tokens@1} = _use0, {ok, {{tuple_type, Types}, Tokens@1}} end ). -spec variant_fields( list(field()), list({glexer@token:token(), glexer:position()}) ) -> {ok, {list(field()), list({glexer@token:token(), glexer:position()})}} | {error, error()}. variant_fields(Fields, Tokens) -> {Label, Tokens@2} = case Tokens of [{{name, Name}, _}, {colon, _} | Tokens@1] -> {{some, Name}, Tokens@1}; _ -> {none, Tokens} end, gleam@result:'try'( type_(Tokens@2), fun(_use0) -> {Type_, Tokens@3} = _use0, case Tokens@3 of [] -> {error, unexpected_end_of_input}; [{right_paren, _} | Tokens@4] -> {ok, {gleam@list:reverse([{field, Label, Type_} | Fields]), Tokens@4}}; [{comma, _}, {right_paren, _} | Tokens@4] -> {ok, {gleam@list:reverse([{field, Label, Type_} | Fields]), Tokens@4}}; [{comma, _} | Tokens@5] -> variant_fields([{field, Label, Type_} | Fields], Tokens@5); [{Token, Position} | _] -> {error, {unexpected_token, Token, Position}} end end ). -spec optional_variant_fields(list({glexer@token:token(), glexer:position()})) -> {ok, {list(field()), 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] -> variant_fields([], Tokens@2); _ -> {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 fn_type(list({glexer@token:token(), glexer:position()})) -> {ok, {type(), list({glexer@token:token(), glexer:position()})}} | {error, error()}. fn_type(Tokens) -> gleam@result:'try'( types_then_paren([], Tokens), 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 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 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 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 ).