aboutsummaryrefslogtreecommitdiffstats
path: root/lib/edoc/src/edoc_parser.yrl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/edoc/src/edoc_parser.yrl')
-rw-r--r--lib/edoc/src/edoc_parser.yrl423
1 files changed, 423 insertions, 0 deletions
diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl
new file mode 100644
index 0000000000..0eea8ae66f
--- /dev/null
+++ b/lib/edoc/src/edoc_parser.yrl
@@ -0,0 +1,423 @@
+%% ========================== -*-Erlang-*- =============================
+%% EDoc type specification grammar for the Yecc parser generator,
+%% adapted from Sven-Olof Nystr�m's type specification parser.
+%%
+%% Also contains entry points for parsing things like typedefs,
+%% references, and throws-declarations.
+%%
+%% Copyright (C) 2002-2005 Richard Carlsson
+%%
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% Author contact: [email protected]
+%%
+%% $Id$
+%%
+%% =====================================================================
+
+Nonterminals
+start spec func_type utype_list utype_tuple utypes utype ptypes ptype
+nutype function_name where_defs defs def typedef etype throws qname ref
+aref mref lref pref var_list vars fields field.
+
+Terminals
+atom float integer var string start_spec start_typedef start_throws
+start_ref
+
+'(' ')' ',' '.' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//' '*'
+'#' 'where'.
+
+Rootsymbol start.
+
+start -> start_spec spec: '$2'.
+start -> start_throws throws: '$2'.
+start -> start_typedef typedef: '$2'.
+start -> start_ref ref: '$2'.
+
+%% Produced in reverse order.
+qname -> atom: [tok_val('$1')].
+qname -> qname '.' atom: [tok_val('$3') | '$1'].
+
+spec -> func_type where_defs:
+ #t_spec{type = '$1', defs = lists:reverse('$2')}.
+spec -> function_name func_type where_defs:
+ #t_spec{name = '$1', type = '$2', defs = lists:reverse('$3')}.
+
+where_defs -> 'where' defs: '$2'.
+where_defs -> defs: '$1'.
+
+function_name -> atom: #t_name{name = tok_val('$1')}.
+
+func_type -> utype_list '->' utype:
+ #t_fun{args = element(1, '$1'), range = '$3'}.
+
+
+%% Paired with line number, for later error reporting
+utype_list -> '(' ')' : {[], tok_line('$1')}.
+utype_list -> '(' utypes ')' : {lists:reverse('$2'), tok_line('$1')}.
+
+utype_tuple -> '{' '}' : [].
+utype_tuple -> '{' utypes '}' : lists:reverse('$2').
+
+%% Produced in reverse order.
+utypes -> utype : ['$1'].
+utypes -> utypes ',' utype : ['$3' | '$1'].
+
+utype -> nutype string: annotate('$1', tok_val('$2')).
+utype -> nutype: '$1'.
+
+nutype -> var '::' ptypes: annotate(union('$3'), tok_val('$1')).
+nutype -> ptypes: union('$1').
+
+%% Produced in reverse order.
+ptypes -> ptype : ['$1'].
+ptypes -> ptypes '+' ptype : ['$3' | '$1'].
+ptypes -> ptypes '|' ptype : ['$3' | '$1'].
+
+ptype -> var : #t_var{name = tok_val('$1')}.
+ptype -> atom : #t_atom{val = tok_val('$1')}.
+ptype -> integer: #t_integer{val = tok_val('$1')}.
+ptype -> float: #t_float{val = tok_val('$1')}.
+ptype -> utype_tuple : #t_tuple{types = '$1'}.
+ptype -> '[' ']' : #t_nil{}.
+ptype -> '[' utype ']' : #t_list{type = '$2'}.
+ptype -> utype_list:
+ if length(element(1, '$1')) == 1 ->
+ %% there must be exactly one utype in the list
+ hd(element(1, '$1'));
+ length(element(1, '$1')) == 0 ->
+ return_error(element(2, '$1'), "syntax error before: ')'");
+ true ->
+ return_error(element(2, '$1'), "syntax error before: ','")
+ end.
+ptype -> utype_list '->' ptype:
+ #t_fun{args = element(1, '$1'), range = '$3'}.
+ptype -> '#' atom '{' '}' :
+ #t_record{name = #t_atom{val = tok_val('$2')}}.
+ptype -> '#' atom '{' fields '}' :
+ #t_record{name = #t_atom{val = tok_val('$2')},
+ fields = lists:reverse('$4')}.
+ptype -> atom utype_list:
+ #t_type{name = #t_name{name = tok_val('$1')},
+ args = element(1, '$2')}.
+ptype -> qname ':' atom utype_list :
+ #t_type{name = #t_name{module = qname('$1'),
+ name = tok_val('$3')},
+ args = element(1, '$4')}.
+ptype -> '//' atom '/' qname ':' atom utype_list :
+ #t_type{name = #t_name{app = tok_val('$2'),
+ module = qname('$4'),
+ name = tok_val('$6')},
+ args = element(1, '$7')}.
+
+%% Produced in reverse order.
+fields -> field : ['$1'].
+fields -> fields ',' field : ['$3' | '$1'].
+
+field -> atom '=' utype :
+ #t_field{name = #t_atom{val = tok_val('$1')}, type = '$3'}.
+
+%% Produced in reverse order.
+defs -> '$empty' : [].
+defs -> defs def : ['$2' | '$1'].
+defs -> defs ',' def : ['$3' | '$1'].
+
+def -> var '=' utype:
+ #t_def{name = #t_var{name = tok_val('$1')},
+ type = '$3'}.
+def -> atom var_list '=' utype:
+ #t_def{name = #t_type{name = #t_name{name = tok_val('$1')},
+ args = '$2'},
+ type = '$4'}.
+
+var_list -> '(' ')' : [].
+var_list -> '(' vars ')' : lists:reverse('$2').
+
+%% Produced in reverse order.
+vars -> var : [#t_var{name = tok_val('$1')}].
+vars -> vars ',' var : [#t_var{name = tok_val('$3')} | '$1'].
+
+typedef -> atom var_list where_defs:
+ #t_typedef{name = #t_name{name = tok_val('$1')},
+ args = '$2',
+ defs = lists:reverse('$3')}.
+typedef -> atom var_list '=' utype where_defs:
+ #t_typedef{name = #t_name{name = tok_val('$1')},
+ args = '$2',
+ type = '$4',
+ defs = lists:reverse('$5')}.
+
+%% References
+
+ref -> aref: '$1'.
+ref -> mref: '$1'.
+ref -> lref: '$1'.
+ref -> pref: '$1'.
+
+aref -> '//' atom:
+ edoc_refs:app(tok_val('$2')).
+aref -> '//' atom '/' mref:
+ edoc_refs:app(tok_val('$2'), '$4').
+aref -> '//' atom '/' pref:
+ edoc_refs:app(tok_val('$2'), '$4').
+
+mref -> qname ':' atom '/' integer:
+ edoc_refs:function(qname('$1'), tok_val('$3'), tok_val('$5')).
+mref -> qname ':' atom '(' ')':
+ edoc_refs:type(qname('$1'), tok_val('$3')).
+mref -> qname:
+ edoc_refs:module(qname('$1')).
+
+pref -> qname '.' '*':
+ edoc_refs:package(qname('$1')).
+
+lref -> atom '/' integer:
+ edoc_refs:function(tok_val('$1'), tok_val('$3')).
+lref -> atom '(' ')':
+ edoc_refs:type(tok_val('$1')).
+
+%% Exception declarations
+
+etype -> utype: '$1'.
+
+throws -> etype where_defs:
+ #t_throws{type = '$1',
+ defs = lists:reverse('$2')}.
+
+%% (commented out for now)
+%% Header
+%% "%% ========================== -*-Erlang-*- ============================="
+%% "%% EDoc function specification parser, generated from the file"
+%% "%% \"edoc_parser.yrl\" by the Yecc parser generator."
+%% "%%"
+%% "%% Copyright (C) 2002-2005 Richard Carlsson"
+%% "%%"
+%% "%% This library is free software; you can redistribute it and/or modify"
+%% "%% it under the terms of the GNU Lesser General Public License as"
+%% "%% published by the Free Software Foundation; either version 2 of the"
+%% "%% License, or (at your option) any later version."
+%% "%%"
+%% "%% This library is distributed in the hope that it will be useful, but"
+%% "%% WITHOUT ANY WARRANTY; without even the implied warranty of"
+%% "%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU"
+%% "%% Lesser General Public License for more details."
+%% "%%"
+%% "%% You should have received a copy of the GNU Lesser General Public"
+%% "%% License along with this library; if not, write to the Free Software"
+%% "%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307"
+%% "%% USA"
+%% "%%"
+%% "%% @private"
+%% "%% @author Richard Carlsson <[email protected]>"
+%% "%% ===================================================================="
+%% .
+
+Erlang code.
+
+%% ========================== -*-Erlang-*- =============================
+%% EDoc function specification parser, generated from the file
+%% "edoc_parser.yrl" by the Yecc parser generator.
+%%
+%% Copyright (C) 2002-2005 Richard Carlsson
+%%
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%% ====================================================================
+
+-export([parse_spec/2, parse_typedef/2, parse_throws/2, parse_ref/2,
+ parse_see/2, parse_param/2]).
+
+-include("edoc_types.hrl").
+
+%% Multiple entry point hack:
+
+start_spec(Ts, L) -> run_parser(Ts, L, start_spec).
+
+start_typedef(Ts, L) -> run_parser(Ts, L, start_typedef).
+
+start_throws(Ts, L) -> run_parser(Ts, L, start_throws).
+
+start_ref(Ts, L) -> run_parser(Ts, L, start_ref).
+
+%% Error reporting fix
+
+run_parser(Ts, L, Start) ->
+ case parse([{Start,L} | Ts]) of
+ {error, {999999,?MODULE,_}} ->
+ What = case Start of
+ start_spec -> "specification";
+ start_typedef -> "type definition";
+ start_throws -> "exception declaration";
+ start_ref -> "reference"
+ end,
+ {error, {L,?MODULE,["unexpected end of ", What]}};
+ Other -> Other
+ end.
+
+%% Utility functions:
+
+tok_val(T) -> element(3, T).
+
+tok_line(T) -> element(2, T).
+
+qname([A]) ->
+ A; % avoid unnecessary call to packages:concat/1.
+qname(List) ->
+ list_to_atom(packages:concat(lists:reverse(List))).
+
+union(Ts) ->
+ case Ts of
+ [T] -> T;
+ _ -> #t_union{types = lists:reverse(Ts)}
+ end.
+
+annotate(T, A) -> ?add_t_ann(T, A).
+
+%% ---------------------------------------------------------------------
+
+%% @doc EDoc type specification parsing. Parses the content of
+%% <a href="overview-summary.html#ftag-spec">`@spec'</a> declarations.
+
+parse_spec(S, L) ->
+ case edoc_scanner:string(S, L) of
+ {ok, Ts, _} ->
+ case start_spec(Ts, L) of
+ {ok, Spec} ->
+ Spec;
+ {error, E} ->
+ throw_error(E, L)
+ end;
+ {error, E, _} ->
+ throw_error(E, L)
+ end.
+
+%% ---------------------------------------------------------------------
+
+%% @doc EDoc type definition parsing. Parses the content of
+%% <a href="overview-summary.html#gtag-type">`@type'</a> declarations.
+
+parse_typedef(S, L) ->
+ {S1, S2} = edoc_lib:split_at_stop(S),
+ N = edoc_lib:count($\n, S1),
+ L1 = L + N,
+ Text = edoc_lib:strip_space(S2),
+ {parse_typedef_1(S1, L), edoc_wiki:parse_xml(Text, L1)}.
+
+parse_typedef_1(S, L) ->
+ case edoc_scanner:string(S, L) of
+ {ok, Ts, _} ->
+ case start_typedef(Ts, L) of
+ {ok, T} ->
+ T;
+ {error, E} ->
+ throw_error({parse_typedef, E}, L)
+ end;
+ {error, E, _} ->
+ throw_error({parse_typedef, E}, L)
+ end.
+
+%% ---------------------------------------------------------------------
+
+%% @doc Parses a <a
+%% href="overview-summary.html#References">reference</a> to a module,
+%% package, function, type, or application
+
+parse_ref(S, L) ->
+ case edoc_scanner:string(S, L) of
+ {ok, Ts, _} ->
+ case start_ref(Ts, L) of
+ {ok, T} ->
+ T;
+ {error, E} ->
+ throw_error({parse_ref, E}, L)
+ end;
+ {error, E, _} ->
+ throw_error({parse_ref, E}, L)
+ end.
+
+%% ---------------------------------------------------------------------
+
+%% @doc Parses the content of
+%% <a href="overview-summary.html#ftag-see">`@see'</a> references.
+parse_see(S, L) ->
+ {S1, S2} = edoc_lib:split_at_stop(S),
+ N = edoc_lib:count($\n, S1),
+ L1 = L + N,
+ Text = edoc_lib:strip_space(S2),
+ {parse_ref(S1, L), edoc_wiki:parse_xml(Text, L1)}.
+
+%% ---------------------------------------------------------------------
+
+%% @doc Parses the content of
+%% <a href="overview-summary.html#ftag-param">`@param'</a> tags.
+parse_param(S, L) ->
+ {S1, S2} = edoc_lib:split_at_space(edoc_lib:strip_space(S)),
+ case edoc_lib:strip_space(S1) of
+ "" -> throw_error(parse_param, L);
+ Name ->
+ Text = edoc_lib:strip_space(S2),
+ {list_to_atom(Name), edoc_wiki:parse_xml(Text, L)}
+ end.
+
+%% ---------------------------------------------------------------------
+
+%% @doc EDoc exception specification parsing. Parses the content of
+%% <a href="overview-summary.html#ftag-throws">`@throws'</a> declarations.
+
+parse_throws(S, L) ->
+ case edoc_scanner:string(S, L) of
+ {ok, Ts, _} ->
+ case start_throws(Ts, L) of
+ {ok, Spec} ->
+ Spec;
+ {error, E} ->
+ throw_error({parse_throws, E}, L)
+ end;
+ {error, E, _} ->
+ throw_error({parse_throws, E}, L)
+ end.
+
+%% ---------------------------------------------------------------------
+
+throw_error({L, M, D}, _L0) ->
+ throw({error,L,{format_error,M,D}});
+throw_error({parse_spec, E}, L) ->
+ throw_error({"specification", E}, L);
+throw_error({parse_typedef, E}, L) ->
+ throw_error({"type definition", E}, L);
+throw_error({parse_ref, E}, L) ->
+ throw_error({"reference", E}, L);
+throw_error({parse_throws, E}, L) ->
+ throw_error({"throws-declaration", E}, L);
+throw_error(parse_param, L) ->
+ throw({error, L, "missing parameter name"});
+throw_error({Where, E}, L) when is_list(Where) ->
+ throw({error,L,{"unknown error parsing ~s: ~P.",[Where,E,15]}});
+throw_error(E, L) ->
+ %% Just in case.
+ throw({error,L,{"unknown parse error: ~P.",[E,15]}}).