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) 2014-2023, VU University Amsterdam 7 CWI, Amsterdam 8 SWI-Prolog Solutions b.v. 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(pengines_io, 38 [ pengine_writeln/1, % +Term 39 pengine_nl/0, 40 pengine_tab/1, 41 pengine_flush_output/0, 42 pengine_format/1, % +Format 43 pengine_format/2, % +Format, +Args 44 45 pengine_write_term/2, % +Term, +Options 46 pengine_write/1, % +Term 47 pengine_writeq/1, % +Term 48 pengine_display/1, % +Term 49 pengine_print/1, % +Term 50 pengine_write_canonical/1, % +Term 51 52 pengine_listing/0, 53 pengine_listing/1, % +Spec 54 pengine_portray_clause/1, % +Term 55 56 pengine_read/1, % -Term 57 pengine_read_line_to_string/2, % +Stream, -LineAsString 58 pengine_read_line_to_codes/2, % +Stream, -LineAsCodes 59 60 pengine_io_predicate/1, % ?Head 61 pengine_bind_io_to_html/1, % +Module 62 pengine_io_goal_expansion/2,% +Goal, -Expanded 63 64 message_lines_to_html/3 % +Lines, +Classes, -HTML 65 ]). 66:- autoload(library(apply),[foldl/4,maplist/3,maplist/4]). 67:- autoload(library(backcomp),[thread_at_exit/1]). 68:- use_module(library(debug),[assertion/1]). 69:- autoload(library(error),[must_be/2]). 70:- autoload(library(listing),[listing/1,portray_clause/1]). 71:- autoload(library(lists),[append/2,append/3,subtract/3]). 72:- autoload(library(option),[option/3,merge_options/3]). 73:- autoload(library(pengines), 74 [ pengine_self/1, 75 pengine_output/1, 76 pengine_input/2, 77 pengine_property/2 78 ]). 79:- autoload(library(prolog_stream),[open_prolog_stream/4]). 80:- autoload(library(readutil),[read_line_to_string/2]). 81:- autoload(library(http/term_html),[term/4]). 82 83:- use_module(library(yall),[(>>)/4]). 84:- use_module(library(http/html_write),[html/3,print_html/1, op(_,_,_)]). 85:- use_module(library(settings),[setting/4,setting/2]). 86 87:- use_module(library(sandbox), []). 88:- autoload(library(thread), [call_in_thread/2]). 89 90:- html_meta send_html( ). 91:- public send_html/1. 92 93:- meta_predicate 94 pengine_format( , ).
129:- setting(write_options, list(any), [max_depth(1000)], 130 'Additional options for stringifying Prolog results'). 131 132 133 /******************************* 134 * OUTPUT * 135 *******************************/
141pengine_writeln(Term) :- 142 pengine_output, 143 !, 144 pengine_module(Module), 145 send_html(span(class(writeln), 146 [ \term(Term, 147 [ module(Module) 148 ]), 149 br([]) 150 ])). 151pengine_writeln(Term) :- 152 writeln(Term).
158pengine_nl :- 159 pengine_output, 160 !, 161 send_html(br([])). 162pengine_nl :- 163 nl.
169pengine_tab(Expr) :- 170 pengine_output, 171 !, 172 N is Expr, 173 length(List, N), 174 maplist(=(&(nbsp)), List), 175 send_html(List). 176pengine_tab(N) :- 177 tab(N).
185pengine_flush_output :- 186 pengine_output, 187 !. 188pengine_flush_output :- 189 flush_output.
write
.199pengine_write_term(Term, Options) :- 200 pengine_output, 201 !, 202 option(class(Class), Options, write), 203 pengine_module(Module), 204 send_html(span(class(Class), \term(Term,[module(Module)|Options]))). 205pengine_write_term(Term, Options) :- 206 write_term(Term, Options).
216pengine_write(Term) :- 217 pengine_write_term(Term, [numbervars(true)]). 218pengine_writeq(Term) :- 219 pengine_write_term(Term, [quoted(true), numbervars(true)]). 220pengine_display(Term) :- 221 pengine_write_term(Term, [quoted(true), ignore_ops(true)]). 222pengine_print(Term) :- 223 current_prolog_flag(print_write_options, Options), 224 pengine_write_term(Term, Options). 225pengine_write_canonical(Term) :- 226 pengine_output, 227 !, 228 with_output_to(string(String), write_canonical(Term)), 229 send_html(span(class([write, cononical]), String)). 230pengine_write_canonical(Term) :- 231 write_canonical(Term).
241pengine_format(Format) :- 242 pengine_format(Format, []). 243pengine_format(Format, Args) :- 244 pengine_output, 245 !, 246 format(string(String), Format, Args), 247 split_string(String, "\n", "", Lines), 248 send_html(\lines(Lines, format)). 249pengine_format(Format, Args) :- 250 format(Format, Args). 251 252 253 /******************************* 254 * LISTING * 255 *******************************/
263pengine_listing :- 264 pengine_listing(_). 265 266pengine_listing(Spec) :- 267 pengine_self(Module), 268 with_output_to(string(String), listing(Module:Spec)), 269 split_string(String, "", "\n", [Pre]), 270 send_html(pre(class(listing), Pre)). 271 272pengine_portray_clause(Term) :- 273 pengine_output, 274 !, 275 with_output_to(string(String), portray_clause(Term)), 276 split_string(String, "", "\n", [Pre]), 277 send_html(pre(class(listing), Pre)). 278pengine_portray_clause(Term) :- 279 portray_clause(Term). 280 281 282 /******************************* 283 * PRINT MESSAGE * 284 *******************************/ 285 286:- multifile user:message_hook/3.
293user:message_hook(Term, Kind, Lines) :-
294 Kind \== silent,
295 pengine_self(_),
296 atom_concat('msg-', Kind, Class),
297 message_lines_to_html(Lines, [Class], HTMlString),
298 ( source_location(File, Line)
299 -> Src = File:Line
300 ; Src = (-)
301 ),
302 pengine_output(message(Term, Kind, HTMlString, Src)).
'prolog-message'
and the given Classes.310message_lines_to_html(Lines, Classes, HTMlString) :- 311 phrase(html(pre(class(['prolog-message'|Classes]), 312 \message_lines(Lines))), Tokens), 313 with_output_to(string(HTMlString), print_html(Tokens)). 314 315message_lines([]) --> 316 !. 317message_lines([nl|T]) --> 318 !, 319 html('\n'), % we are in a <pre> environment 320 message_lines(T). 321message_lines([flush]) --> 322 !. 323message_lines([ansi(Attributes, Fmt, Args)|T]) --> 324 !, 325 { is_list(Attributes) 326 -> foldl(style, Attributes, Fmt-Args, HTML) 327 ; style(Attributes, Fmt-Args, HTML) 328 }, 329 html(HTML), 330 message_lines(T). 331message_lines([url(Pos)|T]) --> 332 !, 333 location(Pos), 334 message_lines(T). 335message_lines([url(HREF, Label)|T]) --> 336 !, 337 html(a(href(HREF),Label)), 338 message_lines(T). 339message_lines([H|T]) --> 340 html(H), 341 message_lines(T). 342 343location(File:Line:Column) --> 344 !, 345 html([File, :, Line, :, Column]). 346location(File:Line) --> 347 !, 348 html([File, :, Line]). 349location(File) --> 350 html([File]). 351 352style(bold, Content, b(Content)) :- !. 353style(fg(default), Content, span(style('color: black'), Content)) :- !. 354style(fg(Color), Content, span(style('color:'+Color), Content)) :- !. 355style(_, Content, Content). 356 357 358 /******************************* 359 * INPUT * 360 *******************************/ 361 362pengine_read(Term) :- 363 pengine_input, 364 !, 365 prompt(Prompt, Prompt), 366 pengine_input(Prompt, Term). 367pengine_read(Term) :- 368 read(Term). 369 370pengine_read_line_to_string(From, String) :- 371 pengine_input, 372 !, 373 must_be(oneof([current_input,user_input]), From), 374 ( prompt(Prompt, Prompt), 375 Prompt \== '' 376 -> true 377 ; Prompt = 'line> ' 378 ), 379 pengine_input(_{type: console, prompt:Prompt}, StringNL), 380 string_concat(String, "\n", StringNL). 381pengine_read_line_to_string(From, String) :- 382 read_line_to_string(From, String). 383 384pengine_read_line_to_codes(From, Codes) :- 385 pengine_read_line_to_string(From, String), 386 string_codes(String, Codes). 387 388 389 /******************************* 390 * HTML * 391 *******************************/ 392 393lines([], _) --> []. 394lines([H|T], Class) --> 395 html(span(class(Class), H)), 396 ( { T == [] } 397 -> [] 398 ; html(br([])), 399 lines(T, Class) 400 ).
407send_html(HTML) :-
408 phrase(html(HTML), Tokens),
409 with_output_to(string(HTMlString), print_html(Tokens)),
410 pengine_output(HTMlString).
417pengine_module(Module) :- 418 pengine_self(Pengine), 419 !, 420 pengine_property(Pengine, module(Module)). 421pengine_module(user). 422 423 /******************************* 424 * OUTPUT FORMAT * 425 *******************************/
454:- multifile
455 pengines:event_to_json/3.
'json-s'
or 'json-html'
, emit a simplified
JSON representation of the data, suitable for notably SWISH.
This deals with Prolog answers and output messages. If a message
originates from print_message/3, it gets several additional
properties:
error
, warning
,
etc.)472penginesevent_to_json(success(ID, Answers0, Projection, Time, More), JSON, 473 'json-s') :- 474 !, 475 JSON0 = json{event:success, id:ID, time:Time, data:Answers, more:More}, 476 maplist(answer_to_json_strings(ID), Answers0, Answers), 477 add_projection(Projection, JSON0, JSON). 478penginesevent_to_json(output(ID, Term), JSON, 'json-s') :- 479 !, 480 map_output(ID, Term, JSON). 481 482add_projection([], JSON, JSON) :- !. 483add_projection(VarNames, JSON0, JSON0.put(projection, VarNames)).
491answer_to_json_strings(Pengine, DictIn, DictOut) :- 492 dict_pairs(DictIn, Tag, Pairs), 493 maplist(term_string_value(Pengine), Pairs, BindingsOut), 494 dict_pairs(DictOut, Tag, BindingsOut). 495 496term_string_value(Pengine, N-V, N-A) :- 497 with_output_to(string(A), 498 write_term(V, 499 [ module(Pengine), 500 quoted(true) 501 ])).
json-html
format. This
format represents the answer as JSON, but the variable bindings are
(structured) HTML strings rather than JSON objects.
CHR residual goals are not bound to the projection variables. We hacked a bypass to fetch these by returning them in a variable named _residuals, which must be bound to a term '$residuals'(List). Such a variable is removed from the projection and added to residual goals.
515penginesevent_to_json(success(ID, Answers0, Projection, Time, More), 516 JSON, 'json-html') :- 517 !, 518 JSON0 = json{event:success, id:ID, time:Time, data:Answers, more:More}, 519 maplist(map_answer(ID), Answers0, ResVars, Answers), 520 add_projection(Projection, ResVars, JSON0, JSON). 521penginesevent_to_json(output(ID, Term), JSON, 'json-html') :- 522 !, 523 map_output(ID, Term, JSON). 524 525map_answer(ID, Bindings0, ResVars, Answer) :- 526 dict_bindings(Bindings0, Bindings1), 527 select_residuals(Bindings1, Bindings2, ResVars, Residuals0, Clauses), 528 append(Residuals0, Residuals1), 529 prolog:translate_bindings(Bindings2, Bindings3, [], Residuals1, 530 ID:Residuals-_HiddenResiduals), 531 maplist(binding_to_html(ID), Bindings3, VarBindings), 532 final_answer(ID, VarBindings, Residuals, Clauses, Answer). 533 534final_answer(_Id, VarBindings, [], [], Answer) :- 535 !, 536 Answer = json{variables:VarBindings}. 537final_answer(ID, VarBindings, Residuals, [], Answer) :- 538 !, 539 residuals_html(Residuals, ID, ResHTML), 540 Answer = json{variables:VarBindings, residuals:ResHTML}. 541final_answer(ID, VarBindings, [], Clauses, Answer) :- 542 !, 543 clauses_html(Clauses, ID, ClausesHTML), 544 Answer = json{variables:VarBindings, wfs_residual_program:ClausesHTML}. 545final_answer(ID, VarBindings, Residuals, Clauses, Answer) :- 546 !, 547 residuals_html(Residuals, ID, ResHTML), 548 clauses_html(Clauses, ID, ClausesHTML), 549 Answer = json{variables:VarBindings, 550 residuals:ResHTML, 551 wfs_residual_program:ClausesHTML}. 552 553residuals_html([], _, []). 554residuals_html([H0|T0], Module, [H|T]) :- 555 term_html_string(H0, [], Module, H, [priority(999)]), 556 residuals_html(T0, Module, T). 557 558clauses_html(Clauses, _ID, HTMLString) :- 559 with_output_to(string(Program), list_clauses(Clauses)), 560 phrase(html(pre([class('wfs-residual-program')], Program)), Tokens), 561 with_output_to(string(HTMLString), print_html(Tokens)). 562 563list_clauses([]). 564list_clauses([H|T]) :- 565 ( system_undefined(H) 566 -> true 567 ; portray_clause(H) 568 ), 569 list_clauses(T). 570 571system_undefined((undefined :- tnot(undefined))). 572system_undefined((answer_count_restraint :- tnot(answer_count_restraint))). 573system_undefined((radial_restraint :- tnot(radial_restraint))). 574 575dict_bindings(Dict, Bindings) :- 576 dict_pairs(Dict, _Tag, Pairs), 577 maplist([N-V,N=V]>>true, Pairs, Bindings). 578 579select_residuals([], [], [], [], []). 580select_residuals([H|T], Bindings, Vars, Residuals, Clauses) :- 581 binding_residual(H, Var, Residual), 582 !, 583 Vars = [Var|TV], 584 Residuals = [Residual|TR], 585 select_residuals(T, Bindings, TV, TR, Clauses). 586select_residuals([H|T], Bindings, Vars, Residuals, Clauses) :- 587 binding_residual_clauses(H, Var, Delays, Clauses0), 588 !, 589 Vars = [Var|TV], 590 Residuals = [Delays|TR], 591 append(Clauses0, CT, Clauses), 592 select_residuals(T, Bindings, TV, TR, CT). 593select_residuals([H|T0], [H|T], Vars, Residuals, Clauses) :- 594 select_residuals(T0, T, Vars, Residuals, Clauses). 595 596binding_residual('_residuals' = '$residuals'(Residuals), '_residuals', Residuals) :- 597 is_list(Residuals). 598binding_residual('Residuals' = '$residuals'(Residuals), 'Residuals', Residuals) :- 599 is_list(Residuals). 600binding_residual('Residual' = '$residual'(Residual), 'Residual', [Residual]) :- 601 callable(Residual). 602 603binding_residual_clauses( 604 '_wfs_residual_program' = '$wfs_residual_program'(Delays, Clauses), 605 '_wfs_residual_program', Residuals, Clauses) :- 606 phrase(delay_list(Delays), Residuals). 607 608delay_list(true) --> !. 609delay_list((A,B)) --> !, delay_list(A), delay_list(B). 610delay_list(M:A) --> !, [M:'$wfs_undefined'(A)]. 611delay_list(A) --> ['$wfs_undefined'(A)]. 612 613add_projection(-, _, JSON, JSON) :- !. 614add_projection(VarNames0, ResVars0, JSON0, JSON) :- 615 append(ResVars0, ResVars1), 616 sort(ResVars1, ResVars), 617 subtract(VarNames0, ResVars, VarNames), 618 add_projection(VarNames, JSON0, JSON).
629binding_to_html(ID, binding(Vars,Term,Substitutions), JSON) :- 630 JSON0 = json{variables:Vars, value:HTMLString}, 631 binding_write_options(ID, Options), 632 term_html_string(Term, Vars, ID, HTMLString, Options), 633 ( Substitutions == [] 634 -> JSON = JSON0 635 ; maplist(subst_to_html(ID), Substitutions, HTMLSubst), 636 JSON = JSON0.put(substitutions, HTMLSubst) 637 ). 638 639binding_write_options(Pengine, Options) :- 640 ( current_predicate(Pengine:screen_property/1), 641 Pengine:screen_property(tabled(true)) 642 -> Options = [] 643 ; Options = [priority(699)] 644 ).
653term_html_string(Term, Vars, Module, HTMLString, Options) :-
654 setting(write_options, WOptions),
655 merge_options(WOptions,
656 [ quoted(true),
657 numbervars(true),
658 module(Module)
659 | Options
660 ], WriteOptions),
661 phrase(term_html(Term, Vars, WriteOptions), Tokens),
662 with_output_to(string(HTMLString), print_html(Tokens)).
674:- multifile binding_term//3. 675 676term_html(Term, Vars, WriteOptions) --> 677 { nonvar(Term) }, 678 binding_term(Term, Vars, WriteOptions), 679 !. 680term_html(Undef, _Vars, WriteOptions) --> 681 { nonvar(Undef), 682 Undef = '$wfs_undefined'(Term), 683 ! 684 }, 685 html(span(class(wfs_undefined), \term(Term, WriteOptions))). 686term_html(Term, _Vars, WriteOptions) --> 687 term(Term, WriteOptions).
694subst_to_html(ID, '$VAR'(Name)=Value, json{var:Name, value:HTMLString}) :- 695 !, 696 binding_write_options(ID, Options), 697 term_html_string(Value, [Name], ID, HTMLString, Options). 698subst_to_html(_, Term, _) :- 699 assertion(Term = '$VAR'(_)).
706map_output(ID, message(Term, Kind, HTMLString, Src), JSON) :- 707 atomic(HTMLString), 708 !, 709 JSON0 = json{event:output, id:ID, message:Kind, data:HTMLString}, 710 pengines:add_error_details(Term, JSON0, JSON1), 711 ( Src = File:Line, 712 \+ JSON1.get(location) = _ 713 -> JSON = JSON1.put(_{location:_{file:File, line:Line}}) 714 ; JSON = JSON1 715 ). 716map_output(ID, Term, json{event:output, id:ID, data:Data}) :- 717 ( atomic(Term) 718 -> Data = Term 719 ; is_dict(Term, json), 720 ground(json) % TBD: Check proper JSON object? 721 -> Data = Term 722 ; term_string(Term, Data) 723 ).
730:- multifile 731 prolog_help:show_html_hook/1. 732 733prolog_helpshow_html_hook(HTML) :- 734 pengine_output, 735 pengine_output(HTML). 736 737 738 /******************************* 739 * SANDBOXING * 740 *******************************/ 741 742:- multifile 743 sandbox:safe_primitive/1, % Goal 744 sandbox:safe_meta/2. % Goal, Called 745 746sandbox:safe_primitive(pengines_io:pengine_listing(_)). 747sandbox:safe_primitive(pengines_io:pengine_nl). 748sandbox:safe_primitive(pengines_io:pengine_tab(_)). 749sandbox:safe_primitive(pengines_io:pengine_flush_output). 750sandbox:safe_primitive(pengines_io:pengine_print(_)). 751sandbox:safe_primitive(pengines_io:pengine_write(_)). 752sandbox:safe_primitive(pengines_io:pengine_read(_)). 753sandbox:safe_primitive(pengines_io:pengine_read_line_to_string(_,_)). 754sandbox:safe_primitive(pengines_io:pengine_read_line_to_codes(_,_)). 755sandbox:safe_primitive(pengines_io:pengine_write_canonical(_)). 756sandbox:safe_primitive(pengines_io:pengine_write_term(_,_)). 757sandbox:safe_primitive(pengines_io:pengine_writeln(_)). 758sandbox:safe_primitive(pengines_io:pengine_writeq(_)). 759sandbox:safe_primitive(pengines_io:pengine_portray_clause(_)). 760sandbox:safe_primitive(system:write_term(_,_)). 761sandbox:safe_primitive(system:prompt(_,_)). 762sandbox:safe_primitive(system:statistics(_,_)). 763 764sandbox:safe_meta(pengines_io:pengine_format(Format, Args), Calls) :- 765 sandbox:format_calls(Format, Args, Calls). 766 767 768 /******************************* 769 * REDEFINITION * 770 *******************************/
777pengine_io_predicate(writeln(_)). 778pengine_io_predicate(nl). 779pengine_io_predicate(tab(_)). 780pengine_io_predicate(flush_output). 781pengine_io_predicate(format(_)). 782pengine_io_predicate(format(_,_)). 783pengine_io_predicate(read(_)). 784pengine_io_predicate(read_line_to_string(_,_)). 785pengine_io_predicate(read_line_to_codes(_,_)). 786pengine_io_predicate(write_term(_,_)). 787pengine_io_predicate(write(_)). 788pengine_io_predicate(writeq(_)). 789pengine_io_predicate(display(_)). 790pengine_io_predicate(print(_)). 791pengine_io_predicate(write_canonical(_)). 792pengine_io_predicate(listing). 793pengine_io_predicate(listing(_)). 794pengine_io_predicate(portray_clause(_)). 795 796term_expansion(pengine_io_goal_expansion(_,_), 797 Clauses) :- 798 findall(Clause, io_mapping(Clause), Clauses). 799 800io_mapping(pengine_io_goal_expansion(Head, Mapped)) :- 801 pengine_io_predicate(Head), 802 Head =.. [Name|Args], 803 atom_concat(pengine_, Name, BodyName), 804 Mapped =.. [BodyName|Args]. 805 806pengine_io_goal_expansion(_, _). 807 808 809 /******************************* 810 * REBIND PENGINE I/O * 811 *******************************/ 812 813:- public 814 stream_write/2, 815 stream_read/2, 816 stream_close/1. 817 818:- thread_local 819 pengine_io/2. 820 821stream_write(Stream, Out) :- 822 ( pengine_io(_,_) 823 -> send_html(pre(class(console), Out)) 824 ; current_prolog_flag(pengine_main_thread, TID), 825 thread_signal(TID, stream_write(Stream, Out)) 826 ). 827stream_read(Stream, Data) :- 828 ( pengine_io(_,_) 829 -> prompt(Prompt, Prompt), 830 pengine_input(_{type:console, prompt:Prompt}, Data) 831 ; current_prolog_flag(pengine_main_thread, TID), 832 call_in_thread(TID, stream_read(Stream, Data)) 833 ). 834stream_close(_Stream).
844pengine_bind_user_streams :- 845 Err = Out, 846 open_prolog_stream(pengines_io, write, Out, []), 847 set_stream(Out, buffer(line)), 848 open_prolog_stream(pengines_io, read, In, []), 849 set_stream(In, alias(user_input)), 850 set_stream(Out, alias(user_output)), 851 set_stream(Err, alias(user_error)), 852 set_stream(In, alias(current_input)), 853 set_stream(Out, alias(current_output)), 854 assertz(pengine_io(In, Out)), 855 thread_self(Me), 856 thread_property(Me, id(Id)), 857 set_prolog_flag(pengine_main_thread, Id), 858 thread_at_exit(close_io). 859 860close_io :- 861 retract(pengine_io(In, Out)), 862 !, 863 close(In, [force(true)]), 864 close(Out, [force(true)]). 865close_io.
872pengine_output :- 873 current_output(Out), 874 pengine_io(_, Out). 875 876pengine_input :- 877 current_input(In), 878 pengine_io(In, _).
886pengine_bind_io_to_html(Module) :- 887 forall(pengine_io_predicate(Head), 888 bind_io(Head, Module)), 889 pengine_bind_user_streams. 890 891bind_io(Head, Module) :- 892 prompt(_, ''), 893 redefine_system_predicate(Module:Head), 894 functor(Head, Name, Arity), 895 Head =.. [Name|Args], 896 atom_concat(pengine_, Name, BodyName), 897 Body =.. [BodyName|Args], 898 assertz(Module:(Head :- Body)), 899 compile_predicates([Module:Name/Arity])
Provide Prolog I/O for HTML clients
This module redefines some of the standard Prolog I/O predicates to behave transparently for HTML clients. It provides two ways to redefine the standard predicates: using goal_expansion/2 and by redefining the system predicates using redefine_system_predicate/1. The latter is the preferred route because it gives a more predictable trace to the user and works regardless of the use of other expansion and meta-calling.
Redefining works by redefining the system predicates in the context of the pengine's module. This is configured using the following code snippet.
Using goal_expansion/2 works by rewriting the corresponding goals using goal_expansion/2 and use the new definition to re-route I/O via pengine_input/2 and pengine_output/1. A pengine application is prepared for using this module with the following code:
*/