/* -*- Mode: Prolog -*- */
/** @copyright
  
  This file is part of PrologDoc (http://prologdoc.sourceforge.net/).

  Copyright (C) 2004 by Salvador Fandino (sfandino@@yahoo.com)

  PrologDoc is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  PrologDoc 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with PrologDoc; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  @/copyright */

:- module(pd_comments_parser, [parse_comments/2]).


:- use_module(library('prologdoc/pd')).
:- use_module(library('prologdoc/pd_util')).
:- use_module(library('prologdoc/pd_comments_reader')).

valid_child(A, B) :-
	is_a(A, A1),
	tag_inside(A1, B1),
	is_a(B, B1), !.

accepts_childs(A) :-
	valid_child(A, _).

accepts_args(A) :-
	is_a(A, B),
	tag_args(B,_),
	!.

parse_comments(File, t($top, [$n_lines=Lines], Childs)) :-
	read_comments(File, Comments),
	n_lines1(Comments, Lines),
	childs($top, Childs1, Comments, Remains),
	(   Remains \= []
        ->  report_syntax_error(File, Comments, Remains, [])
        ;   make_para(Childs1, Childs2),
	    remove_empty_elements(Childs2, Childs) ).

make_para(Tree, [t($para, [], Childs)|Childs1]) :-
	make_para(Tree, Childs, Childs1).

make_para([], [], []).
make_para([text(Text)|Childs], [text(Line)], [t($para, [], Childs1)|More]) :-
	append(Line1, [0'\n|Text1], Text),
	!,
	atom_codes(Line, Line1),
	make_para([text(Text1)|Childs], Childs1, More).
make_para([text(Text)|Childs], [text(Text1)|Childs1], More) :-
	!,
	atom_codes(Text1, Text),
	make_para(Childs, Childs1, More).
make_para([t(Name, Args, Childs)|More], [t(Name, Args, Childs1)|More1], More2) :-
	is_a(Name, $inp),
	!,
	make_para(Childs, Childs1),
	make_para(More, More1, More2).
make_para([t(Name, Args, Childs)|More], [], [t(Name, Args, Childs1)|More1]) :-
	make_para(Childs, Childs1),
	make_para_skip(More, More1).

make_para_skip(Tree, Tree1) :-
	(   Tree = [t(Name, _, _)|_],
	    \+ is_a(Name, $inp)
	->  make_para(Tree, _, Tree1)
	;   Tree1 = [t($para, [], Content)|More1],
	    make_para(Tree, Content, More1) ).

remove_empty_elements([], []).
remove_empty_elements([text('')|More], Tree) :-
	!,
	remove_empty_elements(More, Tree).
remove_empty_elements([text(' ')|More], Tree) :-
	!,
	remove_empty_elements(More, Tree).
remove_empty_elements([text(T)|More], [text(T)|Tree]) :-
	!,
	remove_empty_elements(More, Tree).
remove_empty_elements([t(Name, Args, Childs)|More], Tree) :-
	remove_empty_elements(Childs, Childs1),
	remove_empty_elements1(t(Name, Args, Childs1), More, Tree).

remove_empty_elements1(t(Name, [], []), More, Tree) :-
	is_a(Name, $no_empty),
	!,
	remove_empty_elements(More, Tree).
remove_empty_elements1(H, More, [H|Tree]) :-
	remove_empty_elements(More, Tree).

childs(Parent, [Child|Childs]) --> child(Parent, Child), !, childs(Parent, Childs).
childs(Parent, []) --> "@/", alphanumerics(Codes), { atom(Parent), atom_codes(Parent, Codes) }, !, [].
childs(_, []) --> [].

child(Parent, t(Name, Args, Childs)) --> "@", tag_name(Parent, Name), !,
	(   { accepts_args(Name) }
	->  lines_to_end(LTE), args(Args1), { append(Args1, [$lines_to_end=LTE], Args) }
	;   { Args=[] } ),
	(   { accepts_childs(Name) }
	->  blanks, childs(Name, Childs)
	;   { Childs=[] } ).
child(Parent, text(Text)) --> { valid_child(Parent, $inp) }, text1(Text), !, [].
child(Parent, Child) --> blanks1, child(Parent, Child).

args(Args) --> "(", !, balanced(B), ")", { codes_to_term(B, Args) }.
args([$line=Arg]) --> spaces, args_line(Arg1), { atom_codes(Arg, Arg1) }.

codes_to_term(B, Args) :-
	append([0'[|B], "]", B1),
	atom_codes(A, B1),
	atom_to_term(A, Args).

args_line([]) --> spaces, "\n", !, [].
args_line([]) --> spaces, ":", !, [].
args_line([H|T]) --> "@", !, non_alpha(H), { H \= 0'/ }, args_line(T).
args_line([H|T]) --> [H], args_line(T).

text1([A|M]) --> text([A|M]).
text([0'\n|Text]) --> spaces, "\n", spaces, "\n", !, blanks, text(Text).
text([0' |Text]) --> blanks1, !, text(Text).
text([N|Text]) --> "@", non_alpha(N), { N \= 0'/ }, !, text(Text).
text([N|Text]) --> [N], { N \= 0'@ }, !, text(Text).
text([]) --> [].

blanks1 --> blank, blanks.

blanks --> blank, !, blanks.
blanks --> [].

blank --> [S], { code_type(S, space) }.

spaces --> space, !, spaces.
spaces --> [].

space --> [S], { code_type(S, space), S \= 0'\n }.

tag_name(Parent, Name) --> name(Name), !, { valid_child(Parent, Name) }.

name(N) --> alpha(A), alphanumerics(M), { atom_codes(N, [A|M]) }.

alphanumerics([H|T]) --> alphanumeric(H), !, alphanumerics(T).
alphanumerics([]) --> [].

alphanumeric(A) --> [A], { code_type(A, csym) }.

alpha(A) --> [A], { code_type(A, alpha) }.

numeric(A) --> [A], { code_type(A, digit) }.

non_alpha(A) --> [A], { \+ alpha(_, [A], []) }.

balanced(S, L1, L2) :- balanced1(L1, L2), !, append(S, L2, L1).

balanced1 --> [O], { balanced_pair(O, C) }, !, balanced1, [C], balanced1.
balanced1 --> "\"", !, more_double_quoted, balanced1.
balanced1 --> "'", !, more_single_quoted, balanced1.
balanced1 --> "0'", !, quoted_char, balanced1.
balanced1 --> [L], { \+balanced_pair(_, L) }, !, balanced1.
balanced1 --> [].

balanced_pair(0'(, 0')).
balanced_pair(0'[, 0']).
balanced_pair(0'{, 0'}).
									     
more_double_quoted --> "\"", !, [].
more_double_quoted --> quoted_char, more_double_quoted.

more_single_quoted --> "'", !, [].
more_single_quoted --> quoted_char, more_single_quoted.

quoted_char --> "\\", octal, (octal, (octal; []); []), ("\\"; []), !, [].
quoted_char --> "\\x", hex, (hex ; []), ("\\" ; []), !, [].
quoted_char --> "\\", !, [_].
quoted_char --> [_].

octal --> [O], { member(O, "01234567") }.

hex --> [H], { code_type(H, xdigit)}.

lines_to_end(Pos, A, A) :-
	n_lines(A, Pos).

	