debug has for-trap? field
[bpt/guile.git] / module / system / repl / repl.scm
dissimilarity index 79%
index 57d78b1..fbe7b12 100644 (file)
-;;; Read-Eval-Print Loop
-
-;; Copyright (C) 2001 Free Software Foundation, Inc.
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
-;; 
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-;; 
-;; You should have received a copy of the GNU General Public License
-;; along with this program; see the file COPYING.  If not, write to
-;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
-
-;;; Code:
-
-(define-module (system repl repl)
-  :use-syntax (system base syntax)
-  :use-module (system repl common)
-  :use-module (system repl command)
-  :use-module (system vm core)
-  :use-module (ice-9 rdelim)
-  :export (start-repl))
-
-(define (start-repl lang)
-  (let ((repl (make-repl lang)))
-    (repl-welcome repl)
-    (let prompt-loop ()
-      (repl-prompt repl)
-      (catch 'vm-error
-       (lambda ()
-         (if (eq? (next-char #t) #\,)
-           ;; meta command
-           (begin (read-char) (meta-command repl (read-line)))
-           ;; evaluation
-           (let rep-loop ()
-             (call-with-values (lambda () (repl-eval repl (repl-read repl)))
-                (lambda l (for-each (lambda (v) (repl-print repl v)) l)))
-             (if (next-char #f) (rep-loop)))))
-       (lambda (key fun msg args)
-         (display "ERROR: ")
-         (apply format #t msg args)
-         (newline)))
-      (prompt-loop))))
-
-(define (next-char wait)
-  (if (or wait (char-ready?))
-      (let ((ch (peek-char)))
-       (cond ((eof-object? ch) (throw 'quit))
-             ((char-whitespace? ch) (read-char) (next-char wait))
-             (else ch)))
-      #f))
+;;; Read-Eval-Print Loop
+
+;; Copyright (C) 2001, 2009, 2010 Free Software Foundation, Inc.
+
+;; This library is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU Lesser General Public
+;; License as published by the Free Software Foundation; either
+;; version 3 of the License, or (at your option) any later version.
+;; 
+;; This library is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; Lesser General Public License for more details.
+;; 
+;; You should have received a copy of the GNU Lesser General Public
+;; License along with this library; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+;; 02110-1301 USA
+
+;;; Code:
+
+(define-module (system repl repl)
+  #:use-module (system base syntax)
+  #:use-module (system base pmatch)
+  #:use-module (system base compile)
+  #:use-module (system base language)
+  #:use-module (system vm vm)
+  #:use-module (system repl error-handling)
+  #:use-module (system repl common)
+  #:use-module (system repl command)
+  #:use-module (ice-9 control)
+  #:export (start-repl run-repl))
+
+\f
+
+;;;
+;;; Meta commands
+;;;
+
+(define meta-command-token (cons 'meta 'command))
+
+(define (meta-reader read env)
+  (lambda read-args
+    (let ((port (if (pair? read-args) (car read-args) (current-input-port))))
+      (with-input-from-port port
+        (lambda ()
+          (let ((ch (next-char #t)))
+            (cond ((eof-object? ch)
+                   ;; EOF objects are not buffered. It's quite possible
+                   ;; to peek an EOF then read something else. It's
+                   ;; strange but it's how it works.
+                   ch)
+                  ((eqv? ch #\,)
+                   (read-char port)
+                   meta-command-token)
+                  (else (read port env)))))))))
+        
+;; repl-reader is a function defined in boot-9.scm, and is replaced by
+;; something else if readline has been activated. much of this hoopla is
+;; to be able to re-use the existing readline machinery.
+;;
+;; Catches read errors, returning *unspecified* in that case.
+(define (prompting-meta-read repl)
+  (catch #t
+    (lambda ()
+      (repl-reader (lambda () (repl-prompt repl))
+                   (meta-reader (language-reader (repl-language repl))
+                                (current-module))))
+    (lambda (key . args)
+      (case key
+        ((quit)
+         (apply throw key args))
+        (else
+         (pmatch args
+           ((,subr ,msg ,args . ,rest)
+            (format #t "Throw to key `~a' while reading expression:\n" key)
+            (display-error #f (current-output-port) subr msg args rest))
+           (else
+            (format #t "Throw to key `~a' with args `~s' while reading expression.\n"
+                    key args)))
+         (force-output)
+         *unspecified*)))))
+
+\f
+
+;;;
+;;; The repl
+;;;
+
+(define* (start-repl #:optional (lang (current-language)) #:key debug)
+  (run-repl (make-repl lang debug)))
+
+;; (put 'abort-on-error 'scheme-indent-function 1)
+(define-syntax abort-on-error
+  (syntax-rules ()
+    ((_ string exp)
+     (catch #t
+       (lambda () exp)
+       (lambda (key . args)
+         (format #t "While ~A:~%" string)
+         (pmatch args
+           ((,subr ,msg ,args . ,rest)
+            (display-error #f (current-output-port) subr msg args rest))
+           (else
+            (format #t "ERROR: Throw to key `~a' with args `~s'.\n" key args)))
+         (force-output)
+         (abort))))))
+
+(define (run-repl repl)
+  (define (with-stack-and-prompt thunk)
+    (call-with-prompt (default-prompt-tag)
+                      (lambda () (start-stack #t (thunk)))
+                      (lambda (k proc)
+                        (with-stack-and-prompt (lambda () (proc k))))))
+  
+  (% (with-fluids ((*repl-stack*
+                    (cons repl (or (fluid-ref *repl-stack*) '()))))
+       (if (null? (cdr (fluid-ref *repl-stack*)))
+           (repl-welcome repl))
+       (let prompt-loop ()
+         (let ((exp (prompting-meta-read repl)))
+           (cond
+            ((eqv? exp *unspecified*))  ; read error, pass
+            ((eq? exp meta-command-token)
+             (catch #t
+               (lambda ()
+                 (meta-command repl))
+               (lambda (k . args)
+                 (if (eq? k 'quit)
+                     (abort args)
+                     (begin
+                       (format #t "While executing meta-command:~%")
+                       (pmatch args
+                         ((,subr ,msg ,args . ,rest)
+                          (display-error #f (current-output-port) subr msg args rest))
+                         (else
+                          (format #t "ERROR: Throw to key `~a' with args `~s'.\n" k args)))
+                       (force-output))))))
+            ((eof-object? exp)
+             (newline)
+             (abort '()))
+            (else
+             ;; since the input port is line-buffered, consume up to the
+             ;; newline
+             (flush-to-newline)
+             (call-with-error-handling
+              (lambda ()
+                (catch 'quit
+                  (lambda ()
+                    (call-with-values
+                        (lambda ()
+                          (% (let ((thunk
+                                    (abort-on-error "compiling expression"
+                                      (repl-prepare-eval-thunk
+                                       repl
+                                       (abort-on-error "parsing expression"
+                                         (repl-parse repl exp))))))
+                               (run-hook before-eval-hook exp)
+                               (with-error-handling
+                                 (with-stack-and-prompt thunk)))
+                             (lambda (k) (values))))
+                      (lambda l
+                        (for-each (lambda (v)
+                                    (repl-print repl v))
+                                  l))))
+                  (lambda (k . args)
+                    (abort args))))
+              #:trap-handler 'disabled)))
+           (next-char #f) ;; consume trailing whitespace
+           (prompt-loop))))
+     (lambda (k status)
+       status)))
+
+(define (next-char wait)
+  (if (or wait (char-ready?))
+      (let ((ch (peek-char)))
+       (cond ((eof-object? ch) ch)
+             ((char-whitespace? ch) (read-char) (next-char wait))
+             (else ch)))
+      #f))
+
+(define (flush-to-newline) 
+  (if (char-ready?)
+      (let ((ch (peek-char)))
+        (if (and (not (eof-object? ch)) (char-whitespace? ch))
+            (begin
+              (read-char)
+              (if (not (char=? ch #\newline))
+                  (flush-to-newline)))))))