151 lines
6.0 KiB
Racket
151 lines
6.0 KiB
Racket
#lang racket/base
|
||
|
||
(provide subscript-equal?
|
||
extract-subscripts
|
||
drop-subscripts
|
||
find-subscript-binders)
|
||
|
||
(require (for-template stxparse-info/current-pvars)
|
||
racket/private/sc
|
||
racket/function
|
||
racket/list
|
||
phc-toolkit/untyped
|
||
"optcontract.rkt"
|
||
racket/string
|
||
racket/syntax)
|
||
|
||
(define/contract (extract-subscripts id)
|
||
(-> identifier? string?)
|
||
(let ([match (regexp-match #px".(_.+|[ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]*)$"
|
||
(symbol->string (syntax-e id)))])
|
||
(if (>= (length match) 2)
|
||
(cadr match)
|
||
"")))
|
||
|
||
(define/contract (string-replace* str from* to*)
|
||
(->i ([str string?]
|
||
[from* (listof string?)]
|
||
[to* (from*)
|
||
(and/c (listof string?)
|
||
(λ (to*) (= (length from*) (length to*))))])
|
||
[range string?])
|
||
(if (null? from*)
|
||
str
|
||
(string-replace* (string-replace str (car from*) (car to*))
|
||
(cdr from*)
|
||
(cdr to*))))
|
||
|
||
|
||
(define/contract (normalize-subscripts sub)
|
||
(-> string? string?)
|
||
(if (or (string=? sub "")
|
||
(equal? (string-ref sub 0) #\_))
|
||
sub
|
||
(string-append
|
||
"_"
|
||
(string-replace* sub
|
||
(map symbol->string
|
||
'(ₐ ₑ ₕ ᵢ ⱼ ₖ ₗ ₘ ₙ ₒ ₚ ᵣ ₛ ₜ ᵤ ᵥ ₓ ᵦ ᵧ ᵨ ᵩ ᵪ))
|
||
(map symbol->string
|
||
'(A E H I J K L M N O P R S T U V X β γ ρ ϕ χ))))))
|
||
|
||
(define/contract (subscript-equal? bound binder)
|
||
(-> identifier? identifier? (or/c #f string?))
|
||
(let* ([binder-subscripts (normalize-subscripts (extract-subscripts binder))]
|
||
[bound-subscripts (normalize-subscripts (extract-subscripts bound))])
|
||
(and (string=? binder-subscripts bound-subscripts)
|
||
(not (string=? binder-subscripts ""))
|
||
binder-subscripts)))
|
||
|
||
(define/contract (drop-subscripts id)
|
||
(-> identifier? identifier?)
|
||
(let* ([str (symbol->string (syntax-e id))]
|
||
[sub (extract-subscripts id)]
|
||
[new-str (substring str 0 (- (string-length str)
|
||
(string-length sub)))])
|
||
(datum->syntax id (string->symbol new-str) id id)))
|
||
|
||
(define (filter-current-pvars bound)
|
||
(remove-duplicates
|
||
(map (λ (pv+u) (cons (syntax-local-get-shadower (car pv+u))
|
||
(cdr pv+u)))
|
||
(filter (compose (conjoin identifier?
|
||
(λ~> (syntax-local-value _ (thunk #f))
|
||
syntax-pattern-variable?)
|
||
;; force call syntax-local-value to prevent
|
||
;; ambiguous bindings, as syntax-local-value
|
||
;; triggers an error for those.
|
||
;; Must be done before the free-identifier=?
|
||
;; which just returns #false
|
||
(λ~> (datum->syntax _ (syntax-e bound))
|
||
(syntax-local-value _ (thunk #f))
|
||
(thunk* #t)) ;; ok if no error.
|
||
(λ~> (datum->syntax _ (syntax-e bound))
|
||
(free-identifier=? _ bound))
|
||
(λ~> (subscript-equal? bound _)))
|
||
car)
|
||
(current-pvars+unique)))
|
||
bound-identifier=?
|
||
#:key car))
|
||
|
||
;; Or write it as:
|
||
#;(define (filter-current-pvars bound)
|
||
(for/list ([binder (current-pvars+unique)]
|
||
#:when (identifier? (car binder))
|
||
#:when (syntax-pattern-variable?
|
||
(syntax-local-value (car binder) (thunk #f)))
|
||
;; force call syntax-local-value to prevent ambiguous
|
||
;; bindings, as syntax-local-value triggers an error for
|
||
;; those.
|
||
;; Must be done before the free-identifier=? which just
|
||
;; returns #false
|
||
#:when (begin
|
||
(syntax-local-value
|
||
(datum->syntax _ (syntax-e bound))
|
||
(thunk #f))
|
||
#t) ;; ok if no error.
|
||
#:when (free-identifier=? (datum->syntax (car binder)
|
||
(syntax-e bound))
|
||
bound)
|
||
#:when (subscript-equal? bound (car binder)))
|
||
binder))
|
||
|
||
(define/contract (find-subscript-binders bound)
|
||
(-> identifier?
|
||
(or/c #f (list/c identifier? ; bound
|
||
(syntax/c (listof identifier?)) ; binders
|
||
(syntax/c (listof identifier?)) ; unique-at-runtime ids
|
||
exact-nonnegative-integer?))) ; ellipsis-depth
|
||
(let/cc return
|
||
;; EARLY RETURN (already a pattern variable)
|
||
(when (syntax-pattern-variable?
|
||
(syntax-local-value bound (thunk #f)))
|
||
(return #f))
|
||
|
||
(define/with-syntax ([binder . unique-at-runtime-id] …)
|
||
(filter-current-pvars bound))
|
||
|
||
;; EARLY RETURN (no candidate binders found)
|
||
(when (stx-null? #'(binder …))
|
||
(return #f))
|
||
|
||
(define depths
|
||
(stx-map (∘ syntax-mapping-depth syntax-local-value) #'(binder …)))
|
||
|
||
;; EARLY ERROR (inconsistent depths)
|
||
(unless (or (< (length depths) 2) (apply = depths))
|
||
(car depths)
|
||
(raise-syntax-error 'subtemplate
|
||
(format "inconsistent depths: ~a"
|
||
(map cons
|
||
(syntax->datum #'(binder …))
|
||
depths))
|
||
bound
|
||
(syntax->list #'(binder …))))
|
||
|
||
;; FINAL RETURN (list of same-depth binders + their depth)
|
||
(return (list bound
|
||
#'(binder …)
|
||
#'(unique-at-runtime-id …)
|
||
(car depths)))))
|