Nearly finished subtemplate.

This commit is contained in:
Georges Dupéron 2016-10-05 18:54:43 +02:00
parent 88102c7263
commit 41013e5ef4
3 changed files with 372 additions and 1 deletions

View File

@ -7,7 +7,8 @@
"type-expander" "type-expander"
"hyper-literate" "hyper-literate"
"scribble-enhanced" "scribble-enhanced"
"typed-racket-lib")) "typed-racket-lib"
"srfi-lite-lib"))
(define build-deps '("scribble-lib" (define build-deps '("scribble-lib"
"racket-doc" "racket-doc"
"remember" "remember"

162
subtemplate.rkt Normal file
View File

@ -0,0 +1,162 @@
#lang racket
(require phc-toolkit/untyped
racket/stxparam
syntax/parse
syntax/parse/experimental/template
syntax/id-table
racket/syntax
(for-syntax syntax/parse
racket/private/sc
racket/syntax
racket/list
racket/function
phc-toolkit/untyped
srfi/13
racket/contract))
(provide (rename-out [new-syntax-parse syntax-parse]
[new-syntax-case syntax-case])
subtemplate
quasisubtemplate
(for-syntax find-subscript-binder)) ;; for testing only
(define-syntax-parameter maybe-syntax-pattern-variable-ids '())
(define-syntax-parameter pvar-values-id #f)
(define-syntax/parse (new-syntax-parse . rest)
(quasisyntax/top-loc (stx-car stx)
(let ([the-pvar-values (make-free-id-table)])
(syntax-parameterize ([maybe-syntax-pattern-variable-ids
(cons '#,(remove-duplicates
(filter symbol?
(flatten
(syntax->datum #'rest))))
(syntax-parameter-value
#'maybe-syntax-pattern-variable-ids))]
[pvar-values-id (make-rename-transformer
#'the-pvar-values)])
(syntax-parse . rest))
#;(syntax-parse option
[clause-pat
;; HERE insert a hash table, to cache the uses
;; lifting the define-temp-ids is not likely to work, as they
;; need to define syntax pattern variables so that other macros
;; can recognize them. Instead, we only lift the values, but still
;; do the bindings around the subtemplate.
#:do (define #,(lifted-scope (syntax-local-introduce #'pvar-values)
'add)
(make-free-id-table))
. clause-rest]
))))
(define-syntax/case (new-syntax-case . rest) ()
(quasisyntax/top-loc (stx-car stx)
(syntax-parameterize ([maybe-syntax-pattern-variable-ids
(cons '#,(remove-duplicates
(filter symbol?
(flatten
(syntax->datum #'rest))))
(syntax-parameter-value
#'maybe-syntax-pattern-variable-ids))])
(syntax-case . rest))))
(begin-for-syntax
(define/contract (string-suffix a b)
(-> string? string? string?)
(define suffix-length (string-suffix-length a b))
(substring a
(- (string-length a) suffix-length)))
(define/contract (subscript-binder? bound binder)
(-> identifier? identifier? (or/c #f string?))
(and (syntax-pattern-variable?
(syntax-local-value binder
(thunk #f)))
(let* ([bound-string (symbol->string (syntax-e bound))]
[binder-string (symbol->string (syntax-e binder))]
[suffix (string-suffix bound-string binder-string)]
[subs (regexp-match #px"[ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]+$" suffix)])
(and subs (car subs)))))
(define/contract (find-subscript-binder bound [fallback bound])
(->* (identifier?) (any/c) (or/c identifier? any/c))
(define result/scopes
(for/list ([scope (in-list
(syntax-parameter-value
#'maybe-syntax-pattern-variable-ids))])
(define result
(for*/list ([sym (in-list scope)]
#:unless (string=? (symbol->string sym)
(identifier->string bound))
[binder (in-value (datum->syntax bound sym))]
[subscripts (in-value (subscript-binder? bound
binder))]
#:when subscripts)
(cons binder subscripts)))
(and (not (null? result))
(car (argmax ( string-length cdr) result)))))
(or (ormap identity result/scopes)
fallback))
(define/contract (nest-ellipses id n)
(-> identifier? exact-nonnegative-integer? syntax?)
(if (= n 0)
id
#`(#,(nest-ellipses id (sub1 n))
( )))))
(define-syntax/case (derive bound binder stx-depth) ()
(define depth (syntax-e #'stx-depth))
(define/with-syntax bound-ddd (nest-ellipses #'bound depth))
(define/with-syntax tmp-id (format-id #'here "~a/~a" #'binder #'bound))
(define/with-syntax tmp-str (datum->syntax #'tmp-id (symbol->string
(syntax-e #'tmp-id))))
(define/with-syntax tmp-ddd (nest-ellipses #'tmp-id depth))
(define/with-syntax binder-ddd (nest-ellipses #'binder depth))
;; HERE: cache the define-temp-ids in the free-id-table, and make sure
;; that we retrieve the cached ones, so that two subtemplate within the same
;; syntax-case or syntax-parse clause use the same derived ids.
;; TODO: mark specially those bindings bound by (derive …) so that they are
;; not seen as original bindings in nested subtemplates (e.g. with an
;; "unsyntax"), otherwise that rule may not hold anymore, e.g.
;; (syntax-parse #'(a b c)
;; [(xᵢ …)
;; (quasisubtemplate (yᵢ …
;; #,(quasisubtemplate zᵢ …) ;; must be from xᵢ, not yᵢ
;; zᵢ …))])
;; the test above is not exactly right (zᵢ will still have the correct
;; binding), but it gives the general idea.
#`(begin (define-temp-ids tmp-str binder-ddd)
(define cached (free-id-table-ref! pvar-values-id
(quote-syntax bound)
#'tmp-ddd))
(define/with-syntax bound-ddd cached)))
(define-for-syntax/case-args ((sub*template tmpl-form) (self . tmpl))
(define acc '())
(define result
(quasisyntax/top-loc #'self
(#,tmpl-form
. #,(fold-syntax (λ (stx rec)
(if (identifier? stx)
(let ([binder (find-subscript-binder stx #f)])
(when binder
(let ([depth (syntax-mapping-depth
(syntax-local-value binder))])
(set! acc `((,stx ,binder ,depth) . ,acc))))
stx)
(rec stx)))
#'tmpl))))
;; Make sure that we remove duplicates, otherwise we'll get errors if we use
;; the same derived id twice.
(define/with-syntax ([bound binder depth] )
(remove-duplicates acc free-identifier=? #:key car))
#`(let ()
(derive bound binder depth)
#,result))
(define-syntax subtemplate (sub*template #'template))
(define-syntax quasisubtemplate (sub*template #'quasitemplate))

208
test/test-subtemplate.rkt Normal file
View File

@ -0,0 +1,208 @@
#lang racket
(require "../subtemplate.rkt"
phc-toolkit/untyped
rackunit)
(define-syntax (tst stx)
(syntax-case stx ()
[(_ tt)
#`'#,(find-subscript-binder #'tt #f)]))
(check-false (syntax-case #'(a b) ()
[(_ x)
(tst x)]))
(check-equal? (syntax-parse
#'(a b c)
[(_ x yᵢ)
(list (tst x)
(tst wᵢ))])
'(#f yᵢ))
(check-equal? (syntax->datum (syntax-parse #'(a b c d)
[(_ xⱼ zᵢ )
(subtemplate foo)]))
'foo)
(syntax-parse (syntax-parse #'(a b c d)
[(_ xⱼ zᵢ )
(list (subtemplate ([xⱼ wⱼ] foo [zᵢ pᵢ] ))
(subtemplate ([xⱼ wⱼ] foo [zᵢ pᵢ] )))])
[(([x1 w1] foo1 [z1 p1] [zz1 pp1])
([x2 w2] foo2 [z2 p2] [zz2 pp2]))
(check free-identifier=? #'x1 #'x2)
(check free-identifier=? #'w1 #'w2)
(check free-identifier=? #'foo1 #'foo2)
(check free-identifier=? #'z1 #'z2)
(check free-identifier=? #'p1 #'p2)
(check free-identifier=? #'zz1 #'zz2)
(check free-identifier=? #'pp1 #'pp2)
(check free-identifier=? #'x1 #'b)
(check free-identifier=? #'z1 #'c)
(check free-identifier=? #'zz1 #'d)
(check free-identifier=? #'x2 #'b)
(check free-identifier=? #'z2 #'c)
(check free-identifier=? #'zz2 #'d)
;; The *1 are all different:
(check free-identifier=? #'x1 #'x1)
(check ( not free-identifier=?) #'x1 #'w1)
(check ( not free-identifier=?) #'x1 #'foo1)
(check ( not free-identifier=?) #'x1 #'z1)
(check ( not free-identifier=?) #'x1 #'p1)
(check ( not free-identifier=?) #'x1 #'zz1)
(check ( not free-identifier=?) #'x1 #'pp1)
(check ( not free-identifier=?) #'w1 #'x1)
(check free-identifier=? #'w1 #'w1)
(check ( not free-identifier=?) #'w1 #'foo1)
(check ( not free-identifier=?) #'w1 #'z1)
(check ( not free-identifier=?) #'w1 #'p1)
(check ( not free-identifier=?) #'w1 #'zz1)
(check ( not free-identifier=?) #'w1 #'pp1)
(check ( not free-identifier=?) #'foo1 #'x1)
(check ( not free-identifier=?) #'foo1 #'w1)
(check free-identifier=? #'foo1 #'foo1)
(check ( not free-identifier=?) #'foo1 #'z1)
(check ( not free-identifier=?) #'foo1 #'p1)
(check ( not free-identifier=?) #'foo1 #'zz1)
(check ( not free-identifier=?) #'foo1 #'pp1)
(check ( not free-identifier=?) #'z1 #'x1)
(check ( not free-identifier=?) #'z1 #'w1)
(check ( not free-identifier=?) #'z1 #'foo1)
(check free-identifier=? #'z1 #'z1)
(check ( not free-identifier=?) #'z1 #'p1)
(check ( not free-identifier=?) #'z1 #'zz1)
(check ( not free-identifier=?) #'z1 #'pp1)
(check ( not free-identifier=?) #'p1 #'x1)
(check ( not free-identifier=?) #'p1 #'w1)
(check ( not free-identifier=?) #'p1 #'foo1)
(check ( not free-identifier=?) #'p1 #'z1)
(check free-identifier=? #'p1 #'p1)
(check ( not free-identifier=?) #'p1 #'zz1)
(check ( not free-identifier=?) #'p1 #'pp1)
(check ( not free-identifier=?) #'zz1 #'x1)
(check ( not free-identifier=?) #'zz1 #'w1)
(check ( not free-identifier=?) #'zz1 #'foo1)
(check ( not free-identifier=?) #'zz1 #'z1)
(check ( not free-identifier=?) #'zz1 #'p1)
(check free-identifier=? #'zz1 #'zz1)
(check ( not free-identifier=?) #'zz1 #'pp1)
(check ( not free-identifier=?) #'pp1 #'x1)
(check ( not free-identifier=?) #'pp1 #'w1)
(check ( not free-identifier=?) #'pp1 #'foo1)
(check ( not free-identifier=?) #'pp1 #'z1)
(check ( not free-identifier=?) #'pp1 #'p1)
(check ( not free-identifier=?) #'pp1 #'zz1)
(check free-identifier=? #'pp1 #'pp1)
;; The *2 are all different:
(check free-identifier=? #'x2 #'x2)
(check ( not free-identifier=?) #'x2 #'w2)
(check ( not free-identifier=?) #'x2 #'foo2)
(check ( not free-identifier=?) #'x2 #'z2)
(check ( not free-identifier=?) #'x2 #'p2)
(check ( not free-identifier=?) #'x2 #'zz2)
(check ( not free-identifier=?) #'x2 #'pp2)
(check ( not free-identifier=?) #'w2 #'x2)
(check free-identifier=? #'w2 #'w2)
(check ( not free-identifier=?) #'w2 #'foo2)
(check ( not free-identifier=?) #'w2 #'z2)
(check ( not free-identifier=?) #'w2 #'p2)
(check ( not free-identifier=?) #'w2 #'zz2)
(check ( not free-identifier=?) #'w2 #'pp2)
(check ( not free-identifier=?) #'foo2 #'x2)
(check ( not free-identifier=?) #'foo2 #'w2)
(check free-identifier=? #'foo2 #'foo2)
(check ( not free-identifier=?) #'foo2 #'z2)
(check ( not free-identifier=?) #'foo2 #'p2)
(check ( not free-identifier=?) #'foo2 #'zz2)
(check ( not free-identifier=?) #'foo2 #'pp2)
(check ( not free-identifier=?) #'z2 #'x2)
(check ( not free-identifier=?) #'z2 #'w2)
(check ( not free-identifier=?) #'z2 #'foo2)
(check free-identifier=? #'z2 #'z2)
(check ( not free-identifier=?) #'z2 #'p2)
(check ( not free-identifier=?) #'z2 #'zz2)
(check ( not free-identifier=?) #'z2 #'pp2)
(check ( not free-identifier=?) #'p2 #'x2)
(check ( not free-identifier=?) #'p2 #'w2)
(check ( not free-identifier=?) #'p2 #'foo2)
(check ( not free-identifier=?) #'p2 #'z2)
(check free-identifier=? #'p2 #'p2)
(check ( not free-identifier=?) #'p2 #'zz2)
(check ( not free-identifier=?) #'p2 #'pp2)
(check ( not free-identifier=?) #'zz2 #'x2)
(check ( not free-identifier=?) #'zz2 #'w2)
(check ( not free-identifier=?) #'zz2 #'foo2)
(check ( not free-identifier=?) #'zz2 #'z2)
(check ( not free-identifier=?) #'zz2 #'p2)
(check free-identifier=? #'zz2 #'zz2)
(check ( not free-identifier=?) #'zz2 #'pp2)
(check ( not free-identifier=?) #'pp2 #'x2)
(check ( not free-identifier=?) #'pp2 #'w2)
(check ( not free-identifier=?) #'pp2 #'foo2)
(check ( not free-identifier=?) #'pp2 #'z2)
(check ( not free-identifier=?) #'pp2 #'p2)
(check ( not free-identifier=?) #'pp2 #'zz2)
(check free-identifier=? #'pp2 #'pp2)])
(syntax-parse (syntax-parse #'(a b c)
[(xᵢ )
(define flob (quasisubtemplate (zᵢ )))
(quasisubtemplate (yᵢ
#,flob
zᵢ ))])
[(a1 b1 c1 (a2 b2 c2) a3 b3 c3)
(check free-identifier=? #'a2 #'a3)
(check free-identifier=? #'b2 #'b3)
(check free-identifier=? #'c2 #'c3)
(check ( not free-identifier=?) #'a1 #'a2)
(check ( not free-identifier=?) #'b1 #'b2)
(check ( not free-identifier=?) #'c1 #'c2)])
(syntax-parse (syntax-parse #'(a b c)
[(xᵢ )
(quasisubtemplate (yᵢ
;; must be from xᵢ, not yᵢ
#,(quasisubtemplate (zᵢ ))
zᵢ ))])
[(a1 b1 c1 (a2 b2 c2) a3 b3 c3)
(check free-identifier=? #'a2 #'a3)
(check free-identifier=? #'b2 #'b3)
(check free-identifier=? #'c2 #'c3)
(check ( not free-identifier=?) #'a1 #'a2)
(check ( not free-identifier=?) #'b1 #'b2)
(check ( not free-identifier=?) #'c1 #'c2)])
;; the test above is not exactly right (zᵢ will still have the correct
;; binding), but it gives the general idea.
(syntax->datum
(syntax-parse #'(a b c)
[(xᵢ )
(define flob (syntax-parse #'d [d (quasisubtemplate (zᵢ ))]))
(quasisubtemplate (yᵢ
;; must be from xᵢ, not yᵢ
#,flob
zᵢ ))]))
(syntax->datum
(syntax-parse #'(a b c)
[(xᵢ )
(quasisubtemplate (yᵢ
;; must be from xᵢ, not yᵢ
#,(syntax-parse #'d [d (quasisubtemplate (zᵢ ))])
zᵢ ))]))