This repository has been archived by the owner on Jul 18, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathnrepl.ml
204 lines (173 loc) · 6.66 KB
/
nrepl.ml
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
open Async.Std
open Core.Std
type msg_actions =
{ out : string -> unit;
err : string -> unit;
ex : string -> unit;
value : string -> unit;
eval_error : Async_unix.Reader.t
* Async_unix.Writer.t
* (string, msg_actions) Core.Std.Hashtbl.t
-> (string * Bencode.t) list -> unit;
after_message : unit -> unit;
}
let do_nothing _ = ()
let rec eval_message ?actions code ns session =
([("session", session);
("op", "eval");
("id", "eval-" ^ (Uuid.to_string (Uuid.create ())));
("ns", ns);
("code", code ^ "\n")],
match actions with
None -> default_actions
| Some actions -> actions)
and default_actions =
{ out = Printf.printf "%s%!";
err = Printf.eprintf "%s%!";
value = do_nothing;
ex = do_nothing;
eval_error = (fun (r,w,p) resp ->
eprintf "default after_message\n%!";
());
after_message = do_nothing
}
let stdin_message input session =
let uuid = Uuid.to_string (Uuid.create ()) in
([("op", "stdin");
("id", uuid);
("stdin", input ^ "\n");
("session", session)],
default_actions)
let quiet_actions =
{ default_actions with out = do_nothing; }
let exit_actions =
{ default_actions with ex = fun e -> Pervasives.exit 1 }
let print_all =
{ default_actions with value = Printf.printf "%s\n%!"; }
let buffer_size = (1024 * 16)
let debug out =
match Sys.getenv "DEBUG" with
| Some _ -> Printf.printf "%s\n%!" out
| None -> ()
let rec convert_message message converted =
match message with
| (k, v) :: tl -> convert_message tl ((k, Bencode.String(v)) :: converted)
| [] -> converted
let current_msg_id = ref ""
let send w pending (message,actions) =
let converted = Bencode.Dict(convert_message message []) in
let out = Bencode.marshal converted in
debug ("-> " ^ out);
Writer.write w out;
match List.Assoc.find message "id" with
| Some id ->
Hashtbl.set pending ~key:id ~data:actions;
current_msg_id := id
| None -> Printf.eprintf " Sending message without id!\n%!"
(* Return the id from the response. Returns an Option string. *)
let response_id resp =
match (List.Assoc.find resp "id") with
| Some Bencode.String id -> Some id
| None | Some _ -> None
(* Return the session from the response. Returns an Option string. *)
let response_session resp =
match (List.Assoc.find resp "session") with
| Some Bencode.String session -> Some session
| None | Some _ -> None
(* Return the actions associated with the specified response *)
let response_actions pending resp =
let lookup_actions id = match Hashtbl.find pending id with
| Some actions -> actions
| None -> default_actions in
match response_id resp with
| Some id -> lookup_actions id
| None -> default_actions
(* Remove an id from the pending message table *)
let remove_pending pending id =
debug ("-p " ^ String.concat ~sep:" " (Hashtbl.keys pending));
match id with
| Some id -> if Hashtbl.mem pending id then
Hashtbl.remove pending id
| None -> eprintf " Unknown message id.\n%!"
(* Return a list of pending request ids *)
let pending_ids pending =
Hashtbl.keys pending
let rec loop (r,w,p) handler buffer partial =
let parse_single contents =
try let parsed = Bencode.parse contents in
let bytes_parsed = String.length (Bencode.marshal parsed) in
let total_bytes = String.length contents in
let leftover_length = total_bytes - bytes_parsed in
let leftover = String.sub contents bytes_parsed leftover_length in
(Some parsed, leftover)
with
| _ -> (None, contents) in
let rec handle_responses handler contents =
match parse_single contents with
| Some Bencode.Dict parsed, leftover -> handler (r,w,p) contents parsed;
debug ("<- " ^ (Bencode.marshal (Bencode.Dict parsed)));
handle_responses handler leftover
| Some parsed, leftover -> Printf.printf "Unexpected %s: %s | %s \n%!"
(Bencode.string_of_type parsed) (Bencode.marshal parsed) leftover;
handle_responses handler leftover
| None, leftover -> leftover in
let parse_response handler buffer partial resp =
match resp with
| `Eof -> Pervasives.exit 0
| `Ok bytes_read -> let just_read = String.sub buffer 0 bytes_read in
let partial =
handle_responses handler (partial ^ just_read) in
loop (r,w,p) handler buffer partial in
Reader.read r buffer >>= parse_response handler buffer partial
let get_session buffer resp =
let no_session () = Printf.eprintf "No session!\n"; Pervasives.exit 0 in
match resp with
| `Eof -> no_session ()
| `Ok bytes_read -> match Bencode.parse (String.sub buffer 0 bytes_read) with
| Bencode.Int _ | Bencode.String _ | Bencode.List _ -> no_session ()
| Bencode.Dict(d) -> match List.Assoc.find d "new-session" with
| Some Bencode.String(session) -> session
| Some _ | None -> no_session ()
let interrupt_message session id =
([("session", session);
("op", "interrupt");
("id", "interrupt-" ^ (Uuid.to_string (Uuid.create ())));
("interrupt-id", id)],
quiet_actions)
let interrupt session w p _ =
match Hashtbl.keys p with
| ["init"] | [] -> Pervasives.exit 0
| _ -> send w p (interrupt_message session !current_msg_id)
let register_interrupt session w p =
Caml.Sys.signal Caml.Sys.sigint (Caml.Sys.Signal_handle (interrupt session w p))
let rec send_messages (w,p) messages session =
match messages with
| message :: tail ->
message session |> send w p;
send_messages (w,p) tail session
| [] -> ()
let defer_send_messages (w,p) messages session =
let f ivar = Ivar.fill ivar (send_messages (w,p) messages session) in
Deferred.create f
let initiate_session (s,r,w,p) buffer =
Reader.read r buffer
>>| fun resp -> (s,r,w,p,get_session buffer resp)
let connect host port messages handler =
let buffer = (String.create buffer_size) in
let pending = String.Table.create () in
Tcp.connect (Tcp.to_host_and_port host port)
>>= (fun (s, r, w) ->
send w pending ([("op", "clone"); ("id", "init")], quiet_actions);
initiate_session (s,r,w,pending) buffer
>>= (fun (_, r, w, p, session) ->
ignore (register_interrupt session w p);
ignore (defer_send_messages (w,p) messages session);
loop (r,w,p) handler buffer ""))
(* Create a new session *)
let new_session host port messages handler =
try_with (fun () -> connect host port messages handler)
>>| function
| Ok () -> ()
| Error _ ->
eprintf "Could not connect on port %i.\n%!" port;
Pervasives.exit 111