/collects/racket/syntax.rkt

https://bitbucket.org/agocke/racket · Racket · 188 lines · 139 code · 30 blank · 19 comment · 19 complexity · c3032a9666c00d7d5ce352a771619ee3 MD5 · raw file

  1. #lang racket/base
  2. (require (for-syntax racket/base
  3. racket/private/sc))
  4. (provide define/with-syntax
  5. current-recorded-disappeared-uses
  6. with-disappeared-uses
  7. syntax-local-value/record
  8. record-disappeared-uses
  9. format-symbol
  10. format-id
  11. current-syntax-context
  12. wrong-syntax
  13. generate-temporary
  14. internal-definition-context-apply
  15. syntax-local-eval
  16. with-syntax*)
  17. ;; == Defining pattern variables ==
  18. (define-syntax (define/with-syntax stx)
  19. (syntax-case stx ()
  20. [(define/with-syntax pattern rhs)
  21. (let* ([pvar-env (get-match-vars #'define/with-syntax
  22. stx
  23. #'pattern
  24. '())]
  25. [depthmap (for/list ([x pvar-env])
  26. (let loop ([x x] [d 0])
  27. (if (pair? x)
  28. (loop (car x) (add1 d))
  29. (cons x d))))]
  30. [pvars (map car depthmap)]
  31. [depths (map cdr depthmap)]
  32. [mark (make-syntax-introducer)])
  33. (with-syntax ([(pvar ...) pvars]
  34. [(depth ...) depths]
  35. [(valvar ...) (generate-temporaries pvars)])
  36. #'(begin (define-values (valvar ...)
  37. (with-syntax ([pattern rhs])
  38. (values (pvar-value pvar) ...)))
  39. (define-syntax pvar
  40. (make-syntax-mapping 'depth (quote-syntax valvar)))
  41. ...)))]))
  42. ;; Ryan: alternative name: define/syntax-pattern ??
  43. ;; auxiliary macro
  44. (define-syntax (pvar-value stx)
  45. (syntax-case stx ()
  46. [(_ pvar)
  47. (identifier? #'pvar)
  48. (let ([mapping (syntax-local-value #'pvar)])
  49. (unless (syntax-pattern-variable? mapping)
  50. (raise-syntax-error #f "not a pattern variable" #'pvar))
  51. (syntax-mapping-valvar mapping))]))
  52. ;; == Disappeared uses ==
  53. (define current-recorded-disappeared-uses (make-parameter #f))
  54. (define-syntax-rule (with-disappeared-uses stx-expr)
  55. (let-values ([(stx disappeared-uses)
  56. (parameterize ((current-recorded-disappeared-uses null))
  57. (let ([result stx-expr])
  58. (values result (current-recorded-disappeared-uses))))])
  59. (syntax-property stx
  60. 'disappeared-use
  61. (append (or (syntax-property stx 'disappeared-use) null)
  62. disappeared-uses))))
  63. (define (syntax-local-value/record id pred)
  64. (let ([value (syntax-local-value id (lambda () #f))])
  65. (and (pred value)
  66. (begin (record-disappeared-uses (list id))
  67. value))))
  68. (define (record-disappeared-uses ids)
  69. (let ([uses (current-recorded-disappeared-uses)])
  70. (when uses
  71. (current-recorded-disappeared-uses (append ids uses)))))
  72. ;; == Identifier formatting ==
  73. (define (format-id lctx
  74. #:source [src #f]
  75. #:props [props #f]
  76. #:cert [cert #f]
  77. fmt . args)
  78. (define (convert x) (->atom x 'format-id))
  79. (check-restricted-format-string 'format-id fmt)
  80. (let* ([args (map convert args)]
  81. [str (apply format fmt args)]
  82. [sym (string->symbol str)])
  83. (datum->syntax lctx sym src props cert)))
  84. ;; Eli: This looks very *useful*, but I'd like to see it more convenient to
  85. ;; "preserve everything". Maybe add a keyword argument that when #t makes
  86. ;; all the others use values lctx, and when syntax makes the others use that
  87. ;; syntax?
  88. ;; Finally, if you get to add this, then another useful utility in the same
  89. ;; spirit is one that concatenates symbols and/or strings and/or identifiers
  90. ;; into a new identifier. I considered something like that, which expects a
  91. ;; single syntax among its inputs, and will use it for the context etc, or
  92. ;; throw an error if there's more or less than 1.
  93. (define (format-symbol fmt . args)
  94. (define (convert x) (->atom x 'format-symbol))
  95. (check-restricted-format-string 'format-symbol fmt)
  96. (let ([args (map convert args)])
  97. (string->symbol (apply format fmt args))))
  98. (define (restricted-format-string? fmt)
  99. (regexp-match? #rx"^(?:[^~]|~[aAn~%])*$" fmt))
  100. (define (check-restricted-format-string who fmt)
  101. (unless (restricted-format-string? fmt)
  102. (raise-arguments-error who
  103. (format "format string should have ~a placeholders"
  104. fmt)
  105. "format string" fmt)))
  106. (define (->atom x err)
  107. (cond [(string? x) x]
  108. [(symbol? x) x]
  109. [(identifier? x) (syntax-e x)]
  110. [(keyword? x) (keyword->string x)]
  111. [(number? x) x]
  112. [(char? x) x]
  113. [else (raise-argument-error err
  114. "(or/c string? symbol? identifier? keyword? char? number?)"
  115. x)]))
  116. ;; == Error reporting ==
  117. (define current-syntax-context
  118. (make-parameter #f
  119. (lambda (new-value)
  120. (unless (or (syntax? new-value) (eq? new-value #f))
  121. (raise-argument-error 'current-syntax-context
  122. "(or/c syntax? #f)"
  123. new-value))
  124. new-value)))
  125. (define (wrong-syntax stx #:extra [extras null] format-string . args)
  126. (unless (or (eq? stx #f) (syntax? stx))
  127. (raise-argument-error 'wrong-syntax "(or/c syntax? #f)" 0 (list* stx format-string args)))
  128. (let* ([ctx (current-syntax-context)]
  129. [blame (and (syntax? ctx) (syntax-property ctx 'report-error-as))])
  130. (raise-syntax-error (if (symbol? blame) blame #f)
  131. (apply format format-string args)
  132. ctx
  133. stx
  134. extras)))
  135. ;; Eli: The `report-error-as' thing seems arbitrary to me.
  136. ;; == Other utilities ==
  137. ;; generate-temporary : any -> identifier
  138. (define (generate-temporary [stx 'g])
  139. (car (generate-temporaries (list stx))))
  140. ;; Applies the renaming of intdefs to stx.
  141. (define (internal-definition-context-apply intdefs stx)
  142. (let ([qastx (local-expand #`(quote #,stx) 'expression (list #'quote) intdefs)])
  143. (with-syntax ([(q astx) qastx]) #'astx)))
  144. (define (syntax-local-eval stx [intdef0 #f])
  145. (let* ([name (generate-temporary)]
  146. [intdefs (syntax-local-make-definition-context intdef0)])
  147. (syntax-local-bind-syntaxes (list name)
  148. #`(call-with-values (lambda () #,stx) list)
  149. intdefs)
  150. (internal-definition-context-seal intdefs)
  151. (apply values
  152. (syntax-local-value (internal-definition-context-apply intdefs name)
  153. #f intdefs))))
  154. (define-syntax (with-syntax* stx)
  155. (syntax-case stx ()
  156. [(_ (cl) body ...) #'(with-syntax (cl) body ...)]
  157. [(_ (cl cls ...) body ...)
  158. #'(with-syntax (cl) (with-syntax* (cls ...) body ...))]))