command.scm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; Interpreting commands.
  3. ; Commands begin with a comma because it's an unshifted key and because
  4. ; someone else (I can't remember who) was already using it as a command
  5. ; prefix.
  6. (define command-prefix #\,)
  7. ; Fire up the processor.
  8. ;
  9. ; The double-paren around the WITH-HANDLER is because it returns a
  10. ; thunk which is the thing to do after the command-processor exits.
  11. ;
  12. ; There are two version, one for an initial start and the other for restarting
  13. ; with an existing user context.
  14. (define (start-command-processor resume-args greeting-thunk start-thunk)
  15. (restart-command-processor resume-args #f greeting-thunk start-thunk))
  16. (define (restart-command-processor resume-args context greeting-thunk start-thunk)
  17. ((with-handler command-loop-condition-handler
  18. (lambda ()
  19. (start-command-levels resume-args
  20. context
  21. greeting-thunk
  22. start-thunk
  23. script-runner
  24. real-command-loop
  25. #f ; no condition
  26. #f ; not inspecting
  27. (current-input-port)
  28. (current-output-port)
  29. (current-error-port))))))
  30. ; Entry for initialization & testing.
  31. (define (command-processor command-env resume-args)
  32. (start-command-processor resume-args
  33. values
  34. (lambda ()
  35. (set-user-command-environment! command-env)
  36. unspecific)))
  37. ;----------------
  38. ; Command loop
  39. ; Called from:
  40. ; 1. condition handler, 2. abort-to-level, 3. breakpoint
  41. ;
  42. ; The condition is either #F or whatever caused a new command loop to be
  43. ; started.
  44. (define (command-loop condition)
  45. ;; The handler may have gotten unwound by `raise'
  46. (with-handler command-loop-condition-handler
  47. (lambda ()
  48. (push-command-level condition #f))))
  49. ; Install the handler, bind $NOTE-UNDEFINED to keep from annoying the user,
  50. ; bind $FOCUS-BEFORE to avoid keeping state on the stack where it can be
  51. ; captured by CALL/CC, display the condition and start reading commands.
  52. (define (real-command-loop)
  53. (let ((results-cell (make-cell #f)))
  54. (let-fluids $note-undefined #f ;useful
  55. $command-results results-cell
  56. (lambda ()
  57. (display-command-level-condition
  58. (command-level-condition (command-level)))
  59. (let command-loop ()
  60. (let ((command (read-command-carefully (command-prompt)
  61. (form-preferred?)
  62. (command-input))))
  63. (cell-set! results-cell #f)
  64. (execute-command command)
  65. (let ((results (cell-ref results-cell)))
  66. (if results
  67. (show-command-results results)))
  68. (command-loop)))))))
  69. ; For saving the results returned by a command.
  70. (define $command-results (make-fluid #f))
  71. (define (set-command-results! results . maybe-set-focus-object?)
  72. (fluid-cell-set! $command-results results)
  73. (if (or (null? maybe-set-focus-object?)
  74. (car maybe-set-focus-object?))
  75. (case (length results)
  76. ((0)
  77. (values))
  78. ((1)
  79. (if (not (eq? (car results)
  80. (unspecific)))
  81. (set-focus-object! (car results))))
  82. (else
  83. (set-focus-object! results)))))
  84. (define (display-command-level-condition condition)
  85. (if condition
  86. (display-condition condition (command-output))))
  87. ; If #T anything that doesn't start with the command prefix (a comma) is
  88. ; treated as an argument to RUN. If #F no commas are needed and RUN
  89. ; commands must be explicit.
  90. (define (form-preferred?)
  91. (not (value-stack)))
  92. ; If true then print a menu when showing results.
  93. (define (inspect-mode?)
  94. (value-stack))
  95. ; Go up to the previous level or exit if there are no more levels.
  96. (define (pop-command-level)
  97. (let ((levels (command-levels)))
  98. (if (null? (cdr levels))
  99. (cond ((batch-mode?)
  100. ; perhaps this should use scheme-exit-now, but I'm
  101. ; worried that it is what handles normal EOF. (HCC)
  102. (exit-command-processor (lambda () 0)))
  103. ((y-or-n? "Exit Scheme 48" #t)
  104. (exit-command-processor (lambda () 1)))
  105. (else
  106. (abort-to-command-level (car levels))))
  107. (let ((level (cadr (command-levels))))
  108. (if (command-level-paused-thread level)
  109. (kill-paused-thread! level))
  110. (proceed-with-command-level level)))))
  111. (define (exit-command-processor thunk)
  112. (throw-to-command-level (top-command-level)
  113. (lambda () thunk)))
  114. ; Condition handler.
  115. ; For warnings and notes we go stop the current level or continue, for
  116. ; errors and interrupts we stop the level or exit. We always continue for
  117. ; warnings on the command level thread to avoid circularity problems.
  118. (define (command-loop-condition-handler c next-handler)
  119. (let ((c (coerce-to-condition c)))
  120. (cond ((or (warning? c)
  121. (note? c))
  122. (if (break-on-warnings?)
  123. (deal-with-condition c)
  124. (begin (force-output (current-output-port)) ; keep synchronous
  125. (display-condition c (current-error-port))
  126. (unspecific)))) ;proceed
  127. ((or (error? c) (bug? c) (interrupt? c))
  128. (if (batch-mode?)
  129. (begin (force-output (current-output-port)) ; keep synchronous
  130. (display-condition c (current-error-port))
  131. (let ((status
  132. (cond
  133. ((error? c) 1)
  134. ((bug? c) 3) ; historical, probably nonsense
  135. (else 2))))
  136. (scheme-exit-now status)))
  137. (deal-with-condition c)))
  138. ((reset-command-input? c)
  139. (unspecific)) ;proceed
  140. (else
  141. (next-handler)))))
  142. ; Stop the current level either by pushing a new one or restarting it.
  143. ; If we restart the current level we save it as the focus object to give
  144. ; the user a chance to figure out what happened.
  145. (define (deal-with-condition c)
  146. (if (push-command-levels?)
  147. (command-loop c)
  148. (let ((level (car (command-levels))))
  149. (set-focus-object! level)
  150. (display-condition c (command-output))
  151. (restart-command-level level))))
  152. (define (abort-to-command-level level)
  153. (cond ((eq? level (car (reverse (command-levels))))
  154. (newline (command-output))
  155. (write-line "Top level" (command-output)))
  156. (else
  157. (display "Back to " (command-output))))
  158. (restart-command-level level))
  159. ; The prompt is "level-number environment-id-string> " or just
  160. ; "environment-id-string> " at top level. The id-string is empty for the
  161. ; current user package and the name of the package otherwise.
  162. ; The ">" changes to ":" in command-preferred mode.
  163. (define (command-prompt)
  164. (let ((level (- (length (command-levels)) 1))
  165. (id (environment-id-string (environment-for-commands))))
  166. (string-append (if (= level 0)
  167. ""
  168. (number->string level))
  169. (if (or (= level 0) (= (string-length id) 0))
  170. ""
  171. " ")
  172. id
  173. (if (form-preferred?)
  174. "> "
  175. ": "))))
  176. (define-generic environment-id-string &environment-id-string (env))
  177. (define-method &environment-id-string (env) "")
  178. ;----------------
  179. ; Loading scripts
  180. (define (script-runner)
  181. (run-script (cdr (focus-object))))
  182. ; This loads a script SRFI-22-style
  183. ; ARG is a list of command-line arguments after "run-script"
  184. (define (run-script arg)
  185. (run-script-handler (os-string->string (car arg)) (cdr arg)))
  186. (define *script-handler-alist* '())
  187. (define (define-script-handler tag proc)
  188. (set! *script-handler-alist*
  189. (cons (cons tag proc) *script-handler-alist*)))
  190. (define (run-script-handler tag args)
  191. (cond
  192. ((assoc tag *script-handler-alist*)
  193. => (lambda (pair)
  194. (silently
  195. (lambda ()
  196. ((cdr pair) args)))))
  197. (else
  198. (display "invalid argument to run-script-handler: " (current-error-port))
  199. (display tag (current-error-port))
  200. (newline (current-error-port))
  201. 1)))
  202. (define (EX_SOFTWARE) (shared-binding-ref (lookup-imported-binding "EX_SOFTWARE")))
  203. (define (with-srfi-22-error-handling thunk)
  204. (call-with-current-continuation
  205. (lambda (k)
  206. (with-handler
  207. (lambda (c punt)
  208. (let ((c (coerce-to-condition c)))
  209. (if (or (error? c) (bug? c))
  210. (begin
  211. (display-condition c (current-error-port))
  212. (k (EX_SOFTWARE)))
  213. (punt))))
  214. (lambda ()
  215. (thunk)
  216. 0)))))
  217. (define-script-handler "r5rs"
  218. (lambda (args)
  219. (with-srfi-22-error-handling
  220. (lambda ()
  221. (load-script-into (os-string->string (car args)) (interaction-environment))
  222. ((environment-ref (interaction-environment) 'main) (map os-string->string args))))))
  223. (define-script-handler "srfi-7"
  224. (lambda (args)
  225. (with-srfi-22-error-handling
  226. (lambda ()
  227. (eval '(load-package 'srfi-7) (user-command-environment))
  228. (eval `(load-srfi-7-script 'srfi-7-script ,(os-string->string (car args)))
  229. (user-command-environment))
  230. (let ((cell (make-cell #f))) ; kludge
  231. (let-fluid $command-results cell
  232. (lambda ()
  233. (eval '(in 'srfi-7-script '(run main))
  234. (user-command-environment))))
  235. ((car (cell-ref cell)) (map os-string->string args)))))))
  236. ;----------------
  237. ; Evaluate a form and save its result as the current focus values.
  238. ; The unspecific object is discarded.
  239. (define (evaluate-and-select form env)
  240. (call-with-values (lambda ()
  241. (eval form env))
  242. (lambda results
  243. (set-command-results! results)
  244. (apply values results))))
  245. ;----------------
  246. ; Printing command results. The results are also saved as the current
  247. ; focus object.
  248. (define (show-command-results results)
  249. (let ((out (command-output)))
  250. (case (length results)
  251. ((0)
  252. (display "; no values returned" out)
  253. (newline out))
  254. ((1)
  255. (show-command-result (car results))
  256. (if (inspect-mode?)
  257. (present-menu)))
  258. (else
  259. (display "; " out)
  260. (write (length results) out)
  261. (display " values returned" out)
  262. (if (inspect-mode?)
  263. (present-menu)
  264. (begin
  265. (newline out)
  266. (for-each show-command-result results)))))))
  267. (define (show-command-result result)
  268. (let ((out (command-output)))
  269. ((if (inspect-mode?)
  270. with-limited-output
  271. (lambda (p) (p)))
  272. (lambda ()
  273. (write-carefully (value->expression result)
  274. out)
  275. (newline out)))))
  276. ;----------------
  277. ; Sentinels - run after every command.
  278. (define *sentinels* '())
  279. (define (run-sentinels)
  280. (for-each (lambda (sentinel) (sentinel)) *sentinels*))
  281. (define (add-sentinel! sentinel)
  282. (if (not (memq sentinel *sentinels*))
  283. (set! *sentinels* (cons sentinel *sentinels*))))
  284. ;----------------
  285. ; Commands.
  286. (define environment-for-commands interaction-environment)
  287. (define command-environment
  288. (user-context-accessor 'command-environment interaction-environment))
  289. ;(define *command-structure* (unspecific))
  290. ;
  291. ;(define (command-structure)
  292. ; *command-structure*)
  293. ;
  294. ;(define (set-command-structure! structure) ; called on initial startup
  295. ; (set! *command-structure* structure))
  296. (define command-syntax-table (make-table))
  297. (define *command-help* '())
  298. (define (get-command-syntax name)
  299. (or (table-ref (user-command-syntax-table) name)
  300. (table-ref command-syntax-table name)))
  301. (define (define-command-syntax name help1 help2 arg-descriptions)
  302. (table-set! command-syntax-table name arg-descriptions)
  303. (if help1
  304. (set! *command-help* (add-help *command-help* name help1 help2))))
  305. (define (add-help help name help1 help2)
  306. (insert (list name
  307. (string-append (symbol->string name) " " help1)
  308. help2)
  309. help
  310. (lambda (z1 z2)
  311. (string<=? (cadr z1) (cadr z2)))))
  312. (define user-command-syntax-table
  313. (user-context-accessor 'user-command-syntax-table (lambda () (make-table))))
  314. (define user-command-environment
  315. (user-context-accessor 'user-command-environment (lambda () #f)))
  316. (define set-user-command-environment!
  317. (user-context-modifier 'user-command-environment))
  318. (define user-command-help
  319. (user-context-accessor 'user-command-help (lambda () *command-help*)))
  320. (define set-user-command-help!
  321. (user-context-modifier 'user-command-help))
  322. (define (define-user-command-syntax name help1 help2 arg-descriptions)
  323. (table-set! (user-command-syntax-table) name arg-descriptions)
  324. (if help1
  325. (set-user-command-help!
  326. (add-help (user-command-help) name help1 help2))))
  327. (define (execute-command command)
  328. (run-sentinels)
  329. (cond ((eof-object? command)
  330. (newline (command-output))
  331. (pop-command-level))
  332. ((not command)) ; error while reading
  333. (else
  334. (let* ((name (car command))
  335. (proc (eval name (user-command-environment))))
  336. (apply proc (cdr command))))))
  337. ;----------------
  338. ; Settings - these are cells for controlling the behavior
  339. ; of the command interpreter.
  340. ;
  341. ; This code is here so that the help listing can print out the settings
  342. ; and their current values.
  343. (define-record-type setting :setting
  344. (make-setting name type get set on-doc off-doc)
  345. setting?
  346. (name setting-name)
  347. ;; is #t for boolean or a predicate
  348. (type setting-type)
  349. (get setting-get)
  350. (set setting-set)
  351. ;; We have two documentation strings, one for `on' and one for `off'.
  352. (on-doc setting-on-doc)
  353. (off-doc setting-off-doc))
  354. (define (setting-boolean? setting)
  355. (eqv? #t (setting-type setting)))
  356. ; alist mapping names to :SETTING records
  357. (define *settings-alist* '())
  358. (define (lookup-setting name)
  359. (cond
  360. ((assq name *settings-alist*) => cdr)
  361. (else #f)))
  362. (define (add-setting name boolean? get set on-doc . maybe-off-doc)
  363. (set! *settings-alist*
  364. (insert (cons name
  365. (make-setting name boolean? get set on-doc
  366. (if (null? maybe-off-doc)
  367. #f
  368. (car maybe-off-doc))))
  369. *settings-alist*
  370. (lambda (z1 z2)
  371. (string<=? (symbol->string (car z1))
  372. (symbol->string (car z2)))))))
  373. (define (setting-value setting)
  374. ((setting-get setting)))
  375. (define (setting-set! setting value)
  376. (if (if (setting-boolean? setting)
  377. (and (not (eqv? value #t))
  378. (not (eqv? value #f)))
  379. (not ((setting-type setting) value)))
  380. (error "invalid value for setting" (setting-name setting) value))
  381. ((setting-set setting) value))
  382. (define (setting-doc setting)
  383. (cond
  384. ((not (setting-boolean? setting))
  385. (setting-on-doc setting))
  386. ((setting-value setting)
  387. (setting-on-doc setting))
  388. (else
  389. (setting-off-doc setting))))
  390. ; Print out a list of the settings and their current values.
  391. (define (list-settings)
  392. (let ((o-port (command-output))
  393. (size (apply max
  394. (map (lambda (z)
  395. (string-length (symbol->string (setting-name (cdr z)))))
  396. *settings-alist*))))
  397. (for-each (lambda (z)
  398. (let* ((setting (cdr z))
  399. (name (symbol->string (setting-name setting))))
  400. (display #\space o-port)
  401. (display name o-port)
  402. (display #\space o-port)
  403. (write-spaces (- size (string-length name)) o-port)
  404. (display #\( o-port)
  405. (cond
  406. ((not (setting-boolean? setting))
  407. (write (setting-value setting) o-port)
  408. (display ", " o-port)
  409. (display (setting-on-doc setting) o-port))
  410. ((setting-value setting)
  411. (display "on, " o-port)
  412. (display (setting-on-doc setting) o-port))
  413. (else
  414. (display "off, " o-port)
  415. (display (setting-off-doc setting) o-port)))
  416. (display #\) o-port)
  417. (newline o-port)))
  418. *settings-alist*)))
  419. ;----------------
  420. ; help
  421. (define (help . maybe-id)
  422. (if (null? maybe-id)
  423. (list-commands)
  424. (print-command-help (car maybe-id))))
  425. (define (print-command-help id)
  426. (let ((o-port (command-output)))
  427. (display #\space o-port)
  428. (cond ((assq id (user-command-help))
  429. => (lambda (data)
  430. (if (form-preferred?) (display command-prefix o-port))
  431. (display (cadr data) o-port)
  432. (display " " o-port)
  433. (display (caddr data) o-port)))
  434. (else
  435. (display #\" o-port)
  436. (display id o-port)
  437. (display #\" o-port)
  438. (display #\space o-port)
  439. (display "is not a command.")))
  440. (newline o-port)))
  441. (define (list-commands)
  442. (let ((o-port (command-output))
  443. (widest 28)
  444. (f? (form-preferred?)))
  445. (for-each (lambda (s)
  446. (write-line s o-port))
  447. '(
  448. "This is Scheme 48. You are interacting with the command processor."
  449. "A command is either a Scheme form to evaluate or one of the following:"
  450. ""))
  451. (list-command-help (user-command-help) f? o-port)
  452. (for-each (lambda (s)
  453. (write-line s o-port))
  454. '(
  455. ""
  456. "Square brackets [...] indicate optional arguments."
  457. ""
  458. "The following settings are set by the `set' and `unset' commands:"
  459. ""
  460. ))
  461. (list-settings)
  462. (for-each (lambda (s)
  463. (write-line s o-port))
  464. '(
  465. ""
  466. "The expression ## evaluates to the last value displayed by the command"
  467. "processor."
  468. ))))
  469. (define (list-command-help data prefix? o-port)
  470. (let* ((strings (map (if prefix?
  471. (lambda (d)
  472. (string-append (command-prefix-string
  473. command-prefix)
  474. (cadr d)))
  475. cadr)
  476. data))
  477. (count (length strings))
  478. (back-half (list-tail strings (quotient (+ 1 count) 2))))
  479. (let loop ((s1 strings) (s2 back-half))
  480. (cond ((not (eq? s1 back-half))
  481. (display #\space o-port)
  482. (display (car s1) o-port)
  483. (write-spaces (max 1 (- 32 (string-length (car s1)))) o-port)
  484. (if (not (null? s2))
  485. (display (car s2) o-port))
  486. (newline o-port)
  487. (loop (cdr s1) (if (null? s2) s2 (cdr s2))))))))
  488. ;----------------
  489. ; Utilities
  490. (define (error-form proc args)
  491. (cons proc (map value->expression args)))
  492. ; Print non-self-evaluating value X as 'X.
  493. (define (value->expression obj) ;mumble
  494. (if (or (symbol? obj)
  495. (pair? obj)
  496. (null? obj)
  497. (vector? obj))
  498. `',obj
  499. obj))
  500. (define (write-spaces count o-port)
  501. (do ((count count (- count 1)))
  502. ((<= count 0))
  503. (display #\space o-port)))
  504. (define (command-prefix-string prefix)
  505. (cond ((string? prefix) prefix)
  506. ((char? prefix) (string prefix))
  507. ((symbol? prefix) (symbol->string prefix))))
  508. (define (y-or-n? question eof-value)
  509. (let ((i-port (command-input))
  510. (o-port (command-output)))
  511. (let loop ((count *y-or-n-eof-count*))
  512. (display question o-port)
  513. (display " (y/n)? " o-port)
  514. (let ((line (read-line i-port)))
  515. (cond ((eof-object? line)
  516. (newline o-port)
  517. (if (= count 0)
  518. eof-value
  519. (begin (display "I'll only ask another " o-port)
  520. (write count o-port)
  521. (display " times." o-port)
  522. (newline o-port)
  523. (loop (- count 1)))))
  524. ((< (string-length line) 1) (loop count))
  525. ((char=? (string-ref line 0) #\y) #t)
  526. ((char=? (string-ref line 0) #\n) #f)
  527. (else (loop count)))))))
  528. (define *y-or-n-eof-count* 100)
  529. (define (read-line port)
  530. (let loop ((l '()))
  531. (let ((c (read-char port)))
  532. (if (eof-object? c)
  533. c
  534. (if (char=? c #\newline)
  535. (list->string (reverse l))
  536. (loop (cons c l)))))))
  537. (define (greet-user info)
  538. (let ((port (command-output)))
  539. (display "Welcome to Scheme 48 " port)
  540. (display version-info port)
  541. (if info
  542. (begin (write-char #\space port)
  543. (display info port)))
  544. (newline port)
  545. (write-line "Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees."
  546. port)
  547. (write-line "Please report bugs to scheme-48-bugs@s48.org."
  548. port)
  549. (write-line "Get more information at http://www.s48.org/."
  550. port)
  551. (if (not (batch-mode?))
  552. (write-line "Type ,? (comma question-mark) for help." port))))
  553. (define (command-continuation) ;utility for debugger
  554. (let ((obj (focus-object)))
  555. (cond ((debug-command-level)
  556. => (lambda (level)
  557. (if (command-level-paused-thread level)
  558. (thread-continuation (command-level-paused-thread level))
  559. (let ((threads (command-level-threads level)))
  560. (if (= 1 (length threads))
  561. (thread-continuation (car threads))
  562. #f)))))
  563. ((continuation? obj)
  564. obj)
  565. ((thread? obj)
  566. (thread-continuation obj))
  567. (else #f))))
  568. (define (command-threads) ;utility for debugger
  569. (let ((level (debug-command-level)))
  570. (if level
  571. (command-level-threads level)
  572. #f)))
  573. (define (debug-command-level)
  574. (let* ((obj (focus-object)))
  575. (if (command-level? obj)
  576. obj
  577. (let ((levels (command-levels)))
  578. (if (null? (cdr levels))
  579. #f
  580. (cadr levels))))))