ceving
November 20, 2023, 3:14pm
1
The following code throws the error:
no pattern variables before ellipsis in template
I do not understand why ::f*
is no pattern variable. Can anybody explain?
#lang racket
(define undefined (when #f #f))
(define print~s (lambda (args) (write (car args)) (cdr args)))
(define print~a (lambda (args) (display (car args)) (cdr args)))
(define mkprint (lambda (literal) (lambda (args) (display literal) args)))
(define print~~ (mkprint #\~))
(define-syntax :writer
(syntax-rules (~s ~a ~~)
;; Simple formats
((_ ~s) print~s)
((_ ~a) print~a)
((_ ~~) print~~)
;; List format, which should behave like ~:{~} in CL
((_ (:f1 :f* ...))
;; Macro needs to be changed to add ~^ and early termination.
(letrec-syntax ((::writer
(syntax-rules (~s ~a ~~ ~^)
((_ ~^) (lambda (args) #f)) ;; return #f to stop
((_ ~s) print~s)
((_ ~a) print~a)
((_ ~~) print~~)
((_ ::*) (mkprint ::*))
((_ ::f1 ::f2 ::f* ...)
(lambda (args)
(let ((interim ((::writer ::f1) args)))
(if interim
((::writer ::f2 ::f* ...) interim)
'())))))))
(lambda (args)
(let loop ((args (car args)))
(when (pair? args)
(let ((arg (car args))
(args (cdr args)))
((::writer :f1 :f* ...) arg)
(loop args))))
(cdr args))))
;; Anyting else, which is not a format.
((_ :*) (mkprint :*))
;; Recursion
((_ :f1 :f2 :f* ...)
(lambda (args)
((:writer :f2 :f* ...) ((:writer :f1) args))))))
#;((:writer ~a) '("a\n"))
#;((:writer ~s ~a) '("a" "\n"))
#;((:writer "a\n") '())
#;((:writer ~s (~a) "\n") '("a" ((1) (2) (3))))
((:writer (~a ~^ ",") #\newline) '(((1) (2))))
Short: You want ...
to appear in the output, so you need to quote it with (... ...)
.
When you are writing macro generating macros there are two levels of ellipsis.
The outer one, where you can write as ...
as normal and the inner one where you need to escape.
You can escape it with (... ...)
. That looks odd to me, so I usually bind it to an identifier named ooo
.
Silly example:
#lang racket
(require (for-syntax syntax/parse
racket/syntax))
(define-syntax (make-id-macro stx)
(syntax-parse stx
[(_ id)
(with-syntax ([name (format-id #'id "do-~a" #'id)]
[ooo #'(... ...)])
#'(define-syntax (name stx)
(syntax-parse stx
[(_ parms ooo)
#'(list parms ooo)])))]))
(make-id-macro foo)
(do-foo 1 2)
/Jens Axel
1 Like
sorawee
November 20, 2023, 3:54pm
3
no pattern variables before ellipsis in template in: ::f*
This is actually an error about ellipsis, not pattern variable per se.
In your code, there’s a syntax-rules
inside another syntax-rules
. When you write ...
, Racket gets confused about which syntax-rules
should ...
be associated with.
It’s clear from your code that you meant to associate ...
in ::f* ...
with the inner syntax-rules
. So you need to escape them by replacing these occurrences of ...
with (... ...)
.
#lang racket
(define undefined (when #f #f))
(define print~s (lambda (args) (write (car args)) (cdr args)))
(define print~a (lambda (args) (display (car args)) (cdr args)))
(define mkprint (lambda (literal) (lambda (args) (display literal) args)))
(define print~~ (mkprint #\~))
(define-syntax :writer
(syntax-rules (~s ~a ~~)
;; Simple formats
((_ ~s) print~s)
((_ ~a) print~a)
((_ ~~) print~~)
;; List format, which should behave like ~:{~} in CL
((_ (:f1 :f* ...))
;; Macro needs to be changed to add ~^ and early termination.
(letrec-syntax ((::writer
(syntax-rules (~s ~a ~~ ~^)
((_ ~^) (lambda (args) #f)) ;; return #f to stop
((_ ~s) print~s)
((_ ~a) print~a)
((_ ~~) print~~)
((_ ::*) (mkprint ::*))
((_ ::f1 ::f2 ::f* (... ...))
(lambda (args)
(let ((interim ((::writer ::f1) args)))
(if interim
((::writer ::f2 ::f* (... ...)) interim)
'())))))))
(lambda (args)
(let loop ((args (car args)))
(when (pair? args)
(let ((arg (car args))
(args (cdr args)))
((::writer :f1 :f* ...) arg)
(loop args))))
(cdr args))))
;; Anyting else, which is not a format.
((_ :*) (mkprint :*))
;; Recursion
((_ :f1 :f2 :f* ...)
(lambda (args)
((:writer :f2 :f* ...) ((:writer :f1) args))))))
#;((:writer ~a) '("a\n"))
#;((:writer ~s ~a) '("a" "\n"))
#;((:writer "a\n") '())
#;((:writer ~s (~a) "\n") '("a" ((1) (2) (3))))
((:writer (~a ~^ ",") #\newline) '(((1) (2))))
The above code now works.
ceving
November 20, 2023, 4:05pm
4
Thanks! It works now.
#lang racket
(define undefined (when #f #f))
(define print~s (lambda (args) (write (car args)) (cdr args)))
(define print~a (lambda (args) (display (car args)) (cdr args)))
(define mkprint (lambda (literal) (lambda (args) (display literal) args)))
(define print~~ (mkprint #\~))
(define-syntax :writer
(syntax-rules (~s ~a ~~)
;; Simple formats
((_ ~s) print~s)
((_ ~a) print~a)
((_ ~~) print~~)
;; List format, which should behave like ~:{~} in CL
((_ (:f1 :f* ...))
;; Macro needs to be changed for ~^ to terminate early.
(letrec-syntax ((::writer
(syntax-rules (~s ~a ~~ ~^)
((_ ::last ~^)
(lambda (args)
(if ::last
#f ;; stop here
args)))
((_ ::last ~s) print~s)
((_ ::last ~a) print~a)
((_ ::last ~~) print~~)
((_ ::last ::*) (mkprint ::*))
((_ ::last ::f1 ::f2 ::f* (... ...))
(lambda (args)
(let ((interim ((::writer ::last ::f1) args)))
(if interim
((::writer ::last ::f2 ::f* (... ...)) interim)
'())))))))
(lambda (args)
(let loop ((args (car args)))
(when (pair? args)
(let ((arg (car args))
(args (cdr args)))
((::writer (null? args) :f1 :f* ...) arg)
(loop args))))
(cdr args))))
;; Anyting else, which is not a format.
((_ :*) (mkprint :*))
;; Recursion
((_ :f1 :f2 :f* ...)
(lambda (args)
((:writer :f2 :f* ...) ((:writer :f1) args))))))
#;((:writer ~a) '("a\n"))
#;((:writer ~s ~a) '("a" "\n"))
#;((:writer "a\n") '())
#;((:writer ~s (~a) "\n") '("a" ((1) (2) (3))))
((:writer #\{ (~s #\: ~s ~^ #\, #\space) #\} #\newline)
'((("a" 1) ("b" 2) ("c" 3))))