1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-29 19:30:36 +02:00

Re-implement `guard'

* module/ice-9/exceptions.scm (guard): Add guard definition that
  re-propagates from original continuation, runs consequents in tail
  position in guard continuation, and doesn't rewind the stack.
* module/srfi/srfi-34.scm:
* module/rnrs/exceptions.scm (guard): Re-export from (ice-9
  exceptions).
This commit is contained in:
Andy Wingo 2020-01-10 21:42:26 +01:00
parent 9f2b703101
commit 8068994ba8
3 changed files with 74 additions and 62 deletions

View file

@ -1,5 +1,5 @@
;;; Exceptions
;;; Copyright (C) 2019 Free Software Foundation, Inc.
;;; Copyright (C) 2019-2020 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
@ -97,7 +97,9 @@
make-undefined-variable-error
undefined-variable-error?
raise-continuable))
raise-continuable
guard))
(define-syntax define-exception-type-procedures
(syntax-rules ()
@ -339,3 +341,65 @@
;; Override core definition.
(set! make-exception-from-throw convert-guile-exception)
(define-syntax guard
(lambda (stx)
"Establish an exception handler during the evaluation of an expression.
@example
(guard (@var{exn} @var{clause1} @var{clause2} ...)
@var{body} @var{body*} ...)
@end example
Each @var{clause} should have the same form as a @code{cond} clause.
The @code{(begin body body* ...)} is evaluated with an exception
handler that binds the raised object to @var{exn} and within the scope of
that binding evaluates the clauses as if they were the clauses of a cond
expression.
When a clause of that implicit cond expression matches, its consequent
is evaluated with the continuation and dynamic environment of the
@code{guard} expression.
If every clause's test evaluates to false and there is no @code{else}
clause, then @code{raise-continuable} is re-invoked on the raised
object, within the dynamic environment of the original call to raise
except that the current exception handler is that of the guard
expression.
Note that in a slight deviation from SRFI-34, R6RS, and R7RS, Guile
evaluates the clause tests within the continuation of the exception
handler, not the continuation of the @code{guard}. This allows
unhandled exceptions to continue to dispatch within the original
continuation, without unwinding then rewinding any intermediate
@code{dynamic-wind} invocations."
(define (dispatch tag exn clauses)
(define (build-clause test handler clauses)
#`(let ((t #,test))
(if t
(abort-to-prompt #,tag #,handler t)
#,(dispatch tag exn clauses))))
(syntax-case clauses (=> else)
(() #`(raise-continuable #,exn))
(((test => f) . clauses)
(build-clause #'test #'(lambda (res) (f res)) #'clauses))
(((else e e* ...) . clauses)
(build-clause #'#t #'(lambda (res) e e* ...) #'clauses))
(((test) . clauses)
(build-clause #'test #'(lambda (res) res) #'clauses))
(((test e* ...) . clauses)
(build-clause #'test #'(lambda (res) e* ...) #'clauses))))
(syntax-case stx ()
((guard (exn clause clause* ...) body body* ...)
(identifier? #'exn)
#`(let ((tag (make-prompt-tag)))
(call-with-prompt
tag
(lambda ()
(with-exception-handler
(lambda (exn)
#,(dispatch #'tag #'exn #'(clause clause* ...)))
(lambda () body body* ...)))
(lambda (_ h v)
(h v))))))))

View file

@ -1,6 +1,6 @@
;;; exceptions.scm --- The R6RS exceptions library
;; Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc.
;; Copyright (C) 2010, 2011, 2013, 2020 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
@ -23,20 +23,4 @@
(rnrs control (6))
(rnrs conditions (6))
(rename (ice-9 exceptions)
(raise-exception raise)))
(define-syntax guard0
(syntax-rules ()
((_ (variable cond-clause ...) . body)
(call/cc (lambda (continuation)
(with-exception-handler
(lambda (variable)
(continuation (cond cond-clause ...)))
(lambda () . body)))))))
(define-syntax guard
(syntax-rules (else)
((_ (variable cond-clause ... . ((else else-clause ...))) . body)
(guard0 (variable cond-clause ... (else else-clause ...)) . body))
((_ (variable cond-clause ...) . body)
(guard0 (variable cond-clause ... (else (raise variable))) . body)))))
(raise-exception raise))))

View file

@ -1,6 +1,7 @@
;;; srfi-34.scm --- Exception handling for programs
;; Copyright (C) 2003, 2006, 2008, 2010, 2019 Free Software Foundation, Inc.
;; Copyright (C) 2003,2006,2008-2010,2019-2020
;; 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
@ -27,47 +28,10 @@
;;; Code:
(define-module (srfi srfi-34)
#:use-module ((ice-9 exceptions) #:select (guard))
#:re-export (with-exception-handler
(raise-exception . raise))
#:re-export-and-replace ((raise-exception . raise))
#:export-syntax (guard))
(raise-exception . raise)
guard)
#:re-export-and-replace ((raise-exception . raise)))
(cond-expand-provide (current-module) '(srfi-34))
(define-syntax guard
(syntax-rules (else)
"Syntax: (guard (<var> <clause1> <clause2> ...) <body>)
Each <clause> should have the same form as a `cond' clause.
Semantics: Evaluating a guard form evaluates <body> with an exception
handler that binds the raised object to <var> and within the scope of
that binding evaluates the clauses as if they were the clauses of a
cond expression. That implicit cond expression is evaluated with the
continuation and dynamic environment of the guard expression. If
every <clause>'s <test> evaluates to false and there is no else
clause, then raise is re-invoked on the raised object within the
dynamic environment of the original call to raise except that the
current exception handler is that of the guard expression."
((guard (var clause ... (else e e* ...)) body body* ...)
(with-exception-handler
(lambda (var)
(cond clause ...
(else e e* ...)))
(lambda () body body* ...)
#:unwind? #t))
((guard (var clause clause* ...) body body* ...)
(let ((tag (make-prompt-tag)))
(call-with-prompt
tag
(lambda ()
(with-exception-handler
(lambda (exn)
(abort-to-prompt tag exn)
(raise-exception exn))
(lambda () body body* ...)))
(lambda (rewind var)
(cond clause clause* ...
(else (rewind)))))))))
;;; (srfi srfi-34) ends here.