-
Notifications
You must be signed in to change notification settings - Fork 4
/
top.pl
120 lines (98 loc) · 3.3 KB
/
top.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
:- set_stream(user_input, tty(false)).
:- set_stream(user_input, encoding(utf8)).
:- set_stream(user_output, tty(false)).
:- set_stream(user_output, encoding(utf8)).
:- use_module(library(http/json)).
:- use_module(library(debug)).
:- debug(node).
% Toplevel executor. Implemented as a
% failure-driven loop. Redirects stream
% from user_output to stderr.
loop:-
current_output(Out),
set_stream(user_error, alias(user_output)),
set_output(user_error),
repeat,
catch(read_execute_query(Out), Error, true),
( var(Error)
-> true
; export_error(Out, Error)),
fail.
read_execute_query(Out):-
( at_end_of_stream(user_input)
-> halt(0)
; read_line_to_string(user_input, String),
debug(node, 'got input: ~w', [String]),
atom_json_dict(String, Dict, []), !,
( execute_query(Out, Dict.query)
; export_failure(Out) )).
% Executes the given query. Reports solutions
% through a failure-driven loop which is conditionally
% cut once the user requests to close the query.
execute_query(Out, Query):-
debug(node, 'executing query: ~w', [Query]),
atom_to_term(Query, Goal, Bindings), !,
call(Goal),
export_bindings(Out, Bindings),
( wait_want_next
-> fail
; !, fail).
wait_want_next:-
read_line_to_string(user_input, String), !,
debug(node, 'got input: ~w', [String]),
atom_json_dict(String, Dict, []),
Dict.action = "next".
export_error(Out, Error):-
format(string(String), '~w', [Error]),
debug(node, 'exporting error ~w', [Error]),
export_dict(Out, _{
status: "error",
error: String
}).
export_bindings(Out, Bindings):-
debug(node, 'exporting bindings: ~w', [Bindings]),
maplist(to_binding_pair, Bindings, Pairs),
dict_pairs(Dict, _, Pairs),
export_dict(Out, _{
status: "success",
bindings: Dict
}).
export_failure(Out):-
debug(node, 'exporting failure', []),
export_dict(Out, _{ status: "fail" }).
export_dict(Out, Dict):-
atom_json_dict(String, Dict, [width(0), as(string)]),
debug(node, 'writing to output: ~w', [String]),
writeln(Out, String),
flush_output(Out).
to_binding_pair(Name=Value, Name-Exported):-
export_term(Value, Exported).
% Exports term into a suitable form to transport
% over JSON.
export_term(Variable, Exported):-
var(Variable), !,
format(string(String), '~w', [Variable]),
Exported = _{ variable: String }.
export_term([], "[]"):- !.
export_term(Atomic, Atomic):-
atomic(Atomic), !.
export_term([Head|Tail], Exported):- !,
export_term(Head, HeadExported),
export_term(Tail, TailExported),
Exported = _{ head: HeadExported, tail: TailExported }.
export_term(Dict, Exported):-
is_dict(Dict), !,
dict_pairs(Dict, Tag, Pairs),
maplist(export_dict_pair, Pairs, ExportedPairs),
dict_pairs(ContentExported, _, ExportedPairs),
export_term(Tag, ExportedTag),
Exported = _{ tag: ExportedTag, content: ContentExported }.
export_term(Compound, Exported):-
compound(Compound), !,
Compound =.. [Name|Args],
maplist(export_term, Args, ExportedArgs),
Exported = _{ name: Name, args: ExportedArgs }.
export_term(Term, _):-
throw(error(cannot_export(Term), _)).
export_dict_pair(Key-Value, Key-ExportedValue):-
export_term(Value, ExportedValue).