Done most work concerning syntax/parse.

This commit is contained in:
Georges Dupéron 2016-08-26 22:31:11 +02:00
parent 3feb92c09d
commit c927ae2e3b
3 changed files with 181 additions and 69 deletions

View File

@ -17,10 +17,11 @@
structure-kw-all-mixin)
(define-eh-alternative-mixin structure-kw-instance-or-builder
(pattern (~optional (~and instance-or-builder
(~or (~and instance #:instance)
(~and builder #:builder)))
#:name "either #:instance or #:builder")))
(pattern
(~optional (~and instance-or-builder
(~or (~global-or instance #:instance)
(~global-or builder #:builder)))
#:name "either #:instance or #:builder")))
(define-eh-alternative-mixin structure-kw-predicate
(pattern (~optional (~seq #:? predicate:id)
@ -42,14 +43,18 @@
(pattern
(~optional/else
(~or (~seq (~or-bug [field:id] field:id) …+
(~global-or builder)
(~post-fail no-values-err #:when (attribute instance)))
(~seq [field:id : type] …+
(~global-or builder)
(~post-fail no-values-err #:when (attribute instance)))
(~seq [field:id value:expr] …+
(~global-or instance)
(~post-fail values-err #:when (attribute builder)))
(~seq (~or-bug [field:id value:expr : type]
[field:id : type value:expr])
…+
(~global-or instance)
(~post-fail values-err #:when (attribute builder))))
#:defaults ([(field 1) (list)]
[(value 1) (list)]
@ -70,20 +75,22 @@
(define-splicing-syntax-class structure-kws
(pattern (~no-order (structure-kw-all-mixin))))
(check-equal? (syntax->datum
(syntax-parse #'(#:instance #:? p)
[(:structure-kws)
#'(instance instance-or-builder
predicate
[field ...]
[value ...])]))
'(#:instance #:instance p [] []))
(check-equal? (syntax-parse #'(#:instance #:? p)
[(:structure-kws)
(list* (attribute instance)
(syntax->datum
#'(instance-or-builder
predicate
[field ...]
[value ...])))])
'(#t #:instance p [] []))
(check-equal? (syntax->datum
(syntax-parse #'(#:builder)
[(k:structure-kws)
#'(k.builder k.instance-or-builder [k.field ...])]))
'(#:builder #:builder []))
(check-equal? (syntax-parse #'(#:builder)
[(k:structure-kws)
(list* (attribute k.builder)
(syntax->datum
#'(k.instance-or-builder [k.field ...])))])
'(#t #:builder []))
(test-exn
"Check that () is rejected, as it has neither #:instance nor #:builder"
@ -100,18 +107,57 @@ builder-style field declarations"
(syntax-parse #'(#:instance [f1] [f2])
[(:structure-kws) #'([field ...] instance)])))
(check-equal? (syntax->datum
(syntax-parse #'(#:builder #:? p [f1] [f2])
[(:structure-kws) #'([field ...] builder)]))
'([f1 f2] #:builder))
(check-equal? (syntax-parse #'(#:builder #:? p [f1] [f2])
[(:structure-kws) (list* (attribute builder)
(syntax->datum #'([field ...])))])
'(#t [f1 f2]))
(check-equal? (syntax-parse #'([f1] [f2]); #:? p
(check-equal? (syntax-parse #'([f1] [f2] #:? p)
[(:structure-kws) (cons (attribute builder)
(syntax->datum #'([field ...])))])
'(#f [f1 f2]))
'(#t [f1 f2]))
;; This one is appropriately rejected
(check-exn #px"unexpected term"
(λ ()
(syntax-parse #'(#:instance #:a)
[(:structure-kws) 'err])))
[(:structure-kws) 'err])))
(define instance-or-builder?
(syntax-parser [(:structure-kws) (list (attr instance) (attr builder))]))
(check-equal? '(#t #f) (instance-or-builder? #'(#:instance)))
(check-equal? '(#f #t) (instance-or-builder? #'(#:builder)))
(check-equal? '(#f #t) (instance-or-builder? #'(f1)))
(check-equal? '(#f #t) (instance-or-builder? #'([f1])))
(check-equal? '(#f #t) (instance-or-builder? #'([f1] f2)))
(check-equal? '(#f #t) (instance-or-builder? #'([f1 : type])))
(check-equal? '(#t #f) (instance-or-builder? #'([f1 value])))
(check-equal? '(#t #f) (instance-or-builder? #'([f1 : type value])))
(check-equal? '(#t #f) (instance-or-builder? #'([f1 value : type])))
(check-equal? '(#f #t) (instance-or-builder? #'(f1 #:builder)))
(check-equal? '(#f #t) (instance-or-builder? #'([f1] #:builder)))
(check-equal? '(#f #t) (instance-or-builder? #'([f1] f2 #:builder)))
(check-equal? '(#f #t) (instance-or-builder? #'([f1 : type] #:builder)))
(check-equal? '(#t #f) (instance-or-builder? #'([f1 value] #:instance)))
(check-equal? '(#t #f) (instance-or-builder? #'([f1 : type value] #:instance)))
(check-equal? '(#t #f) (instance-or-builder? #'([f1 value : type] #:instance)))
;; TODO: use (reified-syntax-class-attributes r) to make a simplified version
;; of a macro, which just accepts all the attributes. Another macro can
;; then forward all the attributes at once, with minimal meta-level 1 cost
;; (obviously, constructing the wrappers etx. will have some metal-level 2 cost)
;;
;; Wrapper:
;; (define-syntax (real-macro-name stx)
;; (syntax-parse stx
;; [(~reflect whole some-reified-splicing-syntax-class)
;; (simplified-macro-implementation (attribute attr0) ...)]))
;; Implementation
;; (define-for-syntax (simplified-macro-implementation val0 ...)
;; (syntax-parse #'dummy
;; [(~bind [(attr0 depth) val0] ...)
;; body]))
;;
;; For speed, we could just copy the whole implementation in real-macro-name's
;; definition, instead of calling simplified-macro-implementation.

View File

@ -11,5 +11,37 @@
syntax/stx
racket/format))
(syntax-parse #'(1 #:kw 3)
[{~no-order {~once {~global-counter #:kw }} }
(check-equal? (syntax-parse #'(1 "ab" #:kw "ab" 3 4 5)
[({~no-order {~once {~global-counter cnt 'occurrencea #:kw}}
{~global-counter cnt 'occurrenceb :number}
"ab"})
(attribute cnt)])
5)
(check-equal? (syntax-parse #'(1 "ab" #:kw "ab" 3 4 5)
[({~no-order {~once {~global-or kw-or-number #t #:kw}}
{~global-or kw-or-number #t :number}
"ab"})
(attribute kw-or-number)])
#t)
(check-equal? (syntax-parse #'(1 "ab" "ab" 3 4 5)
[({~no-order {~optional {~global-or kw #t #:kw}}
{~global-or kw #f :number}
"ab"})
(attribute kw)])
#f)
(check-equal? (syntax-parse #'(1 "ab" #:kw "ab" 3 4 5)
[({~no-order {~optional {~global-and kw-and-not-number #t #:kw}}
{~global-and kw-and-not-number #f :number}
"ab"})
(attribute kw-and-not-number)])
#f)
(check-equal? (syntax-parse #'("ab" #:kw "ab")
[({~no-order {~optional {~global-and kw-and-not-number #t #:kw}}
{~global-and kw-and-not-number #f :number}
"ab"})
(attribute kw-and-not-number)])
#t)

View File

@ -5,11 +5,13 @@
generic-syntax-expanders
phc-toolkit/untyped
(for-syntax syntax/parse
syntax/parse/experimental/template
racket/syntax
phc-toolkit/untyped
racket/list
generic-syntax-expanders
racket/contract))
racket/function
racket/pretty))
(provide ;define-splicing-syntax-class-with-eh-mixins
;define-syntax-class-with-eh-mixins
@ -22,7 +24,10 @@
~optional/else
~global-or
~global-and
~global-counter)
~global-counter
aggregate-global-or
aggregate-global-and
aggregate-global-counter)
;; ------------
;; eh-mixin — TODO: move to phc-toolkit once the PR #7 for reqprov in
@ -41,7 +46,7 @@
(apply (parameter-name) args))))
(define-dynamic-accumulator-parameter eh-post-accumulate eh-post-accumulate!)
(define-dynamic-accumulator-parameter eh-pre-declarations eh-pre-declare!)
(define-dynamic-accumulator-parameter eh-post-group eh-post-group!)
;; ----
@ -75,36 +80,42 @@
(λ (stx)
(syntax-case stx ()
[(self pat ...)
(let ()
(define counter 0)
(define (increment-counter)
(begin0 counter
(set! counter (add1 counter))))
(define post-acc '())
(define (add-to-post! v) (set! post-acc (cons v post-acc)))
;; pre-acc gathers some bindings that have to be pre-declared
(define pre-acc (make-hash))
(define/contract (add-to-pre! s v) (-> symbol? any/c identifier?)
(define not-found (gensym))
(define ref (hash-ref pre-acc s #f))
(if ref
(car ref)
(let ([id (datum->syntax (syntax-local-introduce #'here) s)])
(hash-set! pre-acc s (cons id v))
id)))
;(define-values (pre-acc add-to-pre) (make-mutable-accumulator))
(define alts
(parameterize ([eh-post-accumulate add-to-post!]
[eh-pre-declarations add-to-pre!]
[clause-counter increment-counter])
(inline-or (expand-all-eh-mixin-expanders #'(~or pat ...)))))
(define pre-acc-bindings (hash-map pre-acc
(λ (s bv) #`(define . #,bv))))
#`(~delimit-cut
(~and (~do #,@pre-acc-bindings)
(~seq (~or . #,alts) (... ...))
~!
#,@post-acc)))]))))
((λ (x) #;(pretty-write (syntax->datum x)) x)
(let ()
(define counter 0)
(define (increment-counter)
(begin0 counter
(set! counter (add1 counter))))
;; post-acc gathers some a-patterns which will be added after the
;; (~seq (~or ) ...)
(define post-acc '())
(define (add-to-post! v) (set! post-acc (cons v post-acc)))
;; post-groups-acc gathers some attributes that have to be grouped
(define post-groups-acc '())
(define (add-to-post-groups! . v)
(set! post-groups-acc (cons v post-groups-acc)))
;; expand EH alternatives:
(define alts
(parameterize ([eh-post-accumulate add-to-post!]
[eh-post-group add-to-post-groups!]
[clause-counter increment-counter])
(inline-or (expand-all-eh-mixin-expanders #'(~or pat ...)))))
(define post-group-bindings
(for/list ([group (group-by car post-groups-acc free-identifier=?)])
;; each item in `group` is a four-element list:
;; (list result-id aggregate-function attribute)
(define/with-syntax name (first (car group))
#;(syntax-local-introduce
(datum->syntax #'here
(first (car group)))))
(define/with-syntax f (second (car group)))
#`[name (f . #,(map (λ (i) #`(attribute #,(third i)))
group))]))
#`(~delimit-cut
(~and (~seq (~or . #,alts) (... ...))
~!
(~bind #,@post-group-bindings)
#,@post-acc))))]))))
(define-syntax ~nop
(pattern-expander
@ -142,17 +153,40 @@
[(self (mutex:id ...) pat ...)
#'(???)]))))
(define-syntax-rule (define-~global ~global-name init f)
(define-eh-mixin-expander ~global-name
(λ/syntax-case (_ name v pat) ()
(eh-pre-declare! '~bool-or (syntax-e #'name) init)
#`(~and (~do (define tmp name))
(~do (define name (#,f tmp v)))
pat))))
(define-syntax/parse (define-~global global-name (~optional default) f)
(define use-default-v? (syntax-e #'default-v?))
(template
(define-eh-mixin-expander global-name
(syntax-parser
[(_ (?? (~or [name v] (~and name (~bind [v default])))
[name v])
. pat)
(define/with-syntax clause-value (get-new-clause!))
(eh-post-group! '~global-name
#'name ;(syntax-e #'name)
#'f
#'clause-value)
;; protect the values inside an immutable box, so that a #f can be
;; distinguished from a failed match.
#'(~and (~bind [clause-value (box-immutable v)])
. pat)]))))
(define-~global ~global-or #f (λ (acc v) (or acc v)))
(define-~global ~global-and #t (λ (acc v) (and acc v)))
(define-~global ~global-counter 0 add1)
(define (aggregate-global-or . bs)
(ormap unbox ;; remove the layer of protection
(filter identity ;; remove failed bindings
(flatten bs)))) ;; don't care about ellipsis nesting
(define-~global ~global-or #'#t aggregate-global-or)
(define (aggregate-global-and . bs)
(andmap unbox ;; remove the layer of protection
(filter identity ;; remove failed bindings
(flatten bs)))) ;; don't care about ellipsis nesting
(define-~global ~global-and aggregate-global-and)
(define (aggregate-global-counter . bs)
(length (filter identity ;; remove failed bindings
(flatten bs)))) ;; don't care about ellipsis nesting
(define-~global ~global-counter #''occurrence aggregate-global-counter)
(define-eh-mixin-expander ~optional/else
(syntax-parser