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-2025, 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:- use_module(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 \+ pengine_io(_,_), 188 !. 189pengine_flush_output :- 190 flush_output. 191 192:- multifile 193 pengines:pengine_flush_output_hook/0. 194 195penginespengine_flush_output_hook :- 196 pengine_flush_output.
write.206pengine_write_term(Term, Options) :- 207 pengine_output, 208 !, 209 option(class(Class), Options, write), 210 pengine_module(Module), 211 send_html(span(class(Class), \term(Term,[module(Module)|Options]))). 212pengine_write_term(Term, Options) :- 213 write_term(Term, Options).
223pengine_write(Term) :- 224 pengine_write_term(Term, [numbervars(true)]). 225pengine_writeq(Term) :- 226 pengine_write_term(Term, [quoted(true), numbervars(true)]). 227pengine_display(Term) :- 228 pengine_write_term(Term, [quoted(true), ignore_ops(true)]). 229pengine_print(Term) :- 230 current_prolog_flag(print_write_options, Options), 231 pengine_write_term(Term, Options). 232pengine_write_canonical(Term) :- 233 pengine_output, 234 !, 235 with_output_to(string(String), write_canonical(Term)), 236 send_html(span(class([write, cononical]), String)). 237pengine_write_canonical(Term) :- 238 write_canonical(Term).
248pengine_format(Format) :- 249 pengine_format(Format, []). 250pengine_format(Format, Args) :- 251 pengine_output, 252 !, 253 format(string(String), Format, Args), 254 split_string(String, "\n", "", Lines), 255 send_html(\lines(Lines, format)). 256pengine_format(Format, Args) :- 257 format(Format, Args). 258 259 260 /******************************* 261 * LISTING * 262 *******************************/
270pengine_listing :- 271 pengine_listing(_). 272 273pengine_listing(Spec) :- 274 pengine_self(Module), 275 with_output_to(string(String), listing(Module:Spec)), 276 split_string(String, "", "\n", [Pre]), 277 send_html(pre(class(listing), Pre)). 278 279pengine_portray_clause(Term) :- 280 pengine_output, 281 !, 282 with_output_to(string(String), portray_clause(Term)), 283 split_string(String, "", "\n", [Pre]), 284 send_html(pre(class(listing), Pre)). 285pengine_portray_clause(Term) :- 286 portray_clause(Term). 287 288 289 /******************************* 290 * PRINT MESSAGE * 291 *******************************/ 292 293:- multifile user:message_hook/3.
300user:message_hook(Term, Kind, Lines) :-
301 Kind \== silent,
302 pengine_self(_),
303 atom_concat('msg-', Kind, Class),
304 message_lines_to_html(Lines, [Class], HTMlString),
305 ( source_location(File, Line)
306 -> Src = File:Line
307 ; Src = (-)
308 ),
309 pengine_output(message(Term, Kind, HTMlString, Src)).'prolog-message' and the given Classes.317message_lines_to_html(Lines, Classes, HTMlString) :- 318 phrase(html(pre(class(['prolog-message'|Classes]), 319 \message_lines(Lines))), Tokens), 320 with_output_to(string(HTMlString), print_html(Tokens)). 321 322message_lines([]) --> 323 !. 324message_lines([nl|T]) --> 325 !, 326 html('\n'), % we are in a <pre> environment 327 message_lines(T). 328message_lines([flush]) --> 329 !. 330message_lines([ansi(Attributes, Fmt, Args)|T]) --> 331 !, 332 { is_list(Attributes) 333 -> foldl(style, Attributes, Fmt-Args, HTML) 334 ; style(Attributes, Fmt-Args, HTML) 335 }, 336 html(HTML), 337 message_lines(T). 338message_lines([url(Pos)|T]) --> 339 !, 340 location(Pos), 341 message_lines(T). 342message_lines([url(HREF, Label)|T]) --> 343 !, 344 { msg_label(Label, Text) }, 345 ( { atomic(HREF) } 346 -> html(a(href(HREF), Text)) 347 ; html([Text]) % a source location: not a web link 348 ), 349 message_lines(T). 350message_lines([H|T]) --> 351 html(H), 352 message_lines(T).
359msg_label(ansi(_Style, Fmt, Args), Text) :- 360 !, 361 format(string(Text), Fmt, Args). 362msg_label(ansi(_Style, Fmt, Args, _Ctx), Text) :- 363 !, 364 format(string(Text), Fmt, Args). 365msg_label(Fmt-Args, Text) :- 366 !, 367 format(string(Text), Fmt, Args). 368msg_label(Text, Text). 369 370location(File:Line:Column) --> 371 !, 372 html([File, :, Line, :, Column]). 373location(File:Line) --> 374 !, 375 html([File, :, Line]). 376location(File) --> 377 html([File]). 378 379style(bold, Content, b(Content)) :- !. 380style(fg(default), Content, span(style('color: black'), Content)) :- !. 381style(fg(Color), Content, span(style('color:'+Color), Content)) :- !. 382style(_, Content, Content). 383 384 385 /******************************* 386 * INPUT * 387 *******************************/ 388 389pengine_read(Term) :- 390 pengine_input, 391 !, 392 prompt(Prompt, Prompt), 393 pengine_input(Prompt, Term). 394pengine_read(Term) :- 395 read(Term). 396 397pengine_read_line_to_string(From, String) :- 398 pengine_input, 399 !, 400 must_be(oneof([current_input,user_input]), From), 401 ( prompt(Prompt, Prompt), 402 Prompt \== '' 403 -> true 404 ; Prompt = 'line> ' 405 ), 406 pengine_input(_{type: console, prompt:Prompt}, StringNL), 407 string_concat(String, "\n", StringNL). 408pengine_read_line_to_string(From, String) :- 409 read_line_to_string(From, String). 410 411pengine_read_line_to_codes(From, Codes) :- 412 pengine_read_line_to_string(From, String), 413 string_codes(String, Codes). 414 415 416 /******************************* 417 * HTML * 418 *******************************/ 419 420lines([], _) --> []. 421lines([H|T], Class) --> 422 html(span(class(Class), H)), 423 ( { T == [] } 424 -> [] 425 ; html(br([])), 426 lines(T, Class) 427 ).
434send_html(HTML) :-
435 phrase(html(HTML), Tokens),
436 with_output_to(string(HTMlString), print_html(Tokens)),
437 pengine_output(HTMlString).444pengine_module(Module) :- 445 pengine_self(Pengine), 446 !, 447 pengine_property(Pengine, module(Module)). 448pengine_module(user). 449 450 /******************************* 451 * OUTPUT FORMAT * 452 *******************************/
481:- multifile
482 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.)499penginesevent_to_json(success(ID, Answers0, Projection, Time, More), JSON, 500 'json-s') :- 501 !, 502 JSON0 = json{event:success, id:ID, time:Time, data:Answers, more:More}, 503 maplist(answer_to_json_strings(ID), Answers0, Answers), 504 add_projection(Projection, JSON0, JSON). 505penginesevent_to_json(output(ID, Term), JSON, 'json-s') :- 506 !, 507 map_output(ID, Term, JSON). 508 509add_projection([], JSON, JSON) :- !. 510add_projection(VarNames, JSON0, JSON0.put(projection, VarNames)).
518answer_to_json_strings(Pengine, DictIn, DictOut) :- 519 dict_pairs(DictIn, Tag, Pairs), 520 maplist(term_string_value(Pengine), Pairs, BindingsOut), 521 dict_pairs(DictOut, Tag, BindingsOut). 522 523term_string_value(Pengine, N-V, N-A) :- 524 with_output_to(string(A), 525 write_term(V, 526 [ module(Pengine), 527 quoted(true) 528 ])).
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.
542penginesevent_to_json(success(ID, Answers0, Projection, Time, More), 543 JSON, 'json-html') :- 544 !, 545 JSON0 = json{event:success, id:ID, time:Time, data:Answers, more:More}, 546 maplist(map_answer(ID), Answers0, ResVars, Answers), 547 add_projection(Projection, ResVars, JSON0, JSON). 548penginesevent_to_json(output(ID, Term), JSON, 'json-html') :- 549 !, 550 map_output(ID, Term, JSON). 551 552map_answer(ID, Bindings0, ResVars, Answer) :- 553 dict_bindings(Bindings0, Bindings1), 554 select_residuals(Bindings1, Bindings2, ResVars, Residuals0, Clauses), 555 append(Residuals0, Residuals1), 556 prolog:translate_bindings(Bindings2, Bindings3, [], Residuals1, 557 ID:Residuals-_HiddenResiduals), 558 maplist(binding_to_html(ID), Bindings3, VarBindings), 559 final_answer(ID, VarBindings, Residuals, Clauses, Answer). 560 561final_answer(_Id, VarBindings, [], [], Answer) :- 562 !, 563 Answer = json{variables:VarBindings}. 564final_answer(ID, VarBindings, Residuals, [], Answer) :- 565 !, 566 residuals_html(Residuals, ID, ResHTML), 567 Answer = json{variables:VarBindings, residuals:ResHTML}. 568final_answer(ID, VarBindings, [], Clauses, Answer) :- 569 !, 570 clauses_html(Clauses, ID, ClausesHTML), 571 Answer = json{variables:VarBindings, wfs_residual_program:ClausesHTML}. 572final_answer(ID, VarBindings, Residuals, Clauses, Answer) :- 573 !, 574 residuals_html(Residuals, ID, ResHTML), 575 clauses_html(Clauses, ID, ClausesHTML), 576 Answer = json{variables:VarBindings, 577 residuals:ResHTML, 578 wfs_residual_program:ClausesHTML}. 579 580residuals_html([], _, []). 581residuals_html([H0|T0], Module, [H|T]) :- 582 term_html_string(H0, [], Module, H, [priority(999)]), 583 residuals_html(T0, Module, T). 584 585clauses_html(Clauses, _ID, HTMLString) :- 586 with_output_to(string(Program), list_clauses(Clauses)), 587 phrase(html(pre([class('wfs-residual-program')], Program)), Tokens), 588 with_output_to(string(HTMLString), print_html(Tokens)). 589 590list_clauses([]). 591list_clauses([H|T]) :- 592 ( system_undefined(H) 593 -> true 594 ; portray_clause(H) 595 ), 596 list_clauses(T). 597 598system_undefined((undefined :- tnot(undefined))). 599system_undefined((answer_count_restraint :- tnot(answer_count_restraint))). 600system_undefined((radial_restraint :- tnot(radial_restraint))). 601 602dict_bindings(Dict, Bindings) :- 603 dict_pairs(Dict, _Tag, Pairs), 604 maplist([N-V,N=V]>>true, Pairs, Bindings). 605 606select_residuals([], [], [], [], []). 607select_residuals([H|T], Bindings, Vars, Residuals, Clauses) :- 608 binding_residual(H, Var, Residual), 609 !, 610 Vars = [Var|TV], 611 Residuals = [Residual|TR], 612 select_residuals(T, Bindings, TV, TR, Clauses). 613select_residuals([H|T], Bindings, Vars, Residuals, Clauses) :- 614 binding_residual_clauses(H, Var, Delays, Clauses0), 615 !, 616 Vars = [Var|TV], 617 Residuals = [Delays|TR], 618 append(Clauses0, CT, Clauses), 619 select_residuals(T, Bindings, TV, TR, CT). 620select_residuals([H|T0], [H|T], Vars, Residuals, Clauses) :- 621 select_residuals(T0, T, Vars, Residuals, Clauses). 622 623binding_residual('_residuals' = '$residuals'(Residuals), '_residuals', Residuals) :- 624 is_list(Residuals). 625binding_residual('Residuals' = '$residuals'(Residuals), 'Residuals', Residuals) :- 626 is_list(Residuals). 627binding_residual('Residual' = '$residual'(Residual), 'Residual', [Residual]) :- 628 callable(Residual). 629 630binding_residual_clauses( 631 '_wfs_residual_program' = '$wfs_residual_program'(Delays, Clauses), 632 '_wfs_residual_program', Residuals, Clauses) :- 633 phrase(delay_list(Delays), Residuals). 634 635delay_list(true) --> !. 636delay_list((A,B)) --> !, delay_list(A), delay_list(B). 637delay_list(M:A) --> !, [M:'$wfs_undefined'(A)]. 638delay_list(A) --> ['$wfs_undefined'(A)]. 639 640add_projection(-, _, JSON, JSON) :- !. 641add_projection(VarNames0, ResVars0, JSON0, JSON) :- 642 append(ResVars0, ResVars1), 643 sort(ResVars1, ResVars), 644 subtract(VarNames0, ResVars, VarNames), 645 add_projection(VarNames, JSON0, JSON).
656binding_to_html(ID, binding(Vars,Term,Substitutions), JSON) :- 657 JSON0 = json{variables:Vars, value:HTMLString}, 658 binding_write_options(ID, Options), 659 term_html_string(Term, Vars, ID, HTMLString, Options), 660 ( Substitutions == [] 661 -> JSON = JSON0 662 ; maplist(subst_to_html(ID), Substitutions, HTMLSubst), 663 JSON = JSON0.put(substitutions, HTMLSubst) 664 ). 665 666binding_write_options(Pengine, Options) :- 667 ( current_predicate(Pengine:screen_property/1), 668 Pengine:screen_property(tabled(true)) 669 -> Options = [] 670 ; Options = [priority(699)] 671 ).
680term_html_string(Term, Vars, Module, HTMLString, Options) :-
681 setting(write_options, WOptions),
682 merge_options(WOptions,
683 [ quoted(true),
684 numbervars(true),
685 module(Module)
686 | Options
687 ], WriteOptions),
688 phrase(term_html(Term, Vars, WriteOptions), Tokens),
689 with_output_to(string(HTMLString), print_html(Tokens)).701:- multifile binding_term//3. 702 703term_html(Term, Vars, WriteOptions) --> 704 { nonvar(Term) }, 705 binding_term(Term, Vars, WriteOptions), 706 !. 707term_html(Undef, _Vars, WriteOptions) --> 708 { nonvar(Undef), 709 Undef = '$wfs_undefined'(Term), 710 ! 711 }, 712 html(span(class(wfs_undefined), \term(Term, WriteOptions))). 713term_html(Term, _Vars, WriteOptions) --> 714 term(Term, WriteOptions).
721subst_to_html(ID, '$VAR'(Name)=Value, json{var:Name, value:HTMLString}) :- 722 !, 723 binding_write_options(ID, Options), 724 term_html_string(Value, [Name], ID, HTMLString, Options). 725subst_to_html(_, Term, _) :- 726 assertion(Term = '$VAR'(_)).
733map_output(ID, message(Term, Kind, HTMLString, Src), JSON) :- 734 atomic(HTMLString), 735 !, 736 JSON0 = json{event:output, id:ID, message:Kind, data:HTMLString}, 737 pengines:add_error_details(Term, JSON0, JSON1), 738 ( Src = File:Line, 739 \+ JSON1.get(location) = _ 740 -> JSON = JSON1.put(_{location:_{file:File, line:Line}}) 741 ; JSON = JSON1 742 ). 743map_output(ID, Term, json{event:output, id:ID, data:Data}) :- 744 ( atomic(Term) 745 -> Data = Term 746 ; is_dict(Term, json), 747 ground(json) % TBD: Check proper JSON object? 748 -> Data = Term 749 ; term_string(Term, Data) 750 ).
757:- multifile 758 prolog_help:show_html_hook/1. 759 760prolog_helpshow_html_hook(HTML) :- 761 pengine_output, 762 pengine_output(HTML). 763 764 765 /******************************* 766 * SANDBOXING * 767 *******************************/ 768 769:- multifile 770 sandbox:safe_primitive/1, % Goal 771 sandbox:safe_meta/2. % Goal, Called 772 773sandbox:safe_primitive(pengines_io:pengine_listing(_)). 774sandbox:safe_primitive(pengines_io:pengine_nl). 775sandbox:safe_primitive(pengines_io:pengine_tab(_)). 776sandbox:safe_primitive(pengines_io:pengine_flush_output). 777sandbox:safe_primitive(pengines_io:pengine_print(_)). 778sandbox:safe_primitive(pengines_io:pengine_write(_)). 779sandbox:safe_primitive(pengines_io:pengine_read(_)). 780sandbox:safe_primitive(pengines_io:pengine_read_line_to_string(_,_)). 781sandbox:safe_primitive(pengines_io:pengine_read_line_to_codes(_,_)). 782sandbox:safe_primitive(pengines_io:pengine_write_canonical(_)). 783sandbox:safe_primitive(pengines_io:pengine_write_term(_,_)). 784sandbox:safe_primitive(pengines_io:pengine_writeln(_)). 785sandbox:safe_primitive(pengines_io:pengine_writeq(_)). 786sandbox:safe_primitive(pengines_io:pengine_portray_clause(_)). 787sandbox:safe_primitive(system:write_term(_,_)). 788sandbox:safe_primitive(system:prompt(_,_)). 789sandbox:safe_primitive(system:statistics(_,_)). 790sandbox:safe_primitive(system:put_code(_)). 791sandbox:safe_primitive(system:put_char(_)). 792 793sandbox:safe_meta(pengines_io:pengine_format(Format, Args), Calls) :- 794 sandbox:format_calls(Format, Args, Calls). 795 796 797 /******************************* 798 * REDEFINITION * 799 *******************************/
806pengine_io_predicate(writeln(_)). 807pengine_io_predicate(nl). 808pengine_io_predicate(tab(_)). 809pengine_io_predicate(flush_output). 810pengine_io_predicate(format(_)). 811pengine_io_predicate(format(_,_)). 812pengine_io_predicate(read(_)). 813pengine_io_predicate(read_line_to_string(_,_)). 814pengine_io_predicate(read_line_to_codes(_,_)). 815pengine_io_predicate(write_term(_,_)). 816pengine_io_predicate(write(_)). 817pengine_io_predicate(writeq(_)). 818pengine_io_predicate(display(_)). 819pengine_io_predicate(print(_)). 820pengine_io_predicate(write_canonical(_)). 821pengine_io_predicate(listing). 822pengine_io_predicate(listing(_)). 823pengine_io_predicate(portray_clause(_)). 824 825term_expansion(pengine_io_goal_expansion(_,_), 826 Clauses) :- 827 findall(Clause, io_mapping(Clause), Clauses). 828 829io_mapping(pengine_io_goal_expansion(Head, Mapped)) :- 830 pengine_io_predicate(Head), 831 Head =.. [Name|Args], 832 atom_concat(pengine_, Name, BodyName), 833 Mapped =.. [BodyName|Args]. 834 835pengine_io_goal_expansion(_, _). 836 837 838 /******************************* 839 * REBIND PENGINE I/O * 840 *******************************/ 841 842:- public 843 stream_write/2, 844 stream_read/2, 845 stream_close/1. 846 847:- thread_local 848 pengine_io/2. 849 850stream_write(Stream, Out) :- 851 ( pengine_io(_,_) 852 -> send_html(pre(class(console), Out)) 853 ; current_prolog_flag(pengine_main_thread, TID), 854 thread_signal(TID, stream_write(Stream, Out)) 855 ). 856stream_read(Stream, Data) :- 857 ( pengine_io(_,_) 858 -> prompt(Prompt, Prompt), 859 pengine_input(_{type:console, prompt:Prompt}, Data) 860 ; current_prolog_flag(pengine_main_thread, TID), 861 call_in_thread(TID, stream_read(Stream, Data)) 862 ). 863stream_close(_Stream).
873pengine_bind_user_streams :- 874 Err = Out, 875 open_prolog_stream(pengines_io, write, Out, []), 876 set_stream(Out, buffer(line)), 877 open_prolog_stream(pengines_io, read, In, []), 878 set_stream(In, alias(user_input)), 879 set_stream(Out, alias(user_output)), 880 set_stream(Err, alias(user_error)), 881 set_stream(In, alias(current_input)), 882 set_stream(Out, alias(current_output)), 883 assertz(pengine_io(In, Out)), 884 thread_self(Me), 885 thread_property(Me, id(Id)), 886 set_prolog_flag(pengine_main_thread, Id), 887 thread_at_exit(close_io). 888 889close_io :- 890 retract(pengine_io(In, Out)), 891 !, 892 close(In, [force(true)]), 893 close(Out, [force(true)]). 894close_io.
901pengine_output :- 902 current_output(Out), 903 pengine_io(_, Out). 904 905pengine_input :- 906 current_input(In), 907 pengine_io(In, _).
915pengine_bind_io_to_html(Module) :- 916 forall(pengine_io_predicate(Head), 917 bind_io(Head, Module)), 918 pengine_bind_user_streams. 919 920bind_io(Head, Module) :- 921 prompt(_, ''), 922 redefine_system_predicate(Module:Head), 923 functor(Head, Name, Arity), 924 Head =.. [Name|Args], 925 atom_concat(pengine_, Name, BodyName), 926 Body =.. [BodyName|Args], 927 assertz(Module:(Head :- Body)), 928 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.
:- pengine_application(myapp). :- use_module(myapp:library(pengines_io)). pengines:prepare_module(Module, myapp, _Options) :- pengines_io:pengine_bind_io_to_html(Module).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:
:- pengine_application(myapp). :- use_module(myapp:library(pengines_io)). myapp:goal_expansion(In,Out) :- pengine_io_goal_expansion(In, Out).