# HG changeset patch # User Steve Losh # Date 1442428630 0 # Node ID 81c5342870b657731ba15b153b7a53129c98df69 # Parent 800afac9d63a89abe99aa2d1d23c597a7059ba6c Start scratching some things out diff -r 800afac9d63a -r 81c5342870b6 nrepl.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nrepl.lisp Wed Sep 16 18:37:10 2015 +0000 @@ -0,0 +1,127 @@ +; (in-package :nrepl) + +(ql:quickload "bencode") +(ql:quickload "usocket") +(ql:quickload "flexi-streams") +(ql:quickload "bordeaux-threads") + + +;;;; Utilities ---------------------------------------------------------------- +(defun make-hash (&rest keyvals) + (do ((h (make-hash-table :test #'equal)) + (kvs keyvals (cddr kvs))) + ((not kvs) h) + (setf (gethash (first kvs) h) (second kvs)))) + +(defmethod print-object ((object hash-table) stream) + (format stream "#HASH{~{~{(~a : ~a)~}~^ ~}}" + (loop for key being the hash-keys of object + using (hash-value value) + collect (list key value)))) + + +;;;; Sockets ------------------------------------------------------------------ +(defvar *server-thread* nil) +(defvar *socket* nil) + +(defun get-stream (sock) + "Make a flexi stream of the kind bencode wants from the socket." + (flex:make-flexi-stream + (usocket:socket-stream sock) + :external-format :utf-8)) + + +(defun accept-connections (server-socket) + "Accept connections to the server and spawn threads to handle each." + (loop + (format t "Waiting for a connection...~%") + (let ((client-socket (usocket:socket-accept server-socket + :element-type '(unsigned-byte 8)))) + (format t "Connection received. Spinning up handler thread...~%") + (bt:make-thread + (lambda () + (unwind-protect + (let ((*socket* client-socket)) + (handler)) + (format t "Closing client connection...~%") + (usocket:socket-close client-socket))) + :name "NREPL Connection Handler")))) + +(defun start-server (address port) + "Fire up a server thread that will listen for connections." + (format t "Starting server...~%") + (let ((socket (usocket:socket-listen address port :reuse-address t))) + (setf *server-thread* + (bt:make-thread + (lambda () + (unwind-protect + (accept-connections socket) + (format t "Closing server socket...~%") + (usocket:socket-close socket))) + :name (format nil "NREPL Server (~a/~a)" address port))))) + +(defun stop-server () + "Kill the server thread, if it exists." + (let ((s (shiftf *server-thread* nil))) + (when s + (bt:destroy-thread s)))) + + +(defun write-object (o) + "Write an object (bencoded) to *socket*." + (bencode:encode o (get-stream *socket*)) + (force-output (get-stream *socket*))) + +(defun read-object () + "Read an object (and bdecode it) from *socket*." + (bencode:decode (get-stream *socket*))) + + +;;;; NREPL -------------------------------------------------------------------- +(defun respond (message response) + (funcall (gethash "transport" message) response)) + +(defmacro handle-op (message op fallback &rest body) + `(if (equal ,op (gethash "op" ,message)) + (progn ,@body) + (funcall ,fallback ,message))) + +(defun handle-base (message) + (respond message (make-hash "status" "unknown-op"))) + +(defun wrap-time (h) + (lambda (message) + (handle-op + message "time?" h + (respond message (make-hash "status" "done" + "time" (get-universal-time)))))) + +(defun wrap-eval (h) + (lambda (message) + (handle-op + message "eval" h + (let ((code (gethash "code" message))) + (respond message + (make-hash "status" "done" + "result" (eval (read-from-string code)))))))) + +(defparameter *handler* + (wrap-eval (wrap-time #'handle-base))) + +(defun handle (message) + (format t "Handling message:~%~A~%~%" message) + (funcall *handler* message)) + +(defun handle-message () + (let ((message (read-object))) + (setf (gethash "transport" message) #'write-object) + (handle message))) + +(defun handler () + (loop (handle-message))) + + +;;;; Scratch ------------------------------------------------------------------ +; (connect) +; (handle-message) +; (start-server "localhost" 8675) diff -r 800afac9d63a -r 81c5342870b6 sender.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sender.py Wed Sep 16 18:37:10 2015 +0000 @@ -0,0 +1,28 @@ +from __future__ import print_function +import bencode +import socket + + +ADDRESS = '127.0.0.1' +PORT = 8675 + +def repl(): + sock = socket.socket() + sock.connect((ADDRESS, PORT)) + sock.settimeout(0.5) + + while True: + data = raw_input("> ") + if data.strip(): + sock.send(bencode.bencode(eval(data))) + + try: + incoming = sock.recv(4096) + if incoming: + print(bencode.bdecode(incoming)) + except socket.timeout: + pass + + +if __name__ == '__main__': + repl()