diff --git a/module/ice-9/boot-9.scm b/module/ice-9/boot-9.scm index c26a7e678..8d0432354 100644 --- a/module/ice-9/boot-9.scm +++ b/module/ice-9/boot-9.scm @@ -2842,28 +2842,46 @@ module '(ice-9 q) '(make-q q-length))}." ;;; with `continue' and `break'. ;;; -;; The inner `do' loop avoids re-establishing a catch every iteration, -;; that's only necessary if continue is actually used. A new key is -;; generated every time, so break and continue apply to their originating -;; `while' even when recursing. +;; The inliner will remove the prompts at compile-time if it finds that +;; `continue' or `break' are not used. ;; -;; FIXME: This macro is unintentionally unhygienic with respect to let, -;; make-symbol, do, throw, catch, lambda, and not. -;; -(define-macro (while cond . body) - (let ((keyvar (make-symbol "while-keyvar"))) - `(let ((,keyvar (make-symbol "while-key"))) - (do () - ((catch ,keyvar - (lambda () - (let ((break (lambda () (throw ,keyvar #t))) - (continue (lambda () (throw ,keyvar #f)))) - (do () - ((not ,cond)) - ,@body) - #t)) - (lambda (key arg) - arg))))))) +(define-syntax while + (lambda (x) + (syntax-case x () + ((while cond body ...) + #`(let ((break-tag (make-prompt-tag "break")) + (continue-tag (make-prompt-tag "continue"))) + (call-with-prompt + break-tag + (lambda () + (define-syntax #,(datum->syntax #'while 'break) + (lambda (x) + (syntax-case x () + ((_) + #'(abort-to-prompt break-tag)) + ((_ . args) + (syntax-violation 'break "too many arguments" x)) + (_ + #'(lambda () + (abort-to-prompt break-tag)))))) + (let lp () + (call-with-prompt + continue-tag + (lambda () + (define-syntax #,(datum->syntax #'while 'continue) + (lambda (x) + (syntax-case x () + ((_) + #'(abort-to-prompt continue-tag)) + ((_ . args) + (syntax-violation 'continue "too many arguments" x)) + (_ + #'(lambda args + (apply abort-to-prompt continue-tag args)))))) + (do () ((not cond)) body ...)) + (lambda (k) (lp))))) + (lambda (k) + #t))))))) diff --git a/test-suite/tests/syntax.test b/test-suite/tests/syntax.test index 49dca1d24..f347c2cec 100644 --- a/test-suite/tests/syntax.test +++ b/test-suite/tests/syntax.test @@ -1,6 +1,6 @@ ;;;; syntax.test --- test suite for Guile's syntactic forms -*- scheme -*- ;;;; -;;;; Copyright (C) 2001,2003,2004, 2005, 2006, 2009 Free Software Foundation, Inc. +;;;; Copyright (C) 2001,2003,2004, 2005, 2006, 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 @@ -75,6 +75,9 @@ (define exception:bad-cond-clause (cons 'syntax-error "Bad cond clause")) +(define exception:too-many-args + (cons 'syntax-error "too many arguments")) + (with-test-prefix "expressions" @@ -832,7 +835,7 @@ #t)))) - (pass-if-exception "too few args" exception:wrong-num-args + (pass-if-exception "too few args" exception:generic-syncase-error (eval '(while) (interaction-environment))) (with-test-prefix "empty body" @@ -872,7 +875,7 @@ (with-test-prefix "break" - (pass-if-exception "too many args" exception:wrong-num-args + (pass-if-exception "too many args" exception:too-many-args (eval '(while #t (break 1)) (interaction-environment))) @@ -945,7 +948,7 @@ (with-test-prefix "continue" - (pass-if-exception "too many args" exception:wrong-num-args + (pass-if-exception "too many args" exception:too-many-args (eval '(while #t (continue 1)) (interaction-environment)))