139 lines
4.8 KiB
Scheme
139 lines
4.8 KiB
Scheme
#lang scheme/base
|
|
;; owner: ryanc
|
|
(require syntax/kerncase
|
|
syntax/stx
|
|
unstable/struct
|
|
(for-syntax scheme/base
|
|
scheme/private/sc))
|
|
|
|
(provide unwrap-syntax
|
|
|
|
define-pattern-variable
|
|
|
|
with-temporaries
|
|
generate-temporary
|
|
generate-n-temporaries
|
|
|
|
current-caught-disappeared-uses
|
|
with-catching-disappeared-uses
|
|
with-disappeared-uses
|
|
syntax-local-value/catch
|
|
record-disappeared-uses
|
|
|
|
format-symbol
|
|
format-id
|
|
|
|
current-syntax-context
|
|
wrong-syntax)
|
|
|
|
;; Unwrapping syntax
|
|
|
|
;; unwrap-syntax : any #:stop-at (any -> boolean) -> any
|
|
(define (unwrap-syntax stx #:stop-at [stop-at (lambda (x) #f)])
|
|
(let loop ([x stx])
|
|
(cond [(stop-at x) x]
|
|
[(syntax? x) (loop (syntax-e x))]
|
|
[(pair? x) (cons (loop (car x)) (loop (cdr x)))]
|
|
[(vector? x) (apply vector-immutable (loop (vector->list x)))]
|
|
[(box? x) (box-immutable (loop (unbox x)))]
|
|
[(prefab-struct-key x)
|
|
=> (lambda (key)
|
|
(apply make-prefab-struct key
|
|
(loop (struct->list x))))]
|
|
[else x])))
|
|
;; Eli: Is there any difference between this (with the default) and
|
|
;; `syntax->datum'? If not, then maybe add the optional (or keyword) to
|
|
;; there instead?
|
|
|
|
;; Defining pattern variables
|
|
|
|
(define-syntax-rule (define-pattern-variable name expr)
|
|
(begin (define var expr)
|
|
(define-syntax name (make-syntax-mapping '0 (quote-syntax var)))))
|
|
|
|
;; Statics and disappeared uses
|
|
|
|
(define current-caught-disappeared-uses (make-parameter #f))
|
|
|
|
(define-syntax-rule (with-catching-disappeared-uses . body)
|
|
(parameterize ((current-caught-disappeared-uses null))
|
|
(let ([result (let () . body)])
|
|
(values result (current-caught-disappeared-uses)))))
|
|
|
|
(define-syntax-rule (with-disappeared-uses stx-expr)
|
|
(let-values ([(stx disappeared-uses)
|
|
(with-catching-disappeared-uses stx-expr)])
|
|
(syntax-property stx
|
|
'disappeared-use
|
|
(append (or (syntax-property stx 'disappeared-use) null)
|
|
disappeared-uses))))
|
|
|
|
(define (syntax-local-value/catch id pred)
|
|
(let ([value (syntax-local-value id (lambda () #f))])
|
|
(and (pred value)
|
|
(begin (record-disappeared-uses (list id))
|
|
value))))
|
|
|
|
(define (record-disappeared-uses ids)
|
|
(let ([uses (current-caught-disappeared-uses)])
|
|
(when uses
|
|
(current-caught-disappeared-uses (append ids uses)))))
|
|
|
|
;; Generating temporaries
|
|
|
|
;; with-temporaries
|
|
(define-syntax-rule (with-temporaries (temp-name ...) . body)
|
|
(with-syntax ([(temp-name ...) (generate-temporaries (quote-syntax (temp-name ...)))])
|
|
. body))
|
|
;; Eli: +1 to this, not sure about the next two
|
|
|
|
;; generate-temporary : any -> identifier
|
|
(define (generate-temporary [stx 'g])
|
|
(car (generate-temporaries (list stx))))
|
|
|
|
;; generate-n-temporaries : exact-nonnegative-integer -> (listof identifier)
|
|
(define (generate-n-temporaries n)
|
|
(generate-temporaries
|
|
(for/list ([i (in-range n)])
|
|
(string->symbol (format "g~sx" i)))))
|
|
|
|
;; Symbol Formatting
|
|
|
|
(define (format-symbol fmt . args)
|
|
(string->symbol (apply format fmt args)))
|
|
|
|
(define (format-id lctx
|
|
#:source [src #f]
|
|
#:props [props #f]
|
|
#:cert [cert #f]
|
|
fmt . args)
|
|
(let* ([str (apply format fmt args)]
|
|
[sym (string->symbol str)])
|
|
(datum->syntax lctx sym src props cert)))
|
|
;; Eli: This looks very *useful*, but I'd like to see it more convenient to
|
|
;; "preserve everything". Maybe add a keyword argument that when #t makes
|
|
;; all the others use values lctx, and when syntax makes the others use that
|
|
;; syntax? Also, I'd prefer it if each of these keywords would also accept a
|
|
;; syntax instead of a value, to copy the value from.
|
|
;; Finally, if you get to add this, then another useful utility in the same
|
|
;; spirit is one that concatenates symbols and/or strings and/or identifiers
|
|
;; into a new identifier. I considered something like that, which expects a
|
|
;; single syntax among its inputs, and will use it for the context etc, or
|
|
;; throw an error if there's more or less than 1.
|
|
|
|
;; Error reporting
|
|
|
|
(define current-syntax-context (make-parameter #f))
|
|
|
|
(define (wrong-syntax stx #:extra [extras null] format-string . args)
|
|
(unless (or (eq? stx #f) (syntax? stx))
|
|
(raise-type-error 'wrong-syntax "syntax or #f" 0 (list* stx format-string args)))
|
|
(let* ([ctx (current-syntax-context)]
|
|
[blame (and (syntax? ctx) (syntax-property ctx 'report-error-as))])
|
|
(raise-syntax-error (if (symbol? blame) blame #f)
|
|
(apply format format-string args)
|
|
ctx
|
|
stx
|
|
extras)))
|
|
;; Eli: The `report-error-as' thing seems arbitrary to me.
|