|
|
|
#lang racket/base
|
|
|
|
(require (for-syntax racket/base racket/syntax)
|
|
|
|
racket/list
|
|
|
|
racket/syntax
|
|
|
|
br/define
|
|
|
|
br/private/syntax-flatten)
|
|
|
|
(provide (all-defined-out)
|
|
|
|
syntax-flatten)
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(require rackunit))
|
|
|
|
|
|
|
|
|
|
|
|
(define-macro (syntax-match STX-ARG [(syntax PATTERN) BODY ...] ...)
|
|
|
|
#'(syntax-case STX-ARG ()
|
|
|
|
[PATTERN BODY ...] ...))
|
|
|
|
|
|
|
|
|
|
|
|
(define-macro-cases with-pattern
|
|
|
|
[(_ () . BODY) #'(begin . BODY)]
|
|
|
|
[(_ ([SID SID-STX] STX ...) . BODY)
|
|
|
|
#'(with-syntax ([SID SID-STX])
|
|
|
|
(with-pattern (STX ...) . BODY))]
|
|
|
|
[(_ ([SID] STX ...) . BODY) ; standalone id
|
|
|
|
#'(with-pattern ([SID SID] STX ...) . BODY)]) ; convert to previous case
|
|
|
|
|
|
|
|
|
|
|
|
(define (check-syntax-list-argument caller-name arg)
|
|
|
|
(cond
|
|
|
|
[(and (syntax? arg) (syntax->list arg))]
|
|
|
|
[(list? arg) arg]
|
|
|
|
[else (raise-argument-error caller-name "list of syntax, or syntaxed list" arg)]))
|
|
|
|
|
|
|
|
|
|
|
|
(define-macro (define-listy-macro MACRO-ID LIST-FUNC)
|
|
|
|
#'(define-macro (MACRO-ID STX-LIST LITERALS . MATCHERS)
|
|
|
|
#'(LIST-FUNC
|
|
|
|
(λ(stx-item)
|
|
|
|
(with-handlers ([exn:fail:syntax? (λ (exn) #f)])
|
|
|
|
(syntax-case stx-item LITERALS
|
|
|
|
. MATCHERS)))
|
|
|
|
(check-syntax-list-argument 'MACRO-ID STX-LIST))))
|
|
|
|
|
|
|
|
(define-listy-macro syntax-case-partition partition)
|
|
|
|
(define-listy-macro syntax-case-filter filter)
|
|
|
|
(define-listy-macro syntax-case-map map)
|
|
|
|
|
|
|
|
|
|
|
|
(define-macro (reformat-id FMT ID0 ID ...)
|
|
|
|
#'(format-id ID0 FMT ID0 ID ...))
|
|
|
|
|
|
|
|
|
|
|
|
(define-macro (format-string FMT ID0 ID ...)
|
|
|
|
#'(datum->syntax ID0 (format FMT (syntax->datum ID0) (syntax->datum ID) ...)))
|
|
|
|
|
|
|
|
|
|
|
|
(define-macro (->unsyntax X)
|
|
|
|
#'(if (syntax? X)
|
|
|
|
(syntax->datum X)
|
|
|
|
X))
|
|
|
|
|
|
|
|
|
|
|
|
(define-macro (prefix-id PREFIX ... BASE-OR-BASES)
|
|
|
|
#'(let* ([bobs BASE-OR-BASES]
|
|
|
|
[got-single? (and (not (list? bobs)) (not (syntax->list bobs)))]
|
|
|
|
[bases (if got-single?
|
|
|
|
(list bobs)
|
|
|
|
bobs)]
|
|
|
|
[result (syntax-case-map
|
|
|
|
bases ()
|
|
|
|
[base (format-id #'base "~a~a"
|
|
|
|
(string-append (format "~a" (->unsyntax PREFIX)) ...)
|
|
|
|
(syntax-e #'base))])])
|
|
|
|
(if got-single? (car result) result)))
|
|
|
|
|
|
|
|
|
|
|
|
(define-macro (infix-id PREFIX BASE-OR-BASES SUFFIX ...)
|
|
|
|
#'(let* ([bobs BASE-OR-BASES]
|
|
|
|
[got-single? (and (not (list? bobs)) (not (syntax->list bobs)))]
|
|
|
|
[bases (if got-single?
|
|
|
|
(list bobs)
|
|
|
|
bobs)]
|
|
|
|
[result (syntax-case-map
|
|
|
|
bases ()
|
|
|
|
[base (format-id #'base "~a~a~a"
|
|
|
|
(->unsyntax PREFIX)
|
|
|
|
(syntax-e #'base)
|
|
|
|
(string-append (format "~a" (->unsyntax SUFFIX)) ...))])])
|
|
|
|
(if got-single? (car result) result)))
|
|
|
|
|
|
|
|
|
|
|
|
(define-macro (suffix-id BASE-OR-BASES SUFFIX ...)
|
|
|
|
#'(infix-id "" BASE-OR-BASES SUFFIX ...))
|
|
|
|
|
|
|
|
|
|
|
|
(define-macro-cases syntax-property*
|
|
|
|
[(_ STX 'PROP0) ; read one
|
|
|
|
#'(syntax-property STX 'PROP0)]
|
|
|
|
[(_ STX 'PROP0 'PROP ...) ; read multiple
|
|
|
|
#'(cons (syntax-property* STX 'PROP0)
|
|
|
|
(let ([result (syntax-property* STX 'PROP ...)])
|
|
|
|
(if (pair? result)
|
|
|
|
result
|
|
|
|
(list result))))]
|
|
|
|
[(_ STX ['PROP0 VAL0 . PRESERVED0]) ; write one
|
|
|
|
#'(syntax-property STX 'PROP0 VAL0 . PRESERVED0)]
|
|
|
|
[(_ STX ['PROP0 VAL0 . PRESERVED0] ['PROP VAL . PRESERVED] ...) ; write multiple
|
|
|
|
#'(syntax-property* (syntax-property STX 'PROP0 VAL0 . PRESERVED0) ['PROP VAL . PRESERVED] ...)])
|
|
|
|
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(define x (syntax-property* #'foo ['bar #t] ['zam 'boni]))
|
|
|
|
(check-false (syntax-property* x 'foo))
|
|
|
|
(check-true (syntax-property* x 'bar))
|
|
|
|
(check-equal? (syntax-property* x 'foo 'bar 'zam) '(#f #t boni)))
|