34
35:- module(pengines_io,
36 [ pengine_writeln/1, 37 pengine_nl/0,
38 pengine_flush_output/0,
39 pengine_format/1, 40 pengine_format/2, 41
42 pengine_write_term/2, 43 pengine_write/1, 44 pengine_writeq/1, 45 pengine_display/1, 46 pengine_print/1, 47 pengine_write_canonical/1, 48
49 pengine_listing/0,
50 pengine_listing/1, 51 pengine_portray_clause/1, 52
53 pengine_read/1, 54
55 pengine_io_predicate/1, 56 pengine_bind_io_to_html/1, 57 pengine_io_goal_expansion/2 58 ]).
59:- use_module(library(lists)).
60:- use_module(library(pengines)).
61:- use_module(library(option)).
62:- use_module(library(debug)).
63:- use_module(library(apply)).
64:- use_module(library(settings)).
65:- use_module(library(listing)).
66:- use_module(library(yall)).
67:- use_module(library(sandbox), []).
68:- use_module(library(http/html_write)).
69:- use_module(library(http/term_html)).
70:- if(exists_source(library(prolog_stream))).
71:- use_module(library(prolog_stream)).
72:- endif.
73:- html_meta send_html(html).
74
75:- meta_predicate
76 pengine_format(+,:).
77
110
111:- setting(write_options, list(any), [max_depth(1000)],
112 'Additional options for stringifying Prolog results').
113
114
115 118
122
123pengine_writeln(Term) :-
124 pengine_module(Module),
125 send_html(span(class(writeln),
126 [ \term(Term,
127 [ module(Module)
128 ]),
129 br([])
130 ])).
131
135
136pengine_nl :-
137 send_html(br([])).
138
143
144pengine_flush_output.
145
153
154pengine_write_term(Term, Options) :-
155 option(class(Class), Options, write),
156 pengine_module(Module),
157 send_html(span(class(Class), \term(Term,[module(Module)|Options]))).
158
166
167pengine_write(Term) :-
168 pengine_write_term(Term, []).
169pengine_writeq(Term) :-
170 pengine_write_term(Term, [quoted(true), numbervars(true)]).
171pengine_display(Term) :-
172 pengine_write_term(Term, [quoted(true), ignore_ops(true)]).
173pengine_print(Term) :-
174 current_prolog_flag(print_write_options, Options),
175 pengine_write_term(Term, Options).
176pengine_write_canonical(Term) :-
177 with_output_to(string(String), write_canonical(Term)),
178 send_html(span(class([write, cononical]), String)).
179
187
188pengine_format(Format) :-
189 pengine_format(Format, []).
190pengine_format(Format, Args) :-
191 format(string(String), Format, Args),
192 split_string(String, "\n", "", Lines),
193 send_html(\lines(Lines, format)).
194
195
196 199
205
206pengine_listing :-
207 pengine_listing(_).
208
209pengine_listing(Spec) :-
210 pengine_self(Module),
211 with_output_to(string(String), listing(Module:Spec)),
212 split_string(String, "", "\n", [Pre]),
213 send_html(pre(class(listing), Pre)).
214
215pengine_portray_clause(Term) :-
216 with_output_to(string(String), portray_clause(Term)),
217 split_string(String, "", "\n", [Pre]),
218 send_html(pre(class(listing), Pre)).
219
220
221 224
225:- multifile user:message_hook/3.
226
231
232user:message_hook(Term, Kind, Lines) :-
233 Kind \== silent,
234 pengine_self(_),
235 atom_concat('msg-', Kind, Class),
236 phrase(html(pre(class(['prolog-message', Class]),
237 \message_lines(Lines))), Tokens),
238 with_output_to(string(HTMlString), print_html(Tokens)),
239 ( source_location(File, Line)
240 -> Src = File:Line
241 ; Src = (-)
242 ),
243 pengine_output(message(Term, Kind, HTMlString, Src)).
244
245message_lines([]) --> [].
246message_lines([nl|T]) -->
247 !,
248 html('\n'), 249 message_lines(T).
250message_lines([flush]) -->
251 [].
252message_lines([H|T]) -->
253 !,
254 html(H),
255 message_lines(T).
256
257
258 261
262pengine_read(Term) :-
263 prompt(Prompt, Prompt),
264 pengine_input(Prompt, Term).
265
266
267 270
271lines([], _) --> [].
272lines([H|T], Class) -->
273 html(span(class(Class), H)),
274 ( { T == [] }
275 -> []
276 ; html(br([])),
277 lines(T, Class)
278 ).
279
284
285send_html(HTML) :-
286 phrase(html(HTML), Tokens),
287 with_output_to(string(HTMlString), print_html(Tokens)),
288 pengine_output(HTMlString).
289
290
294
295pengine_module(Module) :-
296 pengine_self(Pengine),
297 !,
298 pengine_property(Pengine, module(Module)).
299pengine_module(user).
300
301 304
331
332:- multifile
333 pengines:event_to_json/4.
334
349
350pengines:event_to_json(success(ID, Answers0, Time, More), JSON,
351 'json-s', VarNames) :-
352 !,
353 JSON0 = json{event:success, id:ID, time:Time, data:Answers, more:More},
354 maplist(answer_to_json_strings(ID), Answers0, Answers),
355 add_projection(VarNames, JSON0, JSON).
356pengines:event_to_json(output(ID, Term), JSON, 'json-s', _) :-
357 !,
358 map_output(ID, Term, JSON).
359
360add_projection(-, JSON, JSON) :- !.
361add_projection(VarNames, JSON0, JSON0.put(projection, VarNames)).
362
363
368
369answer_to_json_strings(Pengine, DictIn, DictOut) :-
370 dict_pairs(DictIn, Tag, Pairs),
371 maplist(term_string_value(Pengine), Pairs, BindingsOut),
372 dict_pairs(DictOut, Tag, BindingsOut).
373
374term_string_value(Pengine, N-V, N-A) :-
375 with_output_to(string(A),
376 write_term(V,
377 [ module(Pengine),
378 quoted(true)
379 ])).
380
392
393pengines:event_to_json(success(ID, Answers0, Time, More),
394 JSON, 'json-html', VarNames) :-
395 !,
396 JSON0 = json{event:success, id:ID, time:Time, data:Answers, more:More},
397 maplist(map_answer(ID), Answers0, ResVars, Answers),
398 add_projection(VarNames, ResVars, JSON0, JSON).
399pengines:event_to_json(output(ID, Term), JSON, 'json-html', _) :-
400 !,
401 map_output(ID, Term, JSON).
402
403map_answer(ID, Bindings0, ResVars, Answer) :-
404 dict_bindings(Bindings0, Bindings1),
405 select_residuals(Bindings1, Bindings2, ResVars, Residuals0),
406 append(Residuals0, Residuals1),
407 prolog:translate_bindings(Bindings2, Bindings3, [], Residuals1,
408 ID:Residuals-_HiddenResiduals),
409 maplist(binding_to_html(ID), Bindings3, VarBindings),
410 ( Residuals == []
411 -> Answer = json{variables:VarBindings}
412 ; residuals_html(Residuals, ID, ResHTML),
413 Answer = json{variables:VarBindings, residuals:ResHTML}
414 ).
415
416residuals_html([], _, []).
417residuals_html([H0|T0], Module, [H|T]) :-
418 term_html_string(H0, [], Module, H, [priority(999)]),
419 residuals_html(T0, Module, T).
420
421dict_bindings(Dict, Bindings) :-
422 dict_pairs(Dict, _Tag, Pairs),
423 maplist([N-V,N=V]>>true, Pairs, Bindings).
424
425select_residuals([], [], [], []).
426select_residuals([H|T], Bindings, Vars, Residuals) :-
427 binding_residual(H, Var, Residual),
428 !,
429 Vars = [Var|TV],
430 Residuals = [Residual|TR],
431 select_residuals(T, Bindings, TV, TR).
432select_residuals([H|T0], [H|T], Vars, Residuals) :-
433 select_residuals(T0, T, Vars, Residuals).
434
435binding_residual('_residuals' = '$residuals'(Residuals), '_residuals', Residuals) :-
436 is_list(Residuals).
437binding_residual('Residuals' = '$residuals'(Residuals), 'Residuals', Residuals) :-
438 is_list(Residuals).
439binding_residual('Residual' = '$residual'(Residual), 'Residual', [Residual]) :-
440 callable(Residual).
441
442add_projection(-, _, JSON, JSON) :- !.
443add_projection(VarNames0, ResVars0, JSON0, JSON) :-
444 append(ResVars0, ResVars1),
445 sort(ResVars1, ResVars),
446 subtract(VarNames0, ResVars, VarNames),
447 add_projection(VarNames, JSON0, JSON).
448
449
457
458binding_to_html(ID, binding(Vars,Term,Substitutions), JSON) :-
459 JSON0 = json{variables:Vars, value:HTMLString},
460 term_html_string(Term, Vars, ID, HTMLString, [priority(699)]),
461 ( Substitutions == []
462 -> JSON = JSON0
463 ; maplist(subst_to_html(ID), Substitutions, HTMLSubst),
464 JSON = JSON0.put(substitutions, HTMLSubst)
465 ).
466
473
474term_html_string(Term, Vars, Module, HTMLString, Options) :-
475 setting(write_options, WOptions),
476 merge_options(WOptions,
477 [ quoted(true),
478 numbervars(true),
479 module(Module)
480 | Options
481 ], WriteOptions),
482 phrase(term_html(Term, Vars, WriteOptions), Tokens),
483 with_output_to(string(HTMLString), print_html(Tokens)).
484
494
495:- multifile binding_term//3.
496
497term_html(Term, Vars, WriteOptions) -->
498 { nonvar(Term) },
499 binding_term(Term, Vars, WriteOptions),
500 !.
501term_html(Term, _Vars, WriteOptions) -->
502 term(Term, WriteOptions).
503
508
509subst_to_html(ID, '$VAR'(Name)=Value, json{var:Name, value:HTMLString}) :-
510 !,
511 term_html_string(Value, [Name], ID, HTMLString, [priority(699)]).
512subst_to_html(_, Term, _) :-
513 assertion(Term = '$VAR'(_)).
514
515
519
520map_output(ID, message(Term, Kind, HTMLString, Src), JSON) :-
521 atomic(HTMLString),
522 !,
523 JSON0 = json{event:output, id:ID, message:Kind, data:HTMLString},
524 pengines:add_error_details(Term, JSON0, JSON1),
525 ( Src = File:Line,
526 \+ JSON1.get(location) = _
527 -> JSON = JSON1.put(_{location:_{file:File, line:Line}})
528 ; JSON = JSON1
529 ).
530map_output(ID, Term, json{event:output, id:ID, data:Data}) :-
531 ( atomic(Term)
532 -> Data = Term
533 ; is_dict(Term, json),
534 ground(json) 535 -> Data = Term
536 ; term_string(Term, Data)
537 ).
538
539
540 543
544:- multifile
545 sandbox:safe_primitive/1, 546 sandbox:safe_meta/2. 547
548sandbox:safe_primitive(pengines_io:pengine_listing(_)).
549sandbox:safe_primitive(pengines_io:pengine_nl).
550sandbox:safe_primitive(pengines_io:pengine_print(_)).
551sandbox:safe_primitive(pengines_io:pengine_write(_)).
552sandbox:safe_primitive(pengines_io:pengine_write_canonical(_)).
553sandbox:safe_primitive(pengines_io:pengine_write_term(_,_)).
554sandbox:safe_primitive(pengines_io:pengine_writeln(_)).
555sandbox:safe_primitive(pengines_io:pengine_writeq(_)).
556sandbox:safe_primitive(pengines_io:pengine_portray_clause(_)).
557sandbox:safe_primitive(system:write_term(_,_)).
558sandbox:safe_primitive(system:prompt(_,_)).
559sandbox:safe_primitive(system:statistics(_,_)).
560
561sandbox:safe_meta(pengines_io:pengine_format(Format, Args), Calls) :-
562 sandbox:format_calls(Format, Args, Calls).
563
564
565 568
573
574pengine_io_predicate(writeln(_)).
575pengine_io_predicate(nl).
576pengine_io_predicate(flush_output).
577pengine_io_predicate(format(_)).
578pengine_io_predicate(format(_,_)).
579pengine_io_predicate(read(_)).
580pengine_io_predicate(write_term(_,_)).
581pengine_io_predicate(write(_)).
582pengine_io_predicate(writeq(_)).
583pengine_io_predicate(display(_)).
584pengine_io_predicate(print(_)).
585pengine_io_predicate(write_canonical(_)).
586pengine_io_predicate(listing).
587pengine_io_predicate(listing(_)).
588pengine_io_predicate(portray_clause(_)).
589
590term_expansion(pengine_io_goal_expansion(_,_),
591 Clauses) :-
592 findall(Clause, io_mapping(Clause), Clauses).
593
594io_mapping(pengine_io_goal_expansion(Head, Mapped)) :-
595 pengine_io_predicate(Head),
596 Head =.. [Name|Args],
597 atom_concat(pengine_, Name, BodyName),
598 Mapped =.. [BodyName|Args].
599
600pengine_io_goal_expansion(_, _).
601
602
603 606
607:- if(current_predicate(open_prolog_stream/4)).
608:- public
609 stream_write/2,
610 stream_read/2,
611 stream_close/1.
612
613stream_write(_Stream, Out) :-
614 send_html(pre(class(console), Out)).
615stream_read(_Stream, Data) :-
616 prompt(Prompt, Prompt),
617 pengine_input(_{type:console, prompt:Prompt}, Data).
618stream_close(_Stream).
619
627
628pengine_bind_user_streams :-
629 Err = Out,
630 open_prolog_stream(pengines_io, write, Out, []),
631 set_stream(Out, buffer(line)),
632 open_prolog_stream(pengines_io, read, In, []),
633 set_stream(In, alias(user_input)),
634 set_stream(Out, alias(user_output)),
635 set_stream(Err, alias(user_error)),
636 set_stream(In, alias(current_input)),
637 set_stream(Out, alias(current_output)),
638 thread_at_exit(close_io(In, Out)).
639
640close_io(In, Out) :-
641 close(In, [force(true)]),
642 close(Out, [force(true)]).
643:- else.
644
645pengine_bind_user_streams.
646
647:- endif.
648
649
654
655pengine_bind_io_to_html(Module) :-
656 forall(pengine_io_predicate(Head),
657 bind_io(Head, Module)),
658 pengine_bind_user_streams.
659
660bind_io(Head, Module) :-
661 prompt(_, ''),
662 redefine_system_predicate(Module:Head),
663 functor(Head, Name, Arity),
664 Head =.. [Name|Args],
665 atom_concat(pengine_, Name, BodyName),
666 Body =.. [BodyName|Args],
667 assertz(Module:(Head :- Body)),
668 compile_predicates([Module:Name/Arity]).