Adds define-judgment-form form

This commit is contained in:
Casey Klein 2011-08-05 07:18:35 -05:00
parent 8887ea2ef3
commit 576272362b
32 changed files with 1496 additions and 508 deletions

View File

@ -48,10 +48,12 @@
(provide relation->pict
metafunction->pict
metafunctions->pict
judgment-form->pict
render-relation
render-metafunction
render-metafunctions)
render-metafunctions
render-judgment-form)
(provide/contract
[render-language-nts (parameter/c (or/c false/c (listof (or/c string? symbol?))))]

View File

@ -0,0 +1,17 @@
#lang racket/base
(require "error.rkt")
(provide check-defined-lexical
check-defined-module)
(define (check-defined-lexical value name desc)
(when (eq? (letrec ([x x]) x) value)
(report-undefined name desc)))
(define (check-defined-module thunk name desc)
(with-handlers ([exn:fail:contract:variable?
(λ (_) (report-undefined name desc))])
(thunk)))
(define (report-undefined name desc)
(redex-error #f "~a ~s applied before its definition" desc name))

View File

@ -61,7 +61,8 @@
[x
(and (identifier? #'x)
(and (syntax-transforming?)
(term-fn? (syntax-local-value #'x (λ () #f)))))
(or (term-fn? (syntax-local-value #'x (λ () #f)))
(judgment-form? (syntax-local-value #'x (λ () #f))))))
#`(make-lw
'#,(syntax-e #'x)
#,(syntax-line stx)

View File

@ -14,7 +14,8 @@
"matcher.rkt"
"arrow.rkt"
"core-layout.rkt")
(require (for-syntax racket/base))
(require (for-syntax racket/base
"term-fn.rkt"))
(provide render-term
term->pict
@ -30,10 +31,12 @@
relation->pict
metafunction->pict
metafunctions->pict
judgment-form->pict
render-relation
render-metafunction
render-metafunctions
render-judgment-form
basic-text
@ -727,9 +730,9 @@
(define-syntax (relation->pict stx)
(syntax-case stx ()
[(_ name1)
[(form name1)
(identifier? #'name1)
#'(relation->pict/proc (metafunction name1) 'relation->pict)]))
#'(inference-rules-pict/relation 'form (metafunction name1))]))
(define-syntax (render-metafunctions stx)
(syntax-case stx ()
@ -753,12 +756,12 @@
(define-syntax (render-relation stx)
(syntax-case stx ()
[(_ name)
[(form name)
(identifier? #'name)
#'(render-relation/proc (metafunction name) #f)]
[(_ name #:file filename)
#'(render-relation/proc 'form (metafunction name) #f)]
[(form name #:file filename)
(identifier? #'name)
#'(render-relation/proc (metafunction name) filename)]))
#'(render-relation/proc 'form (metafunction name) filename)]))
(define linebreaks (make-parameter #f))
@ -813,8 +816,7 @@
(map (lambda (eqn)
(wrapper->pict
(metafunction-call (metafunc-proc-name (metafunction-proc mf))
(list-ref eqn 0)
(metafunc-proc-multi-arg? (metafunction-proc mf)))))
(list-ref eqn 0))))
(metafunc-proc-pict-info (metafunction-proc mf))))
mfs))]
[eqns (select-cases all-eqns)]
@ -927,56 +929,28 @@
scs
rhss))))])))
(define (metafunction-call name an-lw flattened?)
(if flattened?
(struct-copy lw an-lw
[e
(list*
;; the first loc wrapper is just there to make the
;; shape of this line be one that the apply-rewrites
;; function (in core-layout.rkt) recognizes as a metafunction
(make-lw "("
(lw-line an-lw)
0
(lw-column an-lw)
0
#f
#f)
(make-lw name
(lw-line an-lw)
0
(lw-column an-lw)
0
#f
#t)
(cdr (lw-e an-lw)))])
(build-lw
(list
(build-lw "("
(lw-line an-lw)
0
(lw-column an-lw)
0)
(make-lw name
(lw-line an-lw)
0
(lw-column an-lw)
0
#f
#t)
an-lw
(build-lw ")"
(+ (lw-line an-lw)
(lw-line-span an-lw))
0
(+ (lw-column an-lw)
(lw-column-span an-lw))
0))
(lw-line an-lw)
(lw-line-span an-lw)
(lw-column an-lw)
(lw-column-span an-lw))))
(define (metafunction-call name an-lw)
(struct-copy lw an-lw
[e
(list*
;; the first loc wrapper is just there to make the
;; shape of this line be one that the apply-rewrites
;; function (in core-layout.rkt) recognizes as a metafunction
(make-lw "("
(lw-line an-lw)
0
(lw-column an-lw)
0
#f
#f)
(make-lw name
(lw-line an-lw)
0
(lw-column an-lw)
0
#f
#t)
(cdr (lw-e an-lw)))]))
(define (add-commas-and-rewrite-parens eles)
(let loop ([eles eles]
@ -1044,36 +1018,42 @@
(parameterize ([dc-for-text-size (make-object bitmap-dc% (make-object bitmap% 1 1))])
(metafunctions->pict/proc mfs name))]))
(define (render-relation/proc mf filename)
(define (render-relation/proc form mf filename)
(render-pict (λ () (inference-rules-pict/relation form mf))
filename))
(define (inference-rules-pict/relation form mf)
(unless (metafunc-proc-relation? (metafunction-proc mf))
(error form "expected relation as argument, got a metafunction"))
(inference-rules-pict (metafunc-proc-name (metafunction-proc mf))
(metafunc-proc-pict-info (metafunction-proc mf))
(metafunc-proc-lang (metafunction-proc mf))))
(define (render-pict make-pict filename)
(cond
[filename
(save-as-ps (λ () (relation->pict/proc mf 'render-reduction-relation))
filename)]
(save-as-ps make-pict filename)]
[else
(parameterize ([dc-for-text-size (make-object bitmap-dc% (make-object bitmap% 1 1))])
(relation->pict/proc mf 'render-reduction-relation))]))
(make-pict))]))
(define (relation->pict/proc mf name)
(unless (metafunc-proc-relation? (metafunction-proc mf))
(error name "expected relation as argument, got a metafunction"))
(let* ([all-nts (language-nts (metafunc-proc-lang (metafunction-proc mf)))]
(define (inference-rules-pict name all-eqns lang)
(let* ([all-nts (language-nts lang)]
[wrapper->pict (lambda (lw) (lw->pict all-nts lw))]
[all-eqns (metafunc-proc-pict-info (metafunction-proc mf))]
[all-conclusions
(map (lambda (eqn)
(wrapper->pict
(metafunction-call (metafunc-proc-name (metafunction-proc mf))
(list-ref eqn 0)
(metafunc-proc-multi-arg? (metafunction-proc mf)))))
(metafunc-proc-pict-info (metafunction-proc mf)))]
(metafunction-call name (list-ref eqn 0))))
all-eqns)]
[eqns (select-cases all-eqns)]
[conclusions (select-cases all-conclusions)]
[premisess (map (lambda (eqn)
(append (map wrapper->pict (list-ref eqn 2))
(map (match-lambda
[(struct metafunc-extra-where (lhs rhs))
(where-pict (wrapper->pict lhs) (wrapper->pict rhs))])
(where-pict (wrapper->pict lhs) (wrapper->pict rhs))]
[(struct metafunc-extra-side-cond (expr))
(wrapper->pict expr)])
(list-ref eqn 1))))
eqns)])
((relation-clauses-combine)
@ -1091,6 +1071,33 @@
(define horizontal-bar-spacing (make-parameter 4))
(define relation-clauses-combine (make-parameter (λ (l) (apply vc-append 20 l))))
(define-for-syntax (inference-rules-pict/judgment-form form-name)
(define jf (syntax-local-value form-name))
(syntax-property
#`(inference-rules-pict '#,(judgment-form-name jf)
#,(judgment-form-lws jf)
#,(judgment-form-lang jf))
'disappeared-use
form-name))
(define-syntax (render-judgment-form stx)
(syntax-case stx ()
[(_ form-name . opt-arg)
(if (judgment-form-id? #'form-name)
(let ([save-as (syntax-case #'opt-arg ()
[() #'#f]
[(path) #'path])])
#`(render-pict (λ () #,(inference-rules-pict/judgment-form #'form-name))
#,save-as))
(raise-syntax-error #f "expected a judgment form name" stx #'form-name))]))
(define-syntax (judgment-form->pict stx)
(syntax-case stx ()
[(_ form-name)
(if (judgment-form-id? #'form-name)
(inference-rules-pict/judgment-form #'form-name)
(raise-syntax-error #f "expected a judgment form name" stx #'form-name))]))
;
;
;

View File

@ -17,8 +17,11 @@
"term-fn.rkt"
"underscore-allowed.rkt"
syntax/boundmap
syntax/id-table
scheme/base
(prefix-in pattern- scheme/match)
racket/list
racket/match
racket/syntax
syntax/parse
syntax/parse/experimental/contract
syntax/name))
@ -271,8 +274,51 @@
(with-syntax ([orig-stx stx])
(syntax/loc stx (do-reduction-relation orig-stx extend-reduction-relation orig-reduction-relation #t lang args ...)))]))
(define-for-syntax (where-keyword? id)
(or (free-identifier=? id #'where)
(free-identifier=? id #'where/hidden)))
(define-for-syntax (split-by-mode xs mode)
(for/fold ([ins '()] [outs '()])
([x (reverse xs)]
[m (reverse mode)])
(match m
['I (values (cons x ins) outs)]
['O (values ins (cons x outs))])))
(define-for-syntax (generate-binding-constraints names names/ellipses bindings syn-err-name)
(define (id/depth stx)
(syntax-case stx ()
[(s (... ...))
(let ([r (id/depth #'s)])
(make-id/depth (id/depth-id r) (add1 (id/depth-depth r))))]
[s (make-id/depth #'s 0)]))
(define temporaries (generate-temporaries names))
(values
(for/fold ([cs '()])
([n names]
[w/e names/ellipses]
[x temporaries])
(cond [(hash-ref bindings (syntax-e n) #f)
=> (λ (b)
(let ([b-id/depth (id/depth b)]
[n-id/depth (id/depth w/e)])
(if (= (id/depth-depth b-id/depth) (id/depth-depth n-id/depth))
(cons #`(equal? #,x (term #,b)) cs)
(raise-ellipsis-depth-error
syn-err-name
(id/depth-id n-id/depth) (id/depth-depth n-id/depth)
(id/depth-id b-id/depth) (id/depth-depth b-id/depth)))))]
[else cs]))
temporaries
(for/fold ([extended bindings])
([name names]
[w/ellipses names/ellipses])
(hash-set extended (syntax-e name) w/ellipses))))
;; the withs, freshs, and side-conditions come in backwards order
(define-for-syntax (bind-withs orig-name main lang lang-nts stx where-mode body names w/ellipses)
(with-disappeared-uses
(let loop ([stx stx]
[to-not-be-in main]
[env (make-immutable-hash
@ -281,52 +327,27 @@
(syntax-case stx (fresh)
[() body]
[((-where x e) y ...)
(or (free-identifier=? #'-where #'where)
(free-identifier=? #'-where #'where/hidden))
(where-keyword? #'-where)
(let-values ([(names names/ellipses) (extract-names lang-nts 'reduction-relation #t #'x)])
(let ([env+ (for/fold ([env env])
([name names]
[w/ellipses names/ellipses])
(hash-set env (syntax-e name) w/ellipses))]
[temporaries (generate-temporaries names)])
(define (id/depth stx)
(syntax-case stx ()
[(s (... ...))
(let ([r (id/depth #'s)])
(make-id/depth (id/depth-id r) (add1 (id/depth-depth r))))]
[s (make-id/depth #'s 0)]))
(with-syntax ([(binding-constraints ...)
(for/fold ([cs '()])
([n names]
[w/e names/ellipses]
[x temporaries])
(cond [(hash-ref env (syntax-e n) #f)
=> (λ (b)
(let ([b-id/depth (id/depth b)]
[n-id/depth (id/depth w/e)])
(if (= (id/depth-depth b-id/depth) (id/depth-depth n-id/depth))
(cons #`(equal? #,x (term #,b)) cs)
(raise-ellipsis-depth-error
orig-name
(id/depth-id n-id/depth) (id/depth-depth n-id/depth)
(id/depth-id b-id/depth) (id/depth-depth b-id/depth)))))]
[else cs]))])
(with-syntax ([side-conditions-rewritten (rewrite-side-conditions/check-errs
lang-nts
'reduction-relation
#f
#'x)]
[(names ...) names]
[(names/ellipses ...) names/ellipses]
[(x ...) temporaries])
(let ([rest-body (loop #'(y ...) #`(list x ... #,to-not-be-in) env+)])
#`(let* ([mtchs (match-pattern (compile-pattern #,lang `side-conditions-rewritten #t) (term e))]
[result (λ (mtch)
(let ([bindings (mtch-bindings mtch)])
(let ([x (lookup-binding bindings 'names)] ...)
(and binding-constraints ...
(term-let ([names/ellipses x] ...)
#,rest-body)))))])
(define-values (binding-constraints temporaries env+)
(generate-binding-constraints names names/ellipses env orig-name))
(with-syntax ([(binding-constraints ...) binding-constraints]
[side-conditions-rewritten (rewrite-side-conditions/check-errs
lang-nts
'reduction-relation
#f
#'x)]
[(names ...) names]
[(names/ellipses ...) names/ellipses]
[(x ...) temporaries])
(let ([rest-body (loop #'(y ...) #`(list x ... #,to-not-be-in) env+)])
#`(let* ([mtchs (match-pattern (compile-pattern #,lang `side-conditions-rewritten #t) (term e))]
[result (λ (mtch)
(let ([bindings (mtch-bindings mtch)])
(let ([x (lookup-binding bindings 'names)] ...)
(and binding-constraints ...
(term-let ([names/ellipses x] ...)
#,rest-body)))))])
(if mtchs
#,
(case where-mode
@ -337,7 +358,7 @@
[(predicate)
#`(ormap result mtchs)]
[else (error 'unknown-where-mode "~s" where-mode)])
#f)))))))]
#f)))))]
[((-side-condition s ...) y ...)
(or (free-identifier=? #'-side-condition #'side-condition)
(free-identifier=? #'-side-condition #'side-condition/hidden))
@ -364,7 +385,68 @@
[len-counter (term (x ...))])
(verify-names-ok '#,orig-name the-names len-counter)
(variables-not-in #,to-not-be-in the-names))])
#,(loop #'(z ...) #`(list (term (y #,'...)) #,to-not-be-in) env))])))
#,(loop #'(z ...) #`(list (term (y #,'...)) #,to-not-be-in) env))]
[((form-name . pats) . rest-clauses)
(judgment-form-id? #'form-name)
(let*-values ([(judgment-form) (syntax-local-value/record #'form-name (λ (_) #t))]
[(mode) (judgment-form-mode judgment-form)]
[(judgment-proc) (judgment-form-proc judgment-form)]
[(input-template output-pre-pattern) (split-by-mode (syntax->list #'pats) mode)]
[(output-pattern)
(rewrite-side-conditions/check-errs lang-nts orig-name #t output-pre-pattern)]
[(output-names output-names/ellipses)
(extract-names lang-nts orig-name #t output-pattern)]
[(binding-constraints temporaries env+)
(generate-binding-constraints output-names output-names/ellipses env orig-name)]
[(rest-body) (loop #'rest-clauses #`(list judgment-output #,to-not-be-in) env+)]
[(call) (quasisyntax/loc #'form-name
(call-judgment-form
'form-name #,judgment-proc '#,mode (term #,input-template)))])
(with-syntax ([(output-name ...) output-names]
[(output-name/ellipsis ...) output-names/ellipses]
[(temp ...) temporaries]
[(binding-constraint ...) binding-constraints])
(syntax-case stx () [(clause . _) #'clause])
#`(begin
(void #,(defined-check judgment-proc "judgment form" #:external #'form-name))
(for/fold ([outputs '()]) ([sub-output #,call])
(define mtchs
(match-pattern (compile-pattern #,lang `#,output-pattern #t) sub-output))
(if mtchs
(for/fold ([outputs outputs]) ([mtch mtchs])
(let ([temp (lookup-binding (mtch-bindings mtch) 'output-name)] ...)
(and binding-constraint ...
(term-let ([output-name/ellipsis temp] ...)
(let ([output-rest #,rest-body])
(and output-rest
(append outputs output-rest)))))))
outputs)))))]))))
(define (call-judgment-form form-name form-proc mode input)
(define traced (current-traced-metafunctions))
(if (or (eq? 'all traced) (memq form-name traced))
(let ([outputs #f])
(define spacers
(for/fold ([s '()]) ([m mode])
(case m [(I) s] [(O) (cons '_ s)])))
(define (assemble inputs outputs)
(let loop ([ms mode] [is inputs] [os outputs])
(if (null? ms)
'()
(case (car ms)
[(I) (cons (car is) (loop (cdr ms) (cdr is) os))]
[(O) (cons (car os) (loop (cdr ms) is (cdr os)))]))))
(define (wrapped . _)
(set! outputs (form-proc input))
(for/list ([output outputs])
(cons form-name (assemble input output))))
(apply trace-call form-name wrapped (assemble input spacers))
outputs)
(form-proc input)))
(define-for-syntax (name-pattern-lws pat)
(map (λ (x) (cons (to-lw/proc (car x)) (to-lw/proc (cdr x))))
(extract-pattern-binds pat)))
(define-syntax-set (do-reduction-relation)
(define (do-reduction-relation/proc stx)
@ -466,6 +548,11 @@
[with (cdr lst)]
[else (loop (cdr lst))]))])))
(define (name-pattern-lws/rr pat)
(for/list ([lw-pair (name-pattern-lws pat)])
(match lw-pair
[(cons l r) #`(cons #,l #,r)])))
(define (rule->lws rule)
(syntax-case rule ()
[(arrow lhs rhs stuff ...)
@ -481,7 +568,8 @@
(syntax-case (car stuffs) (where where/hidden
side-condition side-condition/hidden
fresh variable-not-in
computed-name)
computed-name
judgment-holds)
[(fresh xs ...)
(loop (cdr stuffs)
label
@ -514,7 +602,7 @@
label
computed-label
(cons #`(cons #,(to-lw/proc #'x) #,(to-lw/proc #'e))
scs/withs)
(append (name-pattern-lws/rr #'x) scs/withs))
fvars)]
[(where/hidden x e)
(loop (cdr stuffs) label computed-label scs/withs fvars)]
@ -545,6 +633,17 @@
label
#'e
scs/withs
fvars)]
[(judgment-holds (form-name . pieces))
(judgment-form-id? #'form-name)
(loop (cdr stuffs)
label
computed-label
(let*-values ([(mode) (judgment-form-mode (syntax-local-value #'form-name))]
[(_ outs) (split-by-mode (syntax->list #'pieces) mode)])
(cons (to-lw/proc #'(form-name . pieces))
(for/fold ([binds scs/withs]) ([out outs])
(append (name-pattern-lws/rr out) binds))))
fvars)])]))])
(with-syntax ([(scs/withs ...) scs/withs]
[(fvars ...) fvars]
@ -761,7 +860,7 @@
(if (or (memq x nts) (memq x underscore-allowed))
(string-append (symbol->string x) "_")
x)))
(let-values ([(bound _) (extract-names nts what #t pat #f)])
(let-values ([(bound _) (extract-names nts what #t pat 'binds-anywhere)])
(let ([renames (make-bound-identifier-mapping)])
(for-each
(λ (x)
@ -870,7 +969,7 @@
(cond
[(null? extras) '()]
[else
(syntax-case (car extras) (fresh computed-name)
(syntax-case (car extras) (fresh computed-name judgment-holds)
[name
(or (identifier? (car extras))
(string? (syntax-e (car extras))))
@ -929,12 +1028,10 @@
(free-identifier=? #'-side-condition #'side-condition/hidden))
(cons (car extras) (loop (cdr extras)))]
[(-where x e)
(or (free-identifier=? #'-where #'where)
(free-identifier=? #'-where #'where/hidden))
(where-keyword? #'-where)
(cons (car extras) (loop (cdr extras)))]
[(-where . x)
(or (free-identifier=? #'-where #'where)
(free-identifier=? #'-where #'where/hidden))
(where-keyword? #'-where)
(raise-syntax-error orig-name "malformed where clause" stx (car extras))]
[(computed-name e)
(if computed-name-stx
@ -944,6 +1041,8 @@
(loop (cdr extras))]
[(computed-name . _)
(raise-syntax-error orig-name "malformed computed-name clause" stx (car extras))]
[(judgment-holds judgment)
(cons #'judgment (loop (cdr extras)))]
[_
(raise-syntax-error orig-name "unknown extra" stx (car extras))])]))])
(values the-name computed-name-stx sides/withs/freshs)))
@ -1178,6 +1277,15 @@
((metafunc-proc-in-dom? mp)
exp)))
(define-for-syntax (definition-nts lang orig-stx syn-error-name)
(unless (identifier? lang)
(raise-syntax-error #f "expected an identifier in the language position" orig-stx lang))
(language-id-nts lang syn-error-name))
(define-for-syntax (lhs-lws clauses)
(with-syntax ([((lhs-for-lw _ ...) ...) clauses])
(map (λ (x) (to-lw/proc (datum->syntax #f (cdr (syntax-e x)) x)))
(syntax->list #'(lhs-for-lw ...)))))
;
;
@ -1195,7 +1303,9 @@
;
;
(define-syntax-set (define-metafunction define-metafunction/extension define-relation)
(define-syntax-set (define-metafunction define-metafunction/extension
define-relation
define-judgment-form)
(define (define-metafunction/proc stx)
(syntax-case stx ()
@ -1224,7 +1334,7 @@
'define-metafunction))])
(define lang-nts
;; keep this near the beginning, so it signals the first error (PR 10062)
(relevant-nts #'lang orig-stx syn-error-name))
(definition-nts #'lang orig-stx syn-error-name))
(when (null? (syntax-e #'rest))
(raise-syntax-error syn-error-name "no clauses" orig-stx))
(when prev-metafunction
@ -1233,8 +1343,9 @@
(λ ()
(raise-syntax-error syn-error-name "expected a previously defined metafunction" orig-stx prev-metafunction))))
(let ()
(let-values ([(contract-name dom-ctcs codom-contracts pats)
(split-out-contract orig-stx syn-error-name #'rest relation?)])
(let*-values ([(contract-name dom-ctcs codom-contracts pats)
(split-out-contract orig-stx syn-error-name #'rest relation?)]
[(name _) (defined-name contract-name pats orig-stx)])
(with-syntax ([(((original-names lhs-clauses ...) raw-rhses ...) ...) pats]
[(lhs-for-lw ...) (lhs-lws pats)])
(with-syntax ([((rhs stuff ...) ...) (if relation?
@ -1242,12 +1353,12 @@
#'((raw-rhses ...) ...))])
(parameterize ()
(with-syntax ([(lhs ...) #'((lhs-clauses ...) ...)]
[name (defined-name contract-name pats orig-stx)])
[name name])
(when (and prev-metafunction (eq? (syntax-e #'name) (syntax-e prev-metafunction)))
(raise-syntax-error syn-error-name "the extended and extending metafunctions cannot share a name" orig-stx prev-metafunction))
(parse-extras #'((stuff ...) ...))
(let-values ([(lhs-namess lhs-namess/ellipsess)
(bound-names (syntax->list (syntax (lhs ...))) lang-nts syn-error-name)])
(lhss-bound-names (syntax->list (syntax (lhs ...))) lang-nts syn-error-name)])
(with-syntax ([(rhs/wheres ...)
(map (λ (sc/b rhs names names/ellipses)
(bind-withs
@ -1376,7 +1487,82 @@
(map syntax-local-introduce
(syntax->list #'(original-names ...)))))))))))))))]))
(define (bound-names lhss nts syn-error-name)
(define (define-judgment-form/proc stx)
(syntax-case stx ()
[(def-form-id lang . body)
(let ([lang #'lang]
[syn-err-name (syntax-e #'def-form-id)])
(define nts (definition-nts lang stx syn-err-name))
(define-values (judgment-form-name dup-form-names mode position-contracts clauses)
(parse-define-judgment-form-body #'body syn-err-name stx))
(syntax-property
(prune-syntax
#`(begin
(define-syntax #,judgment-form-name
(judgment-form '#,judgment-form-name '#,mode #'judgment-form-proc #'#,lang #'judgment-form-lws))
(define judgment-form-proc
(let-syntax ([delayed
(λ (stx)
(syntax-case stx ()
[(_ lang clauses ctcs full-def)
(let ([nts (definition-nts #'lang #'full-def '#,syn-err-name)])
(mode-check '#,mode (syntax->list #'clauses) nts '#,syn-err-name)
(compile-judgment-form-proc
'#,judgment-form-name '#,mode (syntax->list #'clauses) #'ctcs nts #'lang '#,syn-err-name))]))])
(delayed #,lang #,clauses #,position-contracts #,stx)))
(define judgment-form-lws
(let-syntax ([delayed
(λ (stx)
(syntax-case stx ()
[(_ clauses)
(compile-judgment-form-lws (syntax->list #'clauses))]))])
(delayed #,clauses)))))
'disappeared-use
(map syntax-local-introduce dup-form-names)))]))
(define (parse-define-judgment-form-body body syn-err-name full-stx)
(define-values (mode rest-body)
(parse-mode-spec body full-stx))
(define-values (declared-form-name contracts clauses)
(syntax-case rest-body ()
[(form-name . rest-contract+clauses)
(identifier? #'form-name)
(let-values ([(contracts clauses)
(parse-relation-contract #'rest-contract+clauses syn-err-name full-stx)])
(values #'form-name contracts clauses))]
[_ (values #f #f (syntax->list rest-body))]))
(check-clauses full-stx syn-err-name clauses #t)
(check-arity-consistency mode contracts clauses full-stx)
(define-values (form-name dup-names)
(syntax-case clauses ()
[() (raise-syntax-error #f "expected at least one clause after mode" full-stx)]
[_ (defined-name declared-form-name clauses full-stx)]))
(values form-name dup-names mode contracts clauses))
(define (check-arity-consistency mode contracts clauses full-def)
(when (and contracts (not (= (length mode) (length contracts))))
(raise-syntax-error
#f "mode and contract specify different numbers of positions" full-def)))
(define (parse-mode-spec body full-stx)
(syntax-case body (mode :)
[(mode : . rest-body)
(let loop ([rest-body #'rest-body]
[pos-modes '()]
[idx 1])
(syntax-case rest-body (I O)
[(I . more)
(loop #'more (cons 'I pos-modes) (+ 1 idx))]
[(O . more)
(loop #'more (cons 'O pos-modes) (+ 1 idx))]
[_ (values (reverse pos-modes) rest-body)]))]
[_ (raise-syntax-error
#f "expected a mode specification after the language declaration"
(if (pair? (syntax-e body))
(car (syntax-e body))
full-stx))]))
(define (lhss-bound-names lhss nts syn-error-name)
(let loop ([lhss lhss])
(if (null? lhss)
(values null null)
@ -1387,36 +1573,27 @@
(values (cons names namess)
(cons names/ellipses namess/ellipsess))))))
(define (lhs-lws clauses)
(with-syntax ([((lhs-for-lw _ ...) ...) clauses])
(map (λ (x) (to-lw/proc (datum->syntax #f (cdr (syntax-e x)) x)))
(syntax->list #'(lhs-for-lw ...)))))
(define (relevant-nts lang orig-stx syn-error-name)
(unless (identifier? lang)
(raise-syntax-error #f "expected an identifier in the language position" orig-stx lang))
(language-id-nts lang syn-error-name))
(define (defined-name declared-name clauses orig-stx)
(with-syntax ([(((used-names _ ...) _ ...) ...) clauses])
(let loop ([name (if declared-name
declared-name
(car (syntax->list #'(used-names ...))))]
[names (if declared-name
(syntax->list #'(used-names ...))
(cdr (syntax->list #'(used-names ...))))])
(define-values (the-name other-names)
(if declared-name
(values declared-name
(syntax->list #'(used-names ...)))
(values (car (syntax->list #'(used-names ...)))
(cdr (syntax->list #'(used-names ...))))))
(let loop ([others other-names])
(cond
[(null? names) name]
[(null? others) (values the-name other-names)]
[else
(unless (eq? (syntax-e name) (syntax-e (car names)))
(unless (eq? (syntax-e the-name) (syntax-e (car others)))
(raise-syntax-error
#f
(if declared-name
"expected each clause and the contract to use the same name"
"expected each clause to use the same name")
orig-stx
name (list (car names))))
(loop name (cdr names))]))))
the-name (list (car others))))
(loop (cdr others))]))))
(define (split-out-contract stx syn-error-name rest relation?)
;; initial test determines if a contract is specified or not
@ -1569,6 +1746,163 @@
(cons (cadr more) arg-pats))]
[else (values (reverse arg-pats) more)])))])))
(define-for-syntax (check-judgment-arity judgment)
(syntax-case judgment ()
[(form-name pat ...)
(judgment-form-id? #'form-name)
(let ([expected (length (judgment-form-mode (syntax-local-value #'form-name)))])
(unless (= (length (syntax->list #'(pat ...))) expected)
(raise-syntax-error #f "arity mismatch" judgment)))]))
(define-syntax (judgment-holds stx)
(syntax-case stx ()
[(j-h judgment)
#`(not (null? #,(syntax/loc stx (j-h judgment #t))))]
[(j-h (form-name . pats) tmpl)
(judgment-form-id? #'form-name)
(let* ([syn-err-name (syntax-e #'j-h)]
[lang (judgment-form-lang (syntax-local-value #'form-name))]
[nts (definition-nts lang stx syn-err-name)])
(check-judgment-arity #'(form-name . pats))
(bind-withs syn-err-name '() lang nts (list #'(form-name . pats))
'flatten #`(list (term #,#'tmpl)) '() '()))]
[(_ (not-form-name . _) . _)
(not (judgment-form-id? #'form-name))
(raise-syntax-error #f "expected a judgment form name" stx #'not-form-name)]))
(define-for-syntax (compile-judgment-form-proc name mode clauses contracts nts lang syn-error-name)
(define (compile-clause clause)
(syntax-case clause ()
[((_ . conc-pats) . prems)
(let-values ([(input-pats output-pats) (split-by-mode (syntax->list #'conc-pats) mode)])
(define-values (input-names input-names/ellipses)
(extract-names nts syn-error-name #t input-pats))
(define ((rewrite-pattern binds?) pat)
(rewrite-side-conditions/check-errs nts syn-error-name binds? pat))
(define (contracts-compilation ctcs)
(and ctcs #`(map (λ (p) (compile-pattern #,lang p #f)) `#,ctcs)))
(define-values (input-contracts output-contracts)
(syntax-case contracts ()
[#f (values #f #f)]
[(p ...)
(let-values ([(ins outs) (split-by-mode (syntax->list #'(p ...)) mode)])
(values (map (rewrite-pattern #f) ins)
(map (rewrite-pattern #f) outs)))]))
(define lhs (map (rewrite-pattern #t) input-pats))
(define body
(bind-withs syn-error-name '() lang nts (syntax->list #'prems)
'flatten #`(list (term (#,@output-pats))) input-names input-names/ellipses))
(with-syntax ([(names ...) input-names]
[(names/ellipses ...) input-names/ellipses])
#`(let ([compiled-lhs (compile-pattern #,lang `#,lhs #t)]
[compiled-input-ctcs #,(contracts-compilation input-contracts)]
[compiled-output-ctcs #,(contracts-compilation output-contracts)])
(λ (input)
(check-judgment-form-contract `#,name input compiled-input-ctcs 'I '#,mode)
(define mtchs (match-pattern compiled-lhs input))
(define outputs
(if mtchs
(for/fold ([outputs '()]) ([m mtchs])
(define os
(term-let ([names/ellipses (lookup-binding (mtch-bindings m) 'names)] ...)
#,body))
(if os (append os outputs) outputs))
'()))
(for ([output outputs])
(check-judgment-form-contract `#,name output compiled-output-ctcs 'O '#,mode))
outputs))))]))
(with-syntax ([(clause-proc ...) (map compile-clause clauses)])
#'(λ (input)
(for/fold ([outputs '()]) ([rule (list clause-proc ...)])
(append (rule input) outputs)))))
(define-for-syntax (in-order-non-hidden extras)
(reverse
(filter (λ (extra)
(syntax-case extra (where/hidden
side-condition/hidden)
[(where/hidden pat exp) #f]
[(side-condition/hidden x) #f]
[_ #t]))
(syntax->list extras))))
(define-for-syntax (compile-judgment-form-lws clauses)
(syntax-case clauses ()
[(((_ . conc-body) . prems) ...)
(let ([rev-premss
; for consistency with metafunction extras
(for/list ([prems (syntax->list #'(prems ...))])
(reverse (syntax->list prems)))]
[no-rhss (map (λ (_) '()) clauses)])
#`(generate-lws #t (conc-body ...) #,(lhs-lws clauses) #,rev-premss #,no-rhss))]))
(define (check-judgment-form-contract form-name terms contracts mode modes)
(define description
(case mode
[(I) "input"]
[(O) "output"]))
(when contracts
(let loop ([rest-modes modes] [rest-terms terms] [rest-ctcs contracts] [pos 1])
(unless (null? rest-modes)
(if (eq? mode (car rest-modes))
(if (match-pattern (car rest-ctcs) (car rest-terms))
(loop (cdr rest-modes) (cdr rest-terms) (cdr rest-ctcs) (+ 1 pos))
(redex-error form-name "~a ~s at position ~s does not match its contract"
description (car rest-terms) pos))
(loop (cdr rest-modes) rest-terms rest-ctcs (+ 1 pos)))))))
(define-for-syntax (mode-check mode clauses nts syn-err-name)
(define ((check-template named-vars) temp bound)
(let check ([t temp])
(syntax-case t (unquote)
[(unquote . _)
(raise-syntax-error syn-err-name "unquote unsupported" t)]
[x
(identifier? #'x)
(when (and (or (id-binds? nts #t #'x) (free-id-table-ref named-vars #'x #f))
(not (free-id-table-ref bound #'x #f)))
(raise-syntax-error syn-err-name "unbound pattern variable" #'x))]
[(u ...)
(for-each check (syntax->list #'(u ...)))]
[_ (void)])))
(define ((bind kind) pat bound)
(define-values (ids _)
(extract-names nts syn-err-name #t pat kind))
(for/fold ([b bound]) ([x ids])
(free-id-table-set b x #t)))
(define (split-body judgment)
(syntax-case judgment ()
[(form-name . body)
(split-by-mode (syntax->list #'body) (judgment-form-mode (syntax-local-value #'form-name)))]))
(define (fold-clause pat-pos tmpl-pos acc-init clause)
(syntax-case clause ()
[(conc . prems)
(let-values ([(conc-in conc-out) (split-body #'conc)])
(check-judgment-arity #'conc)
(define acc-out
(for/fold ([acc (foldl pat-pos acc-init conc-in)])
([prem (syntax->list #'prems)])
(syntax-case prem ()
[(form-name . _)
(judgment-form-id? #'form-name)
(let-values ([(prem-in prem-out) (split-body prem)])
(check-judgment-arity prem)
(for ([pos prem-in]) (tmpl-pos pos acc))
(foldl pat-pos acc prem-out))]
[(-where pat tmpl)
(where-keyword? #'-where)
(begin
(tmpl-pos #'tmpl acc)
(pat-pos #'pat acc))]
[_ (raise-syntax-error syn-err-name "malformed premise" prem)])))
(for ([pos conc-out]) (tmpl-pos pos acc-out))
acc-out)]))
(for ([clause clauses])
(define do-tmpl
(check-template
(fold-clause (bind 'name-only) void (make-immutable-free-id-table) clause)))
(fold-clause (bind 'rhs-only) do-tmpl (make-immutable-free-id-table) clause)))
;; Defined as a macro instead of an ordinary phase 1 function so that the
;; to-lw/proc calls occur after bindings are established for all meta-functions
;; and relations.
@ -1583,16 +1917,16 @@
[#f (map to-lw/proc (syntax->list #'seq-of-rhs))])]
[(((bind-id/lw . bind-pat/lw) ...) ...)
;; Also for pict, extract pattern bindings
(map (λ (x) (map (λ (x) (cons (to-lw/proc (car x)) (to-lw/proc (cdr x))))
(extract-pattern-binds x)))
(syntax->list #'seq-of-lhs))]
(map name-pattern-lws (syntax->list #'seq-of-lhs))]
[((where/sc/lw ...) ...)
;; Also for pict, extract where bindings
(map (λ (hm)
(map
(λ (lst)
(syntax-case lst (unquote side-condition where)
[(form-name . _)
(judgment-form-id? #'form-name)
#`(make-metafunc-extra-side-cond #,(to-lw/proc lst))]
[(where pat (unquote (f _ _)))
(and (or (identifier? #'pat)
(andmap identifier? (syntax->list #'pat)))
@ -1611,16 +1945,21 @@
[(side-condition x)
#`(make-metafunc-extra-side-cond
#,(to-lw/uq/proc #'x))]))
(reverse
(filter (λ (lst)
(syntax-case lst (where/hidden
side-condition/hidden)
[(where/hidden pat exp) #f]
[(side-condition/hidden x) #f]
[_ #t]))
(syntax->list hm)))))
(in-order-non-hidden hm)))
(syntax->list #'seq-of-tl-side-cond/binds))]
[(((where-bind-id/lw . where-bind-pat/lw) ...) ...)
(map (λ (clauses)
(for/fold ([binds '()]) ([clause (in-order-non-hidden clauses)])
(syntax-case clause (where)
[(form-name . pieces)
(judgment-form-id? #'form-name)
(let*-values ([(mode) (judgment-form-mode (syntax-local-value #'form-name))]
[(_ outs) (split-by-mode (syntax->list #'pieces) mode)])
(for/fold ([binds binds]) ([out outs])
(append (name-pattern-lws out) binds)))]
[(where lhs rhs) (append (name-pattern-lws #'lhs) binds)]
[_ binds])))
(syntax->list #'seq-of-tl-side-cond/binds))]
[(((rhs-bind-id/lw . rhs-bind-pat/lw/uq) ...) ...)
;; Also for pict, extract pattern bindings
(map (λ (x) (map (λ (x) (cons (to-lw/proc (car x)) (to-lw/uq/proc (cdr x))))
@ -1630,6 +1969,7 @@
[(x-lhs-for-lw ...) #'seq-of-lhs-for-lw])
#'(list (list x-lhs-for-lw
(list (make-metafunc-extra-where bind-id/lw bind-pat/lw) ...
(make-metafunc-extra-where where-bind-id/lw where-bind-pat/lw) ...
(make-metafunc-extra-where rhs-bind-id/lw rhs-bind-pat/lw/uq) ...
where/sc/lw ...)
rhs/lw)
@ -2387,6 +2727,8 @@
define-metafunction
define-metafunction/extension
define-relation
define-judgment-form
judgment-holds
(rename-out [metafunction-form metafunction])
metafunction? metafunction-proc

View File

@ -8,6 +8,7 @@
(provide rewrite-side-conditions/check-errs
extract-names
(rename-out [binds? id-binds?])
raise-ellipsis-depth-error
make-language-id
language-id-nts)
@ -122,7 +123,7 @@
(define-struct id/depth (id depth))
;; extract-names : syntax syntax -> (values (listof syntax) (listof syntax[x | (x ...) | ((x ...) ...) | ...]))
(define (extract-names all-nts what bind-names? orig-stx [rhs-only? #t])
(define (extract-names all-nts what bind-names? orig-stx [mode 'rhs-only])
(let* ([dups
(let loop ([stx orig-stx]
[names null]
@ -158,7 +159,10 @@
(loop (car pats) names (+ depth 1))))]))]
[x
(and (identifier? (syntax x))
((if rhs-only? binds-in-right-hand-side? binds?)
((case mode
[(rhs-only) binds-in-right-hand-side?]
[(binds-anywhere) binds?]
[(name-only) (λ (_1 _2 _3) #f)])
all-nts bind-names? (syntax x)))
(cons (make-id/depth (syntax x) depth) names)]
[else names]))]

View File

@ -1,12 +1,27 @@
#lang scheme/base
(require (for-template racket/base "defined-checks.rkt"))
(provide make-term-fn
term-fn?
term-fn-get-id
(struct-out term-id))
(struct-out term-id)
(struct-out judgment-form)
judgment-form-id?
defined-check)
(define-values (struct-type make-term-fn term-fn? term-fn-get term-fn-set!)
(make-struct-type 'term-fn #f 1 0))
(define term-fn-get-id (make-struct-field-accessor term-fn-get 0))
(define-struct term-id (id depth))
(define-struct judgment-form (name mode proc lang lws))
(define (judgment-form-id? stx)
(and (identifier? stx)
(judgment-form? (syntax-local-value stx (λ () 'not-a-judgment-form)))))
(define (defined-check id desc #:external [external id])
(if (eq? (identifier-binding id) 'lexical)
(quasisyntax/loc external (check-defined-lexical #,id '#,external #,desc))
(quasisyntax/loc external (check-defined-module (λ () #,id) '#,external #,desc))))

View File

@ -115,10 +115,7 @@
#`(begin
#,@(free-identifier-mapping-map
applied-metafunctions
(λ (f _)
(if (eq? (identifier-binding f) 'lexical)
#`(check-defined-lexical #,f '#,f)
#`(check-defined-module (λ () #,f) '#,f))))
(λ (f _) (defined-check f "metafunction")))
#,(let loop ([bs (reverse outer-bindings)])
(cond
[(null? bs) (syntax (syntax->datum (quasisyntax rewritten)))]
@ -127,18 +124,6 @@
(syntax (with-syntax (fst)
rec)))])))))]))
(define (check-defined-lexical value name)
(when (eq? (letrec ([x x]) x) value)
(report-undefined-metafunction name)))
(define (check-defined-module thunk name)
(with-handlers ([exn:fail:contract:variable?
(λ (_) (report-undefined-metafunction name))])
(thunk)))
(define (report-undefined-metafunction name)
(redex-error #f "metafunction ~s applied before its definition" name))
(define-syntax (term-let-fn stx)
(syntax-case stx ()
[(_ ([f rhs] ...) body1 body2 ...)

View File

@ -697,6 +697,7 @@ otherwise.
(fresh fresh-clause ...)
(side-condition racket-expression)
(where @#,ttpattern @#,tttterm)
(judgment-holds (judgment-form-id pat/term))
(side-condition/hidden racket-expression)
(where/hidden @#,ttpattern @#,tttterm)]
[shortcuts (code:line)
@ -706,7 +707,9 @@ otherwise.
[rule-name identifier
string
(computed-name racket-expression)]
[fresh-clause var ((var1 ...) (var2 ...))])]{
[fresh-clause var ((var1 ...) (var2 ...))]
[pat/term @#,ttpattern
@#,tttterm])]{
Defines a reduction relation casewise, one case for each of the
@racket[reduction-case] clauses.
@ -792,6 +795,10 @@ metafunction result. The bindings are the same as bindings in a
same as a @racket[where] clause, but the clause is not
rendered when typesetting via @racketmodname[redex/pict].
Each @racket[judgment-holds] clause acts like a @racket[where] clause, where
the left-hand side pattern incorporates each of the patterns used in the
judgment form's output positions.
Each @racket[shortcut] clause defines arrow names in terms of
@racket[base-arrow-name] and earlier @racket[shortcut] definitions.
The left- and right-hand sides of a @racket[shortcut] definition
@ -940,7 +947,7 @@ it is traversing through the reduction graph.
@racket[reduction-relation]. A @racket[with] form is an
error elsewhere. }
@section{Metafunctions and Relations}
@section{Other Relations}
@declare-exporting[redex/reduction-semantics redex]
@ -1062,35 +1069,157 @@ legtimate inputs according to @racket[metafunction-name]'s contract,
and @racket[#f] otherwise.
}
@defform/subs[#:literals ()
(define-relation language
relation-contract
[(name @#,ttpattern ...) @#,tttterm ...] ...)
([relation-contract (code:line)
(code:line id ⊂ pat x ... x pat)
(code:line id ⊆ pat × ... × pat)])]{
@defform/subs[#:literals (mode : I O ⊂ ⊆ × x where)
(define-judgment-form language
mode-spec
maybe-contract
[conclusion premise ...] ...)
([mode-spec (code:line mode : use ...)]
[use I
O]
[maybe-contract (code:line)
(code:line form-id ⊂ @#,ttpattern x ... x @#,ttpattern)
(code:line form-id ⊆ @#,ttpattern × ... × @#,ttpattern)]
[conclusion (form-id pat/term ...)]
[premise (judgment-form-id pat/term ...)
(where @#,ttpattern @#,tttterm)]
[pat/term @#,ttpattern
@#,tttterm])]{
Defines @racket[form-id] as a relation on terms via a set of inference rules.
Each rule must be such that its premises can be evaluated left-to-right
without ``guessing'' values for any of their pattern variables. Redex checks this
property using the @racket[mode-spec] declaration, which partitions positions
into inputs @racket[I] and outputs @racket[O]. Output positions in conclusions
and input positions in premises must be @|tttterm|s with no uses of
@racket[unquote]; input positions in conclusions and output positions in
premises must be @|ttpattern|s. When the optional @racket[relation-contract]
declaration is present, Redex dynamically checks that the terms flowing through
these positions match the provided patterns, raising an exception recognized by
@racket[exn:fail:redex] if not.
The @racket[define-relation] form builds a relation on
sexpressions according to the pattern and right-hand-side
expressions. The first argument indicates the language used
to resolve non-terminals in the pattern expressions. Each of
the rhs-expressions is implicitly wrapped in @|tttterm|.
For example, the following defines addition on natural numbers:
@interaction[
#:eval redex-eval
(define-language nats
(n z (s n)))
(define-judgment-form nats
mode : I I O
sum ⊆ n × n × n
[(sum z n n)]
[(sum (s n_1) n_2 (s n_3))
(sum n_1 n_2 n_3)])]
The @racket[judgment-holds] form checks whether a relation holds for any
assignment of pattern variables in output positions.
@examples[
#:eval redex-eval
(judgment-holds (sum (s (s z)) (s z) (s (s (s z)))))
(judgment-holds (sum (s (s z)) (s z) (s (s (s n)))))
(judgment-holds (sum (s (s z)) (s z) (s (s (s (s n))))))]
Alternatively, this form constructs a list of terms based on the satisfying
pattern variable assignments.
@examples[
#:eval redex-eval
(judgment-holds (sum (s (s z)) (s z) (s (s (s n)))) n)
(judgment-holds (sum (s (s z)) (s z) (s (s (s (s n))))) n)
(judgment-holds (sum (s (s z)) (s z) (s (s (s n)))) (s n))]
Relations are like metafunctions in that they are called with
arguments and return results (unlike in, say, prolog, where a relation
definition would be able to synthesize some of the arguments based on
the values of others).
Declaring different modes for the same inference rules enables different forms
of computation. For example, the following mode allows @racket[judgment-holds]
to compute all pairs with a given sum.
@interaction[
#:eval redex-eval
(define-judgment-form nats
mode : O O I
sumr ⊆ n × n × n
[(sumr z n n)]
[(sumr (s n_1) n_2 (s n_3))
(sumr n_1 n_2 n_3)])
(judgment-holds (sumr n_1 n_2 (s (s z))) (n_1 n_2))]
Unlike metafunctions, relations check all possible ways to match each
case, looking for a true result and if none of the clauses match, then
the result is @racket[#f]. If there are multiple expressions on
the right-hand side of a relation, then all of them must be satisfied
in order for that clause of the relation to be satisfied.
A rule's @racket[where] clause premises behave as in @racket[reduction-relation]
and @racket[define-metafunction].
@interaction[
#:eval redex-eval
(define-judgment-form nats
mode : I I
le ⊆ n × n
[(le z n)]
[(le (s n_1) (s n_2))
(le n_1 n_2)])
(define-metafunction nats
pred : n -> n or #f
[(pred z) #f]
[(pred (s n)) n])
(define-judgment-form nats
mode : I I
gt ⊆ n × n
[(gt n_1 n_2)
(where n_3 (pred n_1))
(le n_2 n_3)])
(judgment-holds (gt (s (s z)) (s z)))
(judgment-holds (gt (s z) (s z)))]
The contract specification for a relation restricts the patterns that can
be used as input to a relation. For each argument to the relation, there
should be a single pattern, using @racket[x] or @racket[×] to separate
the argument contracts.
Redex evaluates premises depth-first, even when it doing so leads to
non-termination. For example, consider the following definitions:
@interaction[
#:eval redex-eval
(define-language vertices
(v a b c))
(define-judgment-form vertices
mode : I O
edge ⊆ v × v
[(edge a b)]
[(edge b c)])
(define-judgment-form vertices
mode : I I
path ⊆ v × v
[(path v v)]
[(path v_1 v_2)
(path v_2 v_1)]
[(path v_1 v_3)
(edge v_1 v_2)
(path v_2 v_3)])]
Due to the second @racket[path] rule, the follow query fails to terminate:
@racketinput[(judgment-holds (path a c))]
}
@defform*/subs[((judgment-holds judgment)
(judgment-holds judgment @#,tttterm))
([judgment (judgment-form-id pat/term ...)])]{
In its first form, checks whether @racket[judgment] holds for any assignment of
the pattern variables in @racket[judgment-id]'s output positions. In its second
form, produces a list of terms by instantiating the supplied term template with
each satisfying assignment of pattern variables.
See @racket[define-judgment-form] for examples.
}
@defform[(define-relation language
relation-contract
[(name @#,ttpattern ...) @#,tttterm ...] ...)]{
Similar to @racket[define-judgment-form] but suitable only when every position
is an input. There is no associated form corresponding to
@racket[judgment-holds]; querying the result uses the same syntax as
metafunction application.
@examples[
#:eval redex-eval
(define-language types
((τ σ) int
num
(τ → τ)))
(define-relation types
subtype ⊆ τ × τ
[(subtype int num)]
[(subtype (τ_1 → τ_2) (σ_1 → σ_2))
(subtype σ_1 τ_1)
(subtype τ_2 σ_2)]
[(subtype τ τ)])
(term (subtype int num))
(term (subtype (int → int) (num → num)))
(term (subtype (num → int) (num → num)))]
Note that relations are assumed to always return the same results for
the same inputs, and their results are cached, unless
@ -1098,7 +1227,7 @@ the same inputs, and their results are cached, unless
relation is called with the same inputs twice, then its right-hand
sides are evaluated only once.
}
@defparam[current-traced-metafunctions traced-metafunctions (or/c 'all (listof symbol?))]{
Controls which metafunctions are currently being traced. If it is
@ -1940,6 +2069,7 @@ and for use in DrRacket to easily adjust the typesetting:
@racket[render-language],
@racket[render-reduction-relation],
@racket[render-relation],
@racket[render-judgment-form],
@racket[render-metafunctions], and
@racket[render-lw],
and one
@ -1947,6 +2077,8 @@ for use in combination with other libraries that operate on picts
@racket[term->pict],
@racket[language->pict],
@racket[reduction-relation->pict],
@racket[relation->pict],
@racket[judgment-form->pict],
@racket[metafunction->pict], and
@racket[lw->pict].
The primary difference between these functions is that the former list
@ -2043,12 +2175,7 @@ other tools that combine picts together.
@defform[(render-metafunctions metafunction-name ...)]{}
@defform/none[#:literals (render-metafunctions)
(render-metafunctions metafunction-name ... #:file filename)]{}]]{
If provided with one argument, @racket[render-metafunction]
produces a pict that renders properly in the definitions
window in DrRacket. If given two arguments, it writes
postscript into the file named by @racket[filename] (which
may be either a string or bytes).
Like @racket[render-reduction-relation] but for metafunctions.
Similarly, @racket[render-metafunctions] accepts multiple
metafunctions and renders them together, lining up all of the
@ -2077,23 +2204,32 @@ This function sets @racket[dc-for-text-size]. See also
@deftogether[(@defform[(render-relation relation-name)]{}
@defform/none[#:literals (render-relation)
(render-relation relation-name filename)]{})]{
If provided with one argument, @racket[render-relation]
produces a pict that renders properly in the definitions
window in DrRacket. If given two arguments, it writes
postscript into the file named by @racket[filename] (which
may be either a string or bytes).
Like @racket[render-metafunction] but for relations.
This function sets @racket[dc-for-text-size]. See also
@racket[relation->pict].
}
@deftogether[(@defform[(render-judgment-form judgment-form-name)]{}
@defform/none[#:literals (render-judgment-form)
(render-judgment-form judgment-form-name filename)]{})]{
Like @racket[render-metafunction] but for judgment forms.
This function sets @racket[dc-for-text-size]. See also
@racket[relation->pict].
}
@defform[(relation->pict relation-name)]{
This produces a pict, but without setting @racket[dc-for-text-size].
It is suitable for use in Slideshow or other libraries that combine
picts.
}
@defform[(judgment-form->pict judgment-form-name)]{
This produces a pict, but without setting @racket[dc-for-text-size].
It is suitable for use in Slideshow or other libraries that combine
picts.
}
@subsection{Customization}

View File

@ -28,6 +28,8 @@
define-metafunction
define-metafunction/extension
define-relation
define-judgment-form
judgment-holds
in-domain?
caching-enabled?
make-coverage)

View File

@ -78,6 +78,19 @@
(test (render-reduction-relation red2)
"red2.png")
(let ()
(define-judgment-form lang
mode : I O
[(id e e)])
(test (render-reduction-relation
(reduction-relation
lang
(--> e_1
q
(where (name q e_2) e_1)
(judgment-holds (id e_2 (name r e_3))))))
"red-with-where-name.png"))
(define-metafunction lang
[(S x v e) e])
@ -257,5 +270,90 @@
(r x)])
(test (render-relation r) "relation-with-name.png"))
;; judgment form
(let ()
(define-language nats
(n z (s n)))
(define-judgment-form nats
mode : I I O
[(sum z n n)]
[(sum (s n_1) n_2 (s n_3))
(sum n_1 n_2 n_3)])
(test (render-judgment-form sum) "judgment-form-not-rewritten.png")
(test (with-compound-rewriter
'sum
(λ (lws) (list "" (list-ref lws 2) " + " (list-ref lws 3) " = " (list-ref lws 4)))
(render-judgment-form sum))
"judgment-form-rewritten.png")
(define-judgment-form nats
mode : I O
[(mfw n_1 n_2)
(where n_2 (f n_1))])
(define-metafunction nats
[(f n) n])
(test (render-judgment-form mfw) "judgment-form-metafunction-where.png")
(define-judgment-form nats
mode : I O
[(nps (name a (s n_1)) n_2)
(nps z (name n_1 (s (s n_1))))
(where (name b n_2) z)])
(test (render-judgment-form nps) "judgment-form-name-patterns.png"))
(let ()
(define-language STLC
(e (λ (x : τ) e)
(e e)
x)
(x variable-not-otherwise-mentioned)
((τ σ) b
(τ τ))
(Γ ([x τ] ...)))
(define-judgment-form STLC
mode : I I O
typeof Γ × e × τ
[(typeof Γ (e_1 e_2) τ)
(typeof Γ e_1 (τ_2 τ))
(typeof Γ e_2 τ_2)]
[(typeof Γ (λ (x : τ) e) (τ σ))
(typeof (extend Γ x τ) e σ)]
[(typeof Γ x τ)
(where τ (lookup Γ x))])
(define-metafunction STLC
extend : Γ x τ -> Γ
[(extend ([x_1 τ_1] ...) x_0 τ_0)
([x_0 τ_0] [x_1 τ_1] ...)])
(define-metafunction STLC
lookup : Γ x -> τ
[(lookup ([x_0 τ_0] ... [x_i τ_i] [x_i+1 τ_i+1] ...)) τ_i])
(define (rewrite-typeof lws)
(list "" (list-ref lws 2) "" (list-ref lws 3) " : " (list-ref lws 4)))
(define (rewrite-extend lws)
(list "" (list-ref lws 2) ", " (list-ref lws 3) ":" (list-ref lws 4)))
(define (rewrite-lookup lws)
(list "" (list-ref lws 2) "(" (list-ref lws 3) ")"))
(test (with-compound-rewriter
'typeof rewrite-typeof
(with-compound-rewriter
'extend rewrite-extend
(with-compound-rewriter
'lookup rewrite-lookup
(render-judgment-form typeof))))
"stlc.png"))
(printf "bitmap-test.rkt: ")
(done)
(done)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,12 @@
(#rx"input q at position 1"
([judgment ctc-fail])
(judgment-holds (judgment q s)))
(#rx"output q at position 2"
([judgment ctc-fail])
(judgment-holds (judgment a s)))
(#rx"input q at position 1"
([judgment ctc-fail])
(judgment-holds (judgment b s)))
(#rx"output q at position 2"
([judgment ctc-fail])
(judgment-holds (judgment c s)))

View File

@ -0,0 +1,9 @@
("judgment form q applied before its definition"
([use q]) ([def q])
(let ()
(judgment-holds (use 1))
(define-language L)
(define-judgment-form L
mode : I
[(def 1)])
#f))

View File

@ -0,0 +1,42 @@
(#rx"incompatible ellipsis match counts"
([body (((x y) ...) ...)])
([xlhs (x ...)] [ylhs ((y ...) ...)])
(term-let ([xlhs '(a b c)]
[ylhs '((1 2) (4 5 6) (7 8 9))])
(term body)))
(#rx"incompatible ellipsis match counts"
([body ((((f x) y) ...) ...)])
([fn f] [xlhs (x ...)] [ylhs ((y ...) ...)])
(term-let-fn ([fn car])
(term-let ([xlhs '(a b c)]
[ylhs '((1 2) (4 5 6) (7 8 9))])
(term body))))
(#rx"incompatible ellipsis match counts"
([body (f ((x y) ...))])
([fn f] [xlhs (x ...)] [ylhs (y ...)])
(term-let-fn ([fn car])
(term-let ([xlhs '(a b)]
[ylhs '(c d e)])
(term body))))
(#rx"incompatible ellipsis match counts"
([app (f (x y))])
([fn f] [xlhs (x ...)] [ylhs (y ...)] [ellipsis ...])
(term-let-fn ([fn car])
(term-let ([xlhs '(a b)]
[ylhs '(c d e)])
(term (app ellipsis)))))
(#rx"incompatible ellipsis match counts"
([plug (in-hole hole (x y))])
([xlhs (x ...)] [ylhs (y ...)] [ellipsis ...])
(term-let-fn ([fn car])
(term-let ([xlhs '(a b)]
[ylhs '(c d e)])
(term (plug ellipsis)))))
(#rx"term .* does not match pattern"
([rhs 'a]) ([ellipsis ...])
(term-let ([(x ellipsis) rhs]) 3))

View File

@ -1,6 +1,6 @@
;; require this file to run all of the test suites for redex.
#lang scheme/base
#lang racket/base
(require scheme/runtime-path
scheme/cmdline
scheme/match

View File

@ -0,0 +1,103 @@
(#rx"expected a mode"
([bad-def (define-judgment-form syn-err-lang)])
bad-def)
(#rx"expected a mode"
([mode-kw mode])
(define-judgment-form syn-err-lang mode-kw))
(#rx"expected a clause"
([junk 1])
(define-judgment-form syn-err-lang
mode : junk
[(q 1)]))
(#rx"expected at least one clause"
([bad-def (define-judgment-form syn-err-lang mode :)])
bad-def)
(#rx"expected a pattern to follow"
([cross ×])
(define-judgment-form syn-err-lang
mode : I
J number cross))
(#rx"use the same name"
([name1 J] [name2 K])
(define-judgment-form syn-err-lang
mode : I
name1 number
[(name2 number)]))
(#rx"malformed premise"
([bad-prem (q)])
(let ()
(define-judgment-form syn-err-lang
mode : I
[(J number)
bad-prem])
(void)))
(#rx"different numbers of positions"
([bad-def (define-judgment-form syn-err-lang
mode : I
J number × number
[(J number)])])
bad-def)
(#rx"unbound pattern variable"
([unbound number_2])
(let ()
(define-judgment-form syn-err-lang
mode : I O
[(J number_1 unbound)
(J number_1 number_1)])
(void)))
(#rx"unbound pattern variable"
([unbound number_2])
(let ()
(define-judgment-form syn-err-lang
mode : I O
[(J number_1 number_2)
(J unbound number_1)])
(void)))
(#rx"unbound pattern variable"
([unbound number_3])
(let ()
(define-judgment-form syn-err-lang
mode : I O
[(J number_1 number_2)
(where number_2 unbound)])
(void)))
(#rx"unbound pattern variable"
([unbound q])
(let ()
(define-judgment-form syn-err-lang
mode : I O
[(J number_1 number_2)
(where number_2 unbound)
(where (name q number) number_1)])
(void)))
(#rx"arity"
([bad-conc (J)])
(let ()
(define-judgment-form syn-err-lang
mode : I
[bad-conc])
(void)))
(#rx"arity"
([bad-prem (J)]) ([name J])
(let ()
(define-judgment-form syn-err-lang
mode : I
[(name number)
bad-prem])
(void)))
(#rx"unquote unsupported"
([unq ,(+ 1)])
(let ()
(define-judgment-form syn-err-lang
mode : I
[(uses-unquote n)
(where n unq)])
(void)))
(#rx"unquote unsupported"
([unq ,'z])
(let ()
(define-judgment-form syn-err-lang
mode : I O
[(uses-unquote n unq)])
(void)))

View File

@ -0,0 +1,3 @@
(#rx"expected a judgment form name"
([not-judgment-form junk])
(judgment-holds (not-judgment-form z (s z))))

View File

@ -0,0 +1,25 @@
(#rx"define-language:.*unquote disallowed"
([illegal-unquote ,3])
(let ()
(define-language L
(n illegal-unquote))
(void)))
; error message shows correct form name
(#rx"define-extended-language:.*underscore"
([bad-underscore y_1])
(let ()
(define-language L)
(define-extended-language M L
(z () (1 bad-underscore)))
(void)))
(#rx"expected an identifier" ([not-id (L)]) (define-language not-id))
(#rx"expected at least one production" ([separator ::=]) (define-language L (x separator)))
(#rx"expected at least one production" ([nt x]) (define-language L (nt)))
(#rx"expected at least one production" ([nt-pos (x)]) (define-language L (nt-pos)))
(#rx"expected preceding non-terminal names" ([separator ::=]) (define-language L (separator a b)))
(#rx"expected non-terminal name" ([not-nt (y)]) (define-language L (x not-nt ::= z)))
(#rx"expected production" ([not-prod ::=]) (define-language L (x ::= y not-prod z)))
(#rx"expected non-terminal definition" ([not-def q]) (define-language L not-def))
(#rx"expected non-terminal definition" ([not-def ()]) (define-language L not-def))

View File

@ -0,0 +1,11 @@
(#rx"expected a pattern and a right-hand side"
([clause [(f x)]])
(define-metafunction syn-err-lang
clause))
(#rx"expected an identifier"
([not-id (junk)])
(define-metafunction not-id also-junk))
(#rx"expected an identifier"
([not-id junk])
(define-metafunction not-id also-junk))

View File

@ -0,0 +1,3 @@
(#rx"redex-let: duplicate pattern variable"
([dup number])
(redex-let syn-err-lang ([(dup) 1] [dup 1]) (term dup)))

View File

@ -0,0 +1,126 @@
(#rx"no rules"
([unused ==>])
(reduction-relation
syn-err-lang
(~~> (number_1 number_2)
,(* (term number_1) (term number_2)))
with
[(--> (M a) (M b)) (~~> a b)]
[(~~> (M a) (M b)) (unused a b)]))
(#rx"no rules use -->"
([bad-def (reduction-relation syn-err-lang)])
bad-def)
(#rx"~~> relation is not defined"
([undef ~~>])
(reduction-relation
syn-err-lang
(undef (number_1 number_2)
,(* (term number_1) (term number_2)))))
(#rx"same name on multiple rules"
([name1 mult] [name2 mult])
(reduction-relation
syn-err-lang
(--> (number_1 number_2)
,(* (term number_1) (term number_2))
name2)
(--> (number_1 number_2)
,(* (term number_1) (term number_2))
name1)))
(#rx"different depths"
([binder2 number_1] [binder1 number_1]) ([ellipsis ...])
(reduction-relation
syn-err-lang
(--> binder1
()
(where (binder2 ellipsis) '()))))
(#rx"different depths"
([binder1 x] [binder2 x]) ([ellipsis ...])
(redex-match
syn-err-lang
((name binder1 any) (name binder2 any_2) ellipsis)))
(#rx"different depths"
([binder1 x] [binder2 x]) ([ellipsis ...])
(let ()
(define-language bad-lang5
(e ((name binder1 any) (name binder2 any_2) ellipsis)))
(void)))
(#rx"==> relation is not defined"
([undef ==>])
(reduction-relation
syn-err-lang
(--> 1 2)
(undef 3 4)))
(#rx"~> relation is not defined"
([undef ~>])
(reduction-relation
syn-err-lang
(--> 1 2)
(==> 3 4)
with
[(undef a b) (==> a b)]))
(#rx"expected identifier"
([not-id (+ 3 b)])
(reduction-relation
syn-err-lang
(==> 1 2)
with
[(--> a b)
(==> a not-id)]))
(#rx"expected identifier"
([not-id (+ 3 a)])
(reduction-relation
syn-err-lang
(==> 1 2)
with
[(--> a b)
(==> not-id b)]))
(#rx"name expected to have arguments"
([name-kw name])
(let () (define-language bad-lang1 (e name-kw)) (void)))
(#rx"name expected to have 2 arguments"
([bad-pat (name x)])
(let () (define-language bad-lang2 (e bad-pat)) (void)))
(#rx"cannot use _" ([bad-underscore x_y]) (define-language bad-lang3 (bad-underscore x)))
(#rx"at least one production" ([nt b]) (define-language bad-lang4 (a 1 2) (nt)))
(#rx"at least one production"
([nt a])
(let ()
(define-language good-lang (nt 1 2))
(define-extended-language bad-lang5 good-lang (nt) (b 2))
(void)))
(#rx"same non-terminal"
([nt2 x] [nt1 x])
(define-language bad-lang5 (nt1 1) (nt2 2)))
(#rx"same non-terminal"
([nt2 x] [nt1 x])
(define-language bad-lang6 ((nt1 nt2) 1)))
(#rx"same non-terminal"
([nt2 x] [nt1 x])
(let ()
(define-language good-lang)
(define-extended-language bad-lang7 good-lang ((nt1 nt2) 1))))
(#rx"before underscore"
([bad-underscore m_1])
(redex-match syn-err-lang bad-underscore))
(#rx"expected an identifier"
([not-id 2])
(redex-match syn-err-lang (variable-except a not-id c)))
(#rx"expected an identifier"
([not-id 7])
(redex-match syn-err-lang (variable-prefix not-id)))
(#rx"expected an identifier"
([not-id 7])
(redex-match syn-err-lang (cross not-id)))

View File

@ -0,0 +1,15 @@
(#rx"expected the name of the relation"
([bad-def (define-relation syn-err-lang R)])
bad-def)
(#rx"expected a sequence of patterns separated by"
([subset ])
(define-relation syn-err-lang R subset))
(#rx"expected clause definitions"
([bad-def (define-relation syn-err-lang foo c)])
bad-def)
(#rx"expected a pattern"
([cross ×])
(define-relation syn-err-lang foo c cross))

View File

@ -0,0 +1,8 @@
(#rx"missing ellipses"
([id-no-ellipsis x]) ([ellipsis ...])
(term-let ([(id-no-ellipsis ellipsis) '(a b c)]) (term id-no-ellipsis)))
(#rx"too few ellipses"
([bound x]) ([bind x])
(... (term-let ([((bind ...) ...) '()])
(term (bound ...)))))

View File

@ -103,81 +103,9 @@
(define-namespace-anchor here)
(define ns (namespace-anchor->namespace here))
(let ([src 'term-template])
(test
(parameterize ([current-namespace ns])
(runtime-error-source
'(term-let ([(x ...) '(a b c)]
[((y ...) ...) '((1 2) (4 5 6) (7 8 9))])
(term (((x y) ...) ...)))
src))
src))
(parameterize ([current-namespace ns])
(exec-runtime-error-tests "run-err-tests/term.rktd"))
(let ([src 'term-template-metafunc])
(test
(parameterize ([current-namespace ns])
(runtime-error-source
'(term-let-fn ((f car))
(term-let ([(x ...) '(a b c)]
[((y ...) ...) '((1 2) (4 5 6) (7 8 9))])
(term ((((f x) y) ...) ...))))
src))
src))
(let ([src 'ellipsis-args])
(test
(parameterize ([current-namespace ns])
(runtime-error-source
'(term-let-fn ((f car))
(term-let ([(x ...) '(a b)]
[(y ...) '(c d e)])
(term (f ((x y) ...)))))
src))
src))
(let ([src 'ellipsis-args/map])
(test
(parameterize ([current-namespace ns])
(runtime-error-source
'(term-let-fn ((f car))
(term-let ([(x ...) '(a b)]
[(y ...) '(c d e)])
(term ((f (x y)) ...))))
src))
src))
(let ([src 'ellipsis-args/in-hole])
(test
(parameterize ([current-namespace ns])
(runtime-error-source
'(term-let ([(x ...) '(a b)]
[(y ...) '(c d e)])
(term ((in-hole hole (x y)) ...)))
src))
src))
(let ([src 'term-let-rhs])
(test
(parameterize ([current-namespace ns])
(runtime-error-source
'(term-let ([(x ...) 'a])
3)
src))
src))
(test-syn-err (term-let ([(x ...) '(a b c)]) (term x))
#rx"missing ellipses")
(test (parameterize ([current-namespace syn-err-test-namespace])
(with-handlers ([exn:fail:syntax?
(λ (exn)
(match (exn:fail:syntax-exprs exn)
[(list e) (syntax->datum e)]
[_ (gensym 'wrong)]))])
(expand
'(term-let ([((label ...) ...) '()])
(term (label ...))))
(gensym 'wrong)))
'label)
(exec-syntax-error-tests "syn-err-tests/term.rktd")
(print-tests-passed 'term-test.rkt))

View File

@ -3,18 +3,29 @@
(require "../private/matcher.rkt"
(for-syntax syntax/parse)
errortrace/errortrace-lib
errortrace/errortrace-key)
errortrace/errortrace-key
racket/runtime-path)
(provide test test-syn-err tests reset-count
syn-err-test-namespace
print-tests-passed
equal/bindings?
test-contract-violation
runtime-error-source)
test-runtime-err
exec-syntax-error-tests
exec-runtime-error-tests)
(define-runtime-path this-dir ".")
(define syn-err-test-namespace (make-base-namespace))
(parameterize ([current-namespace syn-err-test-namespace])
(eval '(require redex/reduction-semantics)))
(define (read-syntax-test path)
(call-with-input-file path
(λ (port)
(port-count-lines! port)
(read-syntax path port))))
(define-syntax (test stx)
(syntax-case stx ()
[(_ expected got)
@ -24,40 +35,90 @@
"<unknown file>")])
(syntax/loc stx (test/proc (λ () expected) got line fn)))]))
(define (runtime-error-source sexp src)
(let/ec return
(cadar
(continuation-mark-set->list
(exn-continuation-marks
(with-handlers ((exn:fail? values))
(parameterize ([current-compile (make-errortrace-compile-handler)])
(eval (read-syntax src (open-input-string (format "~s" sexp)))))
(return 'no-source)))
errortrace-key))))
(define (syntax-error-test-setup thunk)
(parameterize ([current-namespace syn-err-test-namespace])
(with-handlers ([exn:fail:syntax?
(λ (exn)
(values (exn-message exn)
(map source-location (exn:fail:syntax-exprs exn))))])
(thunk))))
(define (runtime-error-test-setup thunk)
(parameterize ([current-compile (make-errortrace-compile-handler)])
(with-handlers ([exn:fail?
(λ (exn)
(values (exn-message exn)
(let ([marks (continuation-mark-set->list
(exn-continuation-marks exn)
errortrace-key)])
(if (null? marks) '() (list (cdar marks))))))])
(thunk))))
(define ((exec-error-tests setup exec) path)
(for ([test (read-tests (build-path this-dir path))])
(exec-error-test test exec setup)))
(define exec-syntax-error-tests
(exec-error-tests syntax-error-test-setup expand))
(define exec-runtime-error-tests
(exec-error-tests runtime-error-test-setup eval))
(define (exec-error-test spec exec setup)
(define-values (file line expected-message expected-sources test)
(make-error-test spec))
(let-values ([(actual-message actual-sources)
(setup (λ () (begin (exec test) (values "" '()))))])
(test/proc (λ () actual-message) expected-message line file)
(test/proc (λ () actual-sources) expected-sources line file)))
(define (make-error-test spec)
(syntax-case spec ()
[(message named-pieces body)
(make-error-test (syntax/loc spec (message named-pieces () body)))]
[(message ([loc-name loc-piece] ...) ([non-loc-name non-loc-piece] ...) body)
(values (syntax-source spec)
(syntax-line spec)
(syntax-e #'message)
(map source-location (syntax->list #'(loc-piece ...)))
#'(let-syntax ([subst
(λ (stx)
(syntax-case stx ()
[(_ loc-name ... non-loc-name ...)
#'body]))])
(subst loc-piece ... non-loc-piece ...)))]))
(define (source-location stx)
(list (syntax-source stx)
(syntax-line stx)
(syntax-column stx)
(syntax-position stx)
(syntax-span stx)))
(define (read-tests path)
(call-with-input-file path
(λ (port)
(port-count-lines! port)
(let loop ()
(define test (read-syntax path port))
(if (eof-object? test)
'()
(cons test (loop)))))))
(define-syntax (test-syn-err stx)
(syntax-case stx ()
[(_ exp msg-re num-locs)
(with-syntax ([expected-locs (syntax/loc stx (build-list num-locs (λ (_) src)))])
(syntax
(let* ([src (gensym)]
[p (read-syntax src (open-input-string (format "~s" 'exp)))])
(let-values ([(locs msg)
(with-handlers ([exn:fail:syntax?
(λ (exn)
(values
(if (exn:srclocs? exn)
(map srcloc-source
((exn:srclocs-accessor exn) exn))
null)
(exn-message exn)))])
(parameterize ([current-namespace syn-err-test-namespace])
(expand p))
(values (void) null))])
(test msg msg-re)
(test locs expected-locs)))))]
[(tse exp msg-re)
(syntax/loc stx (tse exp msg-re 1))]))
#'(void))
(define-syntax (test-runtime-err stx)
#'(void)
#;
#`(parameterize ([current-compile (make-errortrace-compile-handler)])
#,(test-error-location
stx
eval
#'[exn:fail?
(λ (exn)
(values (exn-message exn)
(let ([marks (continuation-mark-set->list
(exn-continuation-marks exn)
errortrace-key)])
(if (null? marks) #f (list (cdar marks))))))])))
(define tests 0)
(define failures 0)

View File

@ -1,8 +1,9 @@
#lang racket/gui
#lang racket
(require "../reduction-semantics.rkt"
"test-util.rkt"
(only-in "../private/matcher.rkt" make-bindings make-bind)
racket/match
racket/trace
"../private/struct.rkt")
(reset-count)
@ -10,7 +11,7 @@
(define-namespace-anchor this-namespace)
(parameterize ([current-namespace syn-err-test-namespace])
(eval (quote-syntax
(define-language grammar
(define-language syn-err-lang
(M (M M)
number)
(E hole
@ -101,22 +102,7 @@
12)))
'("...."))
(test-syn-err
(let ()
(define-language L
(n ,3))
(void))
#rx"define-language:.*unquote disallowed" 1)
(let ()
; error message shows correct form name
(test-syn-err
(let ()
(define-language L)
(define-extended-language M L
(z () (1 y_1)))
(void))
#rx"define-extended-language:.*underscore")
; non-terminals added by extension can have underscores
(define-extended-language L base-grammar
(z () (1 z_1 z_1)))
@ -411,15 +397,7 @@
(::= () (number ::=)))
(test (and (redex-match L ::= '(1 ())) #t) #t)))
(test-syn-err (define-language (L)) #rx"expected an identifier")
(test-syn-err (define-language L (x ::=)) #rx"expected at least one production")
(test-syn-err (define-language L (x)) #rx"expected at least one production")
(test-syn-err (define-language L ((x))) #rx"expected at least one production")
(test-syn-err (define-language L (::= a b)) #rx"expected preceding non-terminal names")
(test-syn-err (define-language L (x (y) ::= z)) #rx"expected non-terminal name")
(test-syn-err (define-language L (x ::= y ::= z)) #rx"expected production")
(test-syn-err (define-language L q) #rx"expected non-terminal definition")
(test-syn-err (define-language L ()) #rx"expected non-terminal definition")
(exec-syntax-error-tests "syn-err-tests/language-definition.rktd")
;
;
; ;;; ;
@ -1001,11 +979,10 @@
;; errors for not-yet-defined metafunctions
(test (parameterize ([current-namespace (make-empty-namespace)])
(namespace-attach-module (namespace-anchor->namespace this-namespace) 'racket/gui)
(namespace-attach-module (namespace-anchor->namespace this-namespace) 'redex/reduction-semantics)
(namespace-require 'racket)
(eval '(module m racket
(require redex)
(require redex/reduction-semantics)
(term (q))
(define-language L)
(define-metafunction L [(q) ()])))
@ -1021,17 +998,7 @@
#f))
"metafunction q applied before its definition")
(let ()
(test-syn-err
(define-metafunction grammar
[(f x)])
#rx"expected a pattern and a right-hand side"))
(test-syn-err (define-metafunction (junk) also-junk)
#rx"expected an identifier")
(test-syn-err (define-metafunction junk also-junk)
#rx"expected an identifier")
(exec-syntax-error-tests "syn-err-tests/metafunction-definition.rktd")
;
;
;
@ -1123,21 +1090,7 @@
'failed)
'passed))
(test-syn-err
(define-relation grammar R)
#rx"expected the name of the relation")
(test-syn-err
(define-relation grammar R )
#rx"expected a sequence of patterns separated by")
(test-syn-err
(define-relation grammar foo c)
#rx"expected clause definitions")
(test-syn-err
(define-relation grammar foo c ×)
#rx"expected a pattern")
(exec-syntax-error-tests "syn-err-tests/relation-definition.rktd")
; ;; ; ;; ;
; ; ; ; ;
@ -1547,112 +1500,7 @@
(test (apply-reduction-relation R (term (0 2 3 4 5))) '())
(test (apply-reduction-relation R (term (1 2 3 4 5 () (6) (7 8) (9 10 11)))) '(yes)))
(test-syn-err (reduction-relation
grammar
(~~> (number_1 number_2)
,(* (term number_1) (term number_2)))
with
[(--> (M a) (M b)) (~~> a b)]
[(~~> (M a) (M b)) (==> a b)])
#rx"no rules")
(test-syn-err (reduction-relation
grammar
(~~> (number_1 number_2)
,(* (term number_1) (term number_2)))
with
[(--> (M a) (M b)) (~~> a b)]
[(~~> (M a) (M b)) (==> a b)])
#rx"no rules")
(test-syn-err (reduction-relation grammar)
#rx"no rules use -->")
(test-syn-err (reduction-relation
grammar
(~~> (number_1 number_2)
,(* (term number_1) (term number_2))))
#rx"~~> relation is not defined")
(test-syn-err (reduction-relation
grammar
(--> (number_1 number_2)
,(* (term number_1) (term number_2))
mult)
(--> (number_1 number_2)
,(* (term number_1) (term number_2))
mult))
#rx"same name on multiple rules"
2)
(test-syn-err (reduction-relation
grammar
(--> number_1
()
(where (number_1 ...) '())))
#rx"different depths"
2)
(test-syn-err (redex-match
grammar
((name x any) (name x any_2) ...))
#rx"different depths"
2)
(test-syn-err (define-language bad-lang5
(e ((name x any) (name x any_2) ...)))
#rx"different depths"
2)
(test-syn-err (reduction-relation
grammar
(--> 1 2)
(==> 3 4))
#rx"==> relation is not defined")
(test-syn-err (reduction-relation
grammar
(--> 1 2)
(==> 3 4)
with
[(~> a b) (==> a b)])
#rx"~> relation is not defined")
(test-syn-err (reduction-relation
grammar
(==> 1 2)
with
[(--> a b)
(==> a (+ 3 b))])
#rx"expected identifier")
(test-syn-err (reduction-relation
grammar
(==> 1 2)
with
[(--> a b)
(==> (+ 3 a) b)])
#rx"expected identifier")
(test-syn-err (define-language bad-lang1 (e name)) #rx"name")
(test-syn-err (define-language bad-lang2 (name x)) #rx"name")
(test-syn-err (define-language bad-lang3 (x_y x)) #rx"cannot use _")
(test-syn-err (define-language bad-lang4 (a 1 2) (b)) #rx"at least one production")
(test-syn-err (let ()
(define-language good-lang (a 1 2))
(define-extended-language bad-lang5 good-lang (a) (b 2)))
#rx"at least one production")
(test-syn-err (define-language bad-lang5 (x 1) (x 2)) #rx"same non-terminal" 2)
(test-syn-err (define-language bad-lang6 ((x x) 1)) #rx"same non-terminal" 2)
(test-syn-err (let ()
(define-language good-lang)
(define-extended-language bad-lang7 good-lang ((x x) 1)))
#rx"same non-terminal" 2)
(test-syn-err (redex-match grammar m_1) #rx"before underscore")
(test-syn-err (redex-match grammar (variable-except a 2 c)) #rx"expected an identifier")
(test-syn-err (redex-match grammar (variable-prefix 7)) #rx"expected an identifier")
(test-syn-err (redex-match grammar (cross 7)) #rx"expected an identifier")
(exec-syntax-error-tests "syn-err-tests/reduction-relation-definition.rktd")
;; expect union with duplicate names to fail
(test (with-handlers ((exn? (λ (x) 'passed)))
@ -1957,7 +1805,19 @@
(--> q r y)
(--> r p x))))
'(a b c z y x))
(let ()
(define-judgment-form empty-language
mode : I O
[(R a a)]
[(R a b)])
(test (apply-reduction-relation
(reduction-relation
empty-language
(--> a any
(judgment-holds (R a any))))
'a)
'(b a)))
;
;
@ -2003,13 +1863,186 @@
(test (redex-let L ([(n_1 n_1) '(1 1)]) (term n_1))
1)
(test-syn-err
(redex-let grammar ([(number) 1] [number 1]) (term number))
#rx"redex-let: duplicate pattern variable" 1)
(test
(redex-let* L ([(n_1) '(1)] [n_1 1]) (term n_1))
1))
1)
(exec-syntax-error-tests "syn-err-tests/redex-let.rktd"))
;
;
;
; ; ;; ; ; ; ;;
; ; ; ; ; ; ; ;
; ; ; ; ; ;
; ;;;; ;;; ;;; ; ;;;; ;;; ; ; ; ;;;; ;;;; ;;;;; ;;; ;;;; ;;; ;;; ;;; ; ;; ;;;;;
; ; ; ; ; ; ; ; ; ; ; ;;;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;;; ; ; ; ;; ; ; ; ;
; ; ; ;;;;; ; ; ; ; ;;;;; ; ; ; ; ; ; ; ; ; ; ;;;;; ; ; ; ; ; ; ; ; ; ; ;
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
; ;;;; ;;; ; ; ; ; ;;; ; ;; ; ;;;; ;;;; ; ; ; ;;; ; ; ;; ; ;;; ; ; ; ;
; ; ;
; ; ; ;
; ;; ;;;
(exec-syntax-error-tests "syn-err-tests/judgment-form-definition.rktd")
(exec-syntax-error-tests "syn-err-tests/judgment-holds.rktd")
(let ()
(define-language nats
(n z (s n)))
(define-judgment-form nats
mode : I I O
sumi n × n × n
[(sumi z n n)]
[(sumi (s n_1) n_2 (s n_3))
(sumi n_1 n_2 n_3)])
(test (judgment-holds (sumi z (s z) n) n)
(list (term (s z))))
(test (judgment-holds (sumi (s (s z)) (s z) n) n)
(list (term (s (s (s z))))))
(test (judgment-holds (sumi ,'z (s z) (s z))) #t)
(define-judgment-form nats
mode : O O I
sumo n × n × n
[(sumo z n n)]
[(sumo (s n_1) n_2 (s n_3))
(sumo n_1 n_2 n_3)])
(test (judgment-holds (sumo n_1 n_2 z) ([,'n_1 n_1] [,'n_2 n_2]))
(list (term ([n_1 z] [n_2 z]))))
(test (judgment-holds (sumo n_1 n_2 (s z)) ([,'n_1 n_1] [,'n_2 n_2]))
(list (term ([n_1 (s z)] [n_2 z]))
(term ([n_1 z] [n_2 (s z)]))))
(define-judgment-form nats
mode : O O I
[(sumo-ls (s n_1) n_2 n_3)
(sumo (s n_1) n_2 n_3)])
(test (judgment-holds (sumo-ls n_1 n_2 (s z)) ([,'n_1 n_1] [,'n_2 n_2]))
(list (term ([n_1 (s z)] [n_2 z]))))
(test (judgment-holds (sumo-ls (s n_1) n_2 (s z))) #t)
(test (judgment-holds (sumo-ls z n_2 (s z))) #f)
(test (judgment-holds (sumo-ls z n_2 (s z)) whatever) (list))
(define-judgment-form nats
mode : O O I
[(sumo-lz z n_2 n_3)
(sumo z n_2 n_3)])
(test (judgment-holds (sumo-lz n_1 n_2 (s z)) ([,'n_1 n_1] [,'n_2 n_2]))
(list (term ([n_1 z] [n_2 (s z)]))))
(define-judgment-form nats
mode : O I
[(member n_i (n_0 ... n_i n_i+1 ...))])
(test (judgment-holds (member n (z (s z) z (s (s z)))) n)
(list (term (s (s z))) (term z) (term (s z)) (term z)))
(define-judgment-form nats
mode : I
[(has-zero (n ...))
(member z (n ...))])
(test (judgment-holds (has-zero ((s z) z (s (s z))))) #t)
(define-judgment-form nats
mode : I
[(le2 n)
(le (add2 n) (s (s (s (s z)))))])
(define-judgment-form nats
mode : I I
[(le z n)]
[(le (s n_1) (s n_2))
(le n_1 n_2)])
(define-metafunction nats
add2 : n -> n
[(add2 n) (s (s n))])
(test (judgment-holds (le2 (s (s z)))) #t)
(test (judgment-holds (le2 (s (s (s z))))) #f)
(define-judgment-form nats
mode : I O
uses-add2 n × n
[(uses-add2 n_1 n_2)
(sumo n_2 n_3 n_1)
(where n_2 (add2 n_3))])
(test (judgment-holds (uses-add2 (s (s (s (s z)))) n) n)
(list (term (s (s (s z))))))
(let-syntax ([test-trace
(syntax-rules ()
[(_ expr trace-spec expected)
(test (let ([trace (open-output-string)])
(parameterize ([current-output-port trace]
[current-traced-metafunctions trace-spec])
expr)
(get-output-string trace))
expected)])])
(test-trace (judgment-holds (sumi (s z) (s (s z)) n) n)
'(sumi)
#reader scribble/reader
@string-append{>(sumi '(s z) '(s (s z)) '_)
> (sumi 'z '(s (s z)) '_)
< '((sumi z (s (s z)) (s (s z))))
<'((sumi (s z) (s (s z)) (s (s (s z)))))
})
(test-trace (judgment-holds (sumo n_1 n_2 (s z)))
'all
#reader scribble/reader
@string-append{>(sumo '_ '_ '(s z))
> (sumo '_ '_ 'z)
< '((sumo z z z))
<'((sumo (s z) z (s z)) (sumo z (s z) (s z)))
})
(test-trace (letrec ([f (match-lambda
['z #t]
[`(s ,n) (f n)])])
(define-judgment-form nats
mode : I I
[(ext-trace z (side-condition n (f (term n))))]
[(ext-trace (s n_1) n_2)
(ext-trace n_1 n_2)])
(trace f)
(judgment-holds (ext-trace (s z) (s z))))
'all
#reader scribble/reader
@string-append{>(ext-trace '(s z) '(s z))
> (ext-trace 'z '(s z))
> >(f '(s z))
> >(f 'z)
< <#t
< '((ext-trace z (s z)))
<'((ext-trace (s z) (s z)))
})))
(parameterize ([current-namespace (make-base-namespace)])
(eval '(require redex/reduction-semantics))
(eval '(define-language L
(s a b c)))
(eval '(define-judgment-form L
mode : I O
ctc-fail s × s
[(ctc-fail a q)]
[(ctc-fail b s)
(ctc-fail q s)]
[(ctc-fail c s)
(ctc-fail a s)]))
(exec-runtime-error-tests "run-err-tests/judgment-form-contracts.rktd")
(exec-runtime-error-tests "run-err-tests/judgment-form-undefined.rktd"))
(parameterize ([current-namespace (make-base-namespace)])
(eval '(require redex/reduction-semantics))
(exec-runtime-error-tests "run-err-tests/judgment-form-undefined.rktd"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; examples from doc.txt