An extensible POSIX-compatible implementation of the ed(1) text editor
git clone https://git.8pit.net/edward.git
1;;>| Read–Eval–Print Loop 2;;> 3;;> REPL abstraction which provides a read-eval-print loop that 4;;> continously reads data from standard input and parses this 5;;> input data using provided parser combinators. The REPL 6;;> operates on a [parse stream](#section-parse-streams) internally. 7 8;;> Create an new REPL instance with the given input `prompt` string. 9 10(define (make-repl prompt) 11 (let ((prompt? (not (empty-string? prompt)))) 12 (%make-repl 13 (if prompt? prompt "*") 14 prompt? 15 (make-parse-stream "stdin" fileno/stdin) 16 0))) 17 18(define (repl-state-set! repl source index) 19 (repl-stream-set! repl source) 20 (repl-index-set! repl index)) 21 22;;> Record type representing the REPL. 23(define-record-type Read-Eval-Print-Loop 24 (%make-repl prompt-str prompt? stream index) 25 ;;> Predicate which returns true if the given object was created using [make-repl](#make-repl). 26 repl? 27 ;; Prompt string used for input prompt. 28 (prompt-str repl-prompt-str) 29 ;; Whether the prompt should be shown or hidden. 30 (prompt? 31 ;;> Predicate which returns true if the prompt should be shown. 32 repl-prompt? 33 34 ;;> Change prompt visibility, a truth value means the prompt is shown. 35 repl-set-prompt!) 36 ;; Parse stream used for the parser combinator. 37 (stream repl-stream repl-stream-set!) 38 ;; Last index in parse stream. 39 (index repl-index repl-index-set!)) 40 41;; Skip all buffered chunks, i.e. next read will block. 42 43(define (repl-skip-chunks! repl) 44 (define (%repl-skip-chunks! source i) 45 (if (>= (+ i 1) (vector-length (parse-stream-buffer source))) 46 (%repl-skip-chunks! (parse-stream-tail source) i) ;; go to last chunck 47 (values 48 source 49 ;; inc to go beyond last char. 50 (inc (parse-stream-max-char source))))) 51 52 (let-values (((source i) 53 (%repl-skip-chunks! 54 (repl-stream repl) 55 (repl-index repl)))) 56 (repl-state-set! repl source i))) 57 58(define (repl-parse repl f sk fk) 59 (define (stream-next-line source idx) 60 (let* ((next-index (parse-stream-next-index source idx)) 61 (next-source (parse-stream-next-source source idx)) 62 (char (parse-stream-ref source idx))) 63 (if (or (eof-object? char) (char=? char #\newline)) 64 (cons next-source next-index) ;; first index after newline/eof 65 (stream-next-line 66 next-source 67 next-index)))) 68 69 (call-with-parse f 70 (repl-stream repl) 71 (repl-index repl) 72 (lambda (r s i fk) 73 (repl-state-set! repl s i) 74 (sk (repl-line repl i) r)) 75 (lambda (s i reason) 76 (let ((line (repl-line repl i)) 77 (next (stream-next-line (repl-stream repl) i))) 78 (repl-state-set! repl (car next) (cdr next)) 79 (fk line reason))))) 80 81(define (repl-line repl index) 82 (let ((s (repl-stream repl))) 83 (inc ;; XXX: For some reason line start at zero. 84 (+ 85 (parse-stream-line s) 86 (car (parse-stream-count-lines s (parse-stream-max-char s))))))) 87 88;;> Start the REPL given by `repl`, and continuously parse input using 89;;> the provided parser `f`. Successfully parsed input is passed to 90;;> the success continuation `sk`, which receives the line number and 91;;> parser result as procedure arguments. If the parser failed for the 92;;> current input, the failure continuation `fk` is invoked. This 93;;> continuation receives the line number and failure reason as 94;;> procedure arguments. Lastly, an interrupt continuation must 95;;> also be provided which is invoked on `SIGINT`. This continuation 96;;> is not passed any arguments. 97 98(define (repl-run repl f sk fk ik) 99 (when (repl-prompt? repl)100 (display (repl-prompt-str repl))101 (flush-output-port))102103 ;; Allow parsing itself (especially of input mode commands) to be104 ;; interrupted by SIGINT signals. See "Asynchronous Events" in ed(1).105 (call-with-current-continuation106 (lambda (k)107 (set-signal-handler!108 signal/int109 (lambda (signum)110 (ik)111 (repl-skip-chunks! repl)112 (k #f)))113114 (begin115 (repl-parse repl f sk fk)116 (k #f))))117118 (repl-run repl f sk fk ik))119120;;> Run a parser interactively within the REPL. That is, deviate from121;;> the standard REPL parser and instead parse the next input line122;;> with the given parser `f`. On success, returns the result of `f`123;;> otherwise, invokes the provided failure continuation `fk`.124125(define (repl-interactive repl f fk)126 (repl-parse repl f (lambda (line value) value) fk))