Current section

Files

Jump to
glance src glance.erl
Raw

src/glance.erl

-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(
<<<<Name/binary, "/"/utf8>>/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
).