diff --git a/scribble-doc/scribblings/scribble/demo-manual.scrbl b/scribble-doc/scribblings/scribble/demo-manual.scrbl index ca89f3d5..b3161d8c 100644 --- a/scribble-doc/scribblings/scribble/demo-manual.scrbl +++ b/scribble-doc/scribblings/scribble/demo-manual.scrbl @@ -51,7 +51,7 @@ @item{@css{RktErr} (errors): @racketerror{example} or the error message in - @interaction[(+ 1 'a)]} + @interaction[(eval:error (+ 1 'a))]} @item{@css{RktCmt} (comments): @racketcommentfont{example} or diff --git a/scribble-doc/scribblings/scribble/eval.scrbl b/scribble-doc/scribblings/scribble/eval.scrbl index e7903a96..3d789c68 100644 --- a/scribble-doc/scribblings/scribble/eval.scrbl +++ b/scribble-doc/scribblings/scribble/eval.scrbl @@ -53,6 +53,13 @@ Certain patterns in @racket[datum] are treated specially: @racketidfont{code:blank} are stripped from each @racket[datum] before evaluation.} + @item{A @racket[datum] of the form + @racket[(@#,indexed-racket[eval:error] #,(svar eval-datum))] + is treated like @racket[_eval-datum], but @racket[_eval-datum] is expected to + raise an exception: no error is logged if an exception is raised, + but an exception is raised if @racket[_eval-datum] + does @emph{not} raise an exception.} + @item{A @racket[datum] of the form @racket[(@#,indexed-racket[eval:alts] #,(svar show-datum) #,(svar eval-datum))] is treated as @svar[show-datum] for typesetting and @svar[eval-datum] for evaluation.} @@ -81,6 +88,10 @@ Certain patterns in @racket[datum] are treated specially: ] +By default, if evaluation raises an exception, an error is shown as +the evaluation's result, but an error is also logged. Use @racket[eval:error] +to avoid logging exceptions. + As an example, @codeblock|{ @@ -99,7 +110,12 @@ As an example, (my-sqr 42)] }| -uses an evaluator whose language is @racketmodname[typed/racket/base].} +uses an evaluator whose language is @racketmodname[typed/racket/base]. + +@history[#:changed "1.14" @elem{Added @racket[eval:error] and added + logging of exceptions from expressions + that are not wrapped with + @racket[eval:error].}]} @defform[(interaction0 maybe-eval maybe-escape datum ...)]{ Like @racket[interaction], but without insetting the code via @@ -112,7 +128,8 @@ Like @racket[interaction], but without insetting the code via @defform[(interaction-eval maybe-eval maybe-escape datum)]{ Like @racket[interaction], evaluates the @racket[quote]d form of -@racket[datum], but returns the empty string and does not catch errors.} +@racket[datum], but returns the empty string and does not catch +exceptions (so @racket[eval:error] has no effect).} @defform[(interaction-eval-show maybe-eval maybe-escape datum)]{ diff --git a/scribble-lib/info.rkt b/scribble-lib/info.rkt index c2a8adfb..72562e22 100644 --- a/scribble-lib/info.rkt +++ b/scribble-lib/info.rkt @@ -23,4 +23,4 @@ (define pkg-authors '(mflatt eli)) -(define version "1.13") +(define version "1.14") diff --git a/scribble-lib/scribble/eval.rkt b/scribble-lib/scribble/eval.rkt index 04d4a074..a0ddc701 100644 --- a/scribble-lib/scribble/eval.rkt +++ b/scribble-lib/scribble/eval.rkt @@ -205,20 +205,23 @@ (eval-results (list content) out err)) (define (extract-to-evaluate s) - (let loop ([s s] [expect #f]) - (syntax-case s (code:line code:comment code:contract eval:alts eval:check) + (let loop ([s s] [expect #f] [error-expected? #f]) + (syntax-case s (code:line code:comment code:contract eval:alts eval:check eval:error) [(code:line v (code:comment . rest)) - (loop (extract s cdr car) expect)] + (loop (extract s cdr car) expect error-expected?)] [(code:comment . rest) - (values (nothing-to-eval) expect)] + (values (nothing-to-eval) expect error-expected?)] [(code:contract . rest) - (values (nothing-to-eval) expect)] + (values (nothing-to-eval) expect error-expected?)] + [(eval:error e) + (loop (extract s cdr car) expect #t)] [(eval:alts p e) - (loop (extract s cdr cdr car) expect)] + (loop (extract s cdr cdr car) expect error-expected?)] [(eval:check e expect) (loop (extract s cdr car) - (list (syntax->datum (datum->syntax #f (extract s cdr cdr car)))))] - [else (values s expect)]))) + (list (syntax->datum (datum->syntax #f (extract s cdr cdr car)))) + error-expected?)] + [else (values s expect error-expected?)]))) (define (do-eval ev who) (define (get-outputs) @@ -259,13 +262,22 @@ (map (current-print) v)) (close-output-port out) in)))]))) - (define (do-ev/expect s expect) + (define (do-ev/expect s expect error-expected?) (define-values (val render+output) (with-handlers ([(lambda (x) (not (exn:break? x))) (lambda (e) - (cons ((scribble-exn->string) e) - (get-outputs)))]) + (unless error-expected? + (log-error "interaction without `eval:error` raised an exception: ~s; form: ~.s" + (if (exn? e) + (exn-message e) + e) + s)) + (values e + (cons ((scribble-exn->string) e) + (get-outputs))))]) (define val (do-plain-eval ev s #t)) + (when error-expected? + (log-error "interaction failed to raise an expected exception: ~.s" s)) (values val (cons (render-value val) (get-outputs))))) (when expect (let ([expect (do-plain-eval ev (car expect) #t)]) @@ -277,10 +289,10 @@ (list (map formatted-result (eval-results-contents str)) (eval-results-out str) (eval-results-err str)) - (let-values ([(s expect) (extract-to-evaluate str)]) + (let-values ([(s expect error-expected?) (extract-to-evaluate str)]) (if (nothing-to-eval? s) (list (list (void)) "" "") - (do-ev/expect s expect)))))) + (do-ev/expect s expect error-expected?)))))) (module+ test (require rackunit) @@ -621,7 +633,7 @@ #'(quote e)])])) (define (do-interaction-eval ev e) - (let-values ([(e expect) (extract-to-evaluate e)]) + (let-values ([(e expect error-expected?/ignored) (extract-to-evaluate e)]) (unless (nothing-to-eval? e) (parameterize ([current-command-line-arguments #()]) (do-plain-eval (or ev (make-base-eval)) e #f))) @@ -646,15 +658,19 @@ [(_ e) (do-interaction-eval-show #f (quote-expr e))])) (define-syntax racketinput* - (syntax-rules (eval:alts code:comment) + (syntax-rules (eval:alts code:comment eval:check eval:error) [(_ #:escape id (code:comment . rest)) (racketblock0 #:escape id (code:comment . rest))] [(_ #:escape id (eval:alts a b)) (racketinput* #:escape id a)] + [(_ #:escape id (eval:check a b)) (racketinput* #:escape id a)] + [(_ #:escape id (eval:error a)) (racketinput* #:escape id a)] [(_ #:escape id e) (racketinput0 #:escape id e)])) (define-syntax racketblock* - (syntax-rules (eval:alts code:comment) + (syntax-rules (eval:alts code:comment eval:check eval:error) [(_ #:escape id (code:comment . rest)) (racketblock0 #:escape id (code:comment . rest))] [(_ #:escape id (eval:alts a b)) (racketblock #:escape id a)] + [(_ #:escape id (eval:check a b)) (racketblock #:escape id a)] + [(_ #:escape id (eval:error a)) (racketblock #:escape id a)] [(_ #:escape id e) (racketblock0 #:escape id e)])) (define-code racketblock0+line (to-paragraph/prefix "" "" (list " ")))