81c5342870b6

Start scratching some things out
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Wed, 16 Sep 2015 18:37:10 +0000
parents 800afac9d63a
children 549e41a92839
branches/tags (none)
files nrepl.lisp sender.py

Changes

--- /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)
--- /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()