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-2019, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(pldoc_wiki, 38 [ wiki_codes_to_dom/3, % +Codes, +Args, -DOM 39 wiki_lines_to_dom/3, % +Lines, +Map, -DOM 40 section_comment_header/3, % +Lines, -Header, -RestLines 41 summary_from_lines/2, % +Lines, -Codes 42 indented_lines/3, % +Text, +PrefixChars, -Lines 43 strip_leading_par/2, % +DOM0, -DOM 44 autolink_extension/2, % ?Extension, ?Type 45 autolink_file/2 % +FileName, -Type 46 ]). 47:- use_module(library(lists)). 48:- use_module(library(debug)). 49:- use_module(library(error)). 50:- use_module(library(pairs)). 51:- use_module(library(option)). 52:- use_module(library(debug)). 53:- use_module(library(apply)). 54:- use_module(library(dcg/basics)). 55 56:- use_module(doc_util).
70:- multifile 71 prolog:doc_wiki_face//2, % -Out, +VarNames 72 prolog:doc_url_expansion/3, % +Alias(Rest), -HREF, -Label 73 prolog:url_expansion_hook/3, % +Term, -Ref, -Label 74 prolog:doc_autolink_extension/2.% +Extension, -Type 75 76 77 /******************************* 78 * WIKI PARSING * 79 *******************************/
86wiki_lines_to_dom(Lines, Args, HTML) :-
87 tokenize_lines(Lines, Tokens0),
88 normalise_indentation(Tokens0, Tokens),
89 wiki_structure(Tokens, -1, Pars),
90 wiki_faces(Pars, Args, HTML).
99wiki_codes_to_dom(Codes, Args, DOM) :-
100 indented_lines(Codes, [], Lines),
101 wiki_lines_to_dom(Lines, Args, DOM).111wiki_structure([], _, []) :- !. 112wiki_structure([_-[]|T], BI, Pars) :- % empty lines 113 !, 114 wiki_structure(T, BI, Pars). 115wiki_structure(Lines, _, [\tags(Tags)]) :- 116 tags(Lines, Tags), 117 !. 118wiki_structure(Lines, BI, [P1|PL]) :- 119 take_block(Lines, BI, P1, RestLines), 120 wiki_structure(RestLines, BI, PL).
127take_block([_-[]|Lines], BaseIndent, Block, Rest) :- 128 !, 129 take_block(Lines, BaseIndent, Block, Rest). 130take_block([N-_|_], BaseIndent, _, _) :- 131 N < BaseIndent, 132 !, 133 fail. % less indented 134take_block(Lines, BaseIndent, List, Rest) :- 135 list_item(Lines, Type, Indent, LI, LIT, Rest0), 136 !, 137 Indent > BaseIndent, 138 rest_list(Rest0, Type, Indent, LIT, [], Rest), 139 List0 =.. [Type, LI], 140 ( ul_to_dl(List0, List) 141 -> true 142 ; List0 = dl(Items) 143 -> List = dl(class=wiki, Items) 144 ; List = List0 145 ). 146take_block([N-['|'|RL1]|LT], _, Table, Rest) :- 147 phrase(row(R0), RL1), 148 take_table(LT, N, R0, Table, Rest), 149 !. 150take_block([0-[-,-|More]|LT], _, Block, LT) :- % separation line 151 maplist(=(-), More), 152 !, 153 Block = hr([]). 154take_block([_-Line|LT], _, Block, LT) :- % separation line 155 ruler(Line), 156 !, 157 Block = hr([]). 158take_block([_-[@|_]], _, _, _) :- % starts @tags section 159 !, 160 fail. 161take_block(Lines, _BaseIndent, Section, RestLines) :- 162 section_header(Lines, Section, RestLines), 163 !. 164take_block([_-Verb|Lines], _, Verb, Lines) :- 165 verbatim_term(Verb), 166 !. 167take_block([I-L1|LT], BaseIndent, Elem, Rest) :- 168 !, 169 append(L1, PT, Par), 170 rest_par(LT, PT, I, BaseIndent, MaxI, Rest), 171 ( MaxI >= BaseIndent+16 172 -> Elem = center(Par) 173 ; phrase(blockquote(BQ), Par) 174 -> Elem = blockquote(BQ) 175 ; Elem = p(Par) 176 ). 177take_block([Verb|Lines], _, Verb, Lines). 178 179blockquote(Clean) --> 180 [>, ' '], 181 bq_lines(Clean). 182 183bq_lines([' '|Par]) --> 184 ['\n'], !, [>,' '], 185 bq_lines(Par). 186bq_lines([H|T]) --> 187 [H], 188 bq_lines(T). 189bq_lines([]) --> 190 [].
197ruler([C0|Line]) :- 198 rule_char(C0), 199 phrase(ruler(C0, 1), Line). 200 201ruler(C, N) --> [C], !, { N2 is N+1 }, ruler(C, N2). 202ruler(C, N) --> [' '], !, ruler(C, N). 203ruler(_, N) --> { N >= 3 }. 204 205rule_char('-'). 206rule_char('_'). 207rule_char('*').
217list_item([Indent-Line|LT], Type, Indent, Items, ItemT, Rest) :-
218 !,
219 list_item_prefix(Type, Line, L1),
220 ( Type == dl
221 -> split_dt(L1, DT0, DD1),
222 append(DD1, LIT, DD),
223 strip_ws_tokens(DT0, DT),
224 Items = [dt(DT),dd(DD)|ItemT]
225 ; append(L1, LIT, LI0),
226 Items = [li(LI0)|ItemT]
227 ),
228 rest_list_item(LT, Type, Indent, LIT, Rest).
234rest_list_item(Lines, _Type, Indent, RestItem, RestLines) :-
235 take_blocks_at_indent(Lines, Indent, Blocks, RestLines),
236 ( Blocks = [p(Par)|MoreBlocks]
237 -> append(['\n'|Par], MoreBlocks, RestItem)
238 ; RestItem = Blocks
239 ).245take_blocks_at_indent(Lines, _, [], Lines) :- 246 skip_empty_lines(Lines, Lines1), 247 section_header(Lines1, _, _), 248 !. 249take_blocks_at_indent(Lines, N, [Block|RestBlocks], RestLines) :- 250 take_block(Lines, N, Block, Rest0), 251 !, 252 take_blocks_at_indent(Rest0, N, RestBlocks, RestLines). 253take_blocks_at_indent(Lines, _, [], Lines).
259rest_list(Lines, Type, N, Items, IT, Rest) :- 260 skip_empty_lines(Lines, Lines1), 261 list_item(Lines1, Type, N, Items, IT0, Rest0), 262 !, 263 rest_list(Rest0, Type, N, IT0, IT, Rest). 264rest_list(Rest, _, _, IT, IT, Rest).
268list_item_prefix(ul, [*, ' '|T], T) :- !. 269list_item_prefix(ul, [-, ' '|T], T) :- !. 270list_item_prefix(dl, [$, ' '|T], T) :- 271 split_dt(T, _, _), 272 !. 273list_item_prefix(ol, [w(N), '.', ' '|T], T) :- 274 atom_codes(N, [D]), 275 between(0'0, 0'9, D).
282split_dt(In, DT, []) :- 283 append(DT, [':'], In), 284 !. 285split_dt(In, DT, Rest) :- 286 append(DT, [':'|Rest0], In), 287 ( Rest0 == [] 288 -> Rest = [] 289 ; Rest0 = [' '|Rest] 290 ), 291 !.
301ul_to_dl(ul(Items), Description) :- 302 term_items(Items, DLItems, []), 303 ( terms_to_predicate_includes(DLItems, Preds) 304 -> Description = dl(class(predicates), Preds) 305 ; member(dd(DD), DLItems), DD \== [] 306 -> Description = dl(class(termlist), DLItems) 307 ). 308 309term_items([], T, T). 310term_items([LI|LIs], DLItems, Tail) :- 311 term_item(LI, DLItems, Tail1), 312 term_items(LIs, Tail1, Tail).
\term(Text, Term, Bindings).
321term_item(li(Tokens), 322 [ dt(class=term, \term(Text, Term, Bindings)), 323 dd(Descr) 324 | Tail 325 ], Tail) :- 326 ( ( append(TermTokens, ['\n'|Descr], Tokens) 327 -> true 328 ; TermTokens = Tokens, 329 Descr = [] 330 ) 331 -> with_output_to(string(Tmp), 332 ( forall(member(T, TermTokens), 333 write_token(T)), 334 write(' .\n'))), 335 E = error(_,_), 336 catch(setup_call_cleanup( 337 open_string(Tmp, In), 338 ( read_dt_term(In, Term, Bindings), 339 read_dt_term(In, end_of_file, []), 340 atom_string(Text, Tmp) 341 ), 342 close(In)), 343 E, fail) 344 ). 345 346write_token(w(X)) :- 347 !, 348 write(X). 349write_token(X) :- 350 write(X). 351 352read_dt_term(In, Term, Bindings) :- 353 read_term(In, Term, 354 [ variable_names(Bindings), 355 module(pldoc_modes) 356 ]). 357 358terms_to_predicate_includes([], []). 359terms_to_predicate_includes([dt(class=term, \term(_, [[PI]], [])), dd([])|T0], 360 [\include(PI, predicate, [])|T]) :- 361 is_pi(PI), 362 terms_to_predicate_includes(T0, T). 363 364is_pi(Name/Arity) :- 365 atom(Name), 366 integer(Arity), 367 between(0, 20, Arity). 368is_pi(Name//Arity) :- 369 atom(Name), 370 integer(Arity), 371 between(0, 20, Arity).
376row([C0|CL]) --> 377 cell(C0), 378 !, 379 row(CL). 380row([]) --> 381 []. 382 383cell(td(C)) --> 384 face_tokens(C0), 385 ['|'], 386 !, 387 { strip_ws_tokens(C0, C) 388 }. 389 390face_tokens([]) --> 391 []. 392face_tokens(Tokens) --> 393 face_token(H), % Deal with embedded *|...|*, etc. 394 token('|'), 395 face_tokens(Face), 396 token('|'), 397 face_token(H), 398 !, 399 { append([[H,'|'], Face, ['|', H], Rest], Tokens) }, 400 face_tokens(Rest). 401face_tokens([H|T]) --> 402 token(H), 403 face_tokens(T). 404 405face_token(=) --> [=]. 406face_token(*) --> [*]. 407face_token('_') --> ['_']. 408 409take_table(Lines, Indent, Row0, Table, Rest) :- 410 rest_table(Lines, Indent, Rows, Rest), 411 ( Rows = [align(Align)|Rows1] 412 -> maplist(align_row(Align), Rows1, Rows2), 413 ( maplist(=(td([])), Row0) % empty header 414 -> Table = table(class=wiki, Rows2) 415 ; maplist(td_to_th, Row0, Header), 416 Table = table(class=wiki, [tr(Header)|Rows2]) 417 ) 418 ; Table = table(class=wiki, [tr(Row0)|Rows]) 419 ). 420 421td_to_th(td(X), th(X)) :- !. 422td_to_th(X, X). 423 424align_row(Align, tr(Row0), tr(Row)) :- 425 align_cells(Align, Row0, Row). 426 427align_cells([Align|AT], [Cell0|T0], [Cell|T]) :- 428 align_cell(Align, Cell0, Cell), 429 align_cells(AT, T0, T). 430align_cells(_, Cells, Cells). 431 432align_cell(Align, td(Content), td(class=Align, Content)).
436rest_table([N-Line|LT], N, [align(Align)|RL], Rest) :- 437 phrase(column_alignment(Align), Line), 438 !, 439 rest_table2(LT, N, RL, Rest). 440rest_table(Lines, N, RL, Rest) :- 441 rest_table2(Lines, N, RL, Rest). 442 443rest_table2([N-['|'|RL1]|LT], N, [tr(R0)|RL], Rest) :- 444 !, 445 phrase(row(R0), RL1), 446 rest_table2(LT, N, RL, Rest). 447rest_table2(Rest, _, [], Rest).
453column_alignment([H|T]) --> 454 ['|'], 455 ( colspec(H) 456 -> column_alignment(T) 457 ; {T=[]} 458 ). 459 460colspec(Align) --> 461 ws_tokens, [':'], dashes3, 462 ( [':'] 463 -> {Align = center} 464 ; {Align = left} 465 ), 466 ws_tokens. 467colspec(Align) --> 468 ws_tokens, dashes3, 469 ( [':'] 470 -> {Align = right} 471 ; {Align = left} 472 ), 473 ws_tokens. 474 475dashes3 --> 476 [-,-,-], 477 dashes. 478 479dashes --> [-], !, dashes. 480dashes --> []. 481 482ws_tokens --> [' '], !, ws_tokens. 483ws_tokens --> [].
493rest_par([], [], BI, MaxI0, MaxI, []) :- 494 !, 495 MaxI is max(BI, MaxI0). 496rest_par([_-[]|Rest], [], _, MaxI, MaxI, Rest) :- !. 497rest_par(Lines, [], _, MaxI, MaxI, Lines) :- 498 Lines = [_-Verb|_], 499 verbatim_term(Verb), 500 !. 501rest_par([I-L|Rest], [], _, MaxI, MaxI, [I-L|Rest]) :- 502 list_item_prefix(_, L, _), 503 !. 504rest_par([I-L1|LT], ['\n'|Par], BI, MaxI0, MaxI, Rest) :- 505 append(L1, PT, Par), 506 MaxI1 is max(I, MaxI0), 507 rest_par(LT, PT, BI, MaxI1, MaxI, Rest).
514section_header([_-L1|LT], Section, LT) :- 515 twiki_section_line(L1, Section), 516 !. 517section_header([0-L1|LT], Section, LT) :- 518 md_section_line(L1, Section), 519 !. 520section_header([_-L1,0-L2|LT], Section, LT) :- 521 md_section_line(L1, L2, Section), 522 !.
530twiki_section_line([-,-,-|Rest], Section) :- 531 plusses(Rest, Section). 532 533plusses([+, ' '|Rest], h1(Attrs, Content)) :- 534 hdr_attributes(Rest, Attrs, Content). 535plusses([+, +, ' '|Rest], h2(Attrs, Content)) :- 536 hdr_attributes(Rest, Attrs, Content). 537plusses([+, +, +, ' '|Rest], h3(Attrs, Content)) :- 538 hdr_attributes(Rest, Attrs, Content). 539plusses([+, +, +, +, ' '|Rest], h4(Attrs, Content)) :- 540 hdr_attributes(Rest, Attrs, Content). 541 542hdr_attributes(List, Attrs, Content) :- 543 strip_leading_ws(List, List2), 544 ( List2 = ['[',w(Name),']'|List3] 545 -> strip_ws_tokens(List3, Content), 546 Attrs = [class(wiki), id(Name)] 547 ; Attrs = class(wiki), 548 strip_ws_tokens(List, Content) 549 ).
555md_section_line([#, ' '|Rest], h1(Attrs, Content)) :- 556 md_section_attributes(Rest, Attrs, Content). 557md_section_line([#, #, ' '|Rest], h2(Attrs, Content)) :- 558 md_section_attributes(Rest, Attrs, Content). 559md_section_line([#, #, #, ' '|Rest], h3(Attrs, Content)) :- 560 md_section_attributes(Rest, Attrs, Content). 561md_section_line([#, #, #, #, ' '|Rest], h4(Attrs, Content)) :- 562 md_section_attributes(Rest, Attrs, Content). 563 564md_section_attributes(List, Attrs, Content) :- 565 phrase((tokens(Content), [' '], section_label(Label)), List), 566 !, 567 Attrs = [class(wiki), id(Label)]. 568md_section_attributes(Content, Attrs, Content) :- 569 Attrs = [class(wiki)]. 570 571section_label(Label) --> 572 [ '{', '#', w(Name) ], 573 label_conts(Cont), ['}'], 574 !, 575 { atomic_list_concat([Name|Cont], Label) }. 576 577label_conts([H|T]) --> label_cont(H), !, label_conts(T). 578label_conts([]) --> []. 579 580label_cont(-) --> [-]. 581label_cont(Name) --> [w(Name)]. 582 583 584md_section_line(Line1, Line2, Header) :- 585 Line1 \== [], 586 section_underline(Line2, Type), 587 is_list(Line1), 588 phrase(wiki_words(_), Line1), % Should not have structure elements 589 !, 590 ( phrase(labeled_section_line(Title, Attrs), Line1) 591 -> true 592 ; Title = Line1, 593 Attrs = [] 594 ), 595 Header =.. [Type, [class(wiki)|Attrs], Title]. 596 597section_underline([=,=,=|T], h1) :- 598 maplist(=(=), T), 599 !. 600section_underline([-,-,-|T], h2) :- 601 maplist(=(-), T), 602 !. 603 604labeled_section_line(Title, Attrs) --> 605 tokens(Title), [' '], section_label(Label), 606 !, 607 { Attrs = [id(Label)] }.
615strip_ws_tokens([' '|T0], T) :- 616 !, 617 strip_ws_tokens(T0, T). 618strip_ws_tokens(L0, L) :- 619 append(L, [' '], L0), 620 !. 621strip_ws_tokens(L, L).
628strip_leading_ws([' '|T], T) :- !. 629strip_leading_ws(T, T). 630 631 632 /******************************* 633 * TAGS * 634 *******************************/
tag(Name, Value) terms.
641tags(Lines, Tags) :-
642 collect_tags(Lines, Tags0),
643 keysort(Tags0, Tags1),
644 pairs_values(Tags1, Tags2),
645 combine_tags(Tags2, Tags).tag(Tag,Tokens) for each @tag encountered.
Order is the desired position as defined by tag_order/2.
656collect_tags([], []). 657collect_tags([Indent-[@,String|L0]|Lines], [Order-tag(Tag,Value)|Tags]) :- 658 tag_name(String, Tag, Order), 659 !, 660 strip_leading_ws(L0, L), 661 rest_tag(Lines, Indent, VT, RestLines), 662 normalise_indentation(VT, VT1), 663 wiki_structure([0-L|VT1], -1, Value0), 664 strip_leading_par(Value0, Value), 665 collect_tags(RestLines, Tags).
672tag_name(w(Name), Tag, Order) :- 673 ( renamed_tag(Name, Tag, Level), 674 tag_order(Tag, Order) 675 -> print_message(Level, pldoc(deprecated_tag(Name, Tag))) 676 ; tag_order(Name, Order) 677 -> Tag = Name 678 ; print_message(warning, pldoc(unknown_tag(Name))), 679 fail 680 ). 681 682 683rest_tag([], _, [], []) :- !. 684rest_tag(Lines, Indent, [], Lines) :- 685 Lines = [Indent-[@,Word|_]|_], 686 tag_name(Word, _, _), 687 !. 688rest_tag([L|Lines0], Indent, [L|VT], Lines) :- 689 rest_tag(Lines0, Indent, VT, Lines).
696renamed_tag(exception, throws, warning). 697renamed_tag(param, arg, silent).
706:- multifile 707 pldoc:tag_order/2. 708 709tag_order(Tag, Order) :- 710 pldoc:tag_order(Tag, Order), 711 !. 712tag_order(arg, 100). 713tag_order(error, 200). % same as throw 714tag_order(throws, 300). 715tag_order(author, 400). 716tag_order(version, 500). 717tag_order(see, 600). 718tag_order(deprecated, 700). 719tag_order(compat, 800). % PlDoc extension 720tag_order(copyright, 900). 721tag_order(license, 1000). 722tag_order(bug, 1100). 723tag_order(tbd, 1200).
params(list(param(Name, Descr)))tag(Name, list(Descr))Descr is a list of tokens.
734combine_tags([], []). 735combine_tags([tag(arg, V1)|T0], [\args([P1|PL])|Tags]) :- 736 !, 737 arg_tag(V1, P1), 738 arg_tags(T0, PL, T1), 739 combine_tags(T1, Tags). 740combine_tags([tag(Tag,V0)|T0], [\tag(Tag, [V0|Vs])|T]) :- 741 same_tag(Tag, T0, T1, Vs), 742 combine_tags(T1, T). 743 744arg_tag([PT|Descr0], arg(PN, Descr)) :- 745 word_of(PT, PN), 746 strip_leading_ws(Descr0, Descr). 747 748word_of(w(W), W) :- !. % TBD: check non-word arg 749word_of(W, W). 750 ([tag(arg, V1)|T0], [P1|PL], T) :- 752 !, 753 arg_tag(V1, P1), 754 arg_tags(T0, PL, T). 755arg_tags(T, [], T). 756 757same_tag(Tag, [tag(Tag, V)|T0], T, [V|Vs]) :- 758 !, 759 same_tag(Tag, T0, T, Vs). 760same_tag(_, L, L, []). 761 762 763 /******************************* 764 * FACES * 765 *******************************/
772wiki_faces([dt(Class, \term(Text, Term, Bindings)), dd(Descr0)|T0], 773 ArgNames, 774 [dt(Class, \term(Text, Term, Bindings)), dd(Descr)|T]) :- 775 !, 776 varnames(Bindings, VarNames, ArgNames), 777 wiki_faces(Descr0, VarNames, Descr), 778 wiki_faces(T0, ArgNames, T). 779wiki_faces(DOM0, ArgNames, DOM) :- 780 structure_term(DOM0, Functor, Content0), 781 !, 782 wiki_faces_list(Content0, ArgNames, Content), 783 structure_term(DOM, Functor, Content). 784wiki_faces(Verb, _, Verb) :- 785 verbatim_term(Verb), 786 !. 787wiki_faces(Content0, ArgNames, Content) :- 788 assertion(is_list(Content0)), 789 phrase(wiki_faces(Content, ArgNames), Content0), 790 !. 791 792varnames([], List, List). 793varnames([Name=_|T0], [Name|T], List) :- 794 varnames(T0, T, List). 795 796wiki_faces_list([], _, []). 797wiki_faces_list([H0|T0], Args, [H|T]) :- 798 wiki_faces(H0, Args, H), 799 wiki_faces_list(T0, Args, T).
807structure_term(\tags(Tags), tags, [Tags]) :- !. 808structure_term(\args(Params), args, [Params]) :- !. 809structure_term(arg(Name,Descr), arg(Name), [Descr]) :- !. 810structure_term(\tag(Name,Value), tag(Name), [Value]) :- !. 811structure_term(\include(What,Type,Opts), include(What,Type,Opts), []) :- !. 812structure_term(dl(Att, Args), dl(Att), [Args]) :- !. 813structure_term(dt(Att, Args), dt(Att), [Args]) :- !. 814structure_term(table(Att, Args), table(Att), [Args]) :- !. 815structure_term(td(Att, Args), td(Att), [Args]) :- !. 816structure_term(h1(Att, Args), h1(Att), [Args]) :- !. 817structure_term(h2(Att, Args), h2(Att), [Args]) :- !. 818structure_term(h3(Att, Args), h3(Att), [Args]) :- !. 819structure_term(h4(Att, Args), h4(Att), [Args]) :- !. 820structure_term(hr(Att), hr(Att), []) :- !. 821structure_term(p(Args), p, [Args]) :- !. 822structure_term(Term, Functor, Args) :- 823 structure_term_any(Term, Functor, Args). 824 825structure_term(Term) :- 826 structure_term_any(Term, _Functor, _Args). 827 828structure_term_any(Term, Functor, Args) :- 829 functor(Term, Functor, 1), 830 structure_tag(Functor), 831 !, 832 Term =.. [Functor|Args]. 833 834structure_tag(ul). 835structure_tag(ol). 836structure_tag(dl). 837structure_tag(li). 838structure_tag(dt). 839structure_tag(dd). 840structure_tag(table). 841structure_tag(tr). 842structure_tag(td). 843structure_tag(th). 844structure_tag(blockquote). 845structure_tag(center).
852verbatim_term(pre(_,_)). 853verbatim_term(\term(_,_,_)).
860:- meta_predicate matches(, , , , ). 861 862matches(Goal, Input, Last, List, Rest) :- 863 call(Goal, List, Rest), 864 input(List, Rest, Input, Last). 865 866input([H|T0], Rest, Input, Last) :- 867 ( T0 == Rest 868 -> Input = [H], 869 Last = H 870 ; Input = [H|T], 871 input(T0, Rest, T, Last) 872 ).
884wiki_faces(WithFaces, ArgNames, List, Rest) :- 885 default_faces_options(Options), 886 catch(wiki_faces(WithFaces, ArgNames, Options, List, Rest), 887 pldoc(depth_limit), 888 failed_faces(WithFaces, List, Rest)). 889 890default_faces_options(_{depth:5}). 891 892failed_faces(WithFaces) --> 893 { debug(markdown(overflow), 'Depth limit exceeded', []) }, 894 wiki_words(WithFaces). 895 896wiki_faces([EmphTerm|T], ArgNames, Options) --> 897 emphasis_seq(EmphTerm, ArgNames, Options), 898 !, 899 wiki_faces_int(T, ArgNames). 900wiki_faces(Faces, ArgNames, Options) --> 901 wiki_faces_int(Faces, ArgNames, Options). 902 903wiki_faces_int(WithFaces, ArgNames) --> 904 { default_faces_options(Options) 905 }, 906 wiki_faces_int(WithFaces, ArgNames, Options). 907 908wiki_faces_int([], _, _) --> 909 []. 910wiki_faces_int(List, ArgNames, Options) --> 911 wiki_face(H, ArgNames, Options), 912 !, 913 { is_list(H) 914 -> append(H, T, List) 915 ; List = [H|T] 916 }, 917 wiki_faces(T, ArgNames, Options). 918wiki_faces_int([Before,EmphTerm|T], ArgNames, Options) --> 919 emphasis_before(Before), 920 emphasis_seq(EmphTerm, ArgNames, Options), 921 !, 922 wiki_faces_int(T, ArgNames, Options). 923wiki_faces_int([H|T], ArgNames, Options) --> 924 wiki_face_simple(H, ArgNames, Options), 925 !, 926 wiki_faces_int(T, ArgNames, Options). 927 928next_level(Options0, Options) --> 929 { succ(NewDepth, Options0.depth) 930 -> Options = Options0.put(depth, NewDepth) 931 ; throw(pldoc(depth_limit)) 932 }.
' ' (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.
951wiki_face(Out, Args, _) --> 952 prolog:doc_wiki_face(Out, Args), 953 !. 954wiki_face(var(Arg), ArgNames, _) --> 955 [w(Arg)], 956 { memberchk(Arg, ArgNames) 957 }, 958 !. 959wiki_face(b(Bold), ArgNames, Options) --> 960 [*,'|'], string(Tokens), ['|',*], 961 !, 962 { phrase(wiki_faces(Bold, ArgNames, Options), Tokens) }. 963wiki_face(i(Italic), ArgNames, Options) --> 964 ['_','|'], string(Tokens), ['|','_'], 965 !, 966 { phrase(wiki_faces(Italic, ArgNames, Options), Tokens) }. 967wiki_face(code(Code), _, _) --> 968 [=], eq_code_words(Words), [=], 969 !, 970 { atomic_list_concat(Words, Code) }. 971wiki_face(code(Code), _, _) --> 972 [=,'|'], wiki_words(Code), ['|',=], 973 !. 974wiki_face(PredRef, _, _) --> 975 ['`'], take_predref(PredRef), ['`'], 976 !. 977wiki_face(\nopredref(Pred), _, _) --> 978 ['`', '`'], take_predref(\predref(Pred)), ['`', '`'], 979 !. 980wiki_face([flag, ' ', \flagref(Flag)], _, _) --> 981 [ w('flag'), ' ', '`', w(Flag), '`' ], 982 { current_prolog_flag(Flag, _) }, 983 !. 984wiki_face(code(Code), _, _) --> 985 ['`','`'], wiki_words(Code), ['`','`'], 986 !. 987wiki_face(Code, _, _) --> 988 ( ['`'], code_words(Words), ['`'] 989 -> { atomic_list_concat(Words, Text), 990 E = error(_,_), 991 catch(atom_to_term(Text, Term, Vars), E, fail), 992 !, 993 code_face(Text, Term, Vars, Code) 994 } 995 ). 996wiki_face(Face, _, Options) --> 997 [ w(Name) ], arg_list(List), 998 { atomic_list_concat([Name|List], Text), 999 E = error(_,_), 1000 catch(atom_to_term(Text, Term, Vars), E, fail), 1001 term_face(Text, Term, Vars, Face, Options) 1002 }, 1003 !. 1004wiki_face(br([]), _, _) --> 1005 [<,w(br),>,'\n'], !. 1006wiki_face(br([]), _, _) --> 1007 [<,w(br),/,>,'\n'], !. 1008 % Below this, we only do links. 1009wiki_face(_, _, Options) --> 1010 { Options.get(link) == false, 1011 !, 1012 fail 1013 }. 1014wiki_face(PredRef, _, _) --> 1015 take_predref(PredRef), 1016 !. 1017wiki_face(\cite(Citations), _, _) --> 1018 ['['], citations(Citations), [']']. 1019wiki_face(\include(Name, Type, Options), _, _) --> 1020 ['[','['], file_name(Base, Ext), [']',']'], 1021 { autolink_extension(Ext, Type), 1022 !, 1023 file_name_extension(Base, Ext, Name), 1024 resolve_file(Name, Options, []) 1025 }, 1026 !. 1027wiki_face(\include(Name, Type, [caption(Caption)|Options]), _, _) --> 1028 ( ['!','['], tokens(100, Caption), [']','('] 1029 -> file_name(Base, Ext), [')'], 1030 { autolink_extension(Ext, Type), 1031 !, 1032 file_name_extension(Base, Ext, Name), 1033 resolve_file(Name, Options, []) 1034 } 1035 ), 1036 !. 1037wiki_face(Link, ArgNames, Options) --> % TWiki: [[Label][Link]] 1038 ( ['[','['], wiki_label(Label, ArgNames, Options), [']','['] 1039 -> wiki_link(Link, [label(Label), relative(true), end(']')]), 1040 [']',']'], ! 1041 ). 1042wiki_face(Link, ArgNames, Options) --> % Markdown: [Label](Link) 1043 ( ['['], wiki_label(Label, ArgNames, Options), [']','('] 1044 -> wiki_link(Link, [label(Label), relative(true), end(')')]), 1045 [')'], ! 1046 ). 1047wiki_face(Link, _ArgNames, _) --> 1048 wiki_link(Link, []), 1049 !. 1050 1051wiki_label(Label, _ArgNames, _Options) --> 1052 image_label(Label). 1053wiki_label(Label, ArgNames, Options) --> 1054 next_level(Options, NOptions), 1055 limit(40, wiki_faces(Label, ArgNames, NOptions.put(link,false))).
1061wiki_face_simple(Word, _, _) --> 1062 [ w(Word) ], 1063 !. 1064wiki_face_simple(SpaceOrPunct, _, _) --> 1065 [ SpaceOrPunct ], 1066 { atomic(SpaceOrPunct) }, 1067 !. 1068wiki_face_simple(FT, ArgNames, _) --> 1069 [Structure], 1070 { wiki_faces(Structure, ArgNames, FT) 1071 }. 1072 1073wiki_words([]) --> []. 1074wiki_words([Word|T]) --> [w(Word)], !, wiki_words(T). 1075wiki_words([Punct|T]) --> [Punct], {atomic(Punct)}, wiki_words(T).
`code`,
where `` is mapped to `.1082code_words([]) --> []. 1083code_words([Word|T]) --> [w(Word)], code_words(T). 1084code_words(CodeL) --> ['`','`'], {CodeL = ['`'|T]}, code_words(T). 1085code_words([Punct|T]) --> [Punct], {atomic(Punct)}, code_words(T).
=. This is limited to
.-:/, notably dealing with file names and
identifiers in various external languages.1096eq_code_words([Word]) --> 1097 [ w(Word) ]. 1098eq_code_words([Word|T]) --> 1099 [ w(Word) ], eq_code_internals(T, [End]), [w(End)]. 1100 1101eq_code_internals(T, T) --> []. 1102eq_code_internals([H|T], Tail) --> 1103 eq_code_internal(H), 1104 eq_code_internals(T, Tail). 1105 1106eq_code_internal(Word) --> 1107 [w(Word)]. 1108eq_code_internal(Punct) --> 1109 [Punct], 1110 { eq_code_internal_punct(Punct) }. 1111 1112eq_code_internal_punct('.'). 1113eq_code_internal_punct('-'). 1114eq_code_internal_punct(':'). 1115eq_code_internal_punct('/').
`... code ...` sequences. Text is the matched
text, Term is the parsed Prolog term and Code is the resulting
intermediate code.1124code_face(Text, Var, _, Code) :- 1125 var(Var), 1126 !, 1127 Code = var(Text). 1128code_face(Text, _, _, code(Text)).
1135emphasis_seq(EmphTerm, ArgNames, Options) -->
1136 emphasis_start(C),
1137 next_level(Options, NOptions),
1138 matches(limit(100, wiki_faces(Emph, ArgNames, NOptions)), Input, Last),
1139 emphasis_end(C),
1140 { emph_markdown(Last, Input),
1141 emphasis_term(C, Emph, EmphTerm)
1142 },
1143 !.1153emphasis_term('_', Term, i(Term)). 1154emphasis_term('*', Term, b(Term)). 1155emphasis_term('__', Term, strong(Term)). 1156emphasis_term('**', Term, strong(Term)). 1157 1158emph_markdown(_, [w(_)]) :- !. 1159emph_markdown(Last, Tokens) :- 1160 \+ emphasis_after_sep(Last), 1161 E = error(_,_), 1162 catch(b_getval(pldoc_object, Obj), E, Obj = '??'), 1163 debug(markdown(emphasis), '~q: additionally emphasis: ~p', 1164 [Obj, Tokens]). 1165 1166emphasis_before(Before) --> 1167 [Before], 1168 { emphasis_start_sep(Before) }. 1169 1170emphasis_start_sep('\n'). 1171emphasis_start_sep(' '). 1172emphasis_start_sep('<'). 1173emphasis_start_sep('{'). 1174emphasis_start_sep('('). 1175emphasis_start_sep('['). 1176emphasis_start_sep(','). 1177emphasis_start_sep(':'). 1178emphasis_start_sep(';'). 1179 1180emphasis_start(Which), [w(Word)] --> 1181 emphasis(Which), 1182 [w(Word)]. 1183 1184emphasis(**) --> [*, *]. 1185emphasis(*) --> [*]. 1186emphasis('__') --> ['_', '_']. 1187emphasis('_') --> ['_']. 1188 1189emphasis_end(Which), [After] --> 1190 emphasis(Which), 1191 [ After ], 1192 !, 1193 { emphasis_close_sep(After) -> true }. 1194emphasis_end(Which) --> 1195 emphasis(Which). 1196 1197% these characters should not be before a closing * or _. 1198 1199emphasis_after_sep('\n'). 1200emphasis_after_sep(' '). 1201emphasis_after_sep('('). 1202emphasis_after_sep('['). 1203emphasis_after_sep('<'). 1204emphasis_after_sep('='). 1205emphasis_after_sep('+'). 1206emphasis_after_sep('\\'). 1207emphasis_after_sep('@'). 1208 1209emphasis_close_sep('\n'). % white 1210emphasis_close_sep(' '). % white 1211emphasis_close_sep(','). % sentence punctuation 1212emphasis_close_sep('.'). 1213emphasis_close_sep('!'). 1214emphasis_close_sep('?'). 1215emphasis_close_sep(':'). 1216emphasis_close_sep(';'). 1217emphasis_close_sep(']'). % [**label**](link) 1218emphasis_close_sep(')'). % ... _italic_) 1219emphasis_close_sep('}'). % ... _italic_} 1220emphasis_close_sep(Token) :- 1221 structure_term(Token).
1232arg_list(['('|T]) --> 1233 ['('], arg_list_close(T, 1). 1234 1235arg_list_close(Tokens, Depth) --> 1236 [')'], 1237 !, 1238 ( { Depth == 1 } 1239 -> { Tokens = [')'] } 1240 ; { Depth > 1 } 1241 -> { Tokens = [')'|More], 1242 NewDepth is Depth - 1 1243 }, 1244 arg_list_close(More, NewDepth) 1245 ). 1246arg_list_close(['('|T], Depth) --> 1247 ['('], { NewDepth is Depth+1 }, 1248 arg_list_close(T, NewDepth). 1249arg_list_close([H|T], Depth) --> 1250 [w(H)], 1251 !, 1252 arg_list_close(T, Depth). 1253arg_list_close([H|T], Depth) --> 1254 [H], 1255 arg_list_close(T, Depth).
1264term_face(_Text, Term, _Vars, \file(Name, FileOptions), Options) :- 1265 ground(Term), 1266 compound(Term), 1267 compound_name_arity(Term, Alias, 1), 1268 user:file_search_path(Alias, _), 1269 existing_file(Term, FileOptions, [], Options), 1270 !, 1271 format(atom(Name), '~q', [Term]). 1272term_face(Text, Term, Vars, Face, _Options) :- 1273 code_face(Text, Term, Vars, Face). 1274 1275untag([], []). 1276untag([w(W)|T0], [W|T]) :- 1277 !, 1278 untag(T0, T). 1279untag([H|T0], [H|T]) :- 1280 untag(T0, T).
1286image_label(\include(Name, image, Options)) --> 1287 file_name(Base, Ext), 1288 { autolink_extension(Ext, image), 1289 file_name_extension(Base, Ext, Name), 1290 resolve_file(Name, Options, RestOptions) 1291 }, 1292 file_options(RestOptions). 1293 1294 1295take_predref(\predref(Name/Arity)) --> 1296 [ w(Name), '/' ], arity(Arity), 1297 { functor_name(Name) 1298 }. 1299take_predref(\predref(Module:(Name/Arity))) --> 1300 [ w(Module), ':', w(Name), '/' ], arity(Arity), 1301 { functor_name(Name) 1302 }. 1303take_predref(\predref(Name/Arity)) --> 1304 prolog_symbol_char(S0), 1305 symbol_string(SRest), [ '/' ], arity(Arity), 1306 !, 1307 { atom_chars(Name, [S0|SRest]) 1308 }. 1309take_predref(\predref(Name//Arity)) --> 1310 [ w(Name), '/', '/' ], arity(Arity), 1311 { functor_name(Name) 1312 }. 1313take_predref(\predref(Module:(Name//Arity))) --> 1314 [ w(Module), ':', w(Name), '/', '/' ], arity(Arity), 1315 { functor_name(Name) 1316 }.
1323file_options(Options) --> 1324 [;], nv_pairs(Options), 1325 !. 1326file_options([]) --> 1327 []. 1328 1329nv_pairs([H|T]) --> 1330 nv_pair(H), 1331 ( [','] 1332 -> nv_pairs(T) 1333 ; {T=[]} 1334 ). 1335 1336nv_pair(Option) --> 1337 [ w(Name), =,'"'], tokens(Tokens), ['"'], 1338 !, 1339 { untag(Tokens, Atoms), 1340 atomic_list_concat(Atoms, Value0), 1341 ( atom_number(Value0, Value) 1342 -> true 1343 ; Value = Value0 1344 ), 1345 Option =.. [Name,Value] 1346 }.
1362:- multifile 1363 user:url_path/2. 1364 1365wiki_link(\file(Name, FileOptions), Options) --> 1366 file_name(Base, Ext), 1367 { file_name_extension(Base, Ext, Name), 1368 ( autolink_file(Name, _) 1369 ; autolink_extension(Ext, _) 1370 ), 1371 !, 1372 resolve_file(Name, FileOptions, Options) 1373 }. 1374wiki_link(\file(Name, FileOptions), Options) --> 1375 [w(Name)], 1376 { autolink_file(Name, _), 1377 !, 1378 resolve_file(Name, FileOptions, Options) 1379 }, 1380 !. 1381wiki_link(a(href(Ref), Label), Options) --> 1382 [ w(Prot),:,/,/], { url_protocol(Prot) }, 1383 { option(end(End), Options, space) 1384 }, 1385 tokens_no_whitespace(Rest), peek_end_url(End), 1386 !, 1387 { atomic_list_concat([Prot, :,/,/ | Rest], Ref), 1388 option(label(Label), Options, Ref) 1389 }. 1390wiki_link(a(href(Ref), Label), _Options) --> 1391 [<, w(Alias), :], 1392 tokens_no_whitespace(Rest), [>], 1393 { Term = (Alias:Rest), 1394 prolog:url_expansion_hook(Term, Ref, Label), ! 1395 }. 1396wiki_link(a(href(Ref), Label), Options) --> 1397 [<, w(Alias), :], 1398 { user:url_path(Alias, _) 1399 }, 1400 tokens_no_whitespace(Rest), [>], 1401 { atomic_list_concat(Rest, Local), 1402 ( Local == '' 1403 -> Term =.. [Alias,'.'] 1404 ; Term =.. [Alias,Local] 1405 ), 1406 E = error(_,_), 1407 catch(expand_url_path(Term, Ref), E, fail), 1408 option(label(Label), Options, Ref) 1409 }. 1410wiki_link(a(href(Ref), Label), Options) --> 1411 [#, w(First)], 1412 { option(end(End), Options) }, 1413 tokens_no_whitespace(Rest), 1414 peek_end_url(End), 1415 !, 1416 { atomic_list_concat([#,First|Rest], Ref), 1417 option(label(Label), Options, Ref) 1418 }. 1419wiki_link(a(href(Ref), Label), Options) --> 1420 [<], 1421 ( { option(relative(true), Options), 1422 Parts = Rest 1423 } 1424 -> tokens_no_whitespace(Rest) 1425 ; { Parts = [Prot, : | Rest] 1426 }, 1427 [w(Prot), :], tokens_no_whitespace(Rest) 1428 ), 1429 [>], 1430 !, 1431 { atomic_list_concat(Parts, Ref), 1432 option(label(Label), Options, Ref) 1433 }.
<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.1447file_name(FileBase, Extension) --> 1448 segment(S1), 1449 segments(List), 1450 ['.'], file_extension(Extension), 1451 !, 1452 { atomic_list_concat([S1|List], '/', FileBase) }. 1453file_name(FileBase, Extension) --> 1454 [w(Alias), '('], 1455 { once(user:file_search_path(Alias, _)) }, 1456 segment(S1), 1457 segments(List), 1458 [')'], 1459 !, 1460 { atomic_list_concat([S1|List], '/', Base), 1461 Spec =.. [Alias,Base], 1462 absolute_file_name(Spec, Path, 1463 [ access(read), 1464 extensions([pl]), 1465 file_type(prolog), 1466 file_errors(fail) 1467 ]), 1468 file_name_extension(FileBase, Extension, Path) 1469 }. 1470 1471 1472segment(..) --> 1473 ['.','.'], 1474 !. 1475segment(Word) --> 1476 [w(Word)]. 1477segment(Dir) --> 1478 [w(Word),'.',w(d)], 1479 { atom_concat(Word, '.d', Dir) }. 1480 1481segments([H|T]) --> 1482 ['/'], 1483 !, 1484 segment(H), 1485 segments(T). 1486segments([]) --> 1487 []. 1488 1489file_extension(Ext) --> 1490 [w(Ext)], 1491 { autolink_extension(Ext, _) 1492 }.
absolute_path(Path) that reflects the current location of the
file.1502resolve_file(Name, FileOptions, Rest) :- 1503 existing_file(Name, FileOptions, Rest, []), 1504 !. 1505resolve_file(_, Options, Options). 1506 1507 1508existing_file(Name, FileOptions, Rest, Options) :- 1509 \+ Options.get(link) == false, 1510 E = error(_,_), 1511 catch(existing_file_p(Name, FileOptions, Rest), E, fail). 1512 1513existing_file_p(Name, FileOptions, Rest) :- 1514 ( nb_current(pldoc_file, RelativeTo), 1515 RelativeTo \== [] 1516 -> Extra = [relative_to(RelativeTo)|Extra1] 1517 ; Extra = Extra1 1518 ), 1519 ( compound(Name) 1520 -> Extra1 = [file_type(prolog)] 1521 ; Extra1 = [] 1522 ), 1523 absolute_file_name(Name, Path, 1524 [ access(read), 1525 file_errors(fail) 1526 | Extra 1527 ]), 1528 FileOptions = [ absolute_path(Path) | Rest ].
1537arity(Arity) -->
1538 [ w(Word) ],
1539 { E = error(_,_),
1540 catch(atom_number(Word, Arity), E, fail),
1541 Arity >= 0, Arity < 20
1542 }.1549symbol_string([]) --> 1550 []. 1551symbol_string([H|T]) --> 1552 [H], 1553 { prolog_symbol_char(H) }, 1554 symbol_string(T). 1555 1556prolog_symbol_char(C) --> 1557 [C], 1558 { prolog_symbol_char(C) }.
1564prolog_symbol_char(#). 1565prolog_symbol_char($). 1566prolog_symbol_char(&). 1567prolog_symbol_char(*). 1568prolog_symbol_char(+). 1569prolog_symbol_char(-). 1570prolog_symbol_char(.). 1571prolog_symbol_char(/). 1572prolog_symbol_char(:). 1573prolog_symbol_char(<). 1574prolog_symbol_char(=). 1575prolog_symbol_char(>). 1576prolog_symbol_char(?). 1577prolog_symbol_char(@). 1578prolog_symbol_char(\). 1579prolog_symbol_char(^). 1580prolog_symbol_char(~). 1581 1582 1583functor_name(String) :- 1584 sub_atom(String, 0, 1, _, Char), 1585 char_type(Char, lower). 1586 1587url_protocol(http). 1588url_protocol(https). 1589url_protocol(ftp). 1590url_protocol(mailto). 1591 1592peek_end_url(space) --> 1593 peek(Punct, End), 1594 { punct_token(Punct), 1595 space_token(End) 1596 }, 1597 !. 1598peek_end_url(space) --> 1599 peek(End), 1600 { space_token(End) }, 1601 !. 1602peek_end_url(space, [], []) :- !. 1603peek_end_url(Token) --> 1604 peek(Token), 1605 !. 1606 1607punct_token('.'). 1608punct_token('!'). 1609punct_token('?'). 1610punct_token(','). 1611punct_token(';'). 1612 1613space_token(' ') :- !. 1614space_token('\r') :- !. 1615space_token('\n') :- !. 1616space_token(T) :- 1617 \+ atom(T), % high level format like p(...) 1618 \+ T = w(_).
1625autolink_extension(Ext, Type) :- 1626 prolog:doc_autolink_extension(Ext, Type), 1627 !. 1628autolink_extension(Ext, prolog) :- 1629 user:prolog_file_type(Ext,prolog), 1630 !. 1631autolink_extension(txt, wiki). 1632autolink_extension(md, wiki). 1633autolink_extension(gif, image). 1634autolink_extension(png, image). 1635autolink_extension(jpg, image). 1636autolink_extension(jpeg, image). 1637autolink_extension(svg, image).
1644autolink_file('README', wiki). 1645autolink_file('TODO', wiki). 1646autolink_file('ChangeLog', wiki).
1652citations([H|T]) --> 1653 citation(H), 1654 ( [';'] 1655 -> citations(T) 1656 ; {T=[]} 1657 ). 1658 1659citation(Atom) --> 1660 [@], wiki_words(Atoms), 1661 { length(Atoms, Len), 1662 Len > 10, !, 1663 fail 1664 ; true 1665 }, 1666 end_citation, 1667 !, 1668 { atomic_list_concat(Atoms, Atom) 1669 }. 1670 1671end_citation, [';'] --> [';']. 1672end_citation, ['@'] --> ['@']. 1673end_citation, [']'] --> [']']. 1674 1675 1676 /******************************* 1677 * SECTIONS * 1678 *******************************/
section(Type, Title), where Title is an atom holding the
section title and Type is an atom holding the text between <>.
1691section_comment_header([_-Line|Lines], Header, Lines) :- 1692 phrase(section_line(Header), Line). 1693 1694section_line(\section(Type, Title)) --> 1695 ws, "<", word(Codes), ">", normalise_white_space(TitleCodes), 1696 { atom_codes(Type, Codes), 1697 atom_codes(Title, TitleCodes) 1698 }. 1699 1700 /******************************* 1701 * TOKENIZER * 1702 *******************************/
1708tokenize_lines(Lines, TokenLines) :- 1709 tokenize_lines(Lines, -1, TokenLines). 1710 1711tokenize_lines([], _, []) :- !. 1712tokenize_lines(Lines, Indent, [Pre|T]) :- 1713 verbatim(Lines, Indent, Pre, RestLines), 1714 !, 1715 tokenize_lines(RestLines, Indent, T). 1716tokenize_lines([I-H0|T0], Indent0, [I-H|T]) :- 1717 phrase(line_tokens(H), H0), 1718 ( H == [] 1719 -> Indent = Indent0 1720 ; Indent = I 1721 ), 1722 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), '_'].1733line_tokens([H|T]) --> 1734 line_token(H), 1735 !, 1736 line_tokens(T). 1737line_tokens([]) --> 1738 []. 1739 1740line_token(T) --> 1741 [C], 1742 ( { code_type(C, space) } 1743 -> ws, 1744 { T = ' ' } 1745 ; { code_type(C, alnum) }, 1746 word(Rest), 1747 { atom_codes(W, [C|Rest]), 1748 T = w(W) 1749 } 1750 ; { char_code(T, C) } 1751 ). 1752 1753word([C0|T]) --> 1754 [C0], { code_type(C0, alnum) }, 1755 !, 1756 word(T). 1757word([0'_, C1|T]) --> 1758 [0'_, C1], { code_type(C1, alnum) }, 1759 !, 1760 word(T). 1761word([]) --> 1762 []. 1763 1764alphas([C0|T]) --> 1765 [C0], { code_type(C0, alpha) }, 1766 !, 1767 alphas(T). 1768alphas([]) --> 1769 [].
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.
1791verbatim(Lines, _, 1792 Indent-pre([class(code), ext(Ext)],Pre), 1793 RestLines) :- 1794 skip_empty_lines(Lines, [Indent-FenceLine|CodeLines]), 1795 verbatim_fence(FenceLine, Fence, Ext), 1796 verbatim_body(CodeLines, Indent, [10|PreCodes], [], 1797 [Indent-Fence|RestLines]), 1798 !, 1799 atom_codes(Pre, PreCodes). 1800verbatim([_-[],Indent-Line|Lines], EnvIndent, 1801 Indent-pre(class(code),Pre), 1802 RestLines) :- 1803 EnvIndent >= 0, 1804 Indent >= EnvIndent+4, Indent =< EnvIndent+8, 1805 valid_verbatim_opening(Line), 1806 indented_verbatim_body([Indent-Line|Lines], Indent, 1807 CodeLines, RestLines), 1808 !, 1809 lines_code_text(CodeLines, Indent, [10|PreCodes]), 1810 atom_codes(Pre, PreCodes). 1811 1812verbatim_body(Lines, _, PreT, PreT, Lines). 1813verbatim_body([I-L|Lines], Indent, [10|Pre], PreT, RestLines) :- 1814 PreI is I - Indent, 1815 phrase(pre_indent(PreI), Pre, PreT0), 1816 verbatim_line(L, PreT0, PreT1), 1817 verbatim_body(Lines, Indent, PreT1, PreT, RestLines). 1818 1819verbatim_fence(Line, Fence, '') :- 1820 Line == [0'=,0'=], 1821 !, 1822 Fence = Line. 1823verbatim_fence(Line, Fence, Ext) :- 1824 tilde_fence(Line, Fence, 0, Ext). 1825verbatim_fence(Line, Fence, Ext) :- 1826 md_fence(Line, Fence, 0, Ext). 1827 1828tilde_fence([0'~|T0], [0'~|F0], C0, Ext) :- 1829 !, 1830 C1 is C0+1, 1831 tilde_fence(T0, F0, C1, Ext). 1832tilde_fence(List, [], C, Ext) :- 1833 C >= 3, 1834 ( List == [] 1835 -> Ext = '' 1836 ; phrase(tilde_fence_ext(ExtCodes), List) 1837 -> atom_codes(Ext, ExtCodes) 1838 ).
`{.prolog} (Doxygen) or `{prolog} (GitHub)1844tilde_fence_ext(Ext) --> 1845 "{.", !, alphas(Ext), "}". 1846tilde_fence_ext(Ext) --> 1847 "{", alphas(Ext), "}". 1848 1849md_fence([0'`|T0], [0'`|F0], C0, Ext) :- 1850 !, 1851 C1 is C0+1, 1852 md_fence(T0, F0, C1, Ext). 1853md_fence(List, [], C, Ext) :- 1854 C >= 3, 1855 ( List == [] 1856 -> Ext = '' 1857 ; phrase(md_fence_ext(ExtCodes), List), 1858 atom_codes(Ext, ExtCodes) 1859 ). 1860 1861% Also support Doxygen's curly bracket notation. 1862md_fence_ext(Ext) --> 1863 tilde_fence_ext(Ext), 1864 !. 1865% In Markdown language names appear without brackets. 1866md_fence_ext(Ext) --> 1867 alphas(Ext).
1875indented_verbatim_body([I-L|T0], Indent, [I-L|T], RestLines) :- 1876 L \== [], I >= Indent, 1877 !, 1878 indented_verbatim_body(T0, Indent, T, RestLines). 1879indented_verbatim_body([I0-[],I-L|T0], Indent, [I0-[],I-L|T], RestLines) :- 1880 I >= Indent, 1881 valid_verbatim_opening(L), 1882 indented_verbatim_body(T0, Indent, T, RestLines). 1883indented_verbatim_body(Lines, _, [], Lines).
1889valid_verbatim_opening([0'||_]) :- !, fail. 1890valid_verbatim_opening(Line) :- 1891 Line \== [], 1892 \+ ( phrase(line_tokens(Tokens), Line), 1893 list_item_prefix(_Type, Tokens, _Rest) 1894 ).
1900lines_code_text([], _, []). 1901lines_code_text([_-[]|T0], Indent, [10|T]) :- 1902 !, 1903 lines_code_text(T0, Indent, T). 1904lines_code_text([I-Line|T0], Indent, [10|T]) :- 1905 PreI is I-Indent, 1906 phrase(pre_indent(PreI), T, T1), 1907 verbatim_line(Line, T1, T2), 1908 lines_code_text(T0, Indent, T2).
1916pre_indent(N) --> 1917 { N > 0, 1918 !, 1919 N2 is N - 1 1920 }, " ", 1921 pre_indent(N2). 1922pre_indent(_) --> 1923 "". 1924 1925verbatim_line(Line, Pre, PreT) :- 1926 append(Line, PreT, Pre). 1927 1928 1929 /******************************* 1930 * SUMMARY * 1931 *******************************/
1940summary_from_lines(Lines, Sentence) :- 1941 skip_empty_lines(Lines, Lines1), 1942 summary2(Lines1, Sentence0), 1943 end_sentence(Sentence0, Sentence). 1944 1945summary2(_, Sentence) :- 1946 Sentence == [], 1947 !. % we finished our sentence 1948summary2([], []) :- !. 1949summary2([_-[]|_], []) :- !. % empty line 1950summary2([_-[0'@|_]|_], []) :- !. % keyword line 1951summary2([_-L0|Lines], Sentence) :- 1952 phrase(sentence(Sentence, Tail), L0, _), 1953 summary2(Lines, Tail). 1954 1955sentence([C,End], []) --> 1956 [C,End], 1957 { \+ code_type(C, period), 1958 code_type(End, period) % ., !, ? 1959 }, 1960 space_or_eos, 1961 !. 1962sentence([0' |T0], T) --> 1963 space, 1964 !, 1965 ws, 1966 sentence(T0, T). 1967sentence([H|T0], T) --> 1968 [H], 1969 sentence(T0, T). 1970sentence([0' |T], T) --> % ' 1971 eos. 1972 1973space_or_eos --> 1974 [C], 1975 !, 1976 {code_type(C, space)}. 1977space_or_eos --> 1978 eos.
1985skip_empty_lines([], []). 1986skip_empty_lines([_-[]|Lines0], Lines) :- 1987 !, 1988 skip_empty_lines(Lines0, Lines). 1989skip_empty_lines(Lines, Lines). 1990 1991end_sentence([], []). 1992end_sentence([0'\s], [0'.]) :- !. 1993end_sentence([H|T0], [H|T]) :- 1994 end_sentence(T0, T). 1995 1996 1997 /******************************* 1998 * CREATE LINES * 1999 *******************************/
2008indented_lines(Comment, Prefixes, Lines) :- 2009 must_be(codes, Comment), 2010 phrase(split_lines(Prefixes, Lines), Comment), 2011 !. 2012 2013split_lines(_, []) --> 2014 end_of_comment. 2015split_lines(Prefixes, [Indent-L1|Ls]) --> 2016 take_prefix(Prefixes, 0, Indent0), 2017 white_prefix(Indent0, Indent), 2018 take_line(L1), 2019 split_lines(Prefixes, Ls).
2028end_of_comment --> 2029 eos. 2030end_of_comment --> 2031 ws, stars, "*/". 2032 2033stars --> []. 2034stars --> "*", !, stars.
2042take_prefix(Prefixes, I0, I) --> 2043 { member(Prefix, Prefixes), 2044 string_codes(Prefix, PrefixCodes) 2045 }, 2046 prefix(PrefixCodes), 2047 !, 2048 { string_update_linepos(PrefixCodes, I0, I) }. 2049take_prefix(_, I, I) --> 2050 []. 2051 2052prefix([]) --> []. 2053prefix([H|T]) --> [H], prefix(T). 2054 2055white_prefix(I0, I) --> 2056 [C], 2057 { code_type(C, white), 2058 !, 2059 update_linepos(C, I0, I1) 2060 }, 2061 white_prefix(I1, I). 2062white_prefix(I, I) --> 2063 [].
2069string_update_linepos([], I, I). 2070string_update_linepos([H|T], I0, I) :- 2071 update_linepos(H, I0, I1), 2072 string_update_linepos(T, I1, I).
2080update_linepos(0'\t, I0, I) :- 2081 !, 2082 I is (I0\/7)+1. 2083update_linepos(0'\b, I0, I) :- 2084 !, 2085 I is max(0, I0-1). 2086update_linepos(0'\r, _, 0) :- !. 2087update_linepos(0'\n, _, 0) :- !. 2088update_linepos(_, I0, I) :- 2089 I is I0 + 1.
character(s), nor trailing whitespace.2096take_line([]) --> 2097 "\r\n", 2098 !. % DOS file 2099take_line([]) --> 2100 "\n", 2101 !. % Unix file 2102take_line(Line) --> 2103 [H], { code_type(H, white) }, 2104 !, 2105 take_white(White, WT), 2106 ( nl 2107 -> { Line = [] } 2108 ; { Line = [H|White] }, 2109 take_line(WT) 2110 ). 2111take_line([H|T]) --> 2112 [H], 2113 !, 2114 take_line(T). 2115take_line([]) --> % end of string 2116 []. 2117 2118take_white([H|T0], T) --> 2119 [H], { code_type(H, white) }, 2120 !, 2121 take_white(T0, T). 2122take_white(T, T) --> 2123 [].
2130normalise_indentation(Lines0, Lines) :- 2131 skip_empty_lines(Lines0, Lines1), 2132 Lines1 = [I0-_|Lines2], 2133 !, 2134 smallest_indentation(Lines2, I0, Subtract), 2135 ( Subtract == 0 2136 -> Lines = Lines0 2137 ; maplist(substract_indent(Subtract), Lines0, Lines) 2138 ). 2139normalise_indentation(Lines, Lines). 2140 2141smallest_indentation([], I, I). 2142smallest_indentation([_-[]|T], I0, I) :- 2143 !, 2144 smallest_indentation(T, I0, I). 2145smallest_indentation([X-_|T], I0, I) :- 2146 I1 is min(I0, X), 2147 smallest_indentation(T, I1, I). 2148 2149substract_indent(Subtract, I0-L, I-L) :- 2150 I is max(0,I0-Subtract). 2151 2152 2153 /******************************* 2154 * MISC * 2155 *******************************/
2162strip_leading_par([p(C)|T], L) :- 2163 !, 2164 append(C, T, L). 2165strip_leading_par(L, L). 2166 2167 2168 /******************************* 2169 * DCG BASICS * 2170 *******************************/
2176ws --> 2177 [C], {code_type(C, space)}, 2178 !, 2179 ws. 2180ws --> 2181 []. 2182 2183% space// is det 2184% 2185% True if then next code is layout. 2186 2187space --> 2188 [C], 2189 {code_type(C, space)}.
2195nl --> 2196 "\r\n", 2197 !. 2198nl --> 2199 "\n".
2205peek(H, L, L) :- 2206 L = [H|_]. 2207 2208peek(H1, H2, L, L) :- 2209 L = [H1, H2|_].
2217tokens([]) --> []. 2218tokens([H|T]) --> token(H), tokens(T). 2219 2220tokens(_, []) --> []. 2221tokens(C, [H|T]) --> token(H), {succ(C1, C)}, tokens(C1, T).
2229tokens_no_whitespace([]) --> 2230 []. 2231tokens_no_whitespace([Word|T]) --> 2232 [ w(Word) ], 2233 !, 2234 tokens_no_whitespace(T). 2235tokens_no_whitespace([H|T]) --> 2236 [H], 2237 { \+ space_token(H) }, 2238 tokens_no_whitespace(T). 2239 2240token(Token) --> 2241 [Token], 2242 { token(Token) }. 2243 2244token(w(_)) :- !. 2245token(Token) :- atom(Token).
2251:- meta_predicate limit(,,,). 2252 2253limit(Count, Rule, Input, Rest) :- 2254 Count > 0, 2255 State = count(0), 2256 call(Rule, Input, Rest), 2257 arg(1, State, N0), 2258 N is N0+1, 2259 ( N =:= Count 2260 -> ! 2261 ; nb_setarg(1, State, N) 2262 ). 2263 2264 2265 /******************************* 2266 * MESSAGES * 2267 *******************************/ 2268 2269:- multifile 2270 prolog:message//1. 2271 2272prologmessage(pldoc(deprecated_tag(Name, Tag))) --> 2273 [ 'PlDoc: Deprecated tag @~w (use @~w)'-[Name, Tag] 2274 ]. 2275prologmessage(pldoc(unknown_tag(Name))) --> 2276 [ 'PlDoc: unknown tag @~w'-[Name] 2277 ]
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.