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]).
-export([specs/1,
types/1]).
%% Internal.
-export([spec/1,
type/1]).
-import(docsh_lib, [debug/3]).
-type ast() :: [form()].
-type form() :: erl_parse:abstract_form().
%% TODO: temporary type
-type z() :: {{spec | type, {atom(), arity()}}, {description, binary()}}.
-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} ->
error(no_debug_info_no_src, [Beam]);
{Abst, false} ->
Abst;
{_, Source} ->
{ok, Fs} = epp:parse_file(Source, []),
Fs
end,
{ok, [{module, [{name, module_name(Forms)}]}] ++ specs(Forms) ++ types(Forms)}
catch
_:R -> {error, R, erlang:get_stacktrace()}
end.
module_name(Forms) ->
case lists:keyfind(module, 3, Forms) of
false -> error(not_found, [Forms]);
{_, _, module, Mod} -> Mod
end.
-spec specs(ast()) -> [z()].
specs(Forms) -> attrs(spec, Forms).
-spec types(ast()) -> [z()].
types(Forms) -> attrs(type, Forms).
-spec attrs(spec | type, ast()) -> [z()].
attrs(AttrName, Forms) ->
debug(AttrName, [ {{AttrName, name_arity(AttrName, F)},
{description, ?il2b(desc(F))}}
|| F <- lists:flatmap(fun ?MODULE:AttrName/1, Forms) ]).
desc(Attr) ->
debug('repr:type', format(Attr)).
-spec spec(form()) -> [form()].
spec(Attr) -> attr(spec, Attr).
-spec type(form()) -> [form()].
type(Attr) -> attr(type, Attr).
attr(AttrName, {attribute,_,AttrName,_} = A) -> [A];
attr(_, _) -> [].
-spec name_arity(spec | type, form()) -> {atom(), arity()}.
name_arity(type, {attribute, _, type, Data}) ->
debug('name_arity:in', Data),
{Name, _, Args} = Data,
%% TODO: how to extract type arity? is this correct?
{Name, length(Args)};
name_arity(spec, {attribute, _, spec, Data}) ->
{NameArity, _} = Data,
NameArity.
debug(Tag, Content) ->
docsh_lib:debug(Tag, "~s: ~p~n", [Tag, Content]),
Content.
-ifdef(erl_prettypr_no_specs).
format(Attr) ->
erl_pp:form(Attr).
-else.
format(Attr) ->
[erl_prettypr:format(Attr, [{ribbon, 80}]), $\n].
-endif.