How to parse type specifications?

Hi,

Is there a relatively simple way to take a type spec as a string or binary (something like list(atom()) | {ok, non_neg_integer() | binary()} | 42 | not_found) and parse this string into something like the structures handled by the erl_types module? (Any other correctly parsed structure is fine, not only erl_types format.)

If not, then is by best option to do it by myself with yecc?

1 Like

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}]},
              []}}
4 Likes

Now, if you want something a bit more powerful (particularly around macros) you should consider taking a look at GitHub - inaka/katana-code: Code Utilities for Erlang or maybe the parsing and scanning components under GitHub - WhatsApp/erlfmt: An automated code formatter for Erlang

1 Like

It can be quite simple, I’d say :wink: For example, we can clone Gradualizer, run rebar3 shell in the project directory and therein:

> gradualizer:type("list(atom()) | {ok, non_neg_integer() | binary()} | 42 | not_found").
{type,0,union,
      [{type,0,list,[{type,0,atom,[]}]},
       {type,0,tuple,
             [{atom,0,ok},
              {type,0,union,
                    [{type,0,non_neg_integer,[]},{type,0,binary,[]}]}]},
       {integer,0,42},
       {atom,0,not_found}]}
4 Likes