Commit | Line | Data |
---|---|---|
17e90c5e KN |
1 | ;;; Read-Eval-Print Loop |
2 | ||
eaba53b7 | 3 | ;; Copyright (C) 2001, 2009, 2010, 2011 Free Software Foundation, Inc. |
17e90c5e | 4 | |
eb721799 AW |
5 | ;; This library is free software; you can redistribute it and/or |
6 | ;; modify it under the terms of the GNU Lesser General Public | |
7 | ;; License as published by the Free Software Foundation; either | |
8 | ;; version 3 of the License, or (at your option) any later version. | |
17e90c5e | 9 | ;; |
eb721799 | 10 | ;; This library is distributed in the hope that it will be useful, |
17e90c5e | 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
eb721799 AW |
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | ;; Lesser General Public License for more details. | |
17e90c5e | 14 | ;; |
eb721799 AW |
15 | ;; You should have received a copy of the GNU Lesser General Public |
16 | ;; License along with this library; if not, write to the Free Software | |
17 | ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
18 | ;; 02110-1301 USA | |
17e90c5e KN |
19 | |
20 | ;;; Code: | |
21 | ||
22 | (define-module (system repl repl) | |
8239263f | 23 | #:use-module (system base syntax) |
1a1a10d3 AW |
24 | #:use-module (system base pmatch) |
25 | #:use-module (system base compile) | |
26 | #:use-module (system base language) | |
33df2ec7 AW |
27 | #:use-module (system vm vm) |
28 | #:use-module (system repl error-handling) | |
1a1a10d3 AW |
29 | #:use-module (system repl common) |
30 | #:use-module (system repl command) | |
3ae78d95 AW |
31 | #:use-module (ice-9 control) |
32 | #:export (start-repl run-repl)) | |
17e90c5e | 33 | |
33df2ec7 AW |
34 | \f |
35 | ||
36 | ;;; | |
37 | ;;; Meta commands | |
38 | ;;; | |
39 | ||
3a6f6678 AW |
40 | (define meta-command-token (cons 'meta 'command)) |
41 | ||
4b2afc62 | 42 | (define (meta-reader read env) |
c372cd74 AW |
43 | (lambda* (#:optional (port (current-input-port))) |
44 | (with-input-from-port port | |
45 | (lambda () | |
859e58ae | 46 | (let ((ch (flush-leading-whitespace))) |
c372cd74 AW |
47 | (cond ((eof-object? ch) |
48 | ;; EOF objects are not buffered. It's quite possible | |
49 | ;; to peek an EOF then read something else. It's | |
50 | ;; strange but it's how it works. | |
51 | ch) | |
52 | ((eqv? ch #\,) | |
53 | (read-char port) | |
54 | meta-command-token) | |
55 | (else (read port env)))))))) | |
3a6f6678 | 56 | |
dcb7c7dd AW |
57 | (define (flush-all-input) |
58 | (if (and (char-ready?) | |
59 | (not (eof-object? (peek-char)))) | |
60 | (begin | |
61 | (read-char) | |
62 | (flush-all-input)))) | |
63 | ||
3a6f6678 AW |
64 | ;; repl-reader is a function defined in boot-9.scm, and is replaced by |
65 | ;; something else if readline has been activated. much of this hoopla is | |
66 | ;; to be able to re-use the existing readline machinery. | |
b93c34c0 AW |
67 | ;; |
68 | ;; Catches read errors, returning *unspecified* in that case. | |
3a6f6678 | 69 | (define (prompting-meta-read repl) |
33df2ec7 AW |
70 | (catch #t |
71 | (lambda () | |
72 | (repl-reader (lambda () (repl-prompt repl)) | |
73 | (meta-reader (language-reader (repl-language repl)) | |
74 | (current-module)))) | |
75 | (lambda (key . args) | |
76 | (case key | |
77 | ((quit) | |
78 | (apply throw key args)) | |
79 | (else | |
eaba53b7 AW |
80 | (format (current-output-port) "While reading expression:\n") |
81 | (print-exception (current-output-port) #f key args) | |
dcb7c7dd | 82 | (flush-all-input) |
33df2ec7 | 83 | *unspecified*))))) |
67c4505e | 84 | |
652f48c0 AW |
85 | \f |
86 | ||
87 | ;;; | |
88 | ;;; The repl | |
89 | ;;; | |
90 | ||
33df2ec7 AW |
91 | (define* (start-repl #:optional (lang (current-language)) #:key debug) |
92 | (run-repl (make-repl lang debug))) | |
93 | ||
ffe911f7 AW |
94 | ;; (put 'abort-on-error 'scheme-indent-function 1) |
95 | (define-syntax abort-on-error | |
96 | (syntax-rules () | |
97 | ((_ string exp) | |
98 | (catch #t | |
99 | (lambda () exp) | |
100 | (lambda (key . args) | |
101 | (format #t "While ~A:~%" string) | |
eaba53b7 | 102 | (print-exception (current-output-port) #f key args) |
ffe911f7 AW |
103 | (abort)))))) |
104 | ||
33df2ec7 | 105 | (define (run-repl repl) |
52738540 AW |
106 | (define (with-stack-and-prompt thunk) |
107 | (call-with-prompt (default-prompt-tag) | |
108 | (lambda () (start-stack #t (thunk))) | |
109 | (lambda (k proc) | |
110 | (with-stack-and-prompt (lambda () (proc k)))))) | |
111 | ||
3ae78d95 AW |
112 | (% (with-fluids ((*repl-stack* |
113 | (cons repl (or (fluid-ref *repl-stack*) '())))) | |
114 | (if (null? (cdr (fluid-ref *repl-stack*))) | |
115 | (repl-welcome repl)) | |
116 | (let prompt-loop () | |
117 | (let ((exp (prompting-meta-read repl))) | |
118 | (cond | |
119 | ((eqv? exp *unspecified*)) ; read error, pass | |
120 | ((eq? exp meta-command-token) | |
5bc97ad5 AW |
121 | (catch #t |
122 | (lambda () | |
123 | (meta-command repl)) | |
3ae78d95 | 124 | (lambda (k . args) |
5bc97ad5 AW |
125 | (if (eq? k 'quit) |
126 | (abort args) | |
127 | (begin | |
a627100b | 128 | (format #t "While executing meta-command:~%") |
eaba53b7 | 129 | (print-exception (current-output-port) #f k args)))))) |
3ae78d95 AW |
130 | ((eof-object? exp) |
131 | (newline) | |
132 | (abort '())) | |
133 | (else | |
134 | ;; since the input port is line-buffered, consume up to the | |
135 | ;; newline | |
136 | (flush-to-newline) | |
137 | (call-with-error-handling | |
138 | (lambda () | |
139 | (catch 'quit | |
140 | (lambda () | |
141 | (call-with-values | |
142 | (lambda () | |
ffe911f7 AW |
143 | (% (let ((thunk |
144 | (abort-on-error "compiling expression" | |
145 | (repl-prepare-eval-thunk | |
146 | repl | |
147 | (abort-on-error "parsing expression" | |
148 | (repl-parse repl exp)))))) | |
149 | (run-hook before-eval-hook exp) | |
150 | (with-error-handling | |
52738540 | 151 | (with-stack-and-prompt thunk))) |
ffe911f7 | 152 | (lambda (k) (values)))) |
3ae78d95 AW |
153 | (lambda l |
154 | (for-each (lambda (v) | |
155 | (repl-print repl v)) | |
156 | l)))) | |
157 | (lambda (k . args) | |
b0e556d4 AW |
158 | (abort args)))) |
159 | #:trap-handler 'disabled))) | |
859e58ae | 160 | (flush-to-newline) ;; consume trailing whitespace |
3ae78d95 | 161 | (prompt-loop)))) |
33df2ec7 | 162 | (lambda (k status) |
3ae78d95 | 163 | status))) |
17e90c5e | 164 | |
859e58ae AW |
165 | ;; Returns first non-whitespace char. |
166 | (define (flush-leading-whitespace) | |
167 | (let ((ch (peek-char))) | |
168 | (cond ((eof-object? ch) ch) | |
169 | ((char-whitespace? ch) (read-char) (flush-leading-whitespace)) | |
170 | (else ch)))) | |
0d646345 AW |
171 | |
172 | (define (flush-to-newline) | |
173 | (if (char-ready?) | |
174 | (let ((ch (peek-char))) | |
175 | (if (and (not (eof-object? ch)) (char-whitespace? ch)) | |
176 | (begin | |
177 | (read-char) | |
178 | (if (not (char=? ch #\newline)) | |
179 | (flush-to-newline))))))) |