#lang racket/base
(require (for-syntax racket/base)
racket/stxparam)
(provide while break continue)
(define-syntax-parameter break
(lambda (stx)
(raise-syntax-error #f "Used outside the context of a while body" stx)))
(define-syntax-parameter continue
(lambda (stx)
(raise-syntax-error #f "Used outside the context of a while body" stx)))
(define-for-syntax (force-use-with-parens b)
(lambda (stx)
(syntax-case stx ()
[(_)
(with-syntax ([b b])
(syntax/loc stx
(b)))]
[(kw arg arg-rest ...)
(raise-syntax-error #f
(format "Must be directly used without arguments [e.g: (~a)]"
(syntax->datum #'kw))
stx
#'arg)]
[_
(identifier? stx)
(raise-syntax-error #f
(format "Must be directly used [e.g: (~a)]"
(syntax->datum stx))
stx)])))
(define-syntax (while stx)
(syntax-case stx ()
[(_)
(raise-syntax-error #f "missing test and body" stx)]
[(_ cond)
(raise-syntax-error #f "missing body" stx)]
[(_ cond body ...)
(syntax/loc stx
(let/ec fresh-break
(let loop ()
(when cond
(let/ec fresh-continue
(syntax-parameterize
([break (force-use-with-parens #'fresh-break)]
[continue (force-use-with-parens #'fresh-continue)])
(begin body ...)))
(loop)))))]))