More patterns that match no terms are now syntax errors

For example: (any_1 ..._!_1 any_2 ..._!_1 (any_1 any_2) ...)
This commit is contained in:
Robby Findler 2013-11-14 08:33:22 -06:00
parent 5a0ddb3460
commit c2d7e02c70
3 changed files with 100 additions and 56 deletions

View File

@ -56,18 +56,13 @@
(define last-stx (make-hasheq)) ;; used for syntax error reporting
(define assignments #hasheq())
;; hash[sym -o> (listof (list stx (listof id[mismatch-ellipsis-binders])))
(define vars-under-mismatch-table (make-hash))
;; hash[sym -o> (listof stx)]
(define var-locs-table (make-hash))
(define (record-binder pat-stx under under-mismatch-ellipsis)
(define pat-sym (syntax->datum pat-stx))
(hash-set! vars-under-mismatch-table
(syntax-e pat-stx)
(cons
(list pat-stx under-mismatch-ellipsis)
(hash-ref vars-under-mismatch-table
(syntax-e pat-stx)
'())))
(hash-set! var-locs-table pat-sym (cons pat-stx (hash-ref var-locs-table pat-sym '())))
(set! assignments
(if (null? under)
assignments
@ -77,16 +72,17 @@
[last
(unless (equal? (length last) (length under))
(define stxs (hash-ref last-stx pat-sym))
(raise-syntax-error what
(format "found ~a under ~a ellips~as in one place and ~a ellips~as in another"
pat-sym
(length last)
(if (= 1 (length last)) "i" "e")
(length under)
(if (= 1 (length under)) "i" "e"))
orig-stx
(car stxs)
(cdr stxs)))
(raise-syntax-error
what
(format "found ~a under ~a ellips~as in one place and ~a ellips~as in another"
pat-sym
(length last)
(if (= 1 (length last)) "i" "e")
(length under)
(if (= 1 (length under)) "i" "e"))
orig-stx
(car stxs)
(cdr stxs)))
(foldl (λ (cur last asgns) (union cur last asgns)) assignments under last)]
[else
(hash-set! last-contexts pat-sym under)
@ -107,7 +103,8 @@
(let loop ([term orig-stx]
[under '()]
[under-mismatch-ellipsis '()])
(syntax-case term (side-condition variable-except variable-prefix hole name in-hole hide-hole cross unquote and)
(syntax-case term (side-condition variable-except variable-prefix
hole name in-hole hide-hole cross unquote and)
[(side-condition pre-pat (and))
;; rewriting metafunctions (and possibly other things) that have no where, etc clauses
;; end up with side-conditions that are empty 'and' expressions, so we just toss them here.
@ -237,7 +234,8 @@
[else
(raise-syntax-error
what
(format "before underscore must be either a non-terminal or a built-in pattern, found ~a in ~s"
(format (string-append "before underscore must be either a"
" non-terminal or a built-in pattern, found ~a in ~s")
prefix-sym (syntax-e term))
orig-stx
term)])]
@ -326,7 +324,8 @@
rst-terms)
(append fst-vars rst-vars))]
[else
(define-values (fst-term fst-vars) (loop (car terms) under under-mismatch-ellipsis))
(define-values (fst-term fst-vars)
(loop (car terms) under under-mismatch-ellipsis))
(define-values (rst-terms rst-vars) (t-loop (cdr terms)))
(values (cons fst-term rst-terms)
(append fst-vars rst-vars))])))
@ -368,10 +367,56 @@
(datum->syntax pat new pat)
new))]
[_ pat])))
;(printf "term ~s\n" (syntax->datum (datum->syntax #'here term)))
;(printf "norm ~s\n" (syntax->datum (datum->syntax #'here ellipsis-normalized)))
;(printf "repeat-id-counts ~s\n" repeat-id-counts)
(define (raise-impossible-pattern-error pat1 pat2)
(define (find-pat-var pat)
(syntax-case pat (name)
[(name id pat)
(syntax-e #'id)]
[_ #f]))
(define pat-var1 (find-pat-var pat1))
(define pat-var2 (find-pat-var pat1))
(cond
[(and pat-var1 pat-var2
(not (equal? pat-var1 pat-var2)))
(define all-ids (append (hash-ref var-locs-table pat-var1 '())
(hash-ref var-locs-table pat-var2 '())))
(raise-syntax-error
what
(format
(string-append "no terms match pattern;\n"
" ~a and ~a are together overly constrained")
pat-var1 pat-var2)
orig-stx
(if (null? all-ids) #f (car all-ids))
(if (null? all-ids) '() (cdr all-ids)))]
[(or pat-var1 pat-var2)
=>
(λ (the-pat-var)
(define all-ids (hash-ref var-locs-table the-pat-var '()))
(raise-syntax-error
what
(format (string-append "no terms match pattern;\n"
" ~a is overly constrained")
the-pat-var)
orig-stx
(if (null? all-ids) #f (car all-ids))
(if (null? all-ids) '() (cdr all-ids))))]
[else
(raise-syntax-error
what
"no terms match pattern"
orig-stx)]))
#|
(printf "term\n") ((dynamic-require 'racket/pretty 'pretty-print)
(syntax->datum (datum->syntax #'here term)))
(printf "norm\n") ((dynamic-require 'racket/pretty 'pretty-print)
(syntax->datum (datum->syntax #'here ellipsis-normalized)))
(printf "repeat-id-counts ~s\n" repeat-id-counts)
|#
;; hash[(list symbol[match-id] symbol[mismatch-id]) -o> syntax[repeat pattern]]
(define both-match-and-mismatch-id (make-hash))
(define ellipsis-normalized/simplified
(let loop ([pat ellipsis-normalized])
@ -387,6 +432,14 @@
(= 1 (hash-ref repeat-id-counts (syntax-e #'mismatch-name))))
#f
#'mismatch-name))
(when (and (identifier? final-mismatch-repeat-name)
(identifier? final-match-repeat-name))
(define key (list (syntax-e final-mismatch-repeat-name)
(syntax-e final-match-repeat-name)))
(define already-bound (hash-ref both-match-and-mismatch-id key #f))
(when already-bound
(raise-impossible-pattern-error already-bound #'sub-pat))
(hash-set! both-match-and-mismatch-id key #'sub-pat))
#`(repeat #,(loop #'sub-pat)
#,final-match-repeat-name
#,final-mismatch-repeat-name))]
@ -400,28 +453,6 @@
(filter-duplicates what orig-stx names)
(for ([(id stx+mismatches) (in-hash vars-under-mismatch-table)])
(define all-ids (map car stx+mismatches))
(define all-mismatch-idss (map cadr stx+mismatches))
(let loop ([all-mismatch-idss all-mismatch-idss])
(cond
[(null? all-mismatch-idss) (void)]
[else
(define id-in-multiple-places/f
(for/or ([mismatch-id (in-list (car all-mismatch-idss))])
(for/or ([other-mismatch-ids (in-list (cdr all-mismatch-idss))])
(and (member mismatch-id other-mismatch-ids)
mismatch-id))))
(when id-in-multiple-places/f
(raise-syntax-error what
(format "~a appears under ~a in multiple places (so this pattern would always fail)"
id
id-in-multiple-places/f)
orig-stx
(car all-ids)
(cdr all-ids)))
(loop (cdr all-mismatch-idss))])))
(let ([without-mismatch-names (filter (λ (x) (not (id/depth-mismatch? x))) names)])
(with-syntax ([(name/ellipses ...) (map build-dots without-mismatch-names)]
[(name ...) (map id/depth-id without-mismatch-names)]
@ -431,7 +462,9 @@
(define-struct id/depth (id depth mismatch?))
;; extract-names : syntax syntax -> (values (listof syntax) (listof syntax[x | (x ...) | ((x ...) ...) | ...]))
;; extract-names : syntax syntax ->
;; (values (listof syntax)
;; (listof syntax[x | (x ...) | ((x ...) ...) | ...]))
;; this function is obsolete and uses of it are suspect. Things should be using
;; rewrite-side-conditions/check-errs instead
(define (extract-names all-nts what bind-names? orig-stx [mode 'rhs-only])
@ -500,7 +533,12 @@
(and (not (regexp-match #rx"^\\.\\.\\._" str))
(not (regexp-match #rx"_!_" str))))))
(define (raise-ellipsis-depth-error what one-binder one-depth another-binder another-depth [orig-stx #f])
(define (raise-ellipsis-depth-error what
one-binder
one-depth
another-binder
another-depth
[orig-stx #f])
(raise-syntax-error
what
(format "found the same binder, ~s, at different depths, ~a and ~a"
@ -531,4 +569,3 @@
orig-stx)))
(not same-id?)))
(loop (cdr dups))))])))

View File

@ -27,7 +27,8 @@
(check-equal? (rsc (1 ..._2) () #t) `((list (repeat 1 #f #f)) () ()))
(check-equal? (rsc (1 ..._2 1 ..._2) () #t) `((list (repeat 1 ..._2 #f) (repeat 1 ..._2 #f)) () ()))
(check-equal? (rsc (1 ..._!_3) () #t) `((list (repeat 1 #f #f)) () ()))
(check-equal? (rsc (1 ..._!_3 1 ..._!_3) () #t) `((list (repeat 1 #f ..._!_3) (repeat 1 #f ..._!_3)) () ()))
(check-equal? (rsc (1 ..._!_3 1 ..._!_3) () #t)
`((list (repeat 1 #f ..._!_3) (repeat 1 #f ..._!_3)) () ()))
(check-equal? (rsc x (x) #t) `((name x (nt x)) (x) (x)))
(check-equal? (rsc x (x) #f) `((nt x) () ()))
@ -48,8 +49,17 @@
()))
(check-regexp-match
#rx"any_1 appears under ..._!_1 in multiple places"
#rx"any_1 is overly"
(rsc (any_1 ..._!_1 any_1 ..._!_1) () #f))
(check-regexp-match
#rx"any_1 is overly constrained"
(rsc (any_1 ..._!_1 any_2 ..._!_1 (any_1 any_2) ...) () #f))
(check-regexp-match
#rx"any_1 is overly constrained"
(rsc (any_1 ..._!_1 any_2 ..._!_1 any_1 ..._1 any_2 ..._1) () #f))
(check-regexp-match
#rx"any_1 is overly constrained"
(rsc (any_1 ..._!_1 any_2 ..._!_1 (any_1 any_3) ... (any_3 any_2) ...) () #f))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;

View File

@ -316,7 +316,6 @@
(b (number_1 ..._!_1 number_1 ..._1))
(c (variable_1 ..._1 number_2 ..._1))
(d (z_1 ... z_2 ..._!_1 (z_1 z_2) ...))
(e (n_1 ..._!_1 n_2 ..._!_1 (n_1 n_2) ..._3))
(f (n_1 ..._1 n_2 ..._2 n_2 ..._1))
(g (z_1 ..._!_1 z_2 ... (z_1 z_2) ...))
(n number)
@ -334,8 +333,6 @@
null)
(test (generate-term/decisions lang d 5 0 (decisions #:seq (list (λ (_) 2))))
'(4 4 4 4 (4 4) (4 4)))
(test (raised-exn-msg exn:fail:redex:generation-failure? (generate-term lang e 5 #:retries 42))
#rx"generate-term: unable to generate pattern .* in 42")
(test (raised-exn-msg
exn:fail:redex:generation-failure?
(parameterize ([generation-decisions