diff --git a/pkgs/racket-doc/syntax/scribblings/parse/patterns.scrbl b/pkgs/racket-doc/syntax/scribblings/parse/patterns.scrbl index eb0f7b9ade..3908793c9e 100644 --- a/pkgs/racket-doc/syntax/scribblings/parse/patterns.scrbl +++ b/pkgs/racket-doc/syntax/scribblings/parse/patterns.scrbl @@ -34,7 +34,7 @@ means specifically @tech{@Spattern}. @racketgrammar*[#:literals (_ ~var ~literal ~or ~alt ~or* ~and ~not ~rest ~datum ~describe ~seq ~optional ~rep ~once ~between - ~! ~bind ~fail ~parse ~peek ~peek-not ~do ~post) + ~! ~bind ~fail ~parse ~peek ~peek-not ~do ~undo ~post) [S-pattern pvar-id pvar-id:syntax-class-id @@ -98,7 +98,8 @@ means specifically @tech{@Spattern}. (~parse S-pattern stx-expr) (@#,ref[~and a] A-pattern ...+) (@#,ref[~post a] A-pattern) - (~do defn-or-expr ...)] + (~do defn-or-expr ...) + (~undo defn-or-expr ...)] [proper-S-pattern #, @elem{a @svar{S-pattern} that is not a @svar{A-pattern}}] [proper-H-pattern @@ -1095,6 +1096,36 @@ definition in a @racket[~do] block. ] } +@specsubform[(@#,defhere[~undo] defn-or-expr ...)]{ + +Has no effect when initially matched, but if backtracking returns to a +point @emph{before} the @racket[~undo] pattern, the +@racket[defn-or-expr]s are executed. They are evaluated in the scope +of all previous attribute bindings. + +Use @racket[~do] paired with @racket[~undo] to perform side effects +and then unwind them if the enclosing pattern is later discarded. + +@examples[#:eval the-eval +(define total 0) +(define-syntax-class nat/add + (pattern (~and n:nat + (~do (printf "adding ~s\n" (syntax-e #'n)) + (set! total (+ total (syntax-e #'n)))) + (~undo (printf "subtracting ~s\n" (syntax-e #'n)) + (set! total (- total (syntax-e #'n))))))) + +(syntax-parse #'(1 2 3) + [(x:nat/add ...) 'ok]) +total +(set! total 0) +(syntax-parse #'(1 2 3 bad) + [(x:nat/add ...) 'ok] + [_ 'something-else]) +total +] +} + @specsubform[(@#,def[~post a] A-pattern)]{ Like the @Spattern version, @ref[~post s], but contains only diff --git a/pkgs/racket-doc/syntax/scribblings/parse/stxclasses.scrbl b/pkgs/racket-doc/syntax/scribblings/parse/stxclasses.scrbl index 2fb7b3aa2d..fadef6e92b 100644 --- a/pkgs/racket-doc/syntax/scribblings/parse/stxclasses.scrbl +++ b/pkgs/racket-doc/syntax/scribblings/parse/stxclasses.scrbl @@ -200,7 +200,8 @@ follows: (code:line #:fail-when condition-expr message-expr) (code:line #:fail-unless condition-expr message-expr) (code:line #:when condition-expr) - (code:line #:do [def-or-expr ...])] + (code:line #:do [def-or-expr ...]) + (code:line #:undo [def-or-expr ...])] @specsubform[(code:line #:declare pvar-id stxclass maybe-role) #:grammar @@ -325,7 +326,7 @@ backtracks. In other words, @racket[#:when] is like Equivalent to @racket[#:post (~fail #:unless condition-expr #f)]. } -@specsubform[(code:line #:do [def-or-expr ...])]{ +@specsubform[(code:line #:do [defn-or-expr ...])]{ Takes a sequence of definitions and expressions, which may be intermixed, and evaluates them in the scope of all previous attribute @@ -336,7 +337,17 @@ There is currently no way to bind attributes using a @racket[#:do] block. It is an error to shadow an attribute binding with a definition in a @racket[#:do] block. -Equivalent to @racket[#:and (~do def-or-expr ...)]. +Equivalent to @racket[#:and (~do defn-or-expr ...)]. +} + +@specsubform[(code:line #:undo [defn-or-expr ...])]{ + +Has no effect when initially matched, but if backtracking returns to a +point @emph{before} the @racket[#:undo] directive, the +@racket[defn-or-expr]s are executed. See @racket[~undo] for an +example. + +Equivalent to @racket[#:and (~undo defn-or-expr ...)]. }