syntax/parse: reduce allocation when parsing cannot fail

When parsing cannot fail, avoid allocating expectstacks and
failures (thanks samth for the idea). Allocation still happens
for progress and failuresets (conses of #t, now), though.

Compile with `PLTSTDERR="debug@syntax-parse"` to log cannot-fail
syntax-parse expressions and syntax class definitions.
This commit is contained in:
Ryan Culpepper 2016-12-01 21:26:26 -05:00
parent e676ba74a5
commit bcc8535b78
4 changed files with 56 additions and 31 deletions

View File

@ -97,7 +97,7 @@
(if (predicate x) (if (predicate x)
(success fh0) (success fh0)
(let ([es (es-add-thing pr 'description #t rl es)]) (let ([es (es-add-thing pr 'description #t rl es)])
(fh0 (failure pr es)))))))])) (fh0 (failure* pr es)))))))]))
(define-syntax (parser/rhs stx) (define-syntax (parser/rhs stx)
(syntax-case stx () (syntax-case stx ()
@ -108,23 +108,27 @@
(parameterize ((current-syntax-context #'ctx)) (parameterize ((current-syntax-context #'ctx))
(parse-rhs #'rhss (syntax->datum #'relsattrs) (syntax-e #'splicing?) (parse-rhs #'rhss (syntax->datum #'relsattrs) (syntax-e #'splicing?)
#:context #'ctx))) #:context #'ctx)))
(rhs->parser #'name #'formals #'relsattrs the-rhs (syntax-e #'splicing?))))])) (rhs->parser #'name #'formals #'relsattrs the-rhs (syntax-e #'splicing?) #'ctx)))]))
(begin-for-syntax (begin-for-syntax
(define (rhs->parser name formals relsattrs the-rhs splicing?) (define (rhs->parser name formals relsattrs the-rhs splicing? [ctx #f])
(define-values (transparent? description variants defs commit? delimit-cut?) (define-values (transparent? description variants defs commit? delimit-cut?)
(match the-rhs (match the-rhs
[(rhs _ transparent? description variants defs commit? delimit-cut?) [(rhs _ transparent? description variants defs commit? delimit-cut?)
(values transparent? description variants defs commit? delimit-cut?)])) (values transparent? description variants defs commit? delimit-cut?)]))
(define vdefss (map variant-definitions variants)) (define vdefss (map variant-definitions variants))
(define formals* (rewrite-formals formals #'x #'rl)) (define formals* (rewrite-formals formals #'x #'rl))
(define patterns (map variant-pattern variants))
(define no-fail?
(and (not splicing?) ;; FIXME: commit? needed?
(patterns-cannot-fail? patterns)))
(when no-fail? (log-syntax-parse-debug "(stxclass) cannot fail: ~e" ctx))
(define body (define body
(cond [(null? variants) (cond [(null? patterns)
#'(fail (failure pr es))] #'(fail (failure* pr es))]
[splicing? [splicing?
(with-syntax ([(alternative ...) (with-syntax ([(alternative ...)
(for/list ([variant (in-list variants)]) (for/list ([pattern (in-list patterns)])
(define pattern (variant-pattern variant))
(with-syntax ([pattern pattern] (with-syntax ([pattern pattern]
[relsattrs relsattrs] [relsattrs relsattrs]
[iattrs (pattern-attrs pattern)] [iattrs (pattern-attrs pattern)]
@ -140,8 +144,7 @@
[else [else
(with-syntax ([matrix (with-syntax ([matrix
(optimize-matrix (optimize-matrix
(for/list ([variant (in-list variants)]) (for/list ([pattern (in-list patterns)])
(define pattern (variant-pattern variant))
(with-syntax ([iattrs (pattern-attrs pattern)] (with-syntax ([iattrs (pattern-attrs pattern)]
[relsattrs relsattrs] [relsattrs relsattrs]
[commit? commit?]) [commit? commit?])
@ -165,7 +168,8 @@
(syntax-parameterize ((this-context-syntax (syntax-parameterize ((this-context-syntax
(syntax-rules () (syntax-rules ()
[(tbs) (ps-context-syntax pr)]))) [(tbs) (ps-context-syntax pr)])))
(let ([es (es-add-thing pr description 'transparent? rl es)] (let ([es (es-add-thing pr description 'transparent? rl
#,(if no-fail? #'#f #'es))]
[pr (if 'transparent? pr (ps-add-opaque pr))]) [pr (if 'transparent? pr (ps-add-opaque pr))])
(with ([fail-handler fh0] (with ([fail-handler fh0]
[cut-prompt cp0]) [cut-prompt cp0])
@ -274,6 +278,7 @@ Some optimizations:
- commit protocol for stxclasses (but not ~commit, no point) - commit protocol for stxclasses (but not ~commit, no point)
- avoid continue-vs-end choice point in (EH ... . ()) by eager pair check - avoid continue-vs-end choice point in (EH ... . ()) by eager pair check
- integrable stxclasses, specialize ellipses of integrable stxclasses - integrable stxclasses, specialize ellipses of integrable stxclasses
- pattern lists that cannot fail set es=#f to disable ExpectStack allocation
|# |#
;; ---- ;; ----
@ -425,10 +430,12 @@ Conventions:
(define-values (patterns body-exprs defs2s) (define-values (patterns body-exprs defs2s)
(for/lists (patterns body-exprs defs2s) ([clause (in-list (stx->list clauses-stx))]) (for/lists (patterns body-exprs defs2s) ([clause (in-list (stx->list clauses-stx))])
(for-clause clause))) (for-clause clause)))
(define no-fail? (patterns-cannot-fail? patterns))
(when no-fail? (log-syntax-parse-debug "cannot fail: ~e" #'ctx))
(with-syntax ([(def ...) (apply append (get-txlifts-as-definitions) defs defs2s)]) (with-syntax ([(def ...) (apply append (get-txlifts-as-definitions) defs defs2s)])
#`(let* ([ctx0 (normalize-context '#,who #,context x)] #`(let* ([ctx0 (normalize-context '#,who #,context x)]
[pr (ps-empty x (cadr ctx0))] [pr (ps-empty x (cadr ctx0))]
[es #f] [es #,(if no-fail? #'#f #'#t)]
[cx x] [cx x]
[fh0 (syntax-patterns-fail ctx0)]) [fh0 (syntax-patterns-fail ctx0)])
def ... def ...
@ -450,7 +457,7 @@ Conventions:
#`(try alternative ...)) #`(try alternative ...))
|#] |#]
[else [else
#`(fail (failure pr es))]))))))))])) #`(fail (failure* pr es))]))))))))]))
;; ---- ;; ----
@ -488,7 +495,7 @@ Conventions:
[tpr (ps-add-cdr pr)]) [tpr (ps-add-cdr pr)])
(parse:matrix ((hx hcx hpr es) (tx tcx tpr es) . ins) inner)) (parse:matrix ((hx hcx hpr es) (tx tcx tpr es) . ins) inner))
(let ([es* (if (null? datum) (es-add-proper-pair (first-desc:matrix inner) es) es)]) (let ([es* (if (null? datum) (es-add-proper-pair (first-desc:matrix inner) es) es)])
(fail (failure pr es*)))))] (fail (failure* pr es*)))))]
[(parse:pk (in1 . ins) #s(pk/and inner)) [(parse:pk (in1 . ins) #s(pk/and inner))
#'(parse:matrix (in1 in1 . ins) inner)])) #'(parse:matrix (in1 in1 . ins) inner)]))
@ -569,12 +576,12 @@ Conventions:
#`(let ([d unwrap-x]) #`(let ([d unwrap-x])
(if (equal? d (quote datum)) (if (equal? d (quote datum))
k k
(fail (failure pr (es-add-atom 'datum es))))))] (fail (failure* pr (es-add-atom 'datum es))))))]
[#s(pat:literal literal input-phase lit-phase) [#s(pat:literal literal input-phase lit-phase)
#`(if (and (identifier? x) #`(if (and (identifier? x)
(free-identifier=? x (quote-syntax literal) input-phase lit-phase)) (free-identifier=? x (quote-syntax literal) input-phase lit-phase))
k k
(fail (failure pr (es-add-literal (quote-syntax literal) es))))] (fail (failure* pr (es-add-literal (quote-syntax literal) es))))]
[#s(pat:action action subpattern) [#s(pat:action action subpattern)
#'(parse:A x cx action pr es (parse:S x cx subpattern pr es k))] #'(parse:A x cx action pr es (parse:S x cx subpattern pr es k))]
[#s(pat:head head tail) [#s(pat:head head tail)
@ -607,7 +614,7 @@ Conventions:
(with ([fail-handler fail-to-succeed] (with ([fail-handler fail-to-succeed]
[cut-prompt fail-to-succeed]) ;; to be safe [cut-prompt fail-to-succeed]) ;; to be safe
(parse:S x cx subpattern pr es (parse:S x cx subpattern pr es
(fh0 (failure pr0 es0)))))] (fh0 (failure* pr0 es0)))))]
[#s(pat:pair head tail) [#s(pat:pair head tail)
#`(let ([datum (if (syntax? x) (syntax-e x) x)] #`(let ([datum (if (syntax? x) (syntax-e x) x)]
[cx (if (syntax? x) x cx)]) ;; FIXME: shadowing cx?! [cx (if (syntax? x) x cx)]) ;; FIXME: shadowing cx?!
@ -620,7 +627,7 @@ Conventions:
(parse:S hx hcx head hpr es (parse:S hx hcx head hpr es
(parse:S tx cx tail tpr es k))) (parse:S tx cx tail tpr es k)))
(let ([es* (if (null? datum) (es-add-proper-pair (first-desc:S head) es) es)]) (let ([es* (if (null? datum) (es-add-proper-pair (first-desc:S head) es) es)])
(fail (failure pr es*)))))] (fail (failure* pr es*)))))]
[#s(pat:vector subpattern) [#s(pat:vector subpattern)
#`(let ([datum (if (syntax? x) (syntax-e x) x)]) #`(let ([datum (if (syntax? x) (syntax-e x) x)])
(if (vector? datum) (if (vector? datum)
@ -628,7 +635,7 @@ Conventions:
[vcx (if (syntax? x) x cx)] ;; FIXME: (vector? datum) => (syntax? x) ??? [vcx (if (syntax? x) x cx)] ;; FIXME: (vector? datum) => (syntax? x) ???
[pr* (ps-add-unvector pr)]) [pr* (ps-add-unvector pr)])
(parse:S datum vcx subpattern pr* es k)) (parse:S datum vcx subpattern pr* es k))
(fail (failure pr es))))] (fail (failure* pr es))))]
[#s(pat:box subpattern) [#s(pat:box subpattern)
#`(let ([datum (if (syntax? x) (syntax-e x) x)]) #`(let ([datum (if (syntax? x) (syntax-e x) x)])
(if (box? datum) (if (box? datum)
@ -636,7 +643,7 @@ Conventions:
[bcx (if (syntax? x) x cx)] ;; FIXME: (box? datum) => (syntax? x) ??? [bcx (if (syntax? x) x cx)] ;; FIXME: (box? datum) => (syntax? x) ???
[pr* (ps-add-unbox pr)]) [pr* (ps-add-unbox pr)])
(parse:S datum bcx subpattern pr* es k)) (parse:S datum bcx subpattern pr* es k))
(fail (failure pr es))))] (fail (failure* pr es))))]
[#s(pat:pstruct key subpattern) [#s(pat:pstruct key subpattern)
#`(let ([datum (if (syntax? x) (syntax-e x) x)]) #`(let ([datum (if (syntax? x) (syntax-e x) x)])
(if (let ([xkey (prefab-struct-key datum)]) (if (let ([xkey (prefab-struct-key datum)])
@ -645,7 +652,7 @@ Conventions:
[scx (if (syntax? x) x cx)] ;; FIXME: (struct? datum) => (syntax? x) ??? [scx (if (syntax? x) x cx)] ;; FIXME: (struct? datum) => (syntax? x) ???
[pr* (ps-add-unpstruct pr)]) [pr* (ps-add-unpstruct pr)])
(parse:S datum scx subpattern pr* es k)) (parse:S datum scx subpattern pr* es k))
(fail (failure pr es))))] (fail (failure* pr es))))]
[#s(pat:describe pattern description transparent? role) [#s(pat:describe pattern description transparent? role)
#`(let ([es* (es-add-thing pr description transparent? role es)] #`(let ([es* (es-add-thing pr description transparent? role es)]
[pr* (if 'transparent? pr (ps-add-opaque pr))]) [pr* (if 'transparent? pr (ps-add-opaque pr))])
@ -677,7 +684,7 @@ Conventions:
(if (predicate x*) (if (predicate x*)
(let-attributes (name-attr ...) k) (let-attributes (name-attr ...) k)
(let ([es* (es-add-thing pr 'description #t role es)]) (let ([es* (es-add-thing pr 'description #t role es)])
(fail (failure pr es*))))))])])) (fail (failure* pr es*))))))])]))
;; (first-desc:S S-pattern) : expr[FirstDesc] ;; (first-desc:S S-pattern) : expr[FirstDesc]
(define-syntax (first-desc:S stx) (define-syntax (first-desc:S stx)
@ -758,7 +765,7 @@ Conventions:
(if c (if c
(let ([pr* (if (syntax? c) (ps-add-stx pr c) pr)] (let ([pr* (if (syntax? c) (ps-add-stx pr c) pr)]
[es* (es-add-message message es)]) [es* (es-add-message message es)])
(fail (failure pr* es*))) (fail (failure* pr* es*)))
k))] k))]
[#s(action:parse pattern expr) [#s(action:parse pattern expr)
#`(let* ([y (datum->syntax/with-clause (wrap-user-code expr))] #`(let* ([y (datum->syntax/with-clause (wrap-user-code expr))]
@ -917,7 +924,7 @@ Conventions:
(with ([fail-handler fail-to-succeed] (with ([fail-handler fail-to-succeed]
[cut-prompt fail-to-succeed]) ;; to be safe [cut-prompt fail-to-succeed]) ;; to be safe
(parse:H x cx rest-x rest-cx rest-pr subpattern pr es (parse:H x cx rest-x rest-cx rest-pr subpattern pr es
(fh0 (failure pr0 es0)))))] (fh0 (failure* pr0 es0)))))]
[_ [_
#'(parse:S x cx #'(parse:S x cx
;; FIXME: consider proper-list-pattern? (yes is consistent with ~seq) ;; FIXME: consider proper-list-pattern? (yes is consistent with ~seq)
@ -995,7 +1002,7 @@ Conventions:
...) ...)
(cond [(< rel-rep (rep:min-number rel-repc)) (cond [(< rel-rep (rep:min-number rel-repc))
(let ([es (expectation-of-reps/too-few es rel-rep rel-repc rel-head)]) (let ([es (expectation-of-reps/too-few es rel-rep rel-repc rel-head)])
(fail (failure loop-pr es)))] (fail (failure* loop-pr es)))]
... ...
[else [else
(let-attributes ([a (rep:finalize a attr-repc alt-id)] ...) (let-attributes ([a (rep:finalize a attr-repc alt-id)] ...)
@ -1010,7 +1017,7 @@ Conventions:
[(topc #t x cx pr es pair-alt null-alt) [(topc #t x cx pr es pair-alt null-alt)
(cond [(stx-pair? x) pair-alt] (cond [(stx-pair? x) pair-alt]
[(stx-null? x) null-alt] [(stx-null? x) null-alt]
[else (fail (failure pr es))])] [else (fail (failure* pr es))])]
[(topc _ x cx pr es alt1 alt2) [(topc _ x cx pr es alt1 alt2)
(try alt1 alt2)])) (try alt1 alt2)]))
@ -1041,7 +1048,7 @@ Conventions:
(if (< rep (rep:max-number repc)) (if (< rep (rep:max-number repc))
(let ([rep (add1 rep)]) k*) (let ([rep (add1 rep)]) k*)
(let ([es* (expectation-of-reps/too-many es rep repc)]) (let ([es* (expectation-of-reps/too-many es rep repc)])
(fail (failure pr* es*)))))]))])) (fail (failure* pr* es*)))))]))]))
;; (rep:initial-value RepConstraint) : expr ;; (rep:initial-value RepConstraint) : expr
(define-syntax (rep:initial-value stx) (define-syntax (rep:initial-value stx)

View File

@ -511,6 +511,15 @@ A RepConstraint is one of
;; ---- ;; ----
;; patterns-cannot-fail? : (Listof SinglePattern) -> Boolean
;; Returns true if the disjunction of the patterns always succeeds---and thus no
;; failure-tracking needed. Note: beware cut!
(define (patterns-cannot-fail? patterns)
(and (not (ormap pattern-has-cut? patterns))
(ormap pattern-cannot-fail? patterns)))
;; ----
;; An AbsNullable is 'yes | 'no | 'unknown (3-valued logic) ;; An AbsNullable is 'yes | 'no | 'unknown (3-valued logic)
(define (3and a b) (define (3and a b)

View File

@ -20,6 +20,8 @@
ps-difference ps-difference
(struct-out failure) (struct-out failure)
failure*
expect? expect?
(struct-out expect:thing) (struct-out expect:thing)
(struct-out expect:atom) (struct-out expect:atom)
@ -49,6 +51,8 @@ A FailFunction = (FailureSet -> Answer)
|# |#
(define-struct failure (progress expectstack) #:prefab) (define-struct failure (progress expectstack) #:prefab)
;; failure* : PS ExpectStack/#f -> Failure/#t
(define (failure* ps es) (if es (failure ps es) #t))
;; == Progress == ;; == Progress ==
@ -177,11 +181,15 @@ An ExpectStack (during parsing) is one of
* (expect:atom Datum ExpectStack) * (expect:atom Datum ExpectStack)
* (expect:literal Identifier ExpectStack) * (expect:literal Identifier ExpectStack)
* (expect:proper-pair FirstDesc ExpectStack) * (expect:proper-pair FirstDesc ExpectStack)
* #t
The *-marked variants can only occur at the top of the stack (ie, not The *-marked variants can only occur at the top of the stack (ie, not
in the next field of another Expect). The top of the stack contains in the next field of another Expect). The top of the stack contains
the most specific information. the most specific information.
An ExpectStack can also be #f, which means no failure tracking is
requested (and thus no more ExpectStacks should be allocated).
-- During reporting, the goal is ease of manipulation. -- During reporting, the goal is ease of manipulation.
An ExpectList (during reporting) is (listof Expect). An ExpectList (during reporting) is (listof Expect).
@ -221,23 +229,23 @@ RExpectList when the most specific information comes last.
(expect:proper-pair? x))) (expect:proper-pair? x)))
(define (es-add-thing ps description transparent? role next) (define (es-add-thing ps description transparent? role next)
(if description (if (and next description)
(expect:thing ps description transparent? role next) (expect:thing ps description transparent? role next)
next)) next))
(define (es-add-message message next) (define (es-add-message message next)
(if message (if (and next message)
(expect:message message next) (expect:message message next)
next)) next))
(define (es-add-atom atom next) (define (es-add-atom atom next)
(expect:atom atom next)) (and next (expect:atom atom next)))
(define (es-add-literal literal next) (define (es-add-literal literal next)
(expect:literal literal next)) (and next (expect:literal literal next)))
(define (es-add-proper-pair first-desc next) (define (es-add-proper-pair first-desc next)
(expect:proper-pair first-desc next)) (and next (expect:proper-pair first-desc next)))
#| #|
A FirstDesc is one of A FirstDesc is one of

View File

@ -292,6 +292,7 @@ ie (ps->stx+index ps1) = (ps->stx+index ps2).
(let loop ([es es] [acc null]) (let loop ([es es] [acc null])
(match es (match es
['#f acc] ['#f acc]
['#t acc]
[(expect:thing ps desc tr? role rest-es) [(expect:thing ps desc tr? role rest-es)
(cond [(and truncate-opaque? (not tr?)) (cond [(and truncate-opaque? (not tr?))
(loop rest-es (cons (expect:thing #f desc #t role (ps->stx+index ps)) null))] (loop rest-es (cons (expect:thing #f desc #t role (ps->stx+index ps)) null))]