It’s never as simple as you would like it, but you can go a great length using erl_parse
and erl_scan
, as long as you don’t have macros in the type specs:
1> {ok, T, _} = erl_scan:string("-type a_type() :: list(atom()) | {ok, non_neg_integer() | binary()} | 42 | not_found."), rp(erl_parse:parse_form(T)).
{ok,{attribute,1,type,
{a_type,{type,1,union,
[{type,1,list,[{type,1,atom,[]}]},
{type,1,tuple,
[{atom,1,ok},
{type,1,union,
[{type,1,non_neg_integer,[]},{type,1,binary,[]}]}]},
{integer,1,42},
{atom,1,not_found}]},
[]}}}
ok
You can even go one step further…
2> {ok, T, _} = erl_scan:string("-type a_type() :: list(atom()) | {ok, non_neg_integer() | binary()} | 42 | not_found."), {ok, Attr} = erl_parse:parse_form(T), erl_syntax_lib:analyze_attribute(Attr).
{type,{a_type,{type,1,union,
[{type,1,list,[{type,1,atom,[]}]},
{type,1,tuple,
[{atom,1,ok},
{type,1,union,
[{type,1,non_neg_integer,[]},{type,1,binary,[]}]}]},
{integer,1,42},
{atom,1,not_found}]},
[]}}