36
37:- module(pengines_io,
38 [ pengine_writeln/1, 39 pengine_nl/0,
40 pengine_tab/1,
41 pengine_flush_output/0,
42 pengine_format/1, 43 pengine_format/2, 44
45 pengine_write_term/2, 46 pengine_write/1, 47 pengine_writeq/1, 48 pengine_display/1, 49 pengine_print/1, 50 pengine_write_canonical/1, 51
52 pengine_listing/0,
53 pengine_listing/1, 54 pengine_portray_clause/1, 55
56 pengine_read/1, 57 pengine_read_line_to_string/2, 58 pengine_read_line_to_codes/2, 59
60 pengine_io_predicate/1, 61 pengine_bind_io_to_html/1, 62 pengine_io_goal_expansion/2, 63
64 message_lines_to_html/3 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(html). 91:- public send_html/1. 92
93:- meta_predicate
94 pengine_format(+,:). 95
128
129:- setting(write_options, list(any), [max_depth(1000)],
130 'Additional options for stringifying Prolog results'). 131
132
133 136
140
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).
153
157
158pengine_nl :-
159 pengine_output,
160 !,
161 send_html(br([])).
162pengine_nl :-
163 nl.
164
168
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).
178
179
184
185pengine_flush_output :-
186 pengine_output,
187 !.
188pengine_flush_output :-
189 flush_output.
190
198
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).
207
215
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).
232
240
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 256
262
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 285
286:- multifile user:message_hook/3. 287
292
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)).
303
309
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'), 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 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 392
393lines([], _) --> [].
394lines([H|T], Class) -->
395 html(span(class(Class), H)),
396 ( { T == [] }
397 -> []
398 ; html(br([])),
399 lines(T, Class)
400 ).
401
406
407send_html(HTML) :-
408 phrase(html(HTML), Tokens),
409 with_output_to(string(HTMlString), print_html(Tokens)),
410 pengine_output(HTMlString).
411
412
416
417pengine_module(Module) :-
418 pengine_self(Pengine),
419 !,
420 pengine_property(Pengine, module(Module)).
421pengine_module(user).
422
423 426
453
454:- multifile
455 pengines:event_to_json/3. 456
471
472pengines:event_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).
478pengines:event_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)).
484
485
490
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 ])).
502
514
515pengines:event_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).
521pengines:event_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).
619
620
628
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 ).
645
652
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)).
663
673
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).
688
693
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'(_)).
700
701
705
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) 721 -> Data = Term
722 ; term_string(Term, Data)
723 ).
724
725
729
730:- multifile
731 prolog_help:show_html_hook/1. 732
733prolog_help:show_html_hook(HTML) :-
734 pengine_output,
735 pengine_output(HTML).
736
737
738 741
742:- multifile
743 sandbox:safe_primitive/1, 744 sandbox:safe_meta/2. 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 771
776
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 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).
835
843
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.
866
871
872pengine_output :-
873 current_output(Out),
874 pengine_io(_, Out).
875
876pengine_input :-
877 current_input(In),
878 pengine_io(In, _).
879
880
885
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])