Current section

Files

Jump to
docsh src docsh_syntax.erl
Raw

src/docsh_syntax.erl

-module(docsh_syntax).
-behaviour(docsh_reader).
-export([available/1,
to_internal/1]).
-include("docsh_stacktrace.hrl").
-define(il2b(IOList), iolist_to_binary(IOList)).
-define(l(Args), fun () -> Args end).
-spec available(docsh_beam:t()) -> [docsh_reader:t()].
available(Beam) ->
[ ?MODULE || docsh_beam:abstract_code(Beam) /= false ].
-spec to_internal(docsh_beam:t()) -> R when
R :: {ok, docsh_internal:t()}
| {error, any(), [erlang:stack_item()]}.
to_internal(Beam) ->
try
Forms = case {docsh_beam:abstract_code(Beam), docsh_beam:source_file(Beam)} of
{false, false} ->
erlang:error(no_debug_info_no_src, [Beam]);
{Abst, false} ->
Abst;
{_, Source} ->
{ok, Fs} = epp:parse_file(Source, []),
Fs
end,
%% TODO: get export_type attribute and pass to get_functions / get_types to set visibility properly
Internal = #{name => get_module_name(Forms),
items => get_functions(Forms) ++ get_types(Forms)},
{ok, Internal}
catch ?STACKTRACE(_, R, Stacktrace)
{error, R, Stacktrace}
end.
get_module_name(Forms) ->
case lists:keyfind(module, 3, Forms) of
false -> erlang:error({not_found, module, Forms}, [Forms]);
{_, _, module, Mod} -> Mod
end.
-spec get_functions([erl_parse:abstract_form()]) -> [docsh_internal:item()].
get_functions(Forms) ->
[ function(Spec) || {attribute, _, spec, _} = Spec <- Forms ].
function({attribute, _, spec, _} = Spec) ->
#{kind => 'function',
name => function_name(Spec),
arity => function_arity(Spec),
signature => function_signature(Spec)}.
function_name({attribute, _, spec, Data}) ->
{{Name, _Arity}, _} = Data,
Name.
function_arity({attribute, _, spec, Data}) ->
{{_Name, Arity}, _} = Data,
Arity.
function_signature({attribute, _, spec, _} = Spec) ->
?il2b(format(Spec)).
-spec get_types([erl_parse:abstract_form()]) -> [docsh_internal:item()].
get_types(Forms) ->
lists:filtermap(fun get_type/1, Forms).
get_type({attribute, _, T, _} = Type)
when T =:= opaque;
T =:= type ->
{true, type(Type)};
get_type(_) ->
false.
type({attribute, _, T, _} = Type)
when T =:= opaque;
T =:= type ->
#{kind => 'type',
name => type_name(Type),
arity => type_arity(Type),
signature => type_signature(Type)}.
type_name({attribute, _, T, Data})
when T =:= opaque;
T =:= type ->
{Name, _, _Args} = Data,
Name.
type_arity({attribute, _, T, Data})
when T =:= opaque;
T =:= type ->
{_Name, _, Args} = Data,
length(Args).
type_signature({attribute, _, T, _} = Type)
when T =:= opaque;
T =:= type ->
?il2b(format(Type)).
format(Attr) ->
[erl_prettypr:format(Attr, [{ribbon, 80}]), $\n].