Metafunctions now typeset their contracts as the first line

This is a backwards incompatible change, but there is a keyword
argument to render-metafunction and render-metafunctions that
goes back to the old behavior
This commit is contained in:
Robby Findler 2014-06-12 02:14:06 -05:00
parent 445a5dca15
commit d067311cf7
6 changed files with 242 additions and 167 deletions

View File

@ -2952,18 +2952,22 @@ other tools that combine @racketmodname[pict]s together.
}
@deftogether[[
@defform[(render-metafunction metafunction-name)]{}
@defform[(render-metafunction metafunction-name maybe-contract)]{}
@defform/none[#:literals (render-metafunction)
(render-metafunction metafunction-name filename)]{}
@defform[(render-metafunctions metafunction-name ...)]{}
@defform/none[#:literals (render-metafunctions)
(render-metafunctions metafunction-name ... #:file filename)]{}]]{
(render-metafunction metafunction-name filename maybe-contract)]{}
@defform[(render-metafunctions metafunction-name ... maybe-filename maybe-contract)
#:grammar ([maybe-filename (code:line) (code:line #:file filename)]
[maybe-contract? (code:line) (code:line #:contract? bool-expr)])]{}]]{
Like @racket[render-reduction-relation] but for metafunctions.
Similarly, @racket[render-metafunctions] accepts multiple
metafunctions and renders them together, lining up all of the
clauses together.
If the metafunctions have contracts, they are typeset as the first
lines of the output, unless the expression following @racket[#:contract?]
evaluates to @racket[#f].
This function sets @racket[dc-for-text-size]. See also
@racket[metafunction->pict] and
@racket[metafunctions->pict].
@ -3146,10 +3150,10 @@ precede ellipses that represent argument sequences; when it is
are rendered on two lines and which are rendered on one.
If its value is a list, the length of the list must match
the number of cases and each boolean indicates if that
case has a linebreak or not.
the number of cases plus one if there is a contract.
Each boolean indicates if that case has a linebreak or not.
This influences the @racket['left/right] styles only.
This parameter's value influences the @racket['left/right] styles only.
}
@defparam[metafunction-cases

View File

@ -407,7 +407,7 @@
(begin
(unless (identifier? #'lang)
(raise-syntax-error #f "expected an identifier in the language position" stx #'lang))
(define-values (contract-name dom-ctcs codom-contracts pats)
(define-values (contract-name dom-ctcs pre-condition codom-contracts pats)
(split-out-contract stx (syntax-e #'def-form-id) #'body #t))
(with-syntax* ([((name trms ...) rest ...) (car pats)]
[(mode-stx ...) #`(#:mode (name I))]
@ -416,7 +416,7 @@
#'())]
[(clauses ...) pats]
[new-body #`(mode-stx ... ctc-stx ... clauses ...)])
(do-extended-judgment-form #'lang (syntax-e #'def-form-id) #'new-body #f stx #t)))]))
(do-extended-judgment-form #'lang (syntax-e #'def-form-id) #'new-body #f stx #t)))]))
;; if relation? is true, then the contract is a list of redex patterns
;; if relation? is false, then the contract is a single redex pattern
@ -425,7 +425,7 @@
;; initial test determines if a contract is specified or not
(cond
[(pair? (syntax-e (car (syntax->list rest))))
(values #f #f (list #'any) (check-clauses stx syn-error-name (syntax->list rest) relation?))]
(values #f #f #f (list #'any) (check-clauses stx syn-error-name (syntax->list rest) relation?))]
[else
(syntax-case rest ()
[(id separator more ...)
@ -438,7 +438,7 @@
(raise-syntax-error syn-error-name
"expected clause definitions to follow domain contract"
stx))
(values #'id contract (list #'any) (check-clauses stx syn-error-name clauses #t)))]
(values #'id contract #f (list #'any) (check-clauses stx syn-error-name clauses #t)))]
[else
(unless (eq? ': (syntax-e #'separator))
(raise-syntax-error syn-error-name "expected a colon to follow the meta-function's name" stx #'separator))
@ -481,10 +481,9 @@
#t)])])])))
(let ([doms (reverse dom-pats)]
[clauses (check-clauses stx syn-error-name raw-clauses relation?)])
(values #'id
(if relation?
doms
#`(side-condition #,doms (term #,pre-condition)))
(values #'id
doms
(if relation? #f pre-condition)
(reverse rev-codomains)
clauses))]
[else

View File

@ -1187,7 +1187,7 @@
prev-metafunction
(λ ()
(raise-syntax-error syn-error-name "expected a previously defined metafunction" orig-stx prev-metafunction))))
(let*-values ([(contract-name dom-ctcs codom-contracts pats)
(let*-values ([(contract-name dom-ctcs pre-condition codom-contracts pats)
(split-out-contract orig-stx syn-error-name #'rest #f)]
[(name _) (defined-name (list contract-name) pats orig-stx)])
(when (and prev-metafunction (eq? (syntax-e #'name) (syntax-e prev-metafunction)))
@ -1208,6 +1208,7 @@
name
name-predicate
#,dom-ctcs
#,pre-condition
#,codom-contracts
#,pats
#,syn-error-name))
@ -1251,9 +1252,13 @@
(define-syntax (generate-metafunction stx)
(syntax-case stx ()
[(_ orig-stx lang prev-metafunction name name-predicate dom-ctcs codom-contracts pats syn-error-name)
[(_ orig-stx lang prev-metafunction
name name-predicate
dom-ctcs pre-condition
codom-contracts pats syn-error-name)
(let ([prev-metafunction (and (syntax-e #'prev-metafunction) #'prev-metafunction)]
[dom-ctcs (syntax-e #'dom-ctcs)]
[pre-condition (syntax-e #'pre-condition)]
[codom-contracts (syntax-e #'codom-contracts)]
[pats (syntax-e #'pats)]
[syn-error-name (syntax-e #'syn-error-name)])
@ -1323,7 +1328,7 @@
#'lang
syn-error-name
#f
dom-ctcs)
#`(side-condition #,dom-ctcs (term #,pre-condition)))
#'((void) any () ()))]
[((codom-syncheck-expr codom-side-conditions-rewritten codom-names codom-names/ellipses) ...)
(map (λ (codom-contract)
@ -1369,7 +1374,6 @@
#,(if prev-metafunction
#`(metafunc-proc-cases #,(term-fn-get-id (syntax-local-value prev-metafunction)))
#'null)])
(build-metafunction
lang
cases
@ -1378,12 +1382,23 @@
(make-metafunc-proc
(let ([name (lambda (x) (f/dom x))]) name)
'(clause-name ...)
(generate-lws #f
(lhs ...)
(lhs-for-lw ...)
((stuff ...) ...)
(rhs ...)
#t)
(list
;; mf contract
#,(if (and dom-ctcs codom-contracts)
#`(list
#,(with-syntax ([(dom-ctc ...) dom-ctcs])
#`(list (to-lw dom-ctc) ...))
#,(with-syntax ([(codom-ctc ...) codom-contracts])
#`(list (to-lw codom-ctc) ...)))
#'#f)
;; body of mf
(generate-lws #f
(lhs ...)
(lhs-for-lw ...)
((stuff ...) ...)
(rhs ...)
#t))
lang
#t ;; multi-args?
'name

View File

@ -5,7 +5,7 @@
racket/match
racket/pretty
racket/set
(only-in racket/list drop-right last partition)
(only-in racket/list drop-right last partition add-between)
texpict/mrpict
texpict/utils
@ -21,7 +21,8 @@
(prefix-in lw/rt: redex/private/loc-wrapper-rt))
(require (for-syntax racket/base
redex/private/term-fn))
redex/private/term-fn
syntax/parse))
(provide render-term
term->pict
@ -261,7 +262,6 @@
(apply
vl-append
(add-between
(blank 0 (reduction-relation-rule-separation))
(map (λ (rp)
(side-condition-combiner
(vl-append
@ -274,7 +274,8 @@
(rp->pict-label rp)))
(rule-pict-rhs rp))
(rp->side-condition-pict rp +inf.0)))
rps))))))
rps)
(blank 0 (reduction-relation-rule-separation)))))))
(define compact-vertical-min-width (make-parameter 0))
@ -352,14 +353,14 @@
(apply
hbl-append
(add-between
(basic-text ", " (default-style))
fresh-vars))
fresh-vars
(basic-text ", " (default-style))))
(basic-text " fresh" (default-style)))))]
[lst (add-between
'comma
(append
pattern-binds/sc
frsh))])
frsh)
'comma)])
(if (null? lst)
(blank)
(let ([where ((where-make-prefix-pict))])
@ -414,14 +415,6 @@
label
((current-text) "]" (label-style) (label-font-size))))
(define (add-between i l)
(cond
[(null? l) l]
[else
(cons (car l)
(apply append
(map (λ (x) (list i x)) (cdr l))))]))
(define (make-horiz-space picts) (blank (pict-width (apply cc-superimpose picts)) 0))
(define rule-pict-style (make-parameter 'vertical))
@ -773,11 +766,11 @@
#'(metafunctions->pict name)]))
(define-syntax (metafunctions->pict stx)
(syntax-case stx ()
[(_ name1 name2 ...)
(and (identifier? #'name1)
(andmap identifier? (syntax->list #'(name2 ...))))
#'(metafunctions->pict/proc (list (metafunction name1) (metafunction name2) ...) 'metafunctions->pict)]))
(syntax-parse stx
[(_ name1:id name2:id ... (~optional (~seq #:contract? contract-e:expr) #:defaults ([contract-e #'#t])))
#'(metafunctions->pict/proc (list (metafunction name1) (metafunction name2) ...)
contract-e
'metafunctions->pict)]))
(define-syntax (relation->pict stx)
(syntax-case stx ()
@ -786,24 +779,39 @@
#'(inference-rules-pict/relation 'form (metafunction name1))]))
(define-syntax (render-metafunctions stx)
(syntax-case stx ()
[(_ name1 name2 ...)
(and (identifier? #'name)
(andmap identifier? (syntax->list #'(name2 ...))))
#'(render-metafunction/proc (list (metafunction name1) (metafunction name2) ...) #f 'render-metafunctions)]
[(_ name1 name2 ... #:file filename)
(and (identifier? #'name1)
(andmap identifier? (syntax->list #'(name2 ...))))
#'(render-metafunction/proc (list (metafunction name1) (metafunction name2) ...) filename 'render-metafunctions)]))
(syntax-parse stx
[(_ name1:id name2:id ... (~seq k:keyword e:expr) ...)
(define filename #'#f)
(define contract? #'#t)
(for ([kwd (in-list (syntax->list #'(k ...)))]
[e (in-list (syntax->list #'(e ...)))])
(cond
[(equal? '#:filename (syntax-e kwd))
(set! filename e)]
[(equal? '#:contract? (syntax-e kwd))
(set! contract? e)]
[else
(raise-syntax-error #f "unexpected keyword" stx kwd)]))
#`(render-metafunction/proc
(list (metafunction name1) (metafunction name2) ...)
#,contract?
#,filename
'render-metafunctions)]))
(define-syntax (render-metafunction stx)
(syntax-case stx ()
[(_ name)
(identifier? #'name)
#'(render-metafunction/proc (list (metafunction name)) #f 'render-metafunction)]
[(_ name file)
(identifier? #'name)
#'(render-metafunction/proc (list (metafunction name)) file 'render-metafunction)]))
(syntax-parse stx
[(_ name:id
(~optional file:expr #:defaults ([file #'#f]))
(~optional (~seq k:keyword e:expr)))
#`(render-metafunction/proc (list (metafunction name))
#,(cond
[(not (attribute k)) #'#f]
[(and (equal? (syntax-e (attribute k)) '#:contract?))
#'e]
[else
(raise-syntax-error #f "unknown keyword" stx #'k)])
file
'render-metafunction)]))
(define-syntax (render-relation stx)
(syntax-case stx ()
@ -814,7 +822,7 @@
(define metafunction-pict-style (make-parameter 'left-right))
(define metafunction-cases (make-parameter #f))
(define (select-mf-cases eqns case-labelss)
(define (select-mf-cases contracts eqns case-labelss)
(define mf-cases (metafunction-cases))
(cond
[mf-cases
@ -824,21 +832,29 @@
(apply
append
(for/list ([eqns (in-list eqns)]
[contract (in-list contracts)]
[case-labels (in-list case-labelss)])
(filter
values
(for/list ([eqn (in-list eqns)]
[case-label (in-list case-labels)])
(begin0
(cond
[(and (pair? sorted-cases) (= i (car sorted-cases)))
(set! sorted-cases (cdr sorted-cases))
eqn]
[(set-member? named-cases case-label)
eqn]
[else #f])
(set! i (+ i 1)))))))]
[else (apply append eqns)]))
(cons
contract
(for/list ([eqn (in-list eqns)]
[case-label (in-list case-labels)])
(begin0
(cond
[(and (pair? sorted-cases) (= i (car sorted-cases)))
(set! sorted-cases (cdr sorted-cases))
eqn]
[(set-member? named-cases case-label)
eqn]
[else #f])
(set! i (+ i 1))))))))]
[else (apply append
(for/list ([eqns (in-list eqns)]
[contract (in-list contracts)])
(if contract
(cons contract eqns)
eqns)))]))
(define judgment-form-cases (make-parameter #f))
(define (select-jf-cases eqns conclusions eqn-names)
@ -877,9 +893,9 @@
[else
(cons (car l) (loop (cdr l)))])))
(define (metafunctions->pict/proc mfs name)
(unless (andmap (λ (mf) (eq? (metafunc-proc-lang (metafunction-proc (car mfs)))
(metafunc-proc-lang (metafunction-proc mf))))
(define (metafunctions->pict/proc mfs contract? name)
(unless (andmap (λ (mf) (equal? (metafunc-proc-lang (metafunction-proc (car mfs)))
(metafunc-proc-lang (metafunction-proc mf))))
mfs)
(error name "expected metafunctions that are all drawn from the same language"))
(define current-linebreaks (linebreaks))
@ -887,22 +903,39 @@
(define sep 2)
(define style (metafunction-pict-style))
(define (wrapper->pict lw) (lw->pict all-nts lw))
(define all-eqns (map (λ (mf) (metafunc-proc-pict-info (metafunction-proc mf))) mfs))
(define contracts (for/list ([mf (in-list mfs)])
(define lws (list-ref (metafunc-proc-pict-info (metafunction-proc mf)) 0))
(cond
[(and contract? lws)
(define doms (list-ref lws 0))
(define rngs (list-ref lws 1))
(render-metafunction-contract
(metafunc-proc-lang (metafunction-proc mf))
(metafunc-proc-name (metafunction-proc mf))
doms
rngs)]
[else #f])))
(define all-eqns (map (λ (mf) (list-ref (metafunc-proc-pict-info (metafunction-proc mf)) 1)) mfs))
(define all-lhss
(map (λ (mf)
(map (lambda (eqn)
(wrapper->pict
(metafunction-call (metafunc-proc-name (metafunction-proc mf))
(list-ref eqn 0))))
(metafunc-proc-pict-info (metafunction-proc mf))))
mfs))
(for/list ([mf (in-list mfs)])
(for/list ([eqn (in-list (list-ref (metafunc-proc-pict-info (metafunction-proc mf)) 1))])
(wrapper->pict
(metafunction-call (metafunc-proc-name (metafunction-proc mf))
(list-ref eqn 0))))))
(define case-labels (map (λ (mf) (metafunc-proc-clause-names (metafunction-proc mf))) mfs))
(define eqns (select-mf-cases all-eqns case-labels))
(define lhss (select-mf-cases all-lhss case-labels))
(define rhss (map (lambda (eqn) (wrapper->pict (list-ref eqn 2))) eqns))
(define eqns (select-mf-cases contracts all-eqns case-labels))
(define lhs/contracts (select-mf-cases contracts all-lhss case-labels))
(define rhss (for/list ([eqn/contract (in-list eqns)])
(if (pict? eqn/contract)
'contract
(wrapper->pict (list-ref eqn/contract 2)))))
(unless (or (not current-linebreaks)
(= (length current-linebreaks) (length eqns)))
(error 'metafunction->pict "expected the current-linebreaks parameter to be a list whose length matches the number of cases in the metafunction (~a), but got ~s"
(error 'metafunction->pict
(string-append
"expected the current-linebreaks parameter to be a list"
" whose length matches the number of cases in the metafunction"
" plus one if there is a contract (~a), but got ~s")
(length eqns)
current-linebreaks))
(define linebreak-list (or current-linebreaks
@ -923,45 +956,55 @@
left-right*/compact-side-conditions)))
(define max-line-w/pre-sc (and
compact-side-conditions?
(apply
max
(map (lambda (lhs rhs linebreak?)
(cond
[(eq? mode 'vertical)
(max (+ (pict-width lhs) (pict-width =-pict))
(pict-width rhs))]
[linebreak?
(max (pict-width lhs)
(+ (pict-width rhs) sep (pict-width =-pict)))]
[else
(+ (pict-width lhs) (pict-width rhs) (pict-width =-pict)
(* 2 sep))]))
lhss rhss linebreak-list))))
(for/fold ([biggest 0]) ([lhs/contract (in-list lhs/contracts)]
[rhs (in-list rhss)]
[linebreak? (in-list linebreak-list)])
(cond
[(equal? rhs 'contract)
;; this is a contract
(max biggest (pict-width lhs/contract))]
[(eq? mode 'vertical)
(max biggest
(+ (pict-width lhs/contract) (pict-width =-pict))
(pict-width rhs))]
[linebreak?
(max biggest
(pict-width lhs/contract)
(+ (pict-width rhs) sep (pict-width =-pict)))]
[else
(max biggest
(+ (pict-width lhs/contract) (pict-width rhs) (pict-width =-pict)
(* 2 sep)))]))))
(define scs (for/list ([eqn (in-list eqns)])
(let ([scs (reverse (list-ref eqn 1))])
(if (null? scs)
#f
(let-values ([(fresh where/sc) (partition metafunc-extra-fresh? scs)])
(side-condition-pict (foldl (λ (clause picts)
(foldr (λ (l ps) (cons (wrapper->pict l) ps))
picts (metafunc-extra-fresh-vars clause)))
'() fresh)
(map (match-lambda
[(struct metafunc-extra-where (lhs rhs))
(where-pict (wrapper->pict lhs) (wrapper->pict rhs))]
[(struct metafunc-extra-side-cond (expr))
(wrapper->pict expr)])
where/sc)
(cond
[vertical-side-conditions?
;; maximize line breaks:
0]
[compact-side-conditions?
;; maximize line break as needed:
max-line-w/pre-sc]
[else
;; no line breaks:
+inf.0])))))))
(cond
[(pict? eqn) #f]
[else
(define scs (reverse (list-ref eqn 1)))
(cond
[(null? scs) #f]
[else
(define-values (fresh where/sc) (partition metafunc-extra-fresh? scs))
(side-condition-pict
(foldl (λ (clause picts)
(foldr (λ (l ps) (cons (wrapper->pict l) ps))
picts (metafunc-extra-fresh-vars clause)))
'() fresh)
(map (match-lambda
[(struct metafunc-extra-where (lhs rhs))
(where-pict (wrapper->pict lhs) (wrapper->pict rhs))]
[(struct metafunc-extra-side-cond (expr))
(wrapper->pict expr)])
where/sc)
(cond
[vertical-side-conditions?
;; maximize line breaks:
0]
[compact-side-conditions?
;; maximize line break as needed:
max-line-w/pre-sc]
[else
;; no line breaks:
+inf.0]))])])))
(case mode
[(horizontal)
(define (adjust-for-fills rows)
@ -1000,47 +1043,60 @@
(table 3
(adjust-for-fills
(apply append
(map (lambda (lhs sc rhs linebreak?)
(append
(list
(cond
[linebreak?
(list lhs 'fill 'fill)]
[(and sc (eq? style 'left-right/beside-side-conditions))
(list lhs =-pict (htl-append 10 rhs sc))]
[else
(list lhs =-pict rhs)]))
(if linebreak?
(list
(list (htl-append sep =-pict rhs)
'fill
'fill))
null)
(if (or (not sc)
(and (not linebreak?)
(eq? style 'left-right/beside-side-conditions)))
null
(list
(list sc 'fill 'fill)))))
lhss
scs
rhss
linebreak-list)))
(for/list ([lhs/contract (in-list lhs/contracts)]
[sc (in-list scs)]
[rhs (in-list rhss)]
[linebreak? (in-list linebreak-list)])
(append
(list
(cond
[(equal? rhs 'contract) ;; contract
(list lhs/contract 'fill 'fill)]
[linebreak?
(list lhs/contract 'fill 'fill)]
[(and sc (eq? style 'left-right/beside-side-conditions))
(list lhs/contract =-pict (htl-append 10 rhs sc))]
[else
(list lhs/contract =-pict rhs)]))
(if linebreak?
(list
(list (htl-append sep =-pict rhs)
'fill
'fill))
null)
(if (or (not sc)
(and (not linebreak?)
(eq? style 'left-right/beside-side-conditions)))
null
(list
(list sc 'fill 'fill)))))))
ltl-superimpose ltl-superimpose
sep sep)]
[(vertical)
(apply vl-append
sep
(apply append
(map (lambda (lhs sc rhs)
(cons
(vl-append (htl-append lhs =-pict) rhs)
(if (not sc)
null
(list sc))))
lhss
scs
rhss)))]))
(for/list ([lhs/contract (in-list lhs/contracts)]
[sc (in-list scs)]
[rhs (in-list rhss)])
(cond
[(equal? rhs 'contract) ;; contract
(list lhs/contract)]
[else
(cons
(vl-append (htl-append lhs/contract =-pict) rhs)
(if (not sc)
null
(list sc)))]))))]))
(define (render-metafunction-contract lang name doms rngs)
(hbl-append (basic-text (format "~a" name) (metafunction-style))
(basic-text " : " (default-style))
(apply hbl-append (add-between (map (λ (x) (lw->pict lang x)) doms)
(basic-text " " (default-style))))
(basic-text "" (default-style))
(apply hbl-append (add-between (map (λ (x) (lw->pict lang x)) rngs)
(basic-text " " (default-style))))))
(define (metafunction-call name an-lw)
(struct-copy lw an-lw
@ -1122,14 +1178,14 @@
(basic-text "]" (default-style)))])]
[else x]))
(define (render-metafunction/proc mfs filename name)
(define (render-metafunction/proc mfs contract? filename name)
(cond
[filename
(save-as-ps/pdf (λ () (metafunctions->pict/proc mfs name))
(save-as-ps/pdf (λ () (metafunctions->pict/proc mfs contract? name))
filename)]
[else
(parameterize ([dc-for-text-size (make-object bitmap-dc% (make-object bitmap% 1 1))])
(metafunctions->pict/proc mfs name))]))
(metafunctions->pict/proc mfs contract? name))]))
(define (render-pict make-pict filename)
(cond

View File

@ -96,6 +96,7 @@
"red-with-where-name.png"))
(define-metafunction lang
S : x v e -> e
[(S x v e) e])
(btest (render-metafunction S)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB