1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2006-2024, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 SWI-Prolog Solutions b.v. 10 All rights reserved. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions 14 are met: 15 16 1. Redistributions of source code must retain the above copyright 17 notice, this list of conditions and the following disclaimer. 18 19 2. Redistributions in binary form must reproduce the above copyright 20 notice, this list of conditions and the following disclaimer in 21 the documentation and/or other materials provided with the 22 distribution. 23 24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 POSSIBILITY OF SUCH DAMAGE. 36*/ 37 38:- module(pldoc_wiki, 39 [ wiki_codes_to_dom/3, % +Codes, +Args, -DOM 40 wiki_lines_to_dom/3, % +Lines, +Map, -DOM 41 section_comment_header/3, % +Lines, -Header, -RestLines 42 summary_from_lines/2, % +Lines, -Codes 43 indented_lines/3, % +Text, +PrefixChars, -Lines 44 strip_leading_par/2, % +DOM0, -DOM 45 autolink_extension/2, % ?Extension, ?Type 46 autolink_file/2 % +FileName, -Type 47 ]). 48:- use_module(library(lists)). 49:- use_module(library(debug)). 50:- use_module(library(error)). 51:- use_module(library(pairs)). 52:- use_module(library(option)). 53:- use_module(library(debug)). 54:- use_module(library(apply)). 55:- use_module(library(dcg/basics)). 56 57:- use_module(doc_util).
71:- multifile 72 prolog:doc_wiki_face//2, % -Out, +VarNames 73 prolog:doc_url_expansion/3, % +Alias(Rest), -HREF, -Label 74 prolog:url_expansion_hook/3, % +Term, -Ref, -Label 75 prolog:doc_autolink_extension/2.% +Extension, -Type 76 77 78 /******************************* 79 * WIKI PARSING * 80 *******************************/
87wiki_lines_to_dom(Lines, Args, HTML) :-
88 tokenize_lines(Lines, Tokens0),
89 normalise_indentation(Tokens0, Tokens),
90 wiki_structure(Tokens, -1, Pars),
91 wiki_faces(Pars, Args, HTML).
100wiki_codes_to_dom(Codes, Args, DOM) :-
101 indented_lines(Codes, [], Lines),
102 wiki_lines_to_dom(Lines, Args, DOM).112wiki_structure([], _, []) :- !. 113wiki_structure([_-[]|T], BI, Pars) :- % empty lines 114 !, 115 wiki_structure(T, BI, Pars). 116wiki_structure(Lines, BI, [\tags(Tags)|Rest]) :- 117 tags(Lines, Tags, RestLines), 118 !, 119 wiki_structure(RestLines, BI, Rest). 120wiki_structure(Lines, BI, [P1|PL]) :- 121 take_block(Lines, BI, P1, RestLines), 122 wiki_structure(RestLines, BI, PL).
129take_block([_-[]|Lines], BaseIndent, Block, Rest) :- 130 !, 131 take_block(Lines, BaseIndent, Block, Rest). 132take_block([N-_|_], BaseIndent, _, _) :- 133 N < BaseIndent, 134 !, 135 fail. % less indented 136take_block(Lines, BaseIndent, List, Rest) :- 137 list_item(Lines, Type, Indent, LI, LIT, Rest0), 138 !, 139 Indent > BaseIndent, 140 rest_list(Rest0, Type, Indent, LIT, [], Rest), 141 List0 =.. [Type, LI], 142 ( ul_to_dl(List0, List) 143 -> true 144 ; List0 = dl(Items) 145 -> List = dl(class=wiki, Items) 146 ; List = List0 147 ). 148take_block([N-['|'|RL1]|LT], _, Table, Rest) :- 149 phrase(row(R0), RL1), 150 take_table(LT, N, R0, Table, Rest), 151 !. 152take_block([0-[-,-|More]|LT], _, Block, LT) :- % separation line 153 maplist(=(-), More), 154 !, 155 Block = hr([]). 156take_block([_-Line|LT], _, Block, LT) :- % separation line 157 ruler(Line), 158 !, 159 Block = hr([]). 160take_block([_-[@|_]], _, _, _) :- % starts @tags section 161 !, 162 fail. 163take_block(Lines, _BaseIndent, Section, RestLines) :- 164 section_header(Lines, Section, RestLines), 165 !. 166take_block([_-Verb|Lines], _, Verb, Lines) :- 167 verbatim_term(Verb), 168 !. 169take_block([I-L1|LT], BaseIndent, Elem, Rest) :- 170 !, 171 append(L1, PT, Par), 172 rest_par(LT, PT, I, BaseIndent, MaxI, Rest), 173 ( MaxI >= BaseIndent+16 174 -> Elem = center(Par) 175 ; phrase(blockquote(BQ), Par) 176 -> Elem = blockquote(BQ) 177 ; Elem = p(Par) 178 ). 179take_block([Verb|Lines], _, Verb, Lines). 180 181blockquote(Clean) --> 182 [>, ' '], 183 bq_lines(Clean). 184 185bq_lines([' '|Par]) --> 186 ['\n'], !, [>,' '], 187 bq_lines(Par). 188bq_lines([H|T]) --> 189 [H], 190 bq_lines(T). 191bq_lines([]) --> 192 [].
199ruler([C0|Line]) :- 200 rule_char(C0), 201 phrase(ruler(C0, 1), Line). 202 203ruler(C, N) --> [C], !, { N2 is N+1 }, ruler(C, N2). 204ruler(C, N) --> [' '], !, ruler(C, N). 205ruler(_, N) --> { N >= 3 }. 206 207rule_char('-'). 208rule_char('_'). 209rule_char('*').
219list_item([Indent-Line|LT], Type, Indent, Items, ItemT, Rest) :-
220 !,
221 list_item_prefix(Type, Line, L1),
222 ( Type == dl
223 -> split_dt(L1, DT0, DD1),
224 append(DD1, LIT, DD),
225 strip_ws_tokens(DT0, DT),
226 Items = [dt(DT),dd(DD)|ItemT]
227 ; append(L1, LIT, LI0),
228 Items = [li(LI0)|ItemT]
229 ),
230 rest_list_item(LT, Type, Indent, LIT, Rest).
236rest_list_item(Lines, _Type, Indent, RestItem, RestLines) :-
237 take_blocks_at_indent(Lines, Indent, Blocks, RestLines),
238 ( Blocks = [p(Par)|MoreBlocks]
239 -> append(['\n'|Par], MoreBlocks, RestItem)
240 ; RestItem = Blocks
241 ).247take_blocks_at_indent(Lines, _, [], Lines) :- 248 skip_empty_lines(Lines, Lines1), 249 section_header(Lines1, _, _), 250 !. 251take_blocks_at_indent(Lines, N, [Block|RestBlocks], RestLines) :- 252 take_block(Lines, N, Block, Rest0), 253 !, 254 take_blocks_at_indent(Rest0, N, RestBlocks, RestLines). 255take_blocks_at_indent(Lines, _, [], Lines).
261rest_list(Lines, Type, N, Items, IT, Rest) :- 262 skip_empty_lines(Lines, Lines1), 263 list_item(Lines1, Type, N, Items, IT0, Rest0), 264 !, 265 rest_list(Rest0, Type, N, IT0, IT, Rest). 266rest_list(Rest, _, _, IT, IT, Rest).
270list_item_prefix(ul, [*, ' '|T], T) :- !. 271list_item_prefix(ul, [-, ' '|T], T) :- !. 272list_item_prefix(dl, [$, ' '|T], T) :- 273 split_dt(T, _, _), 274 !. 275list_item_prefix(ol, [w(N), '.', ' '|T], T) :- 276 atom_codes(N, [D]), 277 between(0'0, 0'9, D).
284split_dt(In, DT, []) :- 285 append(DT, [':'], In), 286 !. 287split_dt(In, DT, Rest) :- 288 append(DT, [':'|Rest0], In), 289 ( Rest0 == [] 290 -> Rest = [] 291 ; Rest0 = [' '|Rest] 292 ), 293 !.
303ul_to_dl(ul(Items), Description) :- 304 term_items(Items, DLItems, []), 305 ( terms_to_predicate_includes(DLItems, Preds) 306 -> Description = dl(class(predicates), Preds) 307 ; member(dd(DD), DLItems), DD \== [] 308 -> Description = dl(class(termlist), DLItems) 309 ). 310 311term_items([], T, T). 312term_items([LI|LIs], DLItems, Tail) :- 313 term_item(LI, DLItems, Tail1), 314 term_items(LIs, Tail1, Tail).
\term(Text, Term, Bindings).
323term_item(li(Tokens), 324 [ dt(class=term, \term(Text, Term, Bindings)), 325 dd(Descr) 326 | Tail 327 ], Tail) :- 328 ( ( append(TermTokens, ['\n'|Descr], Tokens) 329 -> true 330 ; TermTokens = Tokens, 331 Descr = [] 332 ) 333 -> with_output_to(string(Tmp), 334 ( forall(member(T, TermTokens), 335 write_token(T)), 336 write(' .\n'))), 337 E = error(_,_), 338 catch(setup_call_cleanup( 339 open_string(Tmp, In), 340 ( read_dt_term(In, Term, Bindings), 341 read_dt_term(In, end_of_file, []), 342 atom_string(Text, Tmp) 343 ), 344 close(In)), 345 E, fail) 346 ). 347 348write_token(w(X)) :- 349 !, 350 write(X). 351write_token(X) :- 352 write(X). 353 354read_dt_term(In, Term, Bindings) :- 355 read_term(In, Term, 356 [ variable_names(Bindings), 357 module(pldoc_modes) 358 ]). 359 360terms_to_predicate_includes([], []). 361terms_to_predicate_includes([dt(class=term, \term(_, [[PI]], [])), dd([])|T0], 362 [\include(PI, predicate, [])|T]) :- 363 is_pi(PI), 364 terms_to_predicate_includes(T0, T). 365 366is_pi(Name/Arity) :- 367 atom(Name), 368 integer(Arity), 369 between(0, 20, Arity). 370is_pi(Name//Arity) :- 371 atom(Name), 372 integer(Arity), 373 between(0, 20, Arity).
378row([C0|CL]) --> 379 cell(C0), 380 !, 381 row(CL). 382row([]) --> 383 []. 384 385cell(td(C)) --> 386 face_tokens(C0), 387 ['|'], 388 !, 389 { strip_ws_tokens(C0, C) 390 }. 391 392face_tokens([]) --> 393 []. 394face_tokens(Tokens) --> 395 face_token(H), % Deal with embedded *|...|*, etc. 396 token('|'), 397 face_tokens(Face), 398 token('|'), 399 face_token(H), 400 !, 401 { append([[H,'|'], Face, ['|', H], Rest], Tokens) }, 402 face_tokens(Rest). 403face_tokens([H|T]) --> 404 token(H), 405 face_tokens(T). 406 407face_token(=) --> [=]. 408face_token(*) --> [*]. 409face_token('_') --> ['_']. 410 411take_table(Lines, Indent, Row0, Table, Rest) :- 412 rest_table(Lines, Indent, Rows, Rest), 413 ( Rows = [align(Align)|Rows1] 414 -> maplist(align_row(Align), Rows1, Rows2), 415 ( maplist(=(td([])), Row0) % empty header 416 -> Table = table(class=wiki, Rows2) 417 ; maplist(td_to_th, Row0, Header), 418 Table = table(class=wiki, [tr(Header)|Rows2]) 419 ) 420 ; Table = table(class=wiki, [tr(Row0)|Rows]) 421 ). 422 423td_to_th(td(X), th(X)) :- !. 424td_to_th(X, X). 425 426align_row(Align, tr(Row0), tr(Row)) :- 427 align_cells(Align, Row0, Row). 428 429align_cells([Align|AT], [Cell0|T0], [Cell|T]) :- 430 align_cell(Align, Cell0, Cell), 431 align_cells(AT, T0, T). 432align_cells(_, Cells, Cells). 433 434align_cell(Align, td(Content), td(class=Align, Content)).
438rest_table([N-Line|LT], N, [align(Align)|RL], Rest) :- 439 phrase(column_alignment(Align), Line), 440 !, 441 rest_table2(LT, N, RL, Rest). 442rest_table(Lines, N, RL, Rest) :- 443 rest_table2(Lines, N, RL, Rest). 444 445rest_table2([N-['|'|RL1]|LT], N, [tr(R0)|RL], Rest) :- 446 !, 447 phrase(row(R0), RL1), 448 rest_table2(LT, N, RL, Rest). 449rest_table2(Rest, _, [], Rest).
455column_alignment([H|T]) --> 456 ['|'], 457 ( colspec(H) 458 -> column_alignment(T) 459 ; {T=[]} 460 ). 461 462colspec(Align) --> 463 ws_tokens, [':'], dashes3, 464 ( [':'] 465 -> {Align = center} 466 ; {Align = left} 467 ), 468 ws_tokens. 469colspec(Align) --> 470 ws_tokens, dashes3, 471 ( [':'] 472 -> {Align = right} 473 ; {Align = left} 474 ), 475 ws_tokens. 476 477dashes3 --> 478 [-,-,-], 479 dashes. 480 481dashes --> [-], !, dashes. 482dashes --> []. 483 484ws_tokens --> [' '], !, ws_tokens. 485ws_tokens --> [].
495rest_par([], [], BI, MaxI0, MaxI, []) :- 496 !, 497 MaxI is max(BI, MaxI0). 498rest_par([_-[]|Rest], [], _, MaxI, MaxI, Rest) :- !. 499rest_par(Lines, [], _, MaxI, MaxI, Lines) :- 500 Lines = [_-Verb|_], 501 verbatim_term(Verb), 502 !. 503rest_par([I-L|Rest], [], _, MaxI, MaxI, [I-L|Rest]) :- 504 list_item_prefix(_, L, _), 505 !. 506rest_par([I-L1|LT], ['\n'|Par], BI, MaxI0, MaxI, Rest) :- 507 append(L1, PT, Par), 508 MaxI1 is max(I, MaxI0), 509 rest_par(LT, PT, BI, MaxI1, MaxI, Rest).
516section_header([_-L1|LT], Section, LT) :- 517 twiki_section_line(L1, Section), 518 !. 519section_header([0-L1|LT], Section, LT) :- 520 md_section_line(L1, Section), 521 !. 522section_header([_-L1,0-L2|LT], Section, LT) :- 523 md_section_line(L1, L2, Section), 524 !.
532twiki_section_line([-,-,-|Rest], Section) :- 533 plusses(Rest, Section). 534 535plusses([+, ' '|Rest], h1(Attrs, Content)) :- 536 hdr_attributes(Rest, Attrs, Content). 537plusses([+, +, ' '|Rest], h2(Attrs, Content)) :- 538 hdr_attributes(Rest, Attrs, Content). 539plusses([+, +, +, ' '|Rest], h3(Attrs, Content)) :- 540 hdr_attributes(Rest, Attrs, Content). 541plusses([+, +, +, +, ' '|Rest], h4(Attrs, Content)) :- 542 hdr_attributes(Rest, Attrs, Content). 543 544hdr_attributes(List, Attrs, Content) :- 545 strip_leading_ws(List, List2), 546 ( List2 = ['[',w(Name),']'|List3] 547 -> strip_ws_tokens(List3, Content), 548 Attrs = [class(wiki), id(Name)] 549 ; Attrs = class(wiki), 550 strip_ws_tokens(List, Content) 551 ).
557md_section_line([#, ' '|Rest], h1(Attrs, Content)) :- 558 md_section_attributes(Rest, Attrs, Content). 559md_section_line([#, #, ' '|Rest], h2(Attrs, Content)) :- 560 md_section_attributes(Rest, Attrs, Content). 561md_section_line([#, #, #, ' '|Rest], h3(Attrs, Content)) :- 562 md_section_attributes(Rest, Attrs, Content). 563md_section_line([#, #, #, #, ' '|Rest], h4(Attrs, Content)) :- 564 md_section_attributes(Rest, Attrs, Content). 565 566md_section_attributes(List, Attrs, Content) :- 567 phrase((tokens(Content), [' '], section_label(Label)), List), 568 !, 569 Attrs = [class(wiki), id(Label)]. 570md_section_attributes(Content, Attrs, Content) :- 571 Attrs = [class(wiki)]. 572 573section_label(Label) --> 574 [ '{', '#', w(Name) ], 575 label_conts(Cont), ['}'], 576 !, 577 { atomic_list_concat([Name|Cont], Label) }. 578 579label_conts([H|T]) --> label_cont(H), !, label_conts(T). 580label_conts([]) --> []. 581 582label_cont(-) --> [-]. 583label_cont('_') --> ['_']. 584label_cont(Name) --> [w(Name)]. 585 586 587md_section_line(Line1, Line2, Header) :- 588 Line1 \== [], 589 section_underline(Line2, Type), 590 is_list(Line1), 591 phrase(wiki_words(_), Line1), % Should not have structure elements 592 !, 593 ( phrase(labeled_section_line(Title, Attrs), Line1) 594 -> true 595 ; Title = Line1, 596 Attrs = [] 597 ), 598 Header =.. [Type, [class(wiki)|Attrs], Title]. 599 600section_underline([=,=,=|T], h1) :- 601 maplist(=(=), T), 602 !. 603section_underline([-,-,-|T], h2) :- 604 maplist(=(-), T), 605 !. 606 607labeled_section_line(Title, Attrs) --> 608 tokens(Title), [' '], section_label(Label), 609 !, 610 { Attrs = [id(Label)] }.
618strip_ws_tokens([' '|T0], T) :- 619 !, 620 strip_ws_tokens(T0, T). 621strip_ws_tokens(L0, L) :- 622 append(L, [' '], L0), 623 !. 624strip_ws_tokens(L, L).
631strip_leading_ws([' '|T], T) :- !. 632strip_leading_ws(T, T). 633 634 635 /******************************* 636 * TAGS * 637 *******************************/
646tags(Lines, Tags, RestLines) :-
647 collect_tags(Lines, Tags0, RestLines),
648 Tags0 \== [],
649 keysort(Tags0, Tags1),
650 pairs_values(Tags1, Tags2),
651 combine_tags(Tags2, Tags).tag(Tag,Tokens) for each @tag encountered.
Order is the desired position as defined by tag_order/2. Stops
at the first non-@ line (returned in RestLines) so that a
following section header or block element closes the tag list
instead of being slurped into the last tag's content.
665collect_tags([], [], []) :- !. 666collect_tags([Indent-[@,String|L0]|Lines], [Order-tag(Tag,Value)|Tags], RestLines) :- 667 tag_name(String, Tag, Order), 668 !, 669 strip_leading_ws(L0, L), 670 rest_tag(Lines, Indent, VT, RestLines0), 671 normalise_indentation(VT, VT1), 672 wiki_structure([0-L|VT1], -1, Value0), 673 strip_leading_par(Value0, Value), 674 collect_tags(RestLines0, Tags, RestLines). 675collect_tags(Lines, [], Lines) :- 676 Lines \= [_-[@,_|_]|_].
683tag_name(w(Name), Tag, Order) :- 684 ( renamed_tag(Name, Tag, Level), 685 tag_order(Tag, Order) 686 -> print_message(Level, pldoc(deprecated_tag(Name, Tag))) 687 ; tag_order(Name, Order) 688 -> Tag = Name 689 ; print_message(warning, pldoc(unknown_tag(Name))), 690 fail 691 ). 692 693 694rest_tag([], _, [], []) :- !. 695rest_tag(Lines, Indent, [], Lines) :- 696 Lines = [Indent-[@,Word|_]|_], 697 tag_name(Word, _, _), 698 !. 699rest_tag(Lines, _, [], Lines) :- 700 section_header(Lines, _, _), 701 !. 702rest_tag([L|Lines0], Indent, [L|VT], Lines) :- 703 rest_tag(Lines0, Indent, VT, Lines).
710renamed_tag(exception, throws, warning). 711renamed_tag(param, arg, silent).
720:- multifile 721 pldoc:tag_order/2. 722 723tag_order(Tag, Order) :- 724 pldoc:tag_order(Tag, Order), 725 !. 726tag_order(arg, 100). 727tag_order(error, 200). % same as throw 728tag_order(throws, 300). 729tag_order(author, 400). 730tag_order(version, 500). 731tag_order(see, 600). 732tag_order(deprecated, 700). 733tag_order(compat, 800). % PlDoc extension 734tag_order(copyright, 900). 735tag_order(license, 1000). 736tag_order(bug, 1100). 737tag_order(tbd, 1200). 738tag_order(since, 1300).
params(list(param(Name, Descr)))tag(Name, list(Descr))Descr is a list of tokens.
749combine_tags([], []). 750combine_tags([tag(arg, V1)|T0], [\args([P1|PL])|Tags]) :- 751 !, 752 arg_tag(V1, P1), 753 arg_tags(T0, PL, T1), 754 combine_tags(T1, Tags). 755combine_tags([tag(Tag,V0)|T0], [\tag(Tag, [V0|Vs])|T]) :- 756 same_tag(Tag, T0, T1, Vs), 757 combine_tags(T1, T). 758 759arg_tag([PT|Descr0], arg(PN, Descr)) :- 760 word_of(PT, PN), 761 strip_leading_ws(Descr0, Descr). 762 763word_of(w(W), W) :- !. % TBD: check non-word arg 764word_of(W, W). 765 ([tag(arg, V1)|T0], [P1|PL], T) :- 767 !, 768 arg_tag(V1, P1), 769 arg_tags(T0, PL, T). 770arg_tags(T, [], T). 771 772same_tag(Tag, [tag(Tag, V)|T0], T, [V|Vs]) :- 773 !, 774 same_tag(Tag, T0, T, Vs). 775same_tag(_, L, L, []). 776 777 778 /******************************* 779 * FACES * 780 *******************************/
787wiki_faces([dt(Class, \term(Text, Term, Bindings)), dd(Descr0)|T0], 788 ArgNames, 789 [dt(Class, \term(Text, Term, Bindings)), dd(Descr)|T]) :- 790 !, 791 varnames(Bindings, VarNames, ArgNames), 792 wiki_faces(Descr0, VarNames, Descr), 793 wiki_faces(T0, ArgNames, T). 794wiki_faces(DOM0, ArgNames, DOM) :- 795 structure_term(DOM0, Functor, Content0), 796 !, 797 wiki_faces_list(Content0, ArgNames, Content), 798 structure_term(DOM, Functor, Content). 799wiki_faces(Verb, _, Verb) :- 800 verbatim_term(Verb), 801 !. 802wiki_faces(Content0, ArgNames, Content) :- 803 assertion(is_list(Content0)), 804 phrase(wiki_faces(Content, ArgNames), Content0), 805 !. 806 807varnames([], List, List). 808varnames([Name=_|T0], [Name|T], List) :- 809 varnames(T0, T, List). 810 811wiki_faces_list([], _, []). 812wiki_faces_list([H0|T0], Args, [H|T]) :- 813 wiki_faces(H0, Args, H), 814 wiki_faces_list(T0, Args, T).
822structure_term(\tags(Tags), tags, [Tags]) :- !. 823structure_term(\args(Params), args, [Params]) :- !. 824structure_term(arg(Name,Descr), arg(Name), [Descr]) :- !. 825structure_term(\tag(Name,Value), tag(Name), [Value]) :- !. 826structure_term(\include(What,Type,Opts), include(What,Type,Opts), []) :- !. 827structure_term(dl(Att, Args), dl(Att), [Args]) :- !. 828structure_term(dt(Att, Args), dt(Att), [Args]) :- !. 829structure_term(table(Att, Args), table(Att), [Args]) :- !. 830structure_term(td(Att, Args), td(Att), [Args]) :- !. 831structure_term(h1(Att, Args), h1(Att), [Args]) :- !. 832structure_term(h2(Att, Args), h2(Att), [Args]) :- !. 833structure_term(h3(Att, Args), h3(Att), [Args]) :- !. 834structure_term(h4(Att, Args), h4(Att), [Args]) :- !. 835structure_term(hr(Att), hr(Att), []) :- !. 836structure_term(p(Args), p, [Args]) :- !. 837structure_term(Term, Functor, Args) :- 838 structure_term_any(Term, Functor, Args). 839 840structure_term(Term) :- 841 structure_term_any(Term, _Functor, _Args). 842 843structure_term_any(Term, Functor, Args) :- 844 functor(Term, Functor, 1), 845 structure_tag(Functor), 846 !, 847 Term =.. [Functor|Args]. 848 849structure_tag(ul). 850structure_tag(ol). 851structure_tag(dl). 852structure_tag(li). 853structure_tag(dt). 854structure_tag(dd). 855structure_tag(table). 856structure_tag(tr). 857structure_tag(td). 858structure_tag(th). 859structure_tag(blockquote). 860structure_tag(center).
867verbatim_term(pre(_,_)). 868verbatim_term(\term(_,_,_)).
875:- meta_predicate matches(2, -, -, ?, ?). 876 877matches(Goal, Input, Last, List, Rest) :- 878 call(Goal, List, Rest), 879 input(List, Rest, Input, Last). 880 881input([H|T0], Rest, Input, Last) :- 882 ( T0 == Rest 883 -> Input = [H], 884 Last = H 885 ; Input = [H|T], 886 input(T0, Rest, T, Last) 887 ).
899wiki_faces(WithFaces, ArgNames, List, Rest) :- 900 default_faces_options(Options), 901 catch(wiki_faces(WithFaces, ArgNames, Options, List, Rest), 902 pldoc(depth_limit), 903 failed_faces(WithFaces, List, Rest)). 904 905default_faces_options(_{depth:5}). 906 907failed_faces(WithFaces) --> 908 { debug(markdown(overflow), 'Depth limit exceeded', []) }, 909 wiki_words(WithFaces). 910 911wiki_faces([EmphTerm|T], ArgNames, Options) --> 912 emphasis_seq(EmphTerm, ArgNames, Options), 913 !, 914 wiki_faces_int(T, ArgNames). 915wiki_faces(Faces, ArgNames, Options) --> 916 wiki_faces_int(Faces, ArgNames, Options). 917 918wiki_faces_int(WithFaces, ArgNames) --> 919 { default_faces_options(Options) 920 }, 921 wiki_faces_int(WithFaces, ArgNames, Options). 922 923wiki_faces_int([], _, _) --> 924 []. 925wiki_faces_int(List, ArgNames, Options) --> 926 wiki_face(H, ArgNames, Options), 927 !, 928 { is_list(H) 929 -> append(H, T, List) 930 ; List = [H|T] 931 }, 932 wiki_faces(T, ArgNames, Options). 933wiki_faces_int([Before,EmphTerm|T], ArgNames, Options) --> 934 emphasis_before(Before), 935 emphasis_seq(EmphTerm, ArgNames, Options), 936 !, 937 wiki_faces_int(T, ArgNames, Options). 938wiki_faces_int([H|T], ArgNames, Options) --> 939 wiki_face_simple(H, ArgNames, Options), 940 !, 941 wiki_faces_int(T, ArgNames, Options). 942 943next_level(Options0, Options) --> 944 { succ(NewDepth, Options0.depth) 945 -> Options = Options0.put(depth, NewDepth) 946 ; throw(pldoc(depth_limit)) 947 }.
' ' (space), representing white-space.
The Out variable is input for the backends defined in
doc_latex.pl and doc_html.pl. Roughly, these are terms similar
to what html//1 from library(http/html_write) accepts.
966wiki_face(Out, Args, _) --> 967 prolog:doc_wiki_face(Out, Args), 968 !. 969wiki_face(var(Arg), ArgNames, _) --> 970 [w(Arg)], 971 { memberchk(Arg, ArgNames) 972 }, 973 !. 974wiki_face(b(Bold), ArgNames, Options) --> 975 [*,'|'], string(Tokens), ['|',*], 976 !, 977 { phrase(wiki_faces(Bold, ArgNames, Options), Tokens) }. 978wiki_face(i(Italic), ArgNames, Options) --> 979 ['_','|'], string(Tokens), ['|','_'], 980 !, 981 { phrase(wiki_faces(Italic, ArgNames, Options), Tokens) }. 982wiki_face(strong(Strong), ArgNames, Options) --> 983 ['_','_'], string(Tokens), ['_','_'], 984 !, 985 { phrase(wiki_faces(Strong, ArgNames, Options), Tokens) }. 986wiki_face(strong(Strong), ArgNames, Options) --> 987 ['*','*'], string(Tokens), ['*','*'], 988 !, 989 { phrase(wiki_faces(Strong, ArgNames, Options), Tokens) }. 990wiki_face(code(Code), _, _) --> 991 [=], eq_code_words(Words), [=], 992 !, 993 { atomic_list_concat(Words, Code) }. 994wiki_face(code(Code), _, _) --> 995 [=,'|'], wiki_words(Code), ['|',=], 996 !. 997wiki_face(PredRef, _, _) --> 998 ['`'], take_predref(PredRef), ['`'], 999 !. 1000wiki_face(\nopredref(Pred), _, _) --> 1001 ['`', '`'], take_predref(\predref(Pred)), ['`', '`'], 1002 !. 1003wiki_face([flag, ' ', \flagref(Flag)], _, _) --> 1004 [ w('flag'), ' ', '`', w(Flag), '`' ], 1005 { current_prolog_flag(Flag, _) }, 1006 !. 1007wiki_face(code(Code), _, _) --> 1008 ['`','`'], wiki_words(Code), ['`','`'], 1009 !. 1010wiki_face(Code, _, _) --> 1011 ( ['`'], code_words(Words), ['`'] 1012 -> { atomic_list_concat(Words, Text), 1013 E = error(_,_), 1014 catch(atom_to_term(Text, Term, Vars), E, fail), 1015 !, 1016 code_face(Text, Term, Vars, Code) 1017 } 1018 ). 1019wiki_face(Face, _, Options) --> 1020 [ w(Name) ], arg_list(List), 1021 { atomic_list_concat([Name|List], Text), 1022 E = error(_,_), 1023 catch(atom_to_term(Text, Term, Vars), E, fail), 1024 term_face(Text, Term, Vars, Face, Options) 1025 }, 1026 !. 1027wiki_face(br([]), _, _) --> 1028 [<,w(br),>,'\n'], !. 1029wiki_face(br([]), _, _) --> 1030 [<,w(br),/,>,'\n'], !. 1031 % Below this, we only do links. 1032wiki_face(_, _, Options) --> 1033 { Options.get(link) == false, 1034 !, 1035 fail 1036 }. 1037wiki_face(PredRef, _, _) --> 1038 take_predref(PredRef), 1039 !. 1040wiki_face(\cite(Citations), _, _) --> 1041 ['['], citations(Citations), [']']. 1042wiki_face(\include(Name, Type, Options), _, _) --> 1043 ['[','['], file_name(Base, Ext), [']',']'], 1044 { autolink_extension(Ext, Type), 1045 !, 1046 file_name_extension(Base, Ext, Name), 1047 resolve_file(Name, Options, []) 1048 }, 1049 !. 1050wiki_face(\include(Name, Type, [caption(Caption)|Options]), _, _) --> 1051 ( ['!','['], tokens(100, Caption), [']','('] 1052 -> file_name(Base, Ext), [')'], 1053 { autolink_extension(Ext, Type), 1054 !, 1055 file_name_extension(Base, Ext, Name), 1056 resolve_file(Name, Options, []) 1057 } 1058 ), 1059 !. 1060wiki_face(Link, ArgNames, Options) --> % TWiki: [[Label][Link]] 1061 ( ['[','['], wiki_label(Label, ArgNames, Options), [']','['] 1062 -> wiki_link(Link, [label(Label), relative(true), end(']')]), 1063 [']',']'], ! 1064 ). 1065wiki_face(Link, ArgNames, Options) --> % Markdown: [Label](Link) 1066 ( ['['], wiki_label(Label, ArgNames, Options), [']','('] 1067 -> wiki_link(Link, [label(Label), relative(true), end(')')]), 1068 [')'], ! 1069 ). 1070wiki_face(Link, _ArgNames, _) --> 1071 wiki_link(Link, []), 1072 !. 1073 1074wiki_label(Label, _ArgNames, _Options) --> 1075 image_label(Label). 1076wiki_label(Label, ArgNames, Options) --> 1077 next_level(Options, NOptions), 1078 limit(40, wiki_faces(Label, ArgNames, NOptions.put(link,false))).
1084wiki_face_simple(Word, _, _) --> 1085 [ w(Word) ], 1086 !. 1087wiki_face_simple(SpaceOrPunct, _, _) --> 1088 [ SpaceOrPunct ], 1089 { atomic(SpaceOrPunct) }, 1090 !. 1091wiki_face_simple(FT, ArgNames, _) --> 1092 [Structure], 1093 { wiki_faces(Structure, ArgNames, FT) 1094 }. 1095 1096wiki_words([]) --> []. 1097wiki_words([Word|T]) --> [w(Word)], !, wiki_words(T). 1098wiki_words([Punct|T]) --> [Punct], {atomic(Punct)}, wiki_words(T).
`code`,
where `` is mapped to `.1105code_words([]) --> []. 1106code_words([Word|T]) --> [w(Word)], code_words(T). 1107code_words(CodeL) --> ['`','`'], {CodeL = ['`'|T]}, code_words(T). 1108code_words([Punct|T]) --> [Punct], {atomic(Punct)}, code_words(T).
=. This is limited to
.-:/, notably dealing with file names and
identifiers in various external languages.1119eq_code_words([Word]) --> 1120 [ w(Word) ]. 1121eq_code_words([Word|T]) --> 1122 [ w(Word) ], eq_code_internals(T, [End]), [w(End)]. 1123 1124eq_code_internals(T, T) --> []. 1125eq_code_internals([H|T], Tail) --> 1126 eq_code_internal(H), 1127 eq_code_internals(T, Tail). 1128 1129eq_code_internal(Word) --> 1130 [w(Word)]. 1131eq_code_internal(Punct) --> 1132 [Punct], 1133 { eq_code_internal_punct(Punct) }. 1134 1135eq_code_internal_punct('.'). 1136eq_code_internal_punct('-'). 1137eq_code_internal_punct(':'). 1138eq_code_internal_punct('/').
`... code ...` sequences. Text is the matched
text, Term is the parsed Prolog term and Code is the resulting
intermediate code.1147code_face(Text, Var, _, Code) :- 1148 var(Var), 1149 !, 1150 Code = var(Text). 1151code_face(Text, _, _, code(Text)).
1158emphasis_seq(EmphTerm, ArgNames, Options) -->
1159 emphasis_start(C),
1160 next_level(Options, NOptions),
1161 matches(limit(100, wiki_faces(Emph, ArgNames, NOptions)), Input, Last),
1162 emphasis_end(C),
1163 { emph_markdown(Last, Input),
1164 emphasis_term(C, Emph, EmphTerm)
1165 },
1166 !.1176emphasis_term('_', Term, i(Term)). 1177emphasis_term('*', Term, b(Term)). 1178 1179emph_markdown(_, [w(_)]) :- !. 1180emph_markdown(Last, Tokens) :- 1181 \+ emphasis_after_sep(Last), 1182 E = error(_,_), 1183 catch(b_getval(pldoc_object, Obj), E, Obj = '??'), 1184 debug(markdown(emphasis), '~q: additionally emphasis: ~p', 1185 [Obj, Tokens]). 1186 1187emphasis_before(Before) --> 1188 [Before], 1189 { emphasis_start_sep(Before) }. 1190 1191emphasis_start_sep('\n'). 1192emphasis_start_sep(' '). 1193emphasis_start_sep('<'). 1194emphasis_start_sep('{'). 1195emphasis_start_sep('('). 1196emphasis_start_sep('['). 1197emphasis_start_sep(','). 1198emphasis_start_sep(':'). 1199emphasis_start_sep(';'). 1200 1201emphasis_start(Which), [w(Word)] --> 1202 emphasis(Which), 1203 [w(Word)]. 1204 1205emphasis(**) --> [*, *]. 1206emphasis(*) --> [*]. 1207emphasis('__') --> ['_', '_']. 1208emphasis('_') --> ['_']. 1209 1210emphasis_end(Which), [After] --> 1211 emphasis(Which), 1212 [ After ], 1213 !, 1214 { emphasis_close_sep(After) -> true }. 1215emphasis_end(Which) --> 1216 emphasis(Which). 1217 1218% these characters should not be before a closing * or _. 1219 1220emphasis_after_sep('\n'). 1221emphasis_after_sep(' '). 1222emphasis_after_sep('('). 1223emphasis_after_sep('['). 1224emphasis_after_sep('<'). 1225emphasis_after_sep('='). 1226emphasis_after_sep('+'). 1227emphasis_after_sep('\\'). 1228emphasis_after_sep('@'). 1229 1230emphasis_close_sep('\n'). % white 1231emphasis_close_sep(' '). % white 1232emphasis_close_sep(','). % sentence punctuation 1233emphasis_close_sep('.'). 1234emphasis_close_sep('!'). 1235emphasis_close_sep('?'). 1236emphasis_close_sep(':'). 1237emphasis_close_sep(';'). 1238emphasis_close_sep(']'). % [**label**](link) 1239emphasis_close_sep(')'). % ... _italic_) 1240emphasis_close_sep('}'). % ... _italic_} 1241emphasis_close_sep(Token) :- 1242 structure_term(Token).
1253arg_list(['('|T]) --> 1254 ['('], arg_list_close(T, 1). 1255 1256arg_list_close(Tokens, Depth) --> 1257 [')'], 1258 !, 1259 ( { Depth == 1 } 1260 -> { Tokens = [')'] } 1261 ; { Depth > 1 } 1262 -> { Tokens = [')'|More], 1263 NewDepth is Depth - 1 1264 }, 1265 arg_list_close(More, NewDepth) 1266 ). 1267arg_list_close(['('|T], Depth) --> 1268 ['('], { NewDepth is Depth+1 }, 1269 arg_list_close(T, NewDepth). 1270arg_list_close([H|T], Depth) --> 1271 [w(H)], 1272 !, 1273 arg_list_close(T, Depth). 1274arg_list_close([H|T], Depth) --> 1275 [H], 1276 arg_list_close(T, Depth).
1285term_face(_Text, Term, _Vars, \file(Name, FileOptions), Options) :- 1286 ground(Term), 1287 compound(Term), 1288 compound_name_arity(Term, Alias, 1), 1289 user:file_search_path(Alias, _), 1290 existing_file(Term, FileOptions, [], Options), 1291 !, 1292 format(atom(Name), '~q', [Term]). 1293term_face(Text, Term, Vars, Face, _Options) :- 1294 code_face(Text, Term, Vars, Face). 1295 1296untag([], []). 1297untag([w(W)|T0], [W|T]) :- 1298 !, 1299 untag(T0, T). 1300untag([H|T0], [H|T]) :- 1301 untag(T0, T).
1307image_label(\include(Name, image, Options)) --> 1308 file_name(Base, Ext), 1309 { autolink_extension(Ext, image), 1310 file_name_extension(Base, Ext, Name), 1311 resolve_file(Name, Options, RestOptions) 1312 }, 1313 file_options(RestOptions). 1314 1315 1316take_predref(\predref(Name/Arity)) --> 1317 [ w(Name), '/' ], arity(Arity), 1318 { functor_name(Name) 1319 }. 1320take_predref(\predref(Module:(Name/Arity))) --> 1321 [ w(Module), ':', w(Name), '/' ], arity(Arity), 1322 { functor_name(Name) 1323 }. 1324take_predref(\predref(Name/Arity)) --> 1325 prolog_symbol_char(S0), 1326 symbol_string(SRest), [ '/' ], arity(Arity), 1327 !, 1328 { atom_chars(Name, [S0|SRest]) 1329 }. 1330take_predref(\predref(Name//Arity)) --> 1331 [ w(Name), '/', '/' ], arity(Arity), 1332 { functor_name(Name) 1333 }. 1334take_predref(\predref(Module:(Name//Arity))) --> 1335 [ w(Module), ':', w(Name), '/', '/' ], arity(Arity), 1336 { functor_name(Name) 1337 }.
1344file_options(Options) --> 1345 [;], nv_pairs(Options), 1346 !. 1347file_options([]) --> 1348 []. 1349 1350nv_pairs([H|T]) --> 1351 nv_pair(H), 1352 ( [','] 1353 -> nv_pairs(T) 1354 ; {T=[]} 1355 ). 1356 1357nv_pair(Option) --> 1358 [ w(Name), =,'"'], tokens(Tokens), ['"'], 1359 !, 1360 { untag(Tokens, Atoms), 1361 atomic_list_concat(Atoms, Value0), 1362 ( atom_number(Value0, Value) 1363 -> true 1364 ; Value = Value0 1365 ), 1366 Option =.. [Name,Value] 1367 }.
1383:- multifile 1384 user:url_path/2. 1385 1386wiki_link(\file(Name, FileOptions), Options) --> 1387 file_name(Base, Ext), 1388 { file_name_extension(Base, Ext, Name), 1389 ( autolink_file(Name, _) 1390 ; autolink_extension(Ext, _) 1391 ), 1392 !, 1393 resolve_file(Name, FileOptions, Options) 1394 }. 1395wiki_link(\file(Name, FileOptions), Options) --> 1396 [w(Name)], 1397 { autolink_file(Name, _), 1398 !, 1399 resolve_file(Name, FileOptions, Options) 1400 }, 1401 !. 1402wiki_link(a(href(Ref), Label), Options) --> 1403 [ w(Prot),:,/,/], { url_protocol(Prot) }, 1404 { option(end(End), Options, space) 1405 }, 1406 tokens_no_whitespace(Rest), peek_end_url(End), 1407 !, 1408 { atomic_list_concat([Prot, :,/,/ | Rest], Ref), 1409 option(label(Label), Options, Ref) 1410 }. 1411wiki_link(a(href(Ref), Label), Options) --> 1412 [ w(mailto),:], 1413 { option(end(End), Options, space) 1414 }, 1415 tokens_no_whitespace(Rest), peek_end_url(End), 1416 !, 1417 { atomic_list_concat([mailto, : | Rest], Ref), 1418 option(label(Label), Options, Ref) 1419 }. 1420wiki_link(a(href(Ref), Label), _Options) --> 1421 [<, w(Alias), :], 1422 tokens_no_whitespace(Rest), [>], 1423 { Term = (Alias:Rest), 1424 prolog:url_expansion_hook(Term, Ref, Label), ! 1425 }. 1426wiki_link(a(href(Ref), Label), Options) --> 1427 [<, w(Alias), :], 1428 { user:url_path(Alias, _) 1429 }, 1430 tokens_no_whitespace(Rest), [>], 1431 { atomic_list_concat(Rest, Local), 1432 ( Local == '' 1433 -> Term =.. [Alias,'.'] 1434 ; Term =.. [Alias,Local] 1435 ), 1436 E = error(_,_), 1437 catch(expand_url_path(Term, Ref), E, fail), 1438 option(label(Label), Options, Ref) 1439 }. 1440wiki_link(a(href(Ref), Label), Options) --> 1441 [#, w(First)], 1442 { option(end(End), Options) }, 1443 tokens_no_whitespace(Rest), 1444 peek_end_url(End), 1445 !, 1446 { atomic_list_concat([#,First|Rest], Ref), 1447 option(label(Label), Options, Ref) 1448 }. 1449wiki_link(a(href(Ref), Label), Options) --> 1450 [<], 1451 ( { option(relative(true), Options), 1452 Parts = Rest 1453 } 1454 -> tokens_no_whitespace(Rest) 1455 ; { Parts = [Prot, : | Rest] 1456 }, 1457 [w(Prot), :], tokens_no_whitespace(Rest) 1458 ), 1459 [>], 1460 !, 1461 { atomic_list_concat(Parts, Ref), 1462 option(label(Label), Options, Ref) 1463 }.
<Alias:Rest>, where
Term is of the form Alias(Rest). If it succeeds, it must bind
HREF to an atom or string representing the link target and Label
to an html//1 expression for the label.1477file_name(FileBase, Extension) --> 1478 segment(S1), 1479 segments(List), 1480 ['.'], file_extension(Extension), 1481 !, 1482 { atomic_list_concat([S1|List], '/', FileBase) }. 1483file_name(FileBase, Extension) --> 1484 [w(Alias), '('], 1485 { once(user:file_search_path(Alias, _)) }, 1486 segment(S1), 1487 segments(List), 1488 [')'], 1489 !, 1490 { atomic_list_concat([S1|List], '/', Base), 1491 Spec =.. [Alias,Base], 1492 absolute_file_name(Spec, Path, 1493 [ access(read), 1494 extensions([pl]), 1495 file_type(prolog), 1496 file_errors(fail) 1497 ]), 1498 file_name_extension(FileBase, Extension, Path) 1499 }. 1500 1501 1502segment(..) --> 1503 ['.','.'], 1504 !. 1505segment(Word) --> 1506 [w(Word)]. 1507segment(Dir) --> 1508 [w(Word),'.',w(d)], 1509 { atom_concat(Word, '.d', Dir) }. 1510 1511segments([H|T]) --> 1512 ['/'], 1513 !, 1514 segment(H), 1515 segments(T). 1516segments([]) --> 1517 []. 1518 1519file_extension(Ext) --> 1520 [w(Ext)], 1521 { autolink_extension(Ext, _) 1522 }.
absolute_path(Path) that reflects the current location of the
file.1532resolve_file(Name, FileOptions, Rest) :- 1533 existing_file(Name, FileOptions, Rest, []), 1534 !. 1535resolve_file(_, Options, Options). 1536 1537 1538existing_file(Name, FileOptions, Rest, Options) :- 1539 \+ Options.get(link) == false, 1540 E = error(_,_), 1541 catch(existing_file_p(Name, FileOptions, Rest), E, fail). 1542 1543existing_file_p(Name, FileOptions, Rest) :- 1544 ( nb_current(pldoc_file, RelativeTo), 1545 RelativeTo \== [] 1546 -> Extra = [relative_to(RelativeTo)|Extra1] 1547 ; Extra = Extra1 1548 ), 1549 ( compound(Name) 1550 -> Extra1 = [file_type(prolog)] 1551 ; Extra1 = [] 1552 ), 1553 absolute_file_name(Name, Path, 1554 [ access(read), 1555 file_errors(fail) 1556 | Extra 1557 ]), 1558 FileOptions = [ absolute_path(Path) | Rest ].
1567arity(Arity) -->
1568 [ w(Word) ],
1569 { E = error(_,_),
1570 catch(atom_number(Word, Arity), E, fail),
1571 Arity >= 0, Arity < 20
1572 }.1579symbol_string([]) --> 1580 []. 1581symbol_string([H|T]) --> 1582 [H], 1583 { prolog_symbol_char(H) }, 1584 symbol_string(T). 1585 1586prolog_symbol_char(C) --> 1587 [C], 1588 { prolog_symbol_char(C) }.
1594prolog_symbol_char(#). 1595prolog_symbol_char($). 1596prolog_symbol_char(&). 1597prolog_symbol_char(*). 1598prolog_symbol_char(+). 1599prolog_symbol_char(-). 1600prolog_symbol_char(.). 1601prolog_symbol_char(/). 1602prolog_symbol_char(:). 1603prolog_symbol_char(<). 1604prolog_symbol_char(=). 1605prolog_symbol_char(>). 1606prolog_symbol_char(?). 1607prolog_symbol_char(@). 1608prolog_symbol_char(\). 1609prolog_symbol_char(^). 1610prolog_symbol_char(~). 1611 1612 1613functor_name(String) :- 1614 sub_atom(String, 0, 1, _, Char), 1615 char_type(Char, lower). 1616 1617url_protocol(http). 1618url_protocol(https). 1619url_protocol(ftp). 1620 1621peek_end_url(space) --> 1622 peek(Punct, End), 1623 { punct_token(Punct), 1624 space_token(End) 1625 }, 1626 !. 1627peek_end_url(space) --> 1628 peek(End), 1629 { space_token(End) }, 1630 !. 1631peek_end_url(space, [], []) :- !. 1632peek_end_url(Token) --> 1633 peek(Token), 1634 !. 1635 1636punct_token('.'). 1637punct_token('!'). 1638punct_token('?'). 1639punct_token(','). 1640punct_token(';'). 1641 1642space_token(' ') :- !. 1643space_token('\r') :- !. 1644space_token('\n') :- !. 1645space_token(T) :- 1646 \+ atom(T), % high level format like p(...) 1647 \+ T = w(_).
1654autolink_extension(Ext, Type) :- 1655 prolog:doc_autolink_extension(Ext, Type), 1656 !. 1657autolink_extension(Ext, prolog) :- 1658 user:prolog_file_type(Ext,prolog), 1659 !. 1660autolink_extension(txt, wiki). 1661autolink_extension(md, wiki). 1662autolink_extension(gif, image). 1663autolink_extension(png, image). 1664autolink_extension(jpg, image). 1665autolink_extension(jpeg, image). 1666autolink_extension(svg, image).
1673autolink_file('README', wiki). 1674autolink_file('TODO', wiki). 1675autolink_file('ChangeLog', wiki).
1681citations([H|T]) --> 1682 citation(H), 1683 ( [';'] 1684 -> citations(T) 1685 ; {T=[]} 1686 ). 1687 1688citation(Atom) --> 1689 [@], wiki_words(Atoms), 1690 { length(Atoms, Len), 1691 Len > 10, !, 1692 fail 1693 ; true 1694 }, 1695 end_citation, 1696 !, 1697 { atomic_list_concat(Atoms, Atom) 1698 }. 1699 1700end_citation, [';'] --> [';']. 1701end_citation, ['@'] --> ['@']. 1702end_citation, [']'] --> [']']. 1703 1704 1705 /******************************* 1706 * SECTIONS * 1707 *******************************/
section(Type, Title), where Title is an atom holding the
section title and Type is an atom holding the text between <>.
1720section_comment_header([_-Line|Lines], Header, Lines) :- 1721 phrase(section_line(Header), Line). 1722 1723section_line(\section(Type, Title)) --> 1724 ws, "<", word(Codes), ">", normalise_white_space(TitleCodes), 1725 { atom_codes(Type, Codes), 1726 atom_codes(Title, TitleCodes) 1727 }. 1728 1729 /******************************* 1730 * TOKENIZER * 1731 *******************************/
1737tokenize_lines(Lines, TokenLines) :- 1738 tokenize_lines(Lines, -1, TokenLines). 1739 1740tokenize_lines([], _, []) :- !. 1741tokenize_lines(Lines, Indent, [Pre|T]) :- 1742 verbatim(Lines, Indent, Pre, RestLines), 1743 !, 1744 tokenize_lines(RestLines, Indent, T). 1745tokenize_lines([I-H0|T0], Indent0, [I-H|T]) :- 1746 phrase(line_tokens(H), H0), 1747 ( H == [] 1748 -> Indent = Indent0 1749 ; Indent = I 1750 ), 1751 tokenize_lines(T0, Indent, T).
w(Word) denoting a word or an atom
denoting a punctuation character. Underscores (_) appearing
inside an alphanumerical string are considered part of the word.
E.g., "hello_world_" tokenizes into [w(hello_world), '_'].1762line_tokens([H|T]) --> 1763 line_token(H), 1764 !, 1765 line_tokens(T). 1766line_tokens([]) --> 1767 []. 1768 1769line_token(T) --> 1770 [C], 1771 ( { code_type(C, space) } 1772 -> ws, 1773 { T = ' ' } 1774 ; { code_type(C, alnum) }, 1775 word(Rest), 1776 { atom_codes(W, [C|Rest]), 1777 T = w(W) 1778 } 1779 ; { char_code(T, C) } 1780 ). 1781 1782word([C0|T]) --> 1783 [C0], { code_type(C0, alnum) }, 1784 !, 1785 word(T). 1786word([0'_, C1|T]) --> 1787 [0'_, C1], { code_type(C1, alnum) }, 1788 !, 1789 word(T). 1790word([]) --> 1791 []. 1792 1793alphas([C0|T]) --> 1794 [C0], { code_type(C0, alpha) }, 1795 !, 1796 alphas(T). 1797alphas([]) --> 1798 [].
pre(Attributes, String). The indentation of the leading
fence is substracted from the indentation of the verbatim lines.
Two types of fences are supported: the traditional == and
the Doxygen ~~~ (minimum 3 ~ characters), optionally
followed by {.ext} to indicate the language.
Verbatim environment is delimited as
..., verbatim(Lines, Pre, Rest) ...,
In addition, a verbatim environment may simply be indented. The restrictions are described in the documentation.
1820verbatim(Lines, _, 1821 Indent-pre([class(code), ext(Ext)],Pre), 1822 RestLines) :- 1823 skip_empty_lines(Lines, [Indent-FenceLine|CodeLines]), 1824 verbatim_fence(FenceLine, Fence, Ext), 1825 verbatim_body(CodeLines, Indent, [10|PreCodes], [], 1826 [Indent-Fence|RestLines]), 1827 !, 1828 atom_codes(Pre, PreCodes). 1829verbatim([_-[],Indent-Line|Lines], EnvIndent, 1830 Indent-pre(class(code),Pre), 1831 RestLines) :- 1832 EnvIndent >= 0, 1833 Indent >= EnvIndent+4, Indent =< EnvIndent+8, 1834 valid_verbatim_opening(Line), 1835 indented_verbatim_body([Indent-Line|Lines], Indent, 1836 CodeLines, RestLines), 1837 !, 1838 lines_code_text(CodeLines, Indent, [10|PreCodes]), 1839 atom_codes(Pre, PreCodes). 1840 1841verbatim_body(Lines, _, PreT, PreT, Lines). 1842verbatim_body([I-L|Lines], Indent, [10|Pre], PreT, RestLines) :- 1843 PreI is I - Indent, 1844 phrase(pre_indent(PreI), Pre, PreT0), 1845 verbatim_line(L, PreT0, PreT1), 1846 verbatim_body(Lines, Indent, PreT1, PreT, RestLines). 1847 1848verbatim_fence(Line, Fence, '') :- 1849 Line == [0'=,0'=], 1850 !, 1851 Fence = Line. 1852verbatim_fence(Line, Fence, Ext) :- 1853 tilde_fence(Line, Fence, 0, Ext). 1854verbatim_fence(Line, Fence, Ext) :- 1855 md_fence(Line, Fence, 0, Ext). 1856 1857tilde_fence([0'~|T0], [0'~|F0], C0, Ext) :- 1858 !, 1859 C1 is C0+1, 1860 tilde_fence(T0, F0, C1, Ext). 1861tilde_fence(List, [], C, Ext) :- 1862 C >= 3, 1863 ( List == [] 1864 -> Ext = '' 1865 ; phrase(tilde_fence_ext(ExtCodes), List) 1866 -> atom_codes(Ext, ExtCodes) 1867 ).
`{.prolog} (Doxygen) or `{prolog} (GitHub)1873tilde_fence_ext(Ext) --> 1874 "{.", !, alphas(Ext), "}". 1875tilde_fence_ext(Ext) --> 1876 "{", alphas(Ext), "}". 1877 1878md_fence([0'`|T0], [0'`|F0], C0, Ext) :- 1879 !, 1880 C1 is C0+1, 1881 md_fence(T0, F0, C1, Ext). 1882md_fence(List, [], C, Ext) :- 1883 C >= 3, 1884 ( List == [] 1885 -> Ext = '' 1886 ; phrase(md_fence_ext(ExtCodes), List), 1887 atom_codes(Ext, ExtCodes) 1888 ). 1889 1890% Also support Doxygen's curly bracket notation. 1891md_fence_ext(Ext) --> 1892 tilde_fence_ext(Ext), 1893 !. 1894% In Markdown language names appear without brackets. 1895md_fence_ext(Ext) --> 1896 alphas(Ext).
1904indented_verbatim_body([I-L|T0], Indent, [I-L|T], RestLines) :- 1905 L \== [], I >= Indent, 1906 !, 1907 indented_verbatim_body(T0, Indent, T, RestLines). 1908indented_verbatim_body([I0-[],I-L|T0], Indent, [I0-[],I-L|T], RestLines) :- 1909 I >= Indent, 1910 valid_verbatim_opening(L), 1911 indented_verbatim_body(T0, Indent, T, RestLines). 1912indented_verbatim_body(Lines, _, [], Lines).
1918valid_verbatim_opening([0'||_]) :- !, fail. 1919valid_verbatim_opening(Line) :- 1920 Line \== [], 1921 \+ ( phrase(line_tokens(Tokens), Line), 1922 list_item_prefix(_Type, Tokens, _Rest) 1923 ).
1929lines_code_text([], _, []). 1930lines_code_text([_-[]|T0], Indent, [10|T]) :- 1931 !, 1932 lines_code_text(T0, Indent, T). 1933lines_code_text([I-Line|T0], Indent, [10|T]) :- 1934 PreI is I-Indent, 1935 phrase(pre_indent(PreI), T, T1), 1936 verbatim_line(Line, T1, T2), 1937 lines_code_text(T0, Indent, T2).
1945pre_indent(N) --> 1946 { N > 0, 1947 !, 1948 N2 is N - 1 1949 }, " ", 1950 pre_indent(N2). 1951pre_indent(_) --> 1952 "". 1953 1954verbatim_line(Line, Pre, PreT) :- 1955 append(Line, PreT, Pre). 1956 1957 1958 /******************************* 1959 * SUMMARY * 1960 *******************************/
1969summary_from_lines(Lines, Sentence) :- 1970 skip_empty_lines(Lines, Lines1), 1971 summary2(Lines1, Sentence0), 1972 end_sentence(Sentence0, Sentence). 1973 1974summary2(_, Sentence) :- 1975 Sentence == [], 1976 !. % we finished our sentence 1977summary2([], []) :- !. 1978summary2([_-[]|_], []) :- !. % empty line 1979summary2([_-[0'@|_]|_], []) :- !. % keyword line 1980summary2([_-L0|Lines], Sentence) :- 1981 phrase(sentence(Sentence, Tail), L0, _), 1982 summary2(Lines, Tail). 1983 1984sentence([C,End], []) --> 1985 [C,End], 1986 { \+ code_type(C, period), 1987 code_type(End, period) % ., !, ? 1988 }, 1989 space_or_eos, 1990 !. 1991sentence([0' |T0], T) --> 1992 space, 1993 !, 1994 ws, 1995 sentence(T0, T). 1996sentence([H|T0], T) --> 1997 [H], 1998 sentence(T0, T). 1999sentence([0' |T], T) --> % ' 2000 eos. 2001 2002space_or_eos --> 2003 [C], 2004 !, 2005 {code_type(C, space)}. 2006space_or_eos --> 2007 eos.
2014skip_empty_lines([], []). 2015skip_empty_lines([_-[]|Lines0], Lines) :- 2016 !, 2017 skip_empty_lines(Lines0, Lines). 2018skip_empty_lines(Lines, Lines). 2019 2020end_sentence([], []). 2021end_sentence([0'\s], [0'.]) :- !. 2022end_sentence([H|T0], [H|T]) :- 2023 end_sentence(T0, T). 2024 2025 2026 /******************************* 2027 * CREATE LINES * 2028 *******************************/
2037indented_lines(Comment, Prefixes, Lines) :- 2038 must_be(codes, Comment), 2039 phrase(split_lines(Prefixes, Lines), Comment), 2040 !. 2041 2042split_lines(_, []) --> 2043 end_of_comment. 2044split_lines(Prefixes, [Indent-L1|Ls]) --> 2045 take_prefix(Prefixes, 0, Indent0), 2046 white_prefix(Indent0, Indent), 2047 take_line(L1), 2048 split_lines(Prefixes, Ls).
2057end_of_comment --> 2058 eos. 2059end_of_comment --> 2060 ws, stars, "*/", ws. 2061 2062stars --> []. 2063stars --> "*", !, stars.
2071take_prefix(Prefixes, I0, I) --> 2072 { member(Prefix, Prefixes), 2073 string_codes(Prefix, PrefixCodes) 2074 }, 2075 prefix(PrefixCodes), 2076 !, 2077 { string_update_linepos(PrefixCodes, I0, I) }. 2078take_prefix(_, I, I) --> 2079 []. 2080 2081prefix([]) --> []. 2082prefix([H|T]) --> [H], prefix(T). 2083 2084white_prefix(I0, I) --> 2085 [C], 2086 { code_type(C, white), 2087 !, 2088 update_linepos(C, I0, I1) 2089 }, 2090 white_prefix(I1, I). 2091white_prefix(I, I) --> 2092 [].
2098string_update_linepos([], I, I). 2099string_update_linepos([H|T], I0, I) :- 2100 update_linepos(H, I0, I1), 2101 string_update_linepos(T, I1, I).
2109update_linepos(0'\t, I0, I) :- 2110 !, 2111 I is (I0\/7)+1. 2112update_linepos(0'\b, I0, I) :- 2113 !, 2114 I is max(0, I0-1). 2115update_linepos(0'\r, _, 0) :- !. 2116update_linepos(0'\n, _, 0) :- !. 2117update_linepos(_, I0, I) :- 2118 I is I0 + 1.
character(s), nor trailing whitespace.2125take_line([]) --> 2126 "\r\n", 2127 !. % DOS file 2128take_line([]) --> 2129 "\n", 2130 !. % Unix file 2131take_line(Line) --> 2132 [H], { code_type(H, white) }, 2133 !, 2134 take_white(White, WT), 2135 ( nl 2136 -> { Line = [] } 2137 ; { Line = [H|White] }, 2138 take_line(WT) 2139 ). 2140take_line([H|T]) --> 2141 [H], 2142 !, 2143 take_line(T). 2144take_line([]) --> % end of string 2145 []. 2146 2147take_white([H|T0], T) --> 2148 [H], { code_type(H, white) }, 2149 !, 2150 take_white(T0, T). 2151take_white(T, T) --> 2152 [].
2159normalise_indentation(Lines0, Lines) :- 2160 skip_empty_lines(Lines0, Lines1), 2161 Lines1 = [I0-_|Lines2], 2162 !, 2163 smallest_indentation(Lines2, I0, Subtract), 2164 ( Subtract == 0 2165 -> Lines = Lines0 2166 ; maplist(substract_indent(Subtract), Lines0, Lines) 2167 ). 2168normalise_indentation(Lines, Lines). 2169 2170smallest_indentation([], I, I). 2171smallest_indentation([_-[]|T], I0, I) :- 2172 !, 2173 smallest_indentation(T, I0, I). 2174smallest_indentation([X-_|T], I0, I) :- 2175 I1 is min(I0, X), 2176 smallest_indentation(T, I1, I). 2177 2178substract_indent(Subtract, I0-L, I-L) :- 2179 I is max(0,I0-Subtract). 2180 2181 2182 /******************************* 2183 * MISC * 2184 *******************************/
2191strip_leading_par([p(C)|T], L) :- 2192 !, 2193 append(C, T, L). 2194strip_leading_par(L, L). 2195 2196 2197 /******************************* 2198 * DCG BASICS * 2199 *******************************/
2205ws --> 2206 [C], {code_type(C, space)}, 2207 !, 2208 ws. 2209ws --> 2210 []. 2211 2212% space// is det 2213% 2214% True if then next code is layout. 2215 2216space --> 2217 [C], 2218 {code_type(C, space)}.
2224nl --> 2225 "\r\n", 2226 !. 2227nl --> 2228 "\n".
2234peek(H, L, L) :- 2235 L = [H|_]. 2236 2237peek(H1, H2, L, L) :- 2238 L = [H1, H2|_].
2246tokens([]) --> []. 2247tokens([H|T]) --> token(H), tokens(T). 2248 2249tokens(_, []) --> []. 2250tokens(C, [H|T]) --> token(H), {succ(C1, C)}, tokens(C1, T).
2258tokens_no_whitespace([]) --> 2259 []. 2260tokens_no_whitespace([Word|T]) --> 2261 [ w(Word) ], 2262 !, 2263 tokens_no_whitespace(T). 2264tokens_no_whitespace([H|T]) --> 2265 [H], 2266 { \+ space_token(H) }, 2267 tokens_no_whitespace(T). 2268 2269token(Token) --> 2270 [Token], 2271 { token(Token) }. 2272 2273token(w(_)) :- !. 2274token(Token) :- atom(Token).
2280:- meta_predicate limit(+,2,?,?). 2281 2282limit(Count, Rule, Input, Rest) :- 2283 Count > 0, 2284 State = count(0), 2285 call(Rule, Input, Rest), 2286 arg(1, State, N0), 2287 N is N0+1, 2288 ( N =:= Count 2289 -> ! 2290 ; nb_setarg(1, State, N) 2291 ). 2292 2293 2294 /******************************* 2295 * MESSAGES * 2296 *******************************/ 2297 2298:- multifile 2299 prolog:message//1. 2300 2301prologmessage(pldoc(deprecated_tag(Name, Tag))) --> 2302 [ 'PlDoc: Deprecated tag @~w (use @~w)'-[Name, Tag] 2303 ]. 2304prologmessage(pldoc(unknown_tag(Name))) --> 2305 [ 'PlDoc: unknown tag @~w'-[Name] 2306 ]
PlDoc wiki parser
This file defines the PlDoc wiki parser, which parses both comments and wiki text files. The original version of this SWI-Prolog wiki format was largely modeled after Twiki (http://twiki.org/). The current version is extended to take many aspects from markdown, in particular the doxygen refinement thereof.