1#| repl.jl -- rep input loop 2 3 $Id: repl.jl,v 1.50 2004/10/07 05:03:54 jsh Exp $ 4 5 Copyright (C) 2000 John Harper <john@dcs.warwick.ac.uk> 6 7 This file is part of librep. 8 9 librep is free software; you can redistribute it and/or modify it 10 under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2, or (at your option) 12 any later version. 13 14 librep is distributed in the hope that it will be useful, but 15 WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with librep; see the file COPYING. If not, write to 21 the Free Software Foundation, 51 Franklin Street, Fifth Floor, 22 Boston, MA 02110-1301 USA 23|# 24 25(define-structure rep.util.repl 26 27 (export repl 28 make-repl 29 repl-struct 30 repl-pending 31 repl-eval 32 repl-iterate 33 repl-completions 34 define-repl-command) 35 36 (open rep 37 rep.structures 38 rep.system 39 rep.regexp 40 rep.io.files) 41 42 (define current-repl (make-fluid)) 43 44 (define (make-repl #!optional initial-struct) 45 (cons (or initial-struct *user-structure*) nil)) 46 47 (define repl-struct car) 48 (define repl-pending cdr) 49 (define repl-set-struct rplaca) 50 (define repl-set-pending rplacd) 51 52 (define (repl-eval form) 53 (eval form (intern-structure (repl-struct (fluid current-repl))))) 54 55 (define (repl-boundp x) 56 (condition-case nil 57 (progn 58 (repl-eval x) 59 t) 60 (void-value nil))) 61 62 ;; returns t if repl should run again 63 (define (repl-iterate repl input) 64 (setq input (concat (repl-pending repl) input)) 65 (repl-set-pending repl nil) 66 (let-fluids ((current-repl repl)) 67 (let ((print-escape t)) 68 (catch 'return 69 (condition-case data 70 (progn 71 (cond 72 ((string-looking-at "\\s*,\\s*" input) 73 ;; a `,' introduces a meta command 74 (let ((stream (make-string-input-stream input (match-end))) 75 (sexps '())) 76 (condition-case nil 77 (while t 78 (setq sexps (cons (read stream) sexps))) 79 (end-of-stream (setq sexps (nreverse sexps)))) 80 (let ((command (repl-command (car sexps)))) 81 (and command (apply command (cdr sexps)))))) 82 83 ;; ignore empty input lines, or lines with comments only 84 ((string-looking-at "\\s*(;.*)?$" input)) 85 86 (t (let ((form (condition-case nil 87 (read-from-string input) 88 (premature-end-of-stream 89 (repl-set-pending repl input) 90 (throw 'return 91 (and input 92 (not (string= "" input)))))))) 93 (let ((result (repl-eval form))) 94 (unless (eq result #undefined) 95 (format standard-output "%S\n" result)))))) 96 t) 97 (error 98 (default-error-handler (car data) (cdr data)) 99 t)))))) 100 101 (define (do-readline prompt completer) 102 (if (file-ttyp standard-input) 103 (progn 104 (require 'rep.io.readline) 105 (readline prompt completer)) 106 (write standard-output prompt) 107 (read-line standard-input))) 108 109 (define (repl #!optional initial-structure) 110 ;; returns t if repl should run again 111 (define (run-repl) 112 (let ((input (do-readline 113 (format nil (if (repl-pending (fluid current-repl)) 114 "" "%s> ") 115 (repl-struct (fluid current-repl))) 116 completion-generator))) 117 (and input (repl-iterate (fluid current-repl) input)))) 118 (define (interrupt-handler data) 119 (if (eq (car data) 'user-interrupt) 120 (progn 121 (format standard-output "User interrupt!\n") 122 t) 123 (raise-exception data))) 124 (let-fluids ((current-repl (make-repl initial-structure))) 125 (write standard-output "\nEnter `,help' to list commands.\n") 126 (let loop () 127 (when (call-with-exception-handler run-repl interrupt-handler) 128 (loop))))) 129 130 (define (print-list data #!optional map) 131 (unless map (setq map identity)) 132 (let* ((count (length data)) 133 (mid (inexact->exact (ceiling (/ count 2))))) 134 (do ((i 0 (1+ i)) 135 (left data (cdr left)) 136 (right (nthcdr mid data) (cdr right))) 137 ((null left)) 138 (when (< i mid) 139 (format standard-output " %-30s" 140 (format nil "%s" (map (car left)))) 141 (when right 142 (format standard-output " %s" (map (car right)))) 143 (write standard-output #\newline))))) 144 145 (define (completion-generator w) 146 ;; Either a special command or unquote. 147 (if (string-head-eq w ",") 148 (mapcar (lambda (x) (concat "," (symbol-name x))) 149 (apropos (concat #\^ (quote-regexp (substring w 1))) 150 (lambda (x) (assq x repl-commands)))) 151 (apropos (concat #\^ (quote-regexp w)) repl-boundp))) 152 153 (define (repl-completions repl word) 154 (let-fluids ((current-repl repl)) 155 (completion-generator word))) 156 157 158;;; module utils 159 160 (define (module-exports-p name var) 161 (structure-exports-p (get-structure name) var)) 162 163 (define (module-imports name) 164 (structure-imports (get-structure name))) 165 166 (define (locate-binding* name) 167 (or (locate-binding name (append (list (repl-struct (fluid current-repl))) 168 (module-imports 169 (repl-struct (fluid current-repl))))) 170 (and (structure-bound-p 171 (get-structure (repl-struct (fluid current-repl))) name) 172 (repl-struct (fluid current-repl))))) 173 174 175;;; commands 176 177 (define repl-commands '()) 178 179 (define (define-repl-command name function #!optional doc) 180 (let ((cell (assq name repl-commands))) 181 (if cell 182 (rplacd cell (list function doc)) 183 (setq repl-commands (cons (list name function doc) repl-commands))))) 184 185 (define (find-command name) 186 (let ((cell (assq name repl-commands))) 187 (if cell 188 cell 189 ;; look for an unambiguous match 190 (let ((re (concat "^" (quote-regexp (symbol-name name))))) 191 (let loop ((rest repl-commands) 192 (matched nil)) 193 (cond ((null rest) 194 (if matched 195 matched 196 (format standard-output "unknown command: ,%s\n" name) 197 nil)) 198 ((string-match re (symbol-name (caar rest))) 199 (if matched 200 ;; already saw something, exit 201 (progn 202 (format standard-output 203 "non-unique abbreviation: ,%s\n" name) 204 nil) 205 (loop (cdr rest) (car rest)))) 206 (t (loop (cdr rest) matched)))))))) 207 208 (define (repl-command name) 209 (let ((cell (find-command name))) 210 (and cell (cadr cell)))) 211 212 (define (repl-documentation name) 213 (let ((cell (find-command name))) 214 (and cell (caddr cell)))) 215 216 (define-repl-command 217 'in 218 (lambda (struct #!optional form) 219 (if form 220 (format standard-output "%S\n" 221 (eval form (get-structure struct))) 222 (repl-set-struct (fluid current-repl) struct))) 223 "STRUCT [FORM ...]") 224 225 (define-repl-command 226 'load 227 (lambda structs 228 (mapc (lambda (struct) 229 (intern-structure struct)) structs)) 230 "STRUCT ...") 231 232 (define-repl-command 233 'reload 234 (lambda structs 235 (mapc (lambda (x) 236 (let ((struct (get-structure x))) 237 (when struct 238 (name-structure struct nil)) 239 (intern-structure x))) structs)) 240 "STRUCT ...") 241 242 (define-repl-command 243 'unload 244 (lambda structs 245 (mapc (lambda (x) 246 (let ((struct (get-structure x))) 247 (when struct 248 (name-structure struct nil)))) structs)) 249 "STRUCT ...") 250 251 (define-repl-command 252 'load-file 253 (lambda files 254 (mapc (lambda (f) 255 (repl-eval `(,load ,f))) files)) 256 "\"FILENAME\" ...") 257 258 (define-repl-command 259 'open 260 (lambda structs 261 (repl-eval `(,open-structures (,quote ,structs)))) 262 "STRUCT ...") 263 264 (define-repl-command 265 'access 266 (lambda structs 267 (repl-eval `(,access-structures (,quote ,structs)))) 268 "STRUCT ...") 269 270 (define-repl-command 271 'structures 272 (lambda () 273 (let (structures) 274 (structure-walk (lambda (var value) 275 (declare (unused value)) 276 (when value 277 (setq structures (cons var structures)))) 278 (get-structure '%structures)) 279 (print-list (sort structures))))) 280 281 (define-repl-command 282 'interfaces 283 (lambda () 284 (let (interfaces) 285 (structure-walk (lambda (var value) 286 (declare (unused value)) 287 (setq interfaces (cons var interfaces))) 288 (get-structure '%interfaces)) 289 (print-list (sort interfaces))))) 290 291 (define-repl-command 292 'bindings 293 (lambda () 294 (structure-walk (lambda (var value) 295 (format standard-output " (%s %S)\n" var value)) 296 (intern-structure 297 (repl-struct (fluid current-repl)))))) 298 299 (define-repl-command 300 'exports 301 (lambda () 302 (print-list (structure-interface 303 (intern-structure 304 (repl-struct (fluid current-repl))))))) 305 306 (define-repl-command 307 'imports 308 (lambda () 309 (print-list (module-imports (repl-struct (fluid current-repl)))))) 310 311 (define-repl-command 312 'accessible 313 (lambda () 314 (print-list (structure-accessible 315 (intern-structure 316 (repl-struct (fluid current-repl))))))) 317 318 (define-repl-command 319 'collect 320 (lambda () 321 (let ((stats (garbage-collect t))) 322 (format standard-output "Used %d/%d cons, %d/%d tuples, %d strings, %d vector slots, %d/%d closures\n" 323 (car (nth 0 stats)) (+ (car (nth 0 stats)) (cdr (nth 0 stats))) 324 (car (nth 1 stats)) (+ (car (nth 1 stats)) (cdr (nth 1 stats))) 325 (car (nth 2 stats)) 326 (nth 3 stats) 327 (car (nth 4 stats)) (+ (car (nth 4 stats)) 328 (cdr (nth 4 stats))))))) 329 330 (define-repl-command 331 'disassemble 332 (lambda (arg) 333 (require 'rep.vm.disassembler) 334 (disassemble (repl-eval arg))) 335 "FORM") 336 337 (define-repl-command 338 'compile-proc 339 (lambda args 340 (require 'rep.vm.compiler) 341 (mapc (lambda (arg) 342 (compile-function (repl-eval arg) arg)) args)) 343 "PROCEDURE ...") 344 345 (define-repl-command 346 'compile 347 (lambda args 348 (require 'rep.vm.compiler) 349 (if (null args) 350 (compile-module (repl-struct (fluid current-repl))) 351 (mapc compile-module args))) 352 "[STRUCT ...]") 353 354 (define-repl-command 355 'compile-file 356 (lambda args 357 (require 'rep.vm.compiler) 358 (mapc compile-file args)) 359 "\"FILENAME\" ...") 360 361 (define-repl-command 362 'new 363 (lambda (name) 364 (declare (bound %open-structures)) 365 (make-structure nil (lambda () 366 (%open-structures '(rep.module-system))) 367 nil name) 368 (repl-set-struct (fluid current-repl) name)) 369 "STRUCT") 370 371 (define-repl-command 372 'expand 373 (lambda (form) 374 (format standard-output "%s\n" (repl-eval `(,macroexpand ',form)))) 375 "FORM") 376 377 (define-repl-command 378 'step 379 (lambda (form) 380 (format standard-output "%s\n" (repl-eval `(,step ',form)))) 381 "FORM") 382 383 (define-repl-command 384 'help 385 (lambda () 386 (write standard-output " 387Either enter lisp forms to be evaluated, and their result printed, or 388enter a meta-command prefixed by a `,' character. Names of meta- 389commands may be abbreviated to their unique leading characters.\n\n") 390 (print-list (sort (mapcar car repl-commands)) 391 (lambda (x) 392 (format nil ",%s %s" x (or (repl-documentation x) "")))))) 393 394 (define-repl-command 'quit (lambda () (throw 'quit 0))) 395 396 (define-repl-command 397 'describe 398 (lambda (name) 399 (require 'rep.lang.doc) 400 (let* ((value (repl-eval name)) 401 (struct (locate-binding* name)) 402 (doc (documentation name struct value))) 403 (write standard-output #\newline) 404 (describe-value value name struct) 405 (write standard-output #\newline) 406 (when doc 407 (format standard-output "%s\n\n" doc)))) 408 "SYMBOL") 409 410 (define-repl-command 411 'apropos 412 (lambda (re) 413 (require 'rep.lang.doc) 414 (let ((funs (apropos re repl-boundp))) 415 (mapc (lambda (x) 416 (describe-value (repl-eval x) x)) funs))) 417 "\"REGEXP\"") 418 419 (define-repl-command 420 'locate 421 (lambda (var) 422 (let ((struct (locate-binding* var))) 423 (if struct 424 (format standard-output "%s is bound in: %s.\n" var struct) 425 (format standard-output "%s is unbound.\n" var)))) 426 "SYMBOL") 427 428 (define-repl-command 429 'whereis 430 (lambda (var) 431 (let ((out '())) 432 (structure-walk (lambda (k v) 433 (declare (unused k)) 434 (when (and v (structure-name v) 435 (structure-exports-p v var)) 436 (setq out (cons (structure-name v) out)))) 437 (get-structure '%structures)) 438 (if out 439 (format standard-output "%s is exported by: %s.\n" 440 var (mapconcat symbol-name (sort out) ", ")) 441 (format standard-output "No module exports %s.\n" var)))) 442 "SYMBOL") 443 444 (define-repl-command 445 'time 446 (lambda (form) 447 (let (t1 t2 ret) 448 (setq t1 (current-utime)) 449 (setq ret (repl-eval form)) 450 (setq t2 (current-utime)) 451 (format standard-output 452 "%S\nElapsed: %d seconds\n" ret (/ (- t2 t1) 1e6)))) 453 "FORM") 454 455 (define-repl-command 456 'profile 457 (lambda (form) 458 (require 'rep.lang.profiler) 459 (format standard-output "%S\n\n" (call-in-profiler 460 (lambda () (repl-eval form)))) 461 (print-profile)) 462 "FORM") 463 464 (define-repl-command 465 'check 466 (lambda (#!optional module) 467 (require 'rep.test.framework) 468 (if (null module) 469 (run-all-self-tests) 470 (run-module-self-tests module))) 471 "[STRUCT]")) 472