yes very good then break si not overwritten, using module, i do not know if modules is finally standardised in R6RS, and in guile? a few problem with module i hope to fix it later but it works with your example, not sure let/ec is standart? i will continue later.... Damien
On Sun, Sep 4, 2022 at 12:44 PM Jean Abou Samra <j...@abou-samra.fr> wrote: > Le 04/09/2022 à 11:54, Damien Mattei a écrit : > > i try to make a for with break and continue the way C language do it, i > > works with break but if i add a continue feature i then loose the break > > feature, here is my code: > > (define-syntax for/bc > > > > (lambda (stx) > > (syntax-case stx () > > ((kwd (init test incrmt) body ...) > > > > (with-syntax > > ((BREAK (datum->syntax #'kwd 'break))) > > > > #'(call/cc > > (lambda (escape) > > (let-syntax > > ((BREAK (identifier-syntax (escape)))) > > init > > (let loop () > > (when test > > > > (with-syntax > > ((CONTINUE (datum->syntax #'kwd 'continue))) > > > > #'(call/cc > > (lambda (next) > > (let-syntax > > ((CONTINUE (identifier-syntax (next)))) > > body ...))) > > > > incrmt > > (loop)))))))))))) > > > > The problem is with the meta level vs. the expanded output level. You have > two nested levels of #' . This (with-syntax ((CONTINUE ...)) ...) is part > of > the expanded output, it doesn't run when your macro is expanded. The body > of > the (expanded) loop just returns a syntax object. That's not what you want. > Here's a definition that works: > > (define-syntax for/bc > (lambda (stx) > (syntax-case stx () > ((kwd (init test incrmt) body ...) > (with-syntax ((BREAK (datum->syntax #'kwd 'break)) > (CONTINUE (datum->syntax #'kwd 'continue))) > #'(call/cc > (lambda (escape) > (let-syntax ((BREAK (identifier-syntax (escape)))) > init > (let loop () > (when test > (call/cc > (lambda (next) > (let-syntax ((CONTINUE (identifier-syntax (next)))) > body ...))) > incrmt > (loop))))))))))) > > (let ((i #f)) > (for/bc ((set! i 0) (< i 10) (set! i (1+ i))) > (when (< i 5) > continue) > (when (> i 9) > break) > (display i) > (newline))) > > > > You could also use quasisyntax (#` and #,) to get the same effect: > > > (define-syntax for/bc > (lambda (stx) > (syntax-case stx () > ((kwd (init test incrmt) body ...) > #`(call/cc > (lambda (escape) > (let-syntax ((#,(datum->syntax #'kwd 'break) > (identifier-syntax (escape)))) > init > (let loop () > (when test > (call/cc > (lambda (next) > (let-syntax ((#,(datum->syntax #'kwd 'continue) > (identifier-syntax (next)))) > body ...))) > incrmt > (loop)))))))))) > > > > That said, I would recommend using syntax parameters for break and > continue. > They're cleaner, since they can be rebound by the user. Also, you can > use let/ec from (ice-9 control) instead of call/cc. It's more efficient > because it doesn't need to actually reify the whole environment, since > an escape continuation is upwards-only (it can be used inside the > expression > to escape it, but it can't be used outside to reinstate its context). > > > (use-modules (ice-9 control)) > > (define-syntax-parameter break > (lambda (sintax) > (syntax-violation 'break "break outside of for/bc" sintax))) > > (define-syntax-parameter continue > (lambda (sintax) > (syntax-violation 'continue "continue outside of for/bc" sintax))) > > (define-syntax-rule (for/bc (init test increment) body body* ...) > (begin > init > (let/ec escape > (syntax-parameterize ((break (identifier-syntax (escape)))) > (let loop () > (when test > (let/ec next > (syntax-parameterize ((continue (identifier-syntax (next)))) > body body* ...)) > increment > (loop))))))) > > (let ((i #f)) > (for/bc ((set! i 0) (< i 10) (set! i (1+ i))) > (when (< i 5) > continue) > (when (> i 9) > break) > (display i) > (newline))) > > > > > And here's an example showing the benefits of syntax parameters. > Add at the beginning of the code above: > > > (define-module (for) > #:export (break continue for/bc)) > > > In the same directory, put a file rename.scm containing: > > (use-modules ((for) > #:select ((break . for-break) continue for/bc)) > (srfi srfi-1) ; contains a break procedure > (ice-9 receive)) > > (let ((i #f)) > (for/bc ((set! i 0) (< i 10) (set! i (1+ i))) > (receive (before after) > (break (lambda (x) > (> x 5)) > (iota i)) > (when (pair? after) > for-break) > (display i)))) > > > > And run as > > guile -L . rename.scm > > As you can see, syntax parameters enable the code to use 'break' > for something else. > > A final note: are you aware of the existence of 'do' in Scheme? > Most cases of a C for loop can be written elegantly using do. > > https://www.gnu.org/software/guile/manual/html_node/while-do.html > > Regards, > Jean > >