|
|
- ;;;; swank-goo.goo --- Swank server for GOO
- ;;;
- ;;; Copyright (C) 2005 Helmut Eller
- ;;;
- ;;; This file is licensed under the terms of the GNU General Public
- ;;; License as distributed with Emacs (press C-h C-c to view it).
-
- ;;;; Installation
- ;;
- ;; 1. Add something like this to your .emacs:
- ;;
- ;; (setq slime-lisp-implementations
- ;; '((goo ("g2c") :init goo-slime-init)))
- ;;
- ;; (defun goo-slime-init (file _)
- ;; (format "%S\n%S\n"
- ;; `(set goo/system:*module-search-path*
- ;; (cat '(".../slime/contrib/")
- ;; goo/system:*module-search-path*))
- ;; `(swank-goo:start-swank ,file)))
- ;;
- ;; 2. Start everything with M-- M-x slime goo
- ;;
-
- ;;;; Code
-
- (use goo)
- (use goo/boot)
- (use goo/x)
- (use goo/io/port)
- (use goo/io/write)
- (use goo/eval)
- (use goo/system)
- (use goo/conditions)
- (use goo/fun)
- (use goo/loc)
- (use goo/chr)
- (use eval/module)
- (use eval/ast)
- (use eval/g2c)
-
- ;;;; server setup
-
- (df create-server (port-number) (setup-server port-number announce-port))
-
- (df start-swank (port-file)
- (setup-server 0 (fun (s) (write-port-file (%local-port s) port-file))))
-
- (df setup-server (port-number announce)
- (let ((s (create-socket port-number)))
- (fin (seq
- (announce s)
- (let ((c (accept s)))
- ;;(post "connection: %s" c)
- (fin (serve-requests c)
- (%close (@fd c)))))
- (post "closing socket: %s" s)
- (%close s))))
-
- (df announce-port (socket)
- (post "Listening on port: %d\n" (%local-port socket)))
-
- (df write-port-file (port-number filename)
- (with-port (file (open <file-out-port> filename))
- (msg file "%d\n" port-number)))
-
- (dc <slime-toplevel> (<restart>))
-
- (dc <connection> (<any>))
- (dp @socket (<connection> => <port>))
- (dp @in (<connection> => <in-port>))
- (dp @out (<connection> => <out-port>))
-
- (dv emacs-connection|(t? <connection>) #f)
-
- (df serve-requests (socket)
- (dlet ((emacs-connection (new <connection>
- @socket socket
- @out (new <slime-out-port> @socket socket)
- @in (new <slime-in-port> @socket socket))))
- (dlet ((out (@out emacs-connection))
- (in (@in emacs-connection)))
- (while #t
- (simple-restart
- <slime-toplevel> "SLIME top-level"
- (fun () (process-next-event socket)))))))
-
- (d. <nil> (t= 'nil))
- (d. t #t)
- (d. cons pair)
-
- (dv tag-counter|<int> 0)
-
- (df process-next-event (port) (dispatch-event (decode-message port) port))
-
- (df dispatch-event (event port)
- ;; (post "%=\n" event)
- (match event
- ((:emacs-rex ,form ,package ,_thread-id ,id)
- (eval-for-emacs form package port id))
- ((:read-string ,_)
- (def tag (incf tag-counter))
- (encode-message `(:read-string ,_ ,tag) port)
- (rep loop ()
- (match (decode-message port)
- ((:emacs-return-string ,_ ,rtag ,str)
- (assert (= tag rtag) "Unexpected reply tag: %d" rtag)
- str)
- ((,@evt)
- (try-recover
- (fun () (dispatch-event evt port))
- (fun () (encode-message `(:read-aborted ,_ ,tag) port)))
- (loop)))))
- ((:emacs-return-string ,_ ,rtag ,str)
- (error "Unexpected event: %=" event))
- ((,@_) (encode-message event port))))
-
- (dc <eval-context> (<any>))
- (dp @module (<eval-context> => <module>))
- (dp @id (<eval-context> => <int>))
- (dp @port (<eval-context> => <port>))
- (dp @prev (<eval-context> => (t? <eval-context>)))
-
- ;; should be ddv
- (dv eval-context|(t? <eval-context>) #f)
-
- (df buffer-module () (@module eval-context))
-
- (df eval-for-emacs (form|<lst> package|(t+ <str> <nil>) port id|<int>)
- (try-recover
- (fun ()
- (try <condition> debugger-hook
- (dlet ((eval-context (new <eval-context>
- @module (find-buffer-module package) @id id
- @port port @prev eval-context)))
- (def result (eval (frob-form-for-eval form) 'swank-goo))
- (force-out out)
- (dispatch-event `(:return (:ok ,result) ,id) port))))
- (fun () (dispatch-event `(:return (:abort) ,id) port))))
-
- (dm find-buffer-module (name|<str> => <module>)
- (or (elt-or (all-modules) (as-sym name) #f)
- (find-buffer-module 'nil)))
-
- (dm find-buffer-module (name|<nil> => <module>) default-module)
-
- (dv default-module|<module> (runtime-module 'goo/user))
-
- (d. slimefuns (fab <tab> 100))
-
- (ds defslimefun (,name ,args ,@body)
- `(set (elt slimefuns ',name)
- (df ,(cat-sym 'swank@ name) ,args ,@body)))
-
- (df slimefun (name)
- (or (elt-or slimefuns name #f)
- (error "Undefined slimefun: %=" name)))
-
- ;; rewrite (swank:foo ...) to ((slimefun 'foo) ...)
- (df frob-form-for-eval (form)
- (match form
- ((,op ,@args)
- (match (map as-sym (split (sym-name op) #\:))
- ((swank ,name)
- `((slimefun ',name) ,@args))))))
-
- ;;;; debugger
-
- (dc <sldb-context> (<any>))
- (dp @level (<sldb-context> => <int>))
- (dp @top-frame (<sldb-context> => <lst>))
- (dp @restarts (<sldb-context> => <lst>))
- (dp @condition (<sldb-context> => <condition>))
- (dp @eval-context (<sldb-context> => (t? <eval-context>)))
-
- (dv sldb-context|(t? <sldb-context>) #f)
-
- (df debugger-hook (c|<condition> resume)
- (let ((tf (find-top-frame 'debugger-hook 2))
- (rs (compute-restarts c))
- (l (if sldb-context (1+ (@level sldb-context)) 1)))
- (cond ((> l 10) (emergency-abort c))
- (#t
- (dlet ((sldb-context (new <sldb-context>
- @level l @top-frame tf
- @restarts rs @condition c
- @eval-context eval-context)))
- (let ((bt (compute-backtrace tf 0 10)))
- (force-out out)
- (dispatch-event `(:debug 0 ,l
- ,@(debugger-info c rs bt eval-context))
- (@port eval-context))
- (sldb-loop l (@port eval-context))))))))
-
- (df emergency-abort (c)
- (post "Maximum debug level reached aborting...\n")
- (post "%s\n" (describe-condition c))
- (do-stack-frames (fun (f args) (msg out " %= %=\n" f args)))
- (invoke-handler-interactively (find-restart <slime-toplevel>) in out))
-
- (df sldb-loop (level port)
- (fin (while #t
- (dispatch-event `(:debug-activate 0 ,level) port)
- (simple-restart
- <restart> (msg-to-str "Return to SLDB level %s" level)
- (fun () (process-next-event port))))
- (dispatch-event `(:debug-return 0 ,level nil) port)))
-
- (defslimefun backtrace (start|<int> end|(t+ <int> <nil>))
- (backtrace-for-emacs
- (compute-backtrace (@top-frame sldb-context)
- start
- (if (isa? end <int>) end #f))))
-
- (defslimefun throw-to-toplevel ()
- (invoke-handler-interactively (find-restart <slime-toplevel>) in out))
-
- (defslimefun invoke-nth-restart-for-emacs (sldb-level|<int> n|<int>)
- (when (= (@level sldb-context) sldb-level)
- (invoke-handler-interactively (elt (@restarts sldb-context) n) in out)))
-
- (defslimefun debugger-info-for-emacs (start end)
- (debugger-info (@condition sldb-context)
- (@restarts sldb-context)
- (compute-backtrace (@top-frame sldb-context)
- start
- (if (isa? end <int>) end #f))))
-
- (defslimefun frame-locals-and-catch-tags (frame-idx)
- (def frame (nth-frame frame-idx))
- (list
- (map-keyed (fun (i name)
- (lst ':name (sym-name name) ':id 0
- ':value (safe-write-to-string (frame-var-value frame i))))
- (frame-var-names frame))
- '()))
-
- (defslimefun inspect-frame-var (frame-idx var-idx)
- (reset-inspector)
- (inspect-object (frame-var-value (nth-frame frame-idx) var-idx)))
-
- (defslimefun inspect-current-condition ()
- (reset-inspector)
- (inspect-object (@condition sldb-context)))
-
- (defslimefun frame-source-location (frame-idx)
- (match (nth-frame frame-idx)
- ((,f ,@_)
- (or (emacs-src-loc f)
- `(:error ,(msg-to-str "No src-loc available for: %s" f))))))
-
- (defslimefun eval-string-in-frame (string frame-idx)
- (def frame (nth-frame frame-idx))
- (let ((names (frame-var-names frame))
- (values (frame-var-values frame)))
- (write-to-string
- (app (eval `(fun ,names ,(read-from-string string))
- (module-name (buffer-module)))
- values))))
-
- (df debugger-info (condition restarts backtrace eval-context)
- (lst `(,(try-or (fun () (describe-condition condition)) "<...>")
- ,(cat " [class: " (class-name-str condition) "]")
- ())
- (restarts-for-emacs restarts)
- (backtrace-for-emacs backtrace)
- (pending-continuations eval-context)))
-
- (df backtrace-for-emacs (backtrace)
- (map (fun (f)
- (match f
- ((,idx (,f ,@args))
- (lst idx (cat (if (fun-name f)
- (sym-name (fun-name f))
- (safe-write-to-string f))
- (safe-write-to-string args))))))
- backtrace))
-
- (df restarts-for-emacs (restarts)
- (map (fun (x) `(,(sym-name (class-name (%handler-condition-type x)))
- ,(describe-restart x)))
- restarts))
-
- (df describe-restart (restart)
- (describe-handler (%handler-info restart) (%handler-condition-type restart)))
-
- (df compute-restarts (condition)
- (packing (%do-handlers-of-type <restart> (fun (c) (pack c)))))
-
- (df find-restart (type)
- (esc ret
- (%do-handlers-of-type type ret)
- #f))
-
- (df pending-continuations (context|(t? <eval-context>))
- (if context
- (pair (@id context) (pending-continuations (@prev context)))
- '()))
-
- (df find-top-frame (fname|<sym> offset|<int>)
- (esc ret
- (let ((top-seen? #f))
- (do-stack-frames (fun (f args)
- (cond (top-seen?
- (cond ((== offset 0)
- (ret (pair f args)))
- (#t (decf offset))))
- ((== (fun-name f) fname)
- (set top-seen? #t))))))))
-
- (df compute-backtrace (top-frame start|<int> end)
- (packing
- (esc break
- (do-user-frames (fun (idx f args)
- (when (and end (<= end idx))
- (break #f))
- (when (<= start idx)
- (pack (lst idx (pair f args)))))
- top-frame))))
-
- (df nth-frame (n|<int>)
- (esc ret
- (do-user-frames
- (fun (idx f args)
- (when (= idx n)
- (ret (pair f args))))
- (@top-frame sldb-context))))
-
- (df frame-var-value (frame var-idx)
- (match frame
- ((,f ,@args)
- (def sig (fun-sig f))
- (def arity (sig-arity sig))
- (def nary? (sig-nary? sig))
- (cond ((< var-idx arity) (elt args var-idx))
- (nary? (sub* args arity))))))
-
- (df frame-var-names (frame)
- (match frame
- ((,f ,@_) (fun-info-names (fun-info f)))))
-
- (df frame-var-values (frame)
- (map (curry frame-var-value frame) (keys (frame-var-names frame))))
-
- (df do-user-frames (f|<fun> top-frame)
- (let ((idx -1)
- (top-seen? #f))
- (do-stack-frames
- (fun (ffun args)
- (cond (top-seen?
- (incf idx)
- (f idx ffun (rev args)))
- ((= (pair ffun args) top-frame)
- (set top-seen? #t)))))))
-
- ;;;; Write some classes a little less verbose
-
- ;; (dm recurring-write (port|<out-port> x d|<int> recur|<fun>)
- ;; (msg port "#{%s &%s}" (class-name-str x)
- ;; (num-to-str-base (address-of x) 16)))
-
- (dm recurring-write (port|<out-port> x|<module> d|<int> recur|<fun>)
- (msg port "#{%s %s}" (class-name-str x) (module-name x)))
-
- (dm recurring-write (port|<out-port> x|<module-binding> d|<int> recur|<fun>)
- (msg port "#{%s %s}" (class-name-str x) (binding-name x)))
-
- (dm recurring-write (port|<out-port> x|<tab> d|<int> recur|<fun>)
- (msg port "#{%s %s}" (class-name-str x) (len x)))
-
- (dm recurring-write (port|<out-port> x|<static-global-environment>
- d|<int> recur|<fun>)
- (msg port "#{%s}" (class-name-str x)))
-
- (dm recurring-write (port|<out-port> x|<regular-application>
- d|<int> recur|<fun>)
- (msg port "#{%s}" (class-name-str x)))
-
- (dm recurring-write (port|<out-port> x|<src-loc> d|<int> recur|<fun>)
- (msg port "#{%s %s:%=}" (class-name-str x)
- (src-loc-file x) (src-loc-line x)))
-
- ;;;; Inspector
-
- (dc <inspector> (<any>))
- (dp! @object (<inspector> => <any>))
- (dp! @parts (<inspector> => <vec>) (new <vec>))
- (dp! @stack (<inspector> => <lst>) '())
-
- (dv inspector #f)
-
- (defslimefun init-inspector (form|<str>)
- (reset-inspector)
- (inspect-object (str-eval form (buffer-module))))
-
- (defslimefun quit-inspector () (reset-inspector) 'nil)
-
- (defslimefun inspect-nth-part (n|<int>)
- (inspect-object (elt (@parts inspector) n)))
-
- (defslimefun inspector-pop ()
- (cond ((<= 2 (len (@stack inspector)))
- (popf (@stack inspector))
- (inspect-object (popf (@stack inspector))))
- (#t 'nil)))
-
- (df reset-inspector () (set inspector (new <inspector>)))
-
- (df inspect-object (o)
- (set (@object inspector) o)
- (set (@parts inspector) (new <vec>))
- (pushf (@stack inspector) o)
- (lst ':title (safe-write-to-string o) ; ':type (class-name-str o)
- ':content (inspector-content
- `("class: " (:value ,(class-of o)) "\n"
- ,@(inspect o)))))
-
- (df inspector-content (content)
- (map (fun (part)
- (case-by part isa?
- ((<str>) part)
- ((<lst>)
- (match part
- ((:value ,o ,@str)
- `(:value ,@(if (nul? str)
- (lst (safe-write-to-string o))
- str)
- ,(assign-index o)))))
- (#t (error "Bad inspector content: %=" part))))
- content))
-
- (df assign-index (o)
- (pushf (@parts inspector) o)
- (1- (len (@parts inspector))))
-
- (dg inspect (o))
-
- ;; a list of dangerous functions
- (d. getter-blacklist (lst fun-code fun-env class-row))
-
- (dm inspect (o)
- (join (map (fun (p)
- (let ((getter (prop-getter p)))
- `(,(sym-name (fun-name getter)) ": "
- ,(cond ((mem? getter-blacklist getter) "<...>")
- ((not (prop-bound? o getter)) "<unbound>")
- (#t (try-or (fun () `(:value ,(getter o)))
- "<...>"))))))
- (class-props (class-of o)))
- '("\n")))
-
- (dm inspect (o|<seq>)
- (join (packing (do-keyed (fun (pos val)
- (pack `(,(num-to-str pos) ": " (:value ,val))))
- o))
- '("\n")))
-
- (dm inspect (o|<tab>)
- (join (packing (do-keyed (fun (key val)
- (pack `((:value ,key) "\t: " (:value ,val))))
- o))
- '("\n")))
-
- ;; inspecting the env of closures is broken
- ;; (dm inspect (o|<met>)
- ;; (cat (sup o)
- ;; '("\n")
- ;; (if (%fun-env? o)
- ;; (inspect (packing (for ((i (below (%fun-env-len o))))
- ;; (pack (%fun-env-elt o i)))))
- ;; '())))
- ;;
- ;; (df %fun-env? (f|<met> => <log>) #eb{ FUNENV($f) != $#f })
- ;; (df %fun-env-len (f|<met> => <int>) #ei{ ((ENV)FUNENV ($f))->size })
- ;; (df %fun-env-elt (f|<met> i|<int> => <any>) #eg{ FUNENVGET($f, @i) })
-
- ;;;; init
-
- (defslimefun connection-info ()
- `(:pid
- ,(process-id) :style nil
- :lisp-implementation (:type "GOO" :name "goo"
- :version ,(%lookup '*goo-version* 'eval/main))
- :machine (:instance "" :type "" :version "")
- :features ()
- :package (:name "goo/user" :prompt "goo/user")))
-
- (defslimefun quit-lisp () #ei{ exit (0),0 })
-
- (defslimefun set-default-directory (dir|<str>) #ei{ chdir(@dir) } dir)
-
- ;;;; eval
-
- (defslimefun ping () "PONG")
-
- (defslimefun create-repl (_)
- (let ((name (sym-name (module-name (buffer-module)))))
- `(,name ,name)))
-
- (defslimefun listener-eval (string)
- (clear-input in)
- `(:values ,(write-to-string (str-eval string (buffer-module)))))
-
- (defslimefun interactive-eval (string)
- (cat "=> " (write-to-string (str-eval string (buffer-module)))))
-
- (df str-eval (s|<str> m|<module>)
- (eval (read-from-string s) (module-name m)))
-
- (df clear-input (in|<in-port>) (while (ready? in) (get in)))
-
- (dc <break> (<restart>))
-
- (defslimefun simple-break ()
- (simple-restart
- <break> "Continue from break"
- (fun () (sig (new <simple-condition>
- condition-message "Interrupt from Emacs"))))
- 'nil)
-
- (defslimefun clear-repl-results () 'nil)
-
- ;;;; compile
-
- (defslimefun compile-string-for-emacs (string buffer position directory)
- (def start (current-time))
- (def r (g2c-eval (read-from-string string)
- (module-target-environment (buffer-module))))
- (lst (write-to-string r)
- (/ (as <flo> (- (current-time) start)) 1000000.0)))
-
- (defslimefun compiler-notes-for-emacs () 'nil)
-
- (defslimefun filename-to-modulename (filename|<str> => (t+ <str> <nil>))
- (try-or (fun () (sym-name (filename-to-modulename filename))) 'nil))
-
- (df filename-to-modulename (filename|<str> => <sym>)
- (def paths (map pathname-to-components
- (map simplify-filename
- (pick file-exists? *module-search-path*))))
- (def filename (pathname-to-components filename))
- (def moddir (rep parent ((modpath filename))
- (cond ((any? (curry = modpath) paths)
- modpath)
- (#t
- (parent (components-parent-directory modpath))))))
- (def modfile (components-to-pathname (sub* filename (len moddir))))
- (as-sym (sub modfile 0 (- (len modfile) (len *goo-extension*)))))
-
-
- ;;;; Load
-
- (defslimefun load-file (filename)
- (let ((file (cond ((= (sub (rev filename) 0 4) "oog.") filename)
- (#t (cat filename ".goo")))))
- (safe-write-to-string (load-file file (filename-to-modulename file)))))
-
- ;;;; background activities
-
- (defslimefun operator-arglist (op _)
- (try-or (fun ()
- (let ((value (str-eval op (buffer-module))))
- (if (isa? value <fun>)
- (write-to-string value)
- 'nil)))
- 'nil))
-
- ;;;; M-.
-
- (defslimefun find-definitions-for-emacs (name|<str>)
- (match (parse-symbol name)
- ((,sym ,modname)
- (def env (module-target-environment (runtime-module modname)))
- (def b (find-binding sym env))
- (cond (b (find-binding-definitions b))
- (#t 'nil)))))
-
- (df parse-symbol (name|<str> => <lst>)
- (if (mem? name #\:)
- (match (split name #\:)
- ((,module ,name) (lst (as-sym name) (as-sym module))))
- (lst (as-sym name) (module-name (buffer-module)))))
-
- (df find-binding-definitions (b|<binding>)
- (def value (case (binding-kind b)
- (('runtime) (loc-val (binding-locative b)))
- (('global) (let ((box (binding-global-box b)))
- (and box (global-box-value box))))
- (('macro) (binding-info b))
- (#t (error "unknown binding kind %=" (binding-kind b)))))
- (map (fun (o)
- (def loc (emacs-src-loc o))
- `(,(write-to-string (dspec o))
- ,(or loc `(:error "no src-loc available"))))
- (defining-objects value)))
-
- (dm defining-objects (o => <lst>) '())
- (dm defining-objects (o|<fun> => <lst>) (lst o))
- (dm defining-objects (o|<gen> => <lst>) (pair o (fun-mets o)))
-
- (dm emacs-src-loc (o|<fun>)
- (def loc (fun-src-loc o))
- (and loc `(:location (:file ,(simplify-filename
- (find-goo-file-in-path
- (module-name-to-relpath (src-loc-file loc))
- *module-search-path*)))
- (:line ,(src-loc-line loc))
- ())))
-
- (dm dspec (f|<fun>)
- (cond ((fun-name f)
- `(,(if (isa? f <gen>) 'dg 'dm) ,(fun-name f) ,@(dspec-arglist f)))
- (#t f)))
-
- (df dspec-arglist (f|<fun>)
- (map2 (fun (name class)
- (cond ((= class <any>) name)
- ((isa? class <class>)
- `(,name ,(class-name class)))
- (#t `(,name ,class))))
- (fun-info-names (fun-info f))
- (sig-specs (fun-sig f))))
-
- (defslimefun buffer-first-change (filename) 'nil)
-
- ;;;; apropos
-
- (defslimefun apropos-list-for-emacs
- (pattern only-external? case-sensitive? package)
- (def matches (fab <tab> 100))
- (do-all-bindings
- (fun (b)
- (when (finds (binding-name-str b) pattern)
- (set (elt matches
- (cat-sym (binding-name b)
- (module-name (binding-module b))))
- b))))
- (set matches (sort-by (packing-as <vec> (for ((b matches)) (pack b)))
- (fun (x y)
- (< (binding-name x)
- (binding-name y)))))
- (map (fun (b)
- `(:designator
- ,(cat (sym-name (module-name (binding-module b))) ":"
- (binding-name-str b)
- "\tkind: " (sym-name (binding-kind b)))))
- (as <lst> matches)))
-
- (df do-all-bindings (f|<fun>)
- (for ((module (%module-loader-modules (runtime-module-loader))))
- (do f (environment-bindings (module-target-environment module)))))
-
- (dm < (s1|<str> s2|<str> => <log>)
- (let ((l1 (len s1)) (l2 (len s2)))
- (rep loop ((i 0))
- (cond ((= i l1) (~= l1 l2))
- ((= i l2) #f)
- ((< (elt s1 i) (elt s2 i)) #t)
- ((= (elt s1 i) (elt s2 i)) (loop (1+ i)))
- (#t #f)))))
-
- (df %binding-info (name|<sym> module|<sym>)
- (binding-info
- (find-binding
- name (module-target-environment (runtime-module module)))))
-
- ;;;; completion
-
- (defslimefun simple-completions (pattern|<str> package)
- (def matches (lst))
- (for ((b (environment-bindings (module-target-environment (buffer-module)))))
- (when (prefix? (binding-name-str b) pattern)
- (pushf matches b)))
- (def strings (map binding-name-str matches))
- `(,strings ,(cond ((nul? strings) pattern)
- (#t (fold+ common-prefix strings)))))
-
- (df common-prefix (s1|<seq> s2|<seq>)
- (let ((limit (min (len s1) (len s2))))
- (rep loop ((i 0))
- (cond ((or (= i limit)
- (~= (elt s1 i) (elt s2 i)))
- (sub s1 0 i))
- (#t (loop (1+ i)))))))
-
- (defslimefun list-all-package-names (_|...)
- (map sym-name (keys (all-modules))))
-
- (df all-modules () (%module-loader-modules (runtime-module-loader)))
-
- ;;;; Macroexpand
-
- (defslimefun swank-macroexpand-1 (str|<str>)
- (write-to-string
- (%ast-macro-expand (read-from-string str)
- (module-target-environment (buffer-module))
- #f)))
-
- ;;;; streams
-
- (dc <slime-out-port> (<out-port>))
- (dp @socket (<slime-out-port> => <port>))
- (dp! @buf-len (<slime-out-port> => <int>) 0)
- (dp @buf (<slime-out-port> => <vec>) (new <vec>))
- (dp! @timestamp (<slime-out-port> => <int>) 0)
-
- (dm recurring-write (port|<out-port> x|<slime-out-port> d|<int> recur|<fun>)
- (msg port "#{%s buf-len: %s}" (class-name-str x) (@buf-len x)))
-
- (dm put (p|<slime-out-port> c|<chr>)
- (add! (@buf p) c)
- (incf (@buf-len p))
- (maybe-flush p (= c #\newline)))
-
- (dm puts (p|<slime-out-port> s|<str>)
- (add! (@buf p) s)
- (incf (@buf-len p) (len s))
- (maybe-flush p (mem? s #\newline)))
-
- (df maybe-flush (p|<slime-out-port> newline?|<log>)
- (and (or (> (@buf-len p) 4000) newline?)
- (> (- (current-time) (@timestamp p)) 100000)
- (force-out p)))
-
- (dm force-out (p|<slime-out-port>)
- (unless (zero? (@buf-len p))
- (dispatch-event `(:write-string ,(%buf-to-str (@buf p))) (@socket p))
- (set (@buf-len p) 0)
- (zap! (@buf p)))
- (set (@timestamp p) (current-time)))
-
- (df %buf-to-str (buf|<vec>)
- (packing-as <str>
- (for ((i buf))
- (cond ((isa? i <str>) (for ((c i)) (pack c)))
- (#t (pack i))))))
-
- (dc <slime-in-port> (<in-port>))
- (dp @socket (<slime-in-port> => <port>))
- (dp! @idx (<slime-in-port> => <int>) 0)
- (dp! @buf (<slime-in-port> => <str>) "")
-
- (df receive-input (p|<slime-in-port>)
- (dispatch-event `(:read-string ,0) (@socket p)))
-
- (dm get (p|<slime-in-port> => <chr>)
- (cond ((< (@idx p) (len (@buf p)))
- (def c (elt (@buf p) (@idx p)))
- (incf (@idx p))
- c)
- (#t
- (def input (receive-input p))
- (cond ((zero? (len input)) (eof-object))
- (#t (set (@buf p) input)
- (set (@idx p) 0)
- (get p))))))
-
- (dm ready? (p|<slime-in-port> => <log>) (< (@idx p) (len (@buf p))))
-
- (dm peek (p|<slime-in-port> => <chr>)
- (let ((c (get p)))
- (unless (eof-object? c)
- (decf (@idx p)))
- c))
-
- ;;;; Message encoding
-
- (df decode-message (port|<in-port>)
- (read-from-string (get-block port (read-message-length port))))
-
- (df read-message-length (port)
- (or (str-to-num (cat "#x" (get-block port 6)))
- (error "can't parse message length")))
-
- (df encode-message (message port)
- (let ((string (dlet ((*max-print-length* 1000000)
- (*max-print-depth* 1000000))
- (write-to-string message))))
- (puts port (encode-message-length (len string)))
- (puts port string)
- (force-out port)))
-
- (df encode-message-length (n)
- (loc ((hex (byte)
- (if (< byte #x10)
- (cat "0" (num-to-str-base byte 16))
- (num-to-str-base byte 16)))
- (byte (i) (hex (& (>> n (* i 8)) 255))))
- (cat (byte 2) (byte 1) (byte 0))))
-
- ;;;; semi general utilities
-
- ;; Return the name of O's class as string.
- (df class-name-str (o => <str>) (sym-name (class-name (class-of o))))
-
- (df binding-name-str (b|<binding> => <str>) (sym-name (binding-name b)))
-
- (df as-sym (str|<str>) (as <sym> str))
-
- ;; Replace '//' in the middle of a filename with with a '/'
- (df simplify-filename (str|<str> => <str>)
- (match (pathname-to-components str)
- ((,hd ,@tl)
- (components-to-pathname (cons hd (del-vals tl 'root))))))
-
- ;; Execute BODY and only if BODY exits abnormally execute RECOVER.
- (df try-recover (body recover)
- (let ((ok #f))
- (fin (let ((val (body)))
- (set ok #t)
- val)
- (unless ok
- (recover)))))
-
- ;; like CL's IGNORE-ERRORS but return VALUE in case of an error.
- (df try-or (body|<fun> value)
- (esc ret
- (try <error> (fun (condition resume) (ret value))
- (body))))
-
- (df simple-restart (type msg body)
- (esc restart
- (try ((type type) (description msg))
- (fun (c r) (restart #f))
- (body))))
-
- (df safe-write-to-string (o)
- (esc ret
- (try <error> (fun (c r)
- (ret (cat "#<error during write " (class-name-str o) ">")))
- (write-to-string o))))
-
- ;; Read a string of length COUNT.
- (df get-block (port|<in-port> count|<int> => <str>)
- (packing-as <str>
- (for ((i (below count)))
- (let ((c (get port)))
- (cond ((eof-object? c)
- (error "Premature EOF (read %d of %d)" i count))
- (#t (pack c)))))))
-
- ;;;; import some internal bindings
-
- (df %lookup (name|<sym> module|<sym>)
- (loc-val
- (binding-locative
- (find-binding
- name (module-target-environment (runtime-module module))))))
-
- (d. %handler-info (%lookup 'handler-info 'goo/conditions))
- (d. %handler-condition-type (%lookup 'handler-condition-type 'goo/conditions))
- (d. %do-handlers-of-type (%lookup 'do-handlers-of-type 'goo/conditions))
- (d. %module-loader-modules (%lookup 'module-loader-modules 'eval/module))
- (d. %ast-macro-expand (%lookup 'ast-macro-expand 'eval/ast))
-
- ;;;; low level socket stuff
- ;;; this shouldn't be here
-
- #{
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <errno.h>
- #include <string.h>
- #include <stdlib.h>
- #include <sys/time.h>
-
- /* convert a goo number to a C long */
- static long g2i (P o) { return untag (o); }
-
- static int
- set_reuse_address (int socket, int value) {
- return setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, &value, sizeof value);
- }
-
- static int
- bind_socket (int socket, int port) {
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons (port);
- addr.sin_addr.s_addr = htonl (INADDR_ANY);
- return bind (socket, (struct sockaddr *)&addr, sizeof addr);
- }
-
- static int
- local_port (int socket) {
- struct sockaddr_in addr;
- socklen_t len = sizeof addr;
- int code = getsockname (socket, (struct sockaddr *)&addr, &len);
- return (code == -1) ? -1 : ntohs (addr.sin_port);
- }
-
- static int
- c_accept (int socket) {
- struct sockaddr_in addr;
- socklen_t len = sizeof addr;
- return accept (socket, (struct sockaddr *)&addr, &len);
- }
-
- static P tup3 (P e0, P e1, P e2) {
- P tup = YPPtfab ((P)3, YPfalse);
- YPtelt_setter (e0, tup, (P)0);
- YPtelt_setter (e1, tup, (P)1);
- YPtelt_setter (e2, tup, (P)2);
- return tup;
- }
-
- static P
- current_time (void) {
- struct timeval timeval;
- int code = gettimeofday (&timeval, NULL);
- if (code == 0) {
- return tup3 (YPib ((P)(timeval.tv_sec >> 24)),
- YPib ((P)(timeval.tv_sec & 0xffffff)),
- YPib ((P)(timeval.tv_usec)));
- } else return YPib ((P)errno);
- }
- }
-
- ;; Return the current time in microsecs
- (df current-time (=> <int>)
- (def t #eg{ current_time () })
- (cond ((isa? t <int>) (error "%s" (strerror t)))
- (#t (+ (* (+ (<< (1st t) 24)
- (2nd t))
- 1000000)
- (3rd t)))))
-
- (dm strerror (e|<int> => <str>) #es{ strerror (g2i ($e)) })
- (dm strerror (e|(t= #f) => <str>) #es{ strerror (errno) })
-
- (df checkr (value|<int>)
- (cond ((~== value -1) value)
- (#t (error "%s" (strerror #f)))))
-
- (df create-socket (port|<int> => <int>)
- (let ((socket (checkr #ei{ socket (PF_INET, SOCK_STREAM, 0) })))
- (checkr #ei{ set_reuse_address (g2i ($socket), 1) })
- (checkr #ei{ bind_socket (g2i ($socket), g2i ($port)) })
- (checkr #ei{ listen (g2i ($socket), 1)})
- socket))
-
- (df %local-port (fd|<int>) (checkr #ei{ local_port (g2i ($fd)) }))
- (df %close (fd|<int>) (checkr #ei{ close (g2i ($fd)) }))
-
- (dc <fd-io-port> (<in-port> <out-port>))
- (dp @fd (<fd-io-port> => <int>))
- (dp @in (<fd-io-port> => <file-in-port>))
- (dp @out (<fd-io-port> => <file-out-port>))
-
- (dm recurring-write (port|<out-port> x|<fd-io-port> d|<int> recur|<fun>)
- (msg port "#{%s fd: %s}" (class-name-str x) (@fd x)))
-
- (dm get (port|<fd-io-port> => <chr>) (get (@in port)))
-
- (dm puts (port|<fd-io-port> s|<str>) (puts (@out port) s))
- (dm force-out (port|<fd-io-port>) (force-out (@out port)))
-
- (dm fdopen (fd|<int> type|(t= <fd-io-port>) => <fd-io-port>)
- (new <fd-io-port> @fd fd
- @in (new <file-in-port> port-handle (%fdopen fd "r"))
- @out (new <file-out-port> port-handle (%fdopen fd "w"))))
-
- (df %fdopen (fd|<int> mode|<str> => <loc>)
- (def addr #ei{ fdopen (g2i ($fd), @mode) })
- (when (zero? addr)
- (error "fdopen failed: %s" (strerror #f)))
- (%lb (%iu addr)))
-
- (df accept (socket|<int> => <fd-io-port>)
- (fdopen (checkr #ei{ c_accept (g2i ($socket)) }) <fd-io-port>))
-
- (export
- start-swank
- create-server)
-
- ;;; swank-goo.goo ends here
|