diff --git a/pkgs/racket-test/tests/stxparse/select.rkt b/pkgs/racket-test/tests/stxparse/select.rkt index 3a6ec96b23..a03e91b4a6 100644 --- a/pkgs/racket-test/tests/stxparse/select.rkt +++ b/pkgs/racket-test/tests/stxparse/select.rkt @@ -232,3 +232,35 @@ #rx"expected (B|C)" #rx"while parsing A" (not #rx"while parsing (B|C)"))) + +;; ------------------------------------------------------------ +;; Regression tests + +;; 4/16/2016, distilled from report by stchang +;; Want error message in second side clause to take precedence over +;; ellipsis-matching failures in first side clause. + +(test-case "side-clauses order 1" + (check-exn #rx"unhappy about last number" + (lambda () + (syntax-parse #'(1 2 3 4) + [(x:nat ...) + #:with (y ... z) #'(x ...) + #:fail-unless (>= (syntax->datum #'z) 10) + "unhappy about last number" + 'ok])))) + +(test-case "side-clauses order 2" + (check-exn (lambda (exn) + (and (regexp-match? #rx"unhappy about last number" (exn-message exn)) + (exn:fail:syntax? exn) + (let* ([terms (exn:fail:syntax-exprs exn)] + [term (and (pair? terms) (syntax->datum (car terms)))]) + (check-equal? term '4)))) + (lambda () + (syntax-parse #'(1 2 3 4) + [(x:nat ...) + #:with (y ... z) #'(x ...) + #:fail-when (and (< (syntax->datum #'z) 10) #'z) + "unhappy about last number" + 'ok])))) diff --git a/racket/collects/syntax/parse/private/parse.rkt b/racket/collects/syntax/parse/private/parse.rkt index 95d6b3e00d..01f2360ece 100644 --- a/racket/collects/syntax/parse/private/parse.rkt +++ b/racket/collects/syntax/parse/private/parse.rkt @@ -732,8 +732,8 @@ Conventions: (parse:S y cy pattern pr* es k))] [#s(action:do _ (stmt ...)) #'(let () (no-shadow stmt) ... (#%expression k))] - [#s(action:post _ pattern) - #'(let ([pr* (ps-add-post pr)]) + [#s(action:post _ pattern group index) + #'(let ([pr* (ps-add-post pr 'group 'index)]) (parse:A x cx pattern pr* es k))])])) ;; (bind/sides clauses k) : expr[Ans] diff --git a/racket/collects/syntax/parse/private/rep-patterns.rkt b/racket/collects/syntax/parse/private/rep-patterns.rkt index 2fecadf19c..1477c080de 100644 --- a/racket/collects/syntax/parse/private/rep-patterns.rkt +++ b/racket/collects/syntax/parse/private/rep-patterns.rkt @@ -79,7 +79,7 @@ A ActionPattern is one of * (action:and Base (listof ActionPattern)) (action:parse Base SinglePattern stx) (action:do Base (listof stx)) - (action:post Base ActionPattern) + (action:post Base ActionPattern Quotable Nat) action:and is desugared below in create-* procedures |# @@ -90,7 +90,7 @@ action:and is desugared below in create-* procedures (define-struct action:and (attrs patterns) #:prefab) (define-struct action:parse (attrs pattern expr) #:prefab) (define-struct action:do (attrs stmts) #:prefab) -(define-struct action:post (attrs pattern) #:prefab) +(define-struct action:post (attrs pattern group index) #:prefab) #| A HeadPattern is one of @@ -333,8 +333,8 @@ A SideClause is one of (define (create-action:do stmts) (make action:do null stmts)) -(define (create-action:post pattern) - (make action:post (pattern-attrs pattern) pattern)) +(define (create-action:post pattern group index) + (make action:post (pattern-attrs pattern) pattern group index)) ;; ---- diff --git a/racket/collects/syntax/parse/private/rep.rkt b/racket/collects/syntax/parse/private/rep.rkt index 10e2e4cfaf..8a216421b2 100644 --- a/racket/collects/syntax/parse/private/rep.rkt +++ b/racket/collects/syntax/parse/private/rep.rkt @@ -399,16 +399,19 @@ ;; combine-pattern+sides : Pattern (listof SideClause) -> Pattern (define (combine-pattern+sides pattern sides splicing?) + (define sides-group (gensym)) (define actions-pattern (create-action:and - (for/list ([side (in-list sides)]) + (for/list ([side (in-list sides)] [index (in-naturals)]) (match side [(clause:fail condition message) (create-action:post - (create-action:fail condition message))] + (create-action:fail condition message) + sides-group index)] [(clause:with wpat expr defs) (let ([ap (create-action:post - (create-action:parse wpat expr))]) + (create-action:parse wpat expr) + sides-group index)]) (if (pair? defs) (create-action:and (list (create-action:do defs) ap)) ap))] @@ -1042,7 +1045,7 @@ [(_ pattern) (let ([p (parse-*-pattern #'pattern decls allow-head? allow-action?)]) (cond [(action-pattern? p) - (cond [allow-action? (create-action:post p)] + (cond [allow-action? (create-action:post p #f 0)] [(not allow-head?) (create-pat:post (action-pattern->single-pattern p))] [else (wrong-syntax stx "action pattern not allowed here")])] [(head-pattern? p) diff --git a/racket/collects/syntax/parse/private/runtime-progress.rkt b/racket/collects/syntax/parse/private/runtime-progress.rkt index 90b1407caf..5df024094b 100644 --- a/racket/collects/syntax/parse/private/runtime-progress.rkt +++ b/racket/collects/syntax/parse/private/runtime-progress.rkt @@ -10,6 +10,7 @@ ps-add-unvector ps-add-unpstruct ps-add-opaque + (struct-out post) ps-pop-opaque ps-context-syntax @@ -52,10 +53,10 @@ A FailFunction = (FailureSet -> Answer) Progress (PS) is a non-empty list of Progress Frames (PF). A Progress Frame (PF) is one of - - stx ;; "Base" frame + - stx ;; "Base" frame, or ~parse/#:with term - 'car ;; car of pair; also vector->list, unbox, struct->list, etc - nat ;; Represents that many repeated cdrs - - 'post + - #s(post group index) ;; late/post-traversal check, only comparable w/in group - 'opaque The error-reporting context (ie, syntax-parse #:context arg) is always @@ -76,6 +77,7 @@ Interpretation: later frames are applied first. means ( car of ( cdr once of stx ) ) NOT apply car, then apply cdr once, then stop |# +(define-struct post (group index) #:prefab) (define (ps-empty stx ctx) (if (eq? stx ctx) @@ -91,8 +93,8 @@ Interpretation: later frames are applied first. (cons (+ times n) (cdr parent))] [_ (cons times parent)]))) -(define (ps-add-post parent) - (cons 'post parent)) +(define (ps-add-post parent [group #f] [index 0]) + (cons (post group index) parent)) (define (ps-add-stx parent stx) (cons stx parent)) (define (ps-add-unbox parent) diff --git a/racket/collects/syntax/parse/private/runtime-report.rkt b/racket/collects/syntax/parse/private/runtime-report.rkt index 536dc86e4e..18ef870ec0 100644 --- a/racket/collects/syntax/parse/private/runtime-report.rkt +++ b/racket/collects/syntax/parse/private/runtime-report.rkt @@ -93,6 +93,8 @@ Progress ordering Lexicographic generalization of partial order on frames CAR < CDR < POST, stx incomparable except to self + (post g i1) < (post g i2) if i1 < i2 + (post g1 i1) incomp (post g2 i2) when g1 != g2 Progress equality ----------------- @@ -148,7 +150,7 @@ ie (ps->stx+index ps1) = (ps->stx+index ps2). [(exact-positive-integer? (car ips)) (set! rCDR (cons a+ips rCDR)) (set! leastCDR (if leastCDR (min leastCDR (car ips)) (car ips)))] - [(eq? (car ips) 'post) + [(post? (car ips)) (set! rPOST (cons a+ips rPOST))] [(syntax? (car ips)) (set! rSTX (cons a+ips rSTX))] @@ -159,7 +161,7 @@ ie (ps->stx+index ps1) = (ps->stx+index ps2). ;; maximal/pf : (listof (cons A IPS))^4 & nat/#f -> (listof (listof A)) (define (maximal/pf rNULL rCAR rCDR rPOST leastCDR) (cond [(pair? rPOST) - (maximal/progress (rmap pop-item-ips rPOST))] + (maximal/post rPOST)] [(pair? rCDR) (maximal/progress (rmap (lambda (a+ips) (pop-item-ips-ncdrs a+ips leastCDR)) rCDR))] [(pair? rCAR) @@ -169,6 +171,32 @@ ie (ps->stx+index ps1) = (ps->stx+index ps2). [else null])) +;; maximal/post : (NEListof (cons A IPS)) -> (NEListof (NEListof A)) +;; PRE: Each IPS starts with a post frame. +(define (maximal/post items) + ;; groups : (Listof (Listof (cons A IPS))) + (define groups (group-by (lambda (a+ips) (post-group (car (cdr a+ips)))) items)) + (define groups* (map post-group-max-items groups)) + (append* + (for/list ([group (in-list groups*)]) + (maximal/progress (map pop-item-ips group))))) + +;; post-group-max-items : (NEListof (cons A IPS)) -> (Listof (cons A IPS)) +;; PRE: Each IPS starts with a post frame; all items have same post-group. +;; Keep only items with max post-index. +(define (post-group-max-items items) + (let loop ([items items] [best-items null] [best-index -inf.0]) + (cond [(null? items) (reverse best-items)] + [else + (define item0 (car items)) + (define index0 (post-index (car (cdr item0)))) + (cond [(> index0 best-index) + (loop (cdr items) (list item0) index0)] + [(= index0 best-index) + (loop (cdr items) (cons item0 best-items) best-index)] + [else + (loop (cdr items) best-items best-index)])]))) + ;; maximal/stx : (listof (cons A IPS)) -> (listof (listof A)) (define (maximal/stx rSTX) (let ([stxs null] @@ -215,7 +243,7 @@ ie (ps->stx+index ps1) = (ps->stx+index ps2). [(cons (? exact-positive-integer? n) parent) (for/fold ([stx (interp parent)]) ([i (in-range n)]) (stx-cdr stx))] - [(cons 'post parent) + [(cons (? post?) parent) (interp parent)])) (let ([ps (ps-truncate-opaque ps)]) (match ps @@ -225,7 +253,7 @@ ie (ps->stx+index ps1) = (ps->stx+index ps2). (cons (interp ps) 0)] [(cons (? exact-positive-integer? n) parent) (cons (interp parent) n)] - [(cons 'post parent) + [(cons (? post?) parent) (ps->stx+index parent)]))) (define (rmap f xs) @@ -738,7 +766,4 @@ This suggests the following new algorithm based on (s): (for/list ([pf (in-list (reverse ps))]) (match pf [(? syntax? stx) 'stx] - ['car 'car] - ['post 'post] - [(? exact-positive-integer? n) n] - ['opaque 'opaque]))) + [_ pf])))