1 ;; Copyright (C) 2015 Daniel J Leslie
3 ;; This program is free software; you can redistribute it and/or
4 ;; modify it under the terms of the Modified BSD License. You should
5 ;; have received a copy of the license along with this program. If
6 ;; not, see <http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5>.
9 ;; A bunch of these needn't be toplevel functions
16 geiser-object-signature
17 geiser-symbol-location
18 geiser-symbol-documentation
20 geiser-add-to-load-path
26 geiser-module-location
27 geiser-module-completions
31 (import chicken scheme)
48 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
50 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
52 (define geiser-r4rs-symbols
54 '(not boolean? eq? eqv? equal? pair? cons car cdr caar cadr cdar cddr
55 caaar caadr cadar caddr cdaar cdadr cddar cdddr caaaar caaadr caadar
56 caaddr cadaar cadadr caddar cadddr cdaaar cdaadr cdadar cdaddr cddaar
57 cddadr cdddar cddddr set-car! set-cdr! null? list? list length
58 list-tail list-ref append reverse memq memv member assq assv assoc
59 symbol? symbol->string string->symbol number? integer? exact? real?
60 complex? inexact? rational? zero? odd? even? positive? negative?
61 max min + - * / = > < >= <= quotient remainder modulo gcd lcm abs
62 floor ceiling truncate round exact->inexact inexact->exact exp log
63 expt sqrt sin cos tan asin acos atan number->string string->number
64 char? char=? char>? char<? char>=? char<=? char-ci=? char-ci<?
65 char-ci>? char-ci>=? char-ci<=? char-alphabetic? char-whitespace?
66 char-numeric? char-upper-case? char-lower-case? char-upcase
67 char-downcase char->integer integer->char string? string=? string>?
68 string<? string>=? string<=? string-ci=? string-ci<? string-ci>?
69 string-ci>=? string-ci<=? make-string string-length string-ref
70 string-set! string-append string-copy string->list list->string
71 substring string-fill! vector? make-vector vector-ref vector-set!
72 string vector vector-length vector->list list->vector vector-fill!
73 procedure? map for-each apply force call-with-current-continuation
74 input-port? output-port? current-input-port current-output-port
75 call-with-input-file call-with-output-file open-input-file
76 open-output-file close-input-port close-output-port load
77 read eof-object? read-char peek-char write display write-char
78 newline with-input-from-file with-output-to-file eval char-ready?
79 imag-part real-part magnitude numerator denominator
80 scheme-report-environment null-environment interaction-environment
83 (define geiser-r5rs-symbols
85 '(abs acos and angle append apply asin assoc assq assv atan begin
86 boolean? caar cadr call-with-current-continuation
87 call-with-input-file call-with-output-file call-with-values
88 car case cdddar cddddr cdr ceiling char->integer char-alphabetic?
89 char-ci<=? char-ci<? char-ci=? char-ci>=? char-ci>? char-downcase
90 char-lower-case? char-numeric? char-ready? char-upcase
91 char-upper-case? char-whitespace? char<=? char<? char=? char>=?
92 char>? char? close-input-port close-output-port complex? cond cons
93 cos current-input-port current-output-port define define-syntax
94 delay denominator display do dynamic-wind else eof-object? eq?
95 equal? eqv? eval even? exact->inexact exact? exp expt floor
96 for-each force gcd if imag-part inexact->exact inexact? input-port?
97 integer->char integer? interaction-environment lambda lcm length
98 let let* let-syntax letrec letrec-syntax list list->string
99 list->vector list-ref list-tail list? load log magnitude make-polar
100 make-rectangular make-string make-vector map max member memq memv
101 min modulo negative? newline not null-environment null?
102 number->string number? numerator odd? open-input-file
103 open-output-file or output-port? pair? peek-char port? positive?
104 procedure? quasiquote quote quotient rational? rationalize read
105 read-char real-part real? remainder reverse round
106 scheme-report-environment set! set-car! set-cdr! setcar sin sqrt
107 string string->list string->number string->symbol string-append
108 string-ci<=? string-ci<? string-ci=? string-ci>=? string-ci>?
109 string-copy string-fill! string-length string-ref string-set!
110 string<=? string<? string=? string>=? string>? string? substring
111 symbol->string symbol? syntax-rules tan transcript-off transcript-on
112 truncate values vector vector->list vector-fill! vector-length
113 vector-ref vector-set! vector? with-input-from-file with-output-to-file
114 write write-char zero?)))
116 (define geiser-r7rs-small-symbols
118 '(* + - ... / < <= = => > >= abs and append apply assoc assq
119 assv begin binary-port? boolean=? boolean? bytevector
120 bytevector-append bytevector-copy bytevector-copy! bytevector-length
121 bytevector-u8-ref bytevector-u8-set! bytevector? caar cadr
122 call-with-current-continuation call-with-port call-with-values call/cc
123 car case cdar cddr cdr ceiling char->integer char-ready? char<=?
124 char<? char=? char>=? char>? char? close-input-port
125 close-output-port close-port complex? cond cond-expand cons
126 current-error-port current-input-port current-output-port
127 define define-record-type define-syntax define-values denominator do
128 dynamic-wind else eof-object? equal? error error-object-message
129 even? exact-integer-sqrt exact? features floor floor-remainder
130 flush-output-port gcd get-output-string if include-ci inexact?
131 input-port? integer? lcm let let*-values let-values letrec* list
132 list->vector list-ref list-tail make-bytevector make-parameter
133 make-vector max memq min negative? not number->string numerator
134 open-input-bytevector open-output-bytevector or output-port?
135 parameterize peek-u8 positive? quasiquote quotient raise-continuable
136 rationalize read-bytevector! read-error? read-string real? reverse
137 set! set-cdr! string string->number string->utf8 string-append
138 eof-object eq? eqv? error-object-irritants error-object? exact
139 exact-integer? expt file-error? floor-quotient floor/ for-each
140 get-output-bytevector guard include inexact input-port-open?
141 integer->char lambda length let* let-syntax letrec letrec-syntax
142 list->string list-copy list-set! list? make-list make-string map
143 member memv modulo newline null? number? odd? open-input-string
144 open-output-string output-port-open? pair? peek-char port?
145 procedure? quote raise rational? read-bytevector read-char read-line
146 read-u8 remainder round set-car! square string->list string->symbol
147 string->vector string-copy string-copy! string-for-each string-map
148 string-set! string<? string>=? string? symbol->string symbol?
149 syntax-rules truncate truncate-remainder u8-ready? unquote
150 utf8->string vector vector->string vector-copy vector-fill!
151 vector-length vector-ref vector? with-exception-handler write-char
152 write-u8 string-fill! string-length string-ref string<=?
153 string=? string>? substring symbol=? syntax-error textual-port?
154 truncate-quotient truncate/ unless unquote-splicing values
155 vector->list vector-append vector-copy! vector-for-each vector-map
156 vector-set! when write-bytevector write-string zero?)))
158 (define geiser-chicken-builtin-symbols
160 '(and-let* assume compiler-typecase cond-expand condition-case cut cute declare define-constant
161 define-inline define-interface define-record define-record-type define-specialization
162 define-syntax-rule define-type define-values dotimes ecase fluid-let foreign-lambda
163 foreign-lambda* foreign-primitive foreign-safe-lambda foreign-safe-lambda* functor
164 handle-exceptions import let*-values let-location let-optionals let-optionals*
165 let-values letrec* letrec-values match-letrec module parameterize regex-case
166 require-extension select set! unless use when with-input-from-pipe match
167 match-lambda match-lambda* match-let match-let* receive)))
169 (define geiser-chicken-crunch-symbols
171 '(* + - / < <= = > >= abs acos add1 argc argv-ref arithmetic-shift asin
172 atan atan2 bitwise-and bitwise-ior bitwise-not bitwise-xor
173 blob->f32vector blob->f32vector/shared blob->f64vector
174 blob->f64vector/shared blob->s16vector blob->s16vector/shared
175 blob->s32vector blob->s32vector/shared blob->s8vector
176 blob->s8vector/shared blob->string blob->string/shared blob->u16vector
177 blob->u16vector/shared blob->u32vector blob->u32vector/shared
178 blob->u8vector blob->u8vector/shared ceiling char->integer
179 char-alphabetic? char-ci<=? char-ci<? char-ci=? char-ci>=? char-ci>?
180 char-downcase char-lower-case? char-numeric? char-upcase
181 char-upper-case? char-whitespace? char<=? char<? char=? char>=? char>?
182 cond-expand cos display display eq? equal? eqv? error even?
183 exact->inexact exact? exit exp expt f32vector->blob
184 f32vector->blob/shared f32vector-length f32vector-ref f32vector-set!
185 f64vector->blob f64vector->blob/shared f64vector-length f64vector-ref
186 f64vector-set! floor flush-output inexact->exact inexact?
187 integer->char integer? log make-f32vector make-f64vector make-s16vector
188 make-s32vector make-s8vector make-string make-u16vector make-u32vector
189 make-u8vector max min modulo negative? newline not number->string odd?
190 pointer-f32-ref pointer-f32-set! pointer-f64-ref pointer-f64-set!
191 pointer-s16-ref pointer-s16-set! pointer-s32-ref pointer-s32-set!
192 pointer-s8-ref pointer-s8-set! pointer-u16-ref pointer-u16-set!
193 pointer-u32-ref pointer-u32-set! pointer-u8-ref pointer-u8-set!
194 positive? quotient rec remainder round s16vector->blob
195 s16vector->blob/shared s16vector-length s16vector-ref s16vector-set!
196 s32vector->blob s32vector->blob/shared s32vector-length s32vector-ref
197 s32vector-set! s8vector->blob s8vector->blob/shared s8vector-length
198 s8vector-ref s8vector-set! sin sqrt string->blob string->blob/shared
199 string->number string-append string-ci<=? string-ci<? string-ci=?
200 string-ci>=? string-ci>? string-copy string-fill! string-length
201 string-ref string-set! string<=? string<? string=? string>=? string>?
202 sub1 subf32vector subf64vector subs16vector subs32vector subs8vector
203 substring subu16vector subu32vector subu8vector switch tan truncate
204 u16vector->blob u16vector->blob/shared u16vector-length u16vector-ref
205 u16vector-set! u32vector->blob u32vector->blob/shared u32vector-length
206 u32vector-ref u32vector-set! u8vector->blob u8vector->blob/shared
207 u8vector-length u8vector-ref u8vector-set! unless void when write-char
210 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
212 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
214 (define geiser-use-debug-log (make-parameter #t))
216 (define find-module ##sys#find-module)
217 (define current-module ##sys#current-module)
218 (define switch-module ##sys#switch-module)
219 (define module-name ##sys#module-name)
220 (define (list-modules) (map car ##sys#module-table))
222 (define debug-log (make-parameter #f))
224 (define (write-to-log form)
225 (when (geiser-use-debug-log)
226 (when (not (debug-log))
227 (debug-log (file-open "geiser.log" (+ open/wronly open/append open/text open/creat)))
228 (set-file-position! (debug-log) 0 seek/end))
229 (file-write (debug-log) (with-all-output-to-string (lambda () (write form) (newline))))
230 (file-write (debug-log) "\n")))
232 (define (remove-internal-name-mangling sym)
233 (let* ((sym (->string sym))
234 (octothorpe-index (string-index-right sym #\#)))
236 (values (substring/shared sym (add1 octothorpe-index))
237 (substring/shared sym 0 octothorpe-index))
240 (define (string-has-prefix? s prefix)
241 (let ((s-length (string-length s))
242 (prefix-length (string-length prefix)))
244 (< prefix-length s-length)
245 (string-contains s prefix 0 prefix-length))))
247 ;; This really should be a chicken library function
248 (define (write-exception exn)
249 (define (write-call-entry call)
250 (let ((type (vector-ref call 0))
251 (line (vector-ref call 1)))
253 ((equal? type "<syntax>")
254 (display (string-append type " ")) (write line) (newline))
255 ((equal? type "<eval>")
256 (display (string-append type " ")) (write line) (newline)))))
258 (display (format "Error: (~s) ~s: ~s"
259 ((condition-property-accessor 'exn 'location) exn)
260 ((condition-property-accessor 'exn 'message) exn)
261 ((condition-property-accessor 'exn 'arguments) exn)))
263 (display "Call history: ") (newline)
264 (map write-call-entry ((condition-property-accessor 'exn 'call-chain) exn))
267 ;; And this should be a chicken library function as well
268 (define (with-all-output-to-string thunk)
269 (with-output-to-string
271 (with-error-output-to-port
272 (current-output-port)
275 (define (maybe-call func val)
276 (if val (func val) #f))
278 ;; Wraps output from geiser functions
279 (define (call-with-result module thunk)
280 (let* ((result (if #f #f))
282 (module (maybe-call (lambda (v) (find-module module)) module))
283 (original-module (current-module)))
286 (handle-exceptions exn
287 (with-all-output-to-string
288 (lambda () (write-exception exn)))
289 (with-all-output-to-string
291 (switch-module module)
292 (call-with-values thunk (lambda v (set! result v)))))))
294 (switch-module original-module)
299 (map (lambda (v) (with-output-to-string (lambda () (write v)))) result))
300 ((eq? result (if #f #t))
303 (list (with-output-to-string (lambda () (write result)))))))
307 (output . ,output))))
309 (write-to-log '[[RESPONSE]])
310 (write-to-log out-form))
314 (define (find-standards-with-symbol sym)
316 (if (any (cut eq? <> sym) (geiser-r4rs-symbols))
319 (if (any (cut eq? <> sym) (geiser-r5rs-symbols))
322 (if (any (cut eq? <> sym) (geiser-r7rs-small-symbols))
325 (if (any (cut eq? <> sym) (geiser-chicken-builtin-symbols))
328 (if (any (cut eq? <> sym) (geiser-chicken-crunch-symbols))
332 ;; Locates any paths at which a particular symbol might be located
333 (define (find-library-paths sym types)
334 ;; Removes the given sym from the node path
335 (define (remove-self sym path)
337 ((not (list? path)) path)
340 (if (eq? (car path) sym)
344 (cons (car path) (remove-self sym (cdr path))))))
349 (find-standards-with-symbol sym))
352 (remove-self sym (node-path node)))
355 (let ((type (node-type n)))
356 (any (cut eq? type <>) types)))
357 (match-nodes sym)))))
359 (define (make-module-list sym module-sym)
360 (if (null? module-sym)
361 (find-standards-with-symbol sym)
362 (cons module-sym (find-standards-with-symbol sym))))
364 (define (fmt sym node)
365 (let* ((entry-str (car node))
368 (type (if (or (list? rest) (pair? rest)) (car rest) rest)))
370 ((equal? 'macro type)
371 `(,entry-str ("args" (("required" <macro>)
374 ("module" ,@(make-module-list sym module))))
375 ((or (equal? 'variable type)
376 (equal? 'constant type))
378 `(,entry-str ("value" . ,(eval sym)))
379 (let* ((original-module (current-module))
380 (desired-module (find-module (string->symbol module)))
381 (value (begin (switch-module desired-module)
383 (switch-module original-module)
384 `(,entry-str ("value" . ,value)
385 ("module" ,@(make-module-list sym module))))))
390 (args (if (or (list? rest) (pair? rest)) (cdr rest) '())))
392 (define (clean-arg arg)
393 (string->symbol (string-substitute "(.*[^0-9]+)[0-9]+" "\\1" (->string arg))))
395 (define (collect-args args #!key (reqs? #t) (opts? #f) (keys? #f))
396 (when (not (null? args))
398 ((or (pair? args) (list? args))
400 ((eq? '#!key (car args))
401 (collect-args (cdr args) reqs?: #f opts?: #f keys?: #t))
402 ((eq? '#!optional (car args))
403 (collect-args (cdr args) reqs?: #f opts?: #t keys?: #f))
408 (set! reqs (append reqs (list (clean-arg (car args))))))
410 (set! opts (append opts (list (cons (clean-arg (caar args)) (cdar args))))))
412 (set! keys (append keys (list (cons (clean-arg (caar args)) (cdar args)))))))
413 (collect-args (cdr args))))))
415 (set! opts (list (clean-arg args) '...))))))
419 `(,entry-str ("args" (("required" ,@reqs)
422 ("module" ,@(make-module-list sym module))))))))
424 ;; Builds a signature list from an identifier
425 (define (find-signatures sym)
426 (let ((str (->string sym)))
434 ;; Remove egg name and add module
436 (((name module) (remove-internal-name-mangling (car s))))
437 (cons (string->symbol name)
438 (cons (if (string? module) (string->symbol module) module)
440 (apropos-information-list sym #:macros? #t))))))
442 ;; Builds the documentation from Chicken Doc for a specific symbol
443 (define (make-doc symbol #!optional (filter-for-type #f))
444 (with-output-to-string
447 (display (string-append "= Node: " (->string (node-id node)) " " " =\n"))
452 (or (not filter-for-type)
453 (eq? (node-type n) filter-for-type)))
454 (match-nodes symbol))))))
457 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
458 ;; Geiser core functions
459 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
461 ;; Basically all non-core functions pass through geiser-eval
463 (define (geiser-eval module form . rest)
464 ;; We can't allow nested module definitions in Chicken
465 (define (form-has-module? form)
466 (let ((reg "\\( *module +|\\( *define-library +"))
467 (string-search reg form)))
470 (not (symbol? module)))
471 (error "Module should be a symbol"))
473 ;; All calls start at toplevel
474 (let* ((str-form (format "~s" form))
475 (is-module? (form-has-module? str-form))
476 (host-module (and (not is-module?)
477 (any (cut equal? module <>) (list-modules))
480 (write-to-log '[[REQUEST]])
483 (call-with-result host-module (lambda () (eval form)))))
487 (define (geiser-load-file file)
488 (let* ((file (if (symbol? file) (symbol->string file) file))
489 (found-file (geiser-find-file #f file)))
493 (load found-file))))))
495 ;; The no-values identity
497 (define (geiser-no-values)
500 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
502 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
506 (define (geiser-newline . rest)
509 ;; Spawn a server for remote repl access
511 (define (geiser-start-server . rest)
512 (let* ((listener (tcp-listen 0))
513 (port (tcp-listener-port listener)))
514 (define (remote-repl)
515 (receive (in out) (tcp-accept listener)
516 (current-input-port in)
517 (current-output-port out)
518 (current-error-port out)
522 (thread-start! (make-thread remote-repl))
524 (write-to-log `(geiser-start-server . ,rest))
525 (write-to-log `(port ,port))
527 (write `(port ,port))
530 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
531 ;; Completions, Autodoc and Signature
532 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
534 (define (geiser-completions prefix . rest)
535 (let ((prefix (->string prefix))
536 (unfiltered (map remove-internal-name-mangling
537 (apropos-list prefix #:macros? #t))))
538 (filter (cut string-has-prefix? <> prefix) unfiltered)))
540 (define (geiser-module-completions prefix . rest)
541 (let ((prefix (->string prefix)))
542 (filter (cut string-has-prefix? <> prefix) (map ->string (list-modules)))))
544 (define (geiser-autodoc ids . rest)
548 (geiser-autodoc (list ids)))
550 (let ((details (find-signatures (car ids))))
552 (geiser-autodoc (cdr ids))
555 (define (geiser-object-signature name object . rest)
556 (let* ((sig (geiser-autodoc `(,name))))
557 (if (null? sig) '() (car sig))))
559 ;; TODO: Divine some way to support this functionality
561 (define (geiser-symbol-location symbol . rest)
562 '(("file") ("line")))
564 (define (geiser-symbol-documentation symbol . rest)
565 (let* ((sig (find-signatures symbol)))
566 `(("signature" ,@(car sig))
567 ("docstring" . ,(make-doc symbol)))))
569 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
570 ;; File and Buffer Operations
571 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
573 (define geiser-load-paths (make-parameter '()))
575 (define (geiser-find-file file . rest)
576 (let ((paths (append '("" ".") (geiser-load-paths))))
577 (define (try-find file paths)
580 ((file-exists? (string-append (car paths) file))
581 (string-append (car paths) file))
582 (else (try-find file (cdr paths)))))
583 (try-find file paths)))
585 (define (geiser-add-to-load-path directory . rest)
586 (let* ((directory (if (symbol? directory)
587 (symbol->string directory)
589 (directory (if (not (equal? #\/ (string-ref directory (- (string-length directory)))))
590 (string-append directory "/")
594 (when (directory-exists? directory)
595 (geiser-load-paths (cons directory (geiser-load-paths))))))))
597 (define (geiser-compile-file file . rest)
598 (let* ((file (if (symbol? file) (symbol->string file) file))
599 (found-file (geiser-find-file file)))
603 (compile-file found-file))))))
605 ;; TODO: Support compiling regions
607 (define (geiser-compile form module . rest)
608 (error "Chicken does not support compiling regions"))
610 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
612 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
615 ;; '(("modules" . sub-modules) ("procs" . procedures) ("syntax" . macros) ("vars" . variables))
616 (define (geiser-module-exports module-name . rest)
617 (let* ((nodes (match-nodes module-name)))
626 (let ((type (node-type node))
627 (name (node-id node))
628 (path (node-path node)))
630 ((memq type '(unit egg))
631 (set! mod (cons name mod)))
632 ((memq type '(procedure record setter class method))
633 (set! proc (cons name proc)))
634 ((memq type '(read syntax))
635 (set! syn (cons name syn)))
636 ((memq type '(parameter constant))
637 (set! var (cons name var))))))
644 ;; Returns the path for the file in which an egg or module was defined
646 (define (geiser-module-path module-name . rest)
650 ;; `(("file" . ,(module-path name)) ("line"))
652 (define (geiser-module-location name . rest)
655 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
657 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
659 (define (geiser-macroexpand form . rest)
660 (with-output-to-string
662 (write (expand form)))))