scsh-expect/scheme/interact.scm

82 lines
3.8 KiB
Scheme
Raw Normal View History

(define-syntax interact-clause-test
(syntax-rules (filter eof)
((interact-clause-test char break) #t)
((interact-clause-test eof break (eof body ...) clause2 ...)
(begin body ...))
((interact-clause-test eof break clause1 clause2 ...)
(interact-clause-test eof break clause2 ...))
((interact-clause-test char break (eof body ...) clause2 ...)
(interact-clause-test char break clause2 ...))
((interact-clause-test char break (filter filter-proc) clause2 ...)
(and (filter-proc char break)
(interact-clause-test char break clause2 ...)))
((interact-clause-test char break (test-char k body ...) clause2 ...)
(if (eq? char test-char)
(and (let ((k break)) body ...)
(interact-clause-test char break clause2 ...))
(interact-clause-test char break clause2 ...)))))
;;; Allow user to interact with program (sometimes there's just no other way)
;;; (interact task) simply switches the user into interact mode with the task.
;;;
;;; (interact task clause ...) filters the input. The clause can either be a
;;; character clause (character continuation-variable exp ...) or a filter
;;; clause (filter proc). A character clause matches the character given,
;;; links the continuation variable to the continuation out of the
;;; interaction, and runs the expressions. A filter clause runs the given
;;; filter-function with two arguments: the character entered and the
;;; continuation out of the interaction. In both cases, if the filter returns
;;; true, the expression falls through. If the expression falls through to
;;; the bottom, the character is printed. Note that the interaction will
;;; continue anyway, unless the continuation was explicitly called.
(define-syntax interact
(syntax-rules ()
;; If there is just one argument, simply enable transfer.
((interact with)
(call-with-current-continuation
(lambda (break)
(let ((ivec (make-vector 2))
(ivec0 (current-input-port))
(ivec1 (task:in with)))
(with-errno-handler ((e p) ((errno/io) #f))
(let lp ()
(vector-set! ivec 0 ivec0)
(vector-set! ivec 1 ivec1)
(select! ivec '#() '#())
(if (char-ready? ivec0)
(write-string (read-string/partial 256 ivec0)
(task:out with)))
(if (char-ready? ivec1)
(write-string (read-string/partial 256 ivec1)))
(lp)))))))
;; If there is more than one argument,
;;enable transfer and allow escape keys.
((interact with clauses ...)
(call-with-current-continuation
(lambda (break)
(let ((ivec (make-vector 2))
(ivec0 (current-input-port))
(ivec1 (task:in with)))
(with-errno-handler ((e p)
((errno/io)
(interact-clause-test eof #f clauses ...)))
(let lp ()
(vector-set! ivec 0 ivec0)
(vector-set! ivec 1 ivec1)
(select! ivec '#() '#())
(if (char-ready? (current-input-port))
(for-each (lambda (transfer)
;; Test for indicated escape keys.
(and (interact-clause-test transfer break
clauses ...)
;; Only write if all clauses "fell through"
(write-char transfer (task:out with))))
(string->list (read-string/partial 256 (current-inpu
t-port)))))
(if (char-ready? (task:in with))
(write-string (read-string/partial 256 (task:in with))))
(lp)))))))))