Added check-metafunction and check-reduction-relation. Fixed bug in

generation of `any' pattern.

svn: r12974
This commit is contained in:
Casey Klein 2009-01-02 20:18:53 +00:00
parent b58664d1f8
commit f402605fed
6 changed files with 392 additions and 155 deletions

View File

@ -11,8 +11,10 @@
(require (for-syntax (lib "name.ss" "syntax")
"rewrite-side-conditions.ss"
"term-fn.ss"
"underscore-allowed.ss"
(lib "boundmap.ss" "syntax")
scheme/base))
scheme/base
(prefix-in pattern- scheme/match)))
(define (language-nts lang)
(hash-map (compiled-lang-ht lang) (λ (x y) x)))
@ -440,16 +442,17 @@
(syntax->list (syntax (extras ...)))
lang-id))]
[((rhs-arrow rhs-from rhs-to) (lhs-arrow lhs-frm-id lhs-to-id))
(let ([lang-nts (language-id-nts lang-id orig-name)])
(let* ([lang-nts (language-id-nts lang-id orig-name)]
[rewrite-side-conds
(λ (pat) (rewrite-side-conditions/check-errs lang-nts orig-name #t pat))])
(let-values ([(names names/ellipses) (extract-names lang-nts orig-name #t (syntax rhs-from))])
(with-syntax ([(names ...) names]
[(names/ellipses ...) names/ellipses]
[side-conditions-rewritten (rewrite-side-conditions/check-errs
lang-nts
orig-name
#t
[side-conditions-rewritten (rewrite-side-conds
(rewrite-node-pat (syntax-e (syntax lhs-frm-id))
(syntax->datum (syntax rhs-from))))]
(syntax rhs-from)))]
[fresh-rhs-from (rewrite-side-conds
(freshen-names #'rhs-from #'lhs-frm-id lang-nts orig-name))]
[lang lang])
(map
(λ (child-proc)
@ -460,19 +463,51 @@
(λ (bindings rhs-binder)
(term-let ([lhs-to-id rhs-binder]
[names/ellipses (lookup-binding bindings 'names)] ...)
(term rhs-to)))
#,child-proc))
(term rhs-to)))
#,child-proc
`fresh-rhs-from))
(get-choices stx orig-name bm #'lang
(syntax lhs-arrow)
name-table lang-id
allow-zero-rules?)))))]))
allow-zero-rules?)))))]))
(define (rewrite-node-pat id term)
(let loop ([term term])
(cond
[(eq? id term) `(name ,id any)]
[(pair? term) (cons (loop (car term))
(loop (cdr term)))]
[else term])))
(let loop ([t term])
(syntax-case t (side-condition)
[(side-condition p c)
#`(side-condition #,(loop #'p) c)]
[(p ...)
(map loop (syntax->list #'(p ...)))]
[else
(if (and (identifier? t) (eq? id (syntax-e t)))
`(name ,id any)
t)])))
(define (freshen-names pat hole-id nts what)
(define (fresh x)
(gensym
(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 ([renames (make-bound-identifier-mapping)])
(for-each
(λ (x)
(unless (bound-identifier=? x hole-id)
(bound-identifier-mapping-put! renames x (fresh (syntax-e x)))))
bound)
(let recur ([p pat])
(syntax-case p (side-condition)
[(side-condition p c)
#`(side-condition
#,(recur #'p)
(term-let (#,@(bound-identifier-mapping-map renames (λ (x y) #`(#,x (term #,y)))))
c))]
[(p ...)
#`(#,@(map recur (syntax->list #'(p ...))))]
[else
(if (identifier? p)
(bound-identifier-mapping-get renames p (λ () p))
p)])))))
(define (do-leaf stx orig-name lang name-table from to extras lang-id)
(let ([lang-nts (language-id-nts lang-id orig-name)])
@ -484,7 +519,7 @@
orig-name
#t
from)]
[to to]
[to to #;#`,(begin (printf "~s\n" #,name) (term #,to))]
[name name]
[lang lang]
[(names ...) names]
@ -639,6 +674,18 @@
(cons (format " ~s" (syntax->datum (car stxs)))
(loop (cdr stxs)))])))))
(define (substitute from to pat)
(let recur ([p pat])
(syntax-case p (side-condition)
[(side-condition p c)
#`(side-condition #,(recur #'p) c)]
[(p ...)
#`(#,@(map recur (syntax->list #'(p ...))))]
[else
(if (and (identifier? p) (bound-identifier=? p from))
to
p)])))
(define (verify-name-ok orig-name the-name)
(unless (symbol? the-name)
(error orig-name "expected a single name, got ~s" the-name)))
@ -679,7 +726,12 @@
(apply append (map reduction-relation-lws lst))
`any)))
(define (do-node-match lhs-frm-id lhs-to-id pat rhs-proc child-make-proc)
(define (do-node-match lhs-frm-id lhs-to-id pat rhs-proc child-make-proc rhs-from)
(define (subst from to in)
(let recur ([p in])
(cond [(eq? from p) to]
[(pair? p) (map recur p)]
[else p])))
;; need call to make-rewrite-proc
;; also need a test case here to check duplication of names.
(make-rewrite-proc
@ -701,7 +753,8 @@
(λ (x) (f (rhs-proc (mtch-bindings (car mtchs)) x)))
acc)))]))
other-matches)))))
(rewrite-proc-name child-make-proc)))
(rewrite-proc-name child-make-proc)
(subst lhs-frm-id (rewrite-proc-lhs child-make-proc) rhs-from)))
(define (do-leaf-match name pat proc)
(make-rewrite-proc
@ -717,7 +770,8 @@
mtchs
other-matches)
other-matches)))))
name))
name
pat))
(define-syntax (test-match stx)
(syntax-case stx ()
@ -758,7 +812,7 @@
(symbol->string (bind-name y))))))
(define-values (struct:metafunc-proc make-metafunc-proc metafunc-proc? metafunc-proc-ref metafunc-proc-set!)
(make-struct-type 'metafunc-proc #f 9 0 #f null (current-inspector) 0))
(make-struct-type 'metafunc-proc #f 10 0 #f null (current-inspector) 0))
(define metafunc-proc-pict-info (make-struct-field-accessor metafunc-proc-ref 1))
(define metafunc-proc-lang (make-struct-field-accessor metafunc-proc-ref 2))
(define metafunc-proc-multi-arg? (make-struct-field-accessor metafunc-proc-ref 3))
@ -767,6 +821,7 @@
(define metafunc-proc-rhss (make-struct-field-accessor metafunc-proc-ref 6))
(define metafunc-proc-in-dom? (make-struct-field-accessor metafunc-proc-ref 7))
(define metafunc-proc-dom-pat (make-struct-field-accessor metafunc-proc-ref 8))
(define metafunc-proc-lhs-pats (make-struct-field-accessor metafunc-proc-ref 9))
(define-struct metafunction (proc))
(define-syntax (in-domain? stx)
@ -937,7 +992,8 @@
cps
rhss
(let ([name (lambda (x) (name-predicate x))]) name)
`dom-side-conditions-rewritten))
`dom-side-conditions-rewritten
`(side-conditions-rewritten ...)))
`dom-side-conditions-rewritten
`codom-side-conditions-rewritten
'name))
@ -1714,6 +1770,7 @@
metafunc-proc-rhss
metafunc-proc-in-dom?
metafunc-proc-dom-pat
metafunc-proc-lhs-pats
(struct-out binds))

View File

@ -1,9 +1,10 @@
(module rewrite-side-conditions mzscheme
(module rewrite-side-conditions scheme/base
(require (lib "list.ss")
"underscore-allowed.ss")
(require-for-template mzscheme
"term.ss"
"matcher.ss")
(require (for-template
mzscheme
"term.ss"
"matcher.ss"))
(provide rewrite-side-conditions/check-errs
extract-names
@ -85,7 +86,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)
(define (extract-names all-nts what bind-names? orig-stx [rhs-only? #t])
(let* ([dups
(let loop ([stx orig-stx]
[names null]
@ -115,8 +116,8 @@
[else
(if (or (null? (cdr pats))
(not (identifier? (cadr pats)))
(not (or (module-identifier=? (quote-syntax ...)
(cadr pats))
(not (or (free-identifier=? (quote-syntax ...)
(cadr pats))
(let ([inside (syntax-e (cadr pats))])
(regexp-match #rx"^\\.\\.\\._" (symbol->string inside))))))
(i-loop (cdr pats)
@ -125,7 +126,8 @@
(loop (car pats) names (+ depth 1))))]))]
[x
(and (identifier? (syntax x))
(binds-in-right-hand-side? all-nts bind-names? (syntax x)))
((if rhs-only? binds-in-right-hand-side? binds?)
all-nts bind-names? (syntax x)))
(cons (make-id/depth (syntax x) depth) names)]
[else names]))]
[no-dups (filter-duplicates what orig-stx dups)])
@ -141,14 +143,16 @@
[dots (quote-syntax ...)])
(syntax (rest dots)))])))
(define (binds-in-right-hand-side? nts bind-names? x)
(define (binds? nts bind-names? x)
(or (and bind-names? (memq (syntax-e x) nts))
(and bind-names? (memq (syntax-e x) underscore-allowed))
(let ([str (symbol->string (syntax-e x))])
(and (regexp-match #rx"_" str)
(not (regexp-match #rx"^\\.\\.\\._" str))
(not (regexp-match #rx"_!_" str))))))
(regexp-match #rx"_" (symbol->string (syntax-e x)))))
(define (binds-in-right-hand-side? nts bind-names? x)
(and (binds? nts bind-names? x)
(let ([str (symbol->string (syntax-e x))])
(and (not (regexp-match #rx"^\\.\\.\\._" str))
(not (regexp-match #rx"_!_" str))))))
(define (filter-duplicates what orig-stx dups)
(let loop ([dups dups])
@ -158,8 +162,8 @@
(cons
(car dups)
(filter (lambda (x)
(let ([same-id? (module-identifier=? (id/depth-id x)
(id/depth-id (car dups)))])
(let ([same-id? (free-identifier=? (id/depth-id x)
(id/depth-id (car dups)))])
(when same-id?
(unless (equal? (id/depth-depth x)
(id/depth-depth (car dups)))
@ -167,7 +171,7 @@
(make-exn:fail:syntax
(format "~a: found the same binder, ~s, at different depths, ~a and ~a"
what
(syntax-object->datum (id/depth-id x))
(syntax->datum (id/depth-id x))
(id/depth-depth x)
(id/depth-depth (car dups)))
(current-continuation-marks)

View File

@ -90,6 +90,21 @@
(test (pick-char 0 null (make-random 65)) #\a)
(test (random-string null null 1 0 (make-random 65)) "a"))
(let ()
(define-language L
(a 5 (x a) #:binds x a)
(b 4))
(test ((pick-nt 'dontcare) 'a L '(x) 1)
(nt-rhs (car (compiled-lang-lang L))))
(test ((pick-nt 'dontcare (make-random 1)) 'a L '(x) preferred-production-threshold)
(nt-rhs (car (compiled-lang-lang L))))
(let ([pref (car (nt-rhs (car (compiled-lang-lang L))))])
(test ((pick-nt (make-immutable-hash `((a ,pref))) (make-random 0))
'a L '(x) preferred-production-threshold)
(list pref)))
(test ((pick-nt 'dontcare) 'sexp sexp null preferred-production-threshold)
(nt-rhs (car (compiled-lang-lang sexp)))))
(define-syntax exn:fail-message
(syntax-rules ()
[(_ expr)
@ -101,7 +116,9 @@
(make-exn-not-raised))))]))
(define (patterns . selectors)
(map (λ (selector) (λ (name prods vars size) (list (selector prods))))
(map (λ (selector)
(λ (name lang vars size)
(list (selector (nt-rhs (nt-by-name lang name))))))
selectors))
(define (iterator name items)
@ -145,7 +162,7 @@
;; Generate (λ (x) x)
(test
(generate/decisions
(generate-term
lc e 1 0
(decisions #:var (list (λ _ 'x) (λ _'x))
#:nt (patterns third first first first)))
@ -153,14 +170,14 @@
;; Generate pattern that's not a non-terminal
(test
(generate/decisions
(generate-term
lc (x x x_1 x_1) 1 0
(decisions #:var (list (λ _ 'x) (λ _ 'y))))
'(x x y y))
; After choosing (e e), size decremented forces each e to x.
(test
(generate/decisions
(generate-term
lc e 1 0
(decisions #:nt (patterns first)
#:var (list (λ _ 'x) (λ _ 'y))))
@ -176,7 +193,7 @@
(let* ([x null]
[prepend! (λ (c l b a) (begin (set! x (cons (car b) x)) 'x))])
(test (begin
(generate/decisions lang a 5 0 (decisions #:var (list (λ _ 'x) prepend! prepend!)))
(generate-term lang a 5 0 (decisions #:var (list (λ _ 'x) prepend! prepend!)))
x)
'(x x))))
@ -187,7 +204,7 @@
(x (variable-except λ)))
(test
(exn:fail-message
(generate/decisions
(generate-term
postfix e 2 0
(decisions #:var (list (λ _ 'x) (λ _ 'y))
#:nt (patterns third second first first))))
@ -198,7 +215,7 @@
(define-language var
(e (variable-except x y)))
(test
(generate/decisions
(generate-term
var e 2 0
(decisions #:var (list (λ _ 'x) (λ _ 'y) (λ _ 'x) (λ _ 'z))))
'z))
@ -215,25 +232,25 @@
(n number)
(z 4))
(test
(generate/decisions
(generate-term
lang a 2 0
(decisions #:num (build-list 3 (λ (n) (λ (_) n)))
#:seq (list (λ (_) 2) (λ (_) 3) (λ (_) 1))))
`(0 1 2 "foo" "foo" "foo" "bar" #t))
(test (generate/decisions lang b 5 0 (decisions #:seq (list (λ (_) 0))))
(test (generate-term lang b 5 0 (decisions #:seq (list (λ (_) 0))))
null)
(test (generate/decisions lang c 5 0 (decisions #:seq (list (λ (_) 0))))
(test (generate-term lang c 5 0 (decisions #:seq (list (λ (_) 0))))
null)
(test (generate/decisions lang d 5 0 (decisions #:seq (list (λ (_) 2))))
(test (generate-term lang d 5 0 (decisions #:seq (list (λ (_) 2))))
'(4 4 4 4 (4 4) (4 4)))
(test (exn:fail-message (generate lang e 5))
(test (exn:fail-message (generate-term lang e 5))
#rx"generate: unable to generate pattern e")
(test (generate/decisions lang f 5 0 (decisions #:seq (list (λ (_) 0)))) null)
(test (generate/decisions lang ((0 ..._!_1) ... (1 ..._!_1) ...) 5 0
(test (generate-term lang f 5 0 (decisions #:seq (list (λ (_) 0)))) null)
(test (generate-term lang ((0 ..._!_1) ... (1 ..._!_1) ...) 5 0
(decisions #:seq (list (λ (_) 2) (λ (_) 3) (λ (_) 4) (λ (_) 2) (λ (_) 3) (λ (_) 4)
(λ (_) 2) (λ (_) 3) (λ (_) 4) (λ (_) 1) (λ (_) 3))))
'((0 0 0) (0 0 0 0) (1 1 1)))
(test (generate/decisions lang ((0 ..._!_1) ... (1 ..._!_1) ...) 5 0
(test (generate-term lang ((0 ..._!_1) ... (1 ..._!_1) ...) 5 0
(decisions #:seq (list (λ (_) 2) (λ (_) 3) (λ (_) 4) (λ (_) 2) (λ (_) 3) (λ (_) 5))))
'((0 0 0) (0 0 0 0) (1 1 1) (1 1 1 1 1))))
@ -247,7 +264,7 @@
;; x and y bound in body
(test
(let/ec k
(generate/decisions
(generate-term
lc e 10 0
(decisions #:var (list (λ _ 'x) (λ _ 'y) (λ (c l b a) (k b)))
#:nt (patterns first first first third first)
@ -257,7 +274,7 @@
(let ()
(define-language lang (e (variable-prefix pf)))
(test
(generate/decisions
(generate-term
lang e 5 0
(decisions #:var (list (λ _ 'x))))
'pfx))
@ -271,7 +288,7 @@
(define-language lang
(e number (e_1 e_2 e e_1 e_2)))
(test
(generate/decisions
(generate-term
lang e 5 0
(decisions #:nt (patterns second first first first)
#:num (list (λ _ 2) (λ _ 3) (λ _ 4))))
@ -283,7 +300,7 @@
(x variable))
(test
(let/ec k
(generate/decisions
(generate-term
lang e 5 0
(decisions #:var (list (λ _ 'x) (λ (c l b a) (k b))))))
'(x)))
@ -294,17 +311,17 @@
(b (c_!_1 c_!_1 c_!_1))
(c 1 2))
(test
(generate/decisions
(generate-term
lang a 5 0
(decisions #:num (list (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 2))))
'(1 1 2))
(test
(generate/decisions
(generate-term
lang (number_!_1 number_!_2 number_!_1) 5 0
(decisions #:num (list (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 2))))
'(1 1 2))
(test
(exn:fail-message (generate lang b 5000))
(exn:fail-message (generate-term lang b 5000))
#rx"unable"))
(let ()
@ -313,7 +330,7 @@
(f foo bar))
(test
(let/ec k
(generate/decisions
(generate-term
lang e 5 0
(decisions #:str (list (λ (c l a) (k (cons (sort c char<=?) (sort l string<=?))))))))
(cons '(#\a #\b #\f #\o #\r)
@ -327,28 +344,28 @@
(d (side-condition (x_1 x_1 x) (not (eq? (term x_1) 'x))) #:binds x_1 x)
(e (side-condition (x_1 x_!_2 x_!_2) (not (eq? (term x_1) 'x))))
(x variable))
(test (generate lang b 5) 43)
(test (generate lang (side-condition a (odd? (term a))) 5) 43)
(test (exn:fail-message (generate lang c 5))
(test (generate-term lang b 5) 43)
(test (generate-term lang (side-condition a (odd? (term a))) 5) 43)
(test (exn:fail-message (generate-term lang c 5))
#rx"unable to generate")
(test ; binding works for with side-conditions failure/retry
(let/ec k
(generate/decisions
(generate-term
lang d 5 0
(decisions #:var (list (λ _ 'x) (λ _ 'x) (λ _ 'y) (λ (c l b a) (k b))))))
'(y))
(test ; mismatch patterns work with side-condition failure/retry
(generate/decisions
(generate-term
lang e 5 0
(decisions #:var (list (λ _ 'x) (λ _ 'x) (λ _ 'y) (λ _ 'y) (λ _ 'x) (λ _ 'y))))
'(y x y))
(test ; generate compiles side-conditions in pattern
(generate/decisions lang (side-condition x_1 (not (eq? (term x_1) 'x))) 5 0
(generate-term lang (side-condition x_1 (not (eq? (term x_1) 'x))) 5 0
(decisions #:var (list (λ _ 'x) (λ _ 'y))))
'y)
(test ; bindings within ellipses collected properly
(let/ec k
(generate/decisions lang (side-condition (((number_1 3) ...) ...) (k (term ((number_1 ...) ...)))) 5 0
(generate-term lang (side-condition (((number_1 3) ...) ...) (k (term ((number_1 ...) ...)))) 5 0
(decisions #:seq (list (λ (_) 2) (λ (_) 3) (λ (_) 4))
#:num (build-list 7 (λ (n) (λ (_) n))))))
'((0 1 2) (3 4 5 6))))
@ -360,9 +377,9 @@
(c (side-condition (name x d) (zero? (term x))))
(d 2 1 0)
(e ((side-condition (name d_1 d) (zero? (term d_1))) d_1)))
(test (generate lang a 5) 4)
(test (generate lang c 5) 0)
(test (generate lang e 5) '(0 0)))
(test (generate-term lang a 5) 4)
(test (generate-term lang c 5) 0)
(test (generate-term lang e 5) '(0 0)))
(let ()
(define-language lang
@ -380,28 +397,28 @@
(y variable))
(test
(generate/decisions
(generate-term
lang (in-hole A number ) 5 0
(decisions
#:nt (patterns second second first first third first second first first)
#:num (build-list 5 (λ (x) (λ (_) x)))))
'(+ (+ 1 2) (+ 0 (+ 3 4))))
(test (generate lang (in-hole (in-hole (1 hole) hole) 5) 5) '(1 5))
(test (generate lang (hole 4) 5) (term (hole 4)))
(test (generate/decisions lang (variable_1 (in-hole C variable_1)) 5 0
(test (generate-term lang (in-hole (in-hole (1 hole) hole) 5) 5) '(1 5))
(test (generate-term lang (hole 4) 5) (term (hole 4)))
(test (generate-term lang (variable_1 (in-hole C variable_1)) 5 0
(decisions #:var (list (λ _ 'x) (λ _ 'y) (λ _ 'x))))
'(x x))
(test (generate/decisions lang (variable_!_1 (in-hole C variable_!_1)) 5 0
(test (generate-term lang (variable_!_1 (in-hole C variable_!_1)) 5 0
(decisions #:var (list (λ _ 'x) (λ _ 'x) (λ _ 'x) (λ _ 'y))))
'(x y))
(test (let/ec k (generate/decisions lang d 5 0 (decisions #:var (list (λ _ 'x) (λ (c l b a) (k b))))))
(test (let/ec k (generate-term lang d 5 0 (decisions #:var (list (λ _ 'x) (λ (c l b a) (k b))))))
'(x))
(test (generate/decisions lang e 5 0 (decisions #:num (list (λ _ 1) (λ _ 2))))
(test (generate-term lang e 5 0 (decisions #:num (list (λ _ 1) (λ _ 2))))
'((2 (1 1)) 1))
(test (generate/decisions lang g 5 0 (decisions #:num (list (λ _ 1) (λ _ 2) (λ _ 1) (λ _ 0))))
(test (generate-term lang g 5 0 (decisions #:num (list (λ _ 1) (λ _ 2) (λ _ 1) (λ _ 0))))
'(1 0))
(test (generate/decisions lang h 5 0 (decisions #:num (list (λ _ 1) (λ _ 2) (λ _ 3))))
(test (generate-term lang h 5 0 (decisions #:num (list (λ _ 1) (λ _ 2) (λ _ 3))))
'((2 ((3 (2 1)) 3)) 1)))
(let ()
@ -409,7 +426,7 @@
(e (e e) (+ e e) x v)
(v (λ (x) e) number)
(x variable-not-otherwise-mentioned))
(test (generate/decisions lc x 5 0 (decisions #:var (list (λ _ ) (λ _ '+) (λ _ 'x))))
(test (generate-term lc x 5 0 (decisions #:var (list (λ _ ) (λ _ '+) (λ _ 'x))))
'x))
(let ()
@ -423,14 +440,14 @@
(list four 'f))
(test (call-with-values (λ () (pick-any four (make-random 1))) list)
(list sexp 'sexp))
(test (generate/decisions four any 5 0 (decisions #:any (list (λ _ (values four 'e))))) 4)
(test (generate/decisions four any 5 0
(test (generate-term four any 5 0 (decisions #:any (list (λ _ (values four 'e))))) 4)
(test (generate-term four any 5 0
(decisions #:any (list (λ _ (values sexp 'sexp)))
#:nt (patterns fifth second second second)
#:seq (list (λ _ 3))
#:str (list (λ _ "foo") (λ _ "bar") (λ _ "baz"))))
'("foo" "bar" "baz"))
(test (generate/decisions empty any 5 0 (decisions #:nt (patterns first)
(test (generate-term empty any 5 0 (decisions #:nt (patterns first)
#:var (list (λ _ 'x))))
'x))
@ -438,7 +455,7 @@
(let ()
(define-language lang
(e (hide-hole (in-hole ((hide-hole hole) hole) 1))))
(test (generate lang e 5) (term (hole 1))))
(test (generate-term lang e 5) (term (hole 1))))
(define (output-error-port thunk)
(let ([port (open-output-string)])
@ -452,14 +469,11 @@
(e x (e e) v)
(v (λ (x) e))
(x variable-not-otherwise-mentioned))
(test (generate/decisions lang (cross e) 3 0
(test (generate-term lang (cross e) 3 0
(decisions #:nt (patterns fourth first first second first first first)
#:var (list (λ _ 'x) (λ _ 'y))))
(term (λ (x) (hole y)))))
; preferred productions
;; current-error-port-output : (-> (-> any) string)
(define (current-error-port-output thunk)
(let ([p (open-output-string)])
@ -521,6 +535,52 @@
;; OK -- generated from pattern (any ...)
(test (check-metafunction-contract i) #t))
;; check-reduction-relation
(let ()
(define-language L
(e (+ e ...) number)
(E (+ number ... E* e ...))
(E* hole E*))
(define R
(reduction-relation
L
(==> (+ number ...) whatever)
(--> (side-condition number (even? (term number))) whatever)
with
[(--> (in-hole E a) whatever)
(==> a b)]))
(let ([generated null])
(test (begin
(check-reduction-relation
R (λ (term) (set! generated (cons term generated)))
#:decisions (decisions #:seq (list (λ _ 0) (λ _ 0) (λ _ 0))
#:num (list (λ _ 1) (λ _ 1) (λ _ 0)))
#:attempts 1)
generated)
(reverse '((+ (+)) 0))))
(let ([S (reduction-relation L (--> 1 2 name) (--> 3 4))])
(test (check-reduction-relation S (λ (x) #t) #:attempts 1) #t)
(test (current-error-port-output
(λ () (check-reduction-relation S (λ (x) #f))))
"checking name failed after 1 attempts:\n1\n")
(test (current-error-port-output
(λ () (check-reduction-relation S (curry eq? 1))))
"checking unnamed failed after 1 attempts:\n3\n")))
; check-metafunction
(let ()
(define-language empty)
(define-metafunction empty
[(m 1) whatever]
[(m 2) whatever])
(let ([generated null])
(test (begin
(check-metafunction m (λ (t) (set! generated (cons t generated))) 1)
generated)
(reverse '((1) (2)))))
(test (current-error-port-output (λ () (check-metafunction m (curry eq? 1))))
#rx"checking clause #1 failed after 1 attempt"))
;; parse/unparse-pattern
(let-syntax ([test-match (syntax-rules () [(_ p x) (test (match x [p #t] [_ #f]) #t)])])
(define-language lang (x variable))

View File

@ -20,6 +20,7 @@ To do a better job of not generating programs with free variables,
"underscore-allowed.ss"
"term.ss"
"error.ss"
"struct.ss"
(for-syntax "rewrite-side-conditions.ss")
(for-syntax "term-fn.ss")
(for-syntax "reduction-semantics.ss")
@ -83,13 +84,16 @@ To do a better job of not generating programs with free variables,
(define (pick-string lang-chars lang-lits attempt [random random])
(random-string lang-chars lang-lits (random-natural 1/5 random) attempt random))
(define ((pick-nt pref-prods) nt prods bound-vars attempt)
(let* ([binders (filter (λ (x) (not (null? (rhs-var-info x)))) prods)]
(define ((pick-nt pref-prods [random random]) name lang bound-vars attempt)
(let* ([prods (nt-rhs (nt-by-name lang name))]
[binders (filter (λ (x) (not (null? (rhs-var-info x)))) prods)]
[do-intro-binder? (and (null? bound-vars)
(not (null? binders))
(try-to-introduce-binder?))])
(cond [do-intro-binder? binders]
[(preferred-production? attempt) (list (hash-ref pref-prods nt))]
[(and (not (eq? lang sexp))
(preferred-production? attempt random))
(hash-ref pref-prods name)]
[else prods])))
(define (pick-from-list l [random random]) (list-ref l (random (length l))))
@ -155,7 +159,7 @@ To do a better job of not generating programs with free variables,
[zip (λ (l m) (map cons l m))])
(map cdr (filter (λ (x) (equal? min-size (car x))) (zip sizes (nt-rhs nt))))))
(define (generate* lang pat decisions@)
(define (generate lang decisions@)
(define-values/invoke-unit decisions@
(import) (export decisions^))
@ -164,19 +168,16 @@ To do a better job of not generating programs with free variables,
(define base-table (find-base-cases lang))
(define (generate-nt name fvt-id bound-vars size attempt in-hole state)
(let*-values
([(nt) (findf (λ (nt) (eq? name (nt-name nt)))
(append (compiled-lang-lang lang)
(compiled-lang-cclang lang)))]
[(bound-vars) (append (extract-bound-vars fvt-id state) bound-vars)]
(let*-values
([(bound-vars) (append (extract-bound-vars fvt-id state) bound-vars)]
[(term _)
(generate/pred
name
(λ ()
(let ([rhs (pick-from-list
(if (zero? size)
(min-prods nt base-table)
((next-non-terminal-decision) name (nt-rhs nt) bound-vars attempt)))])
(min-prods (nt-by-name lang name) base-table)
((next-non-terminal-decision) name lang bound-vars attempt)))])
(((generate-pat bound-vars (max 0 (sub1 size)) attempt) (rhs-pattern rhs) in-hole)
(make-state (map fvt-entry (rhs-var-info rhs)) #hash()))))
(λ (_ env) (mismatches-satisfied? env)))])
@ -289,7 +290,7 @@ To do a better job of not generating programs with free variables,
[`(hide-hole ,pattern) ((recur pattern the-hole) state)]
[`any
(let*-values ([(lang nt) ((next-any-decision) lang)]
[(term _) ((generate* lang nt decisions@) size attempt)])
[(term _) (((generate lang decisions@) nt) size attempt)])
(values term state))]
[(? (is-nt? lang))
(generate-nt pat pat bound-vars size attempt in-hole state)]
@ -355,15 +356,17 @@ To do a better job of not generating programs with free variables,
(state-fvt state))
(state-env state)))
(λ (size attempt)
(let-values ([(term state)
(generate/pred
(unparse-pattern pat)
(λ ()
(((generate-pat null size attempt) pat the-hole)
(make-state null #hash())))
(λ (_ env) (mismatches-satisfied? env)))])
(values term (bindings (state-env state))))))
(λ (pat)
(let ([unparsed (unparse-pattern pat)])
(λ (size attempt)
(let-values ([(term state)
(generate/pred
unparsed
(λ ()
(((generate-pat null size attempt) pat the-hole)
(make-state null #hash())))
(λ (_ env) (mismatches-satisfied? env)))])
(values term (bindings (state-env state))))))))
;; find-base-cases : compiled-language -> hash-table
(define (find-base-cases lang)
@ -446,6 +449,12 @@ To do a better job of not generating programs with free variables,
(define (built-in? x)
(and (memq x underscore-allowed) #t))
;; nt-by-name : lang symbol -> nt
(define (nt-by-name lang name)
(findf (λ (nt) (eq? name (nt-name nt)))
(append (compiled-lang-lang lang)
(compiled-lang-cclang lang))))
(define named-nt-rx #rx"^([^_]+)_[^_]*$")
(define mismatch-nt-rx #rx"([^_]+)_!_[^_]*$")
(define named-ellipsis-rx #rx"^\\.\\.\\._[^_]*$")
@ -591,7 +600,7 @@ To do a better job of not generating programs with free variables,
[(struct ellipsis (name sub-pat class vars))
(make-ellipsis name (recur sub-pat) (rewrite class)
(map (λ (v) (if (class? v) (rewrite v) v)) vars))]
[(? list?) (map (λ (p) (recur p)) pat)]
[(? list?) (map recur pat)]
[_ pat]))))
;; used in generating the `any' pattern
@ -610,15 +619,16 @@ To do a better job of not generating programs with free variables,
(with-syntax ([(name ...) names]
[(name/ellipses ...) names/ellipses])
(syntax/loc stx
(check-property
(term-generator lang pat random-decisions)
(λ (_ bindings)
(with-handlers ([exn:fail? (λ (_) #f)])
(term-let ([name/ellipses (lookup-binding bindings 'name)] ...)
property)))
attempts))))]))
(or (check-property
(term-generator lang pat random-decisions)
(λ (_ bindings)
(with-handlers ([exn:fail? (λ (_) #f)])
(term-let ([name/ellipses (lookup-binding bindings 'name)] ...)
property)))
attempts)
(void)))))]))
(define (check-property generate property attempts)
(define (check-property generate property attempts [source #f])
(let loop ([remaining attempts])
(if (zero? remaining)
#t
@ -628,23 +638,21 @@ To do a better job of not generating programs with free variables,
(if (property term bindings)
(loop (sub1 remaining))
(begin
(fprintf (current-error-port)
"failed after ~s attempts:\n"
attempt)
(pretty-print term (current-error-port)))))))))
(when source
(fprintf (current-error-port) "checking ~a " source))
(fprintf (current-error-port) "failed after ~s attempts:\n" attempt)
(pretty-print term (current-error-port))
#f)))))))
(define-syntax generate
(define-syntax generate-term
(syntax-rules ()
[(_ lang pat size attempt)
(let-values ([(term _) ((term-generator lang pat random-decisions) size attempt)])
[(_ lang pat size attempt decisions)
(let-values ([(term _) ((term-generator lang pat decisions) size attempt)])
term)]
[(_ lang pat size) (generate lang pat size 0)]))
(define-syntax generate/decisions
(syntax-rules ()
[(_ lang pat size attempt decisions@)
(let-values ([(term _) ((term-generator lang pat decisions@) size attempt)])
term)]))
[(_ lang pat size attempt)
(generate-term lang pat size attempt random-decisions)]
[(_ lang pat size)
(generate-term lang pat size 1)]))
(define-syntax (term-generator stx)
(syntax-case stx ()
@ -655,10 +663,14 @@ To do a better job of not generating programs with free variables,
'generate #t #'pat)])
(syntax/loc stx
(let ([lang (parse-language lang)])
(generate*
lang
(reassign-classes (parse-pattern `pattern lang 'top-level))
(decisions lang)))))]))
((generate lang (decisions lang))
(reassign-classes (parse-pattern `pattern lang 'top-level))))))]))
(define-for-syntax (metafunc name stx)
(let ([tf (syntax-local-value name (λ () #f))])
(if (term-fn? tf)
(term-fn-get-id tf)
(raise-syntax-error #f "not a metafunction" stx name))))
(define-syntax (check-metafunction-contract stx)
(syntax-case stx ()
@ -666,22 +678,64 @@ To do a better job of not generating programs with free variables,
(syntax/loc stx (check-metafunction-contract name random-decisions))]
[(_ name decisions)
(identifier? #'name)
(with-syntax ([m (let ([tf (syntax-local-value #'name (λ () #f))])
(if (term-fn? tf)
(term-fn-get-id tf)
(raise-syntax-error #f "not a metafunction" stx #'name)))])
(with-syntax ([m (metafunc #'name stx)])
(syntax/loc stx
(let ([lang (parse-language (metafunc-proc-lang m))]
[dom (metafunc-proc-dom-pat m)])
(check-property
(generate* lang
(reassign-classes (parse-pattern (if dom dom '(any (... ...))) lang 'top-level))
(decisions lang))
((generate lang (decisions lang))
(reassign-classes (parse-pattern (if dom dom '(any (... ...))) lang 'top-level)))
(λ (t _)
(with-handlers ([exn:fail:redex? (λ (_) #f)])
(begin (term (name ,@t)) #t)))
default-check-attempts))))]))
(define (check-property-many lang patterns ids prop decisions attempts)
(let* ([lang-gen (generate lang (decisions lang))]
[pat-gens (map (λ (pat) (lang-gen (reassign-classes (parse-pattern pat lang 'top-level))))
patterns)])
(for/and ([pat patterns]
[id ids])
(check-property
(let ([gen (lang-gen (reassign-classes (parse-pattern pat lang 'top-level)))])
(λ (size attempt) (gen size attempt)))
(λ (term _) (prop term))
attempts
id))))
(define-syntax (check-metafunction stx)
(syntax-case stx ()
[(_ name property)
(syntax/loc stx (check-metafunction name property default-check-attempts))]
[(_ name property attempts)
(syntax/loc stx (check-metafunction name property random-decisions attempts))]
[(_ name property decisions attempts)
(with-syntax ([m (metafunc #'name stx)])
(syntax/loc stx
(or (check-property-many
(parse-language (metafunc-proc-lang m))
(metafunc-proc-lhs-pats m)
(build-list (length (metafunc-proc-lhs-pats m))
(compose (curry format "clause #~s") add1))
property
decisions
attempts)
(void))))]))
(define (check-reduction-relation
relation property
#:decisions [decisions random-decisions]
#:attempts [attempts default-check-attempts])
(or (check-property-many
(parse-language (reduction-relation-lang relation))
(map rewrite-proc-lhs (reduction-relation-make-procs relation))
(map (λ (proc) (or (rewrite-proc-name proc) 'unnamed))
(reduction-relation-make-procs relation))
property
decisions
attempts)
(void)))
(define-signature decisions^
(next-variable-decision
next-number-decision
@ -693,7 +747,7 @@ To do a better job of not generating programs with free variables,
(define (random-decisions lang)
(define preferred-productions
(make-immutable-hasheq
(map (λ (nt) (cons (nt-name nt) (pick-from-list (nt-rhs nt))))
(map (λ (nt) (cons (nt-name nt) (list (pick-from-list (nt-rhs nt)))))
(append (compiled-lang-lang lang)
(compiled-lang-cclang lang)))))
(unit (import) (export decisions^)
@ -705,12 +759,13 @@ To do a better job of not generating programs with free variables,
(define (next-string-decision) pick-string)))
(provide pick-from-list pick-var min-prods decisions^ pick-sequence-length
is-nt? pick-char random-string pick-string check
pick-nt unique-chars pick-any sexp generate parse-pattern
is-nt? pick-char random-string pick-string check nt-by-name
pick-nt unique-chars pick-any sexp generate-term parse-pattern
class-reassignments reassign-classes unparse-pattern
(struct-out ellipsis) (struct-out mismatch) (struct-out class)
(struct-out binder) generate/decisions check-metafunction-contract
pick-number parse-language)
(struct-out binder) check-metafunction-contract
pick-number parse-language check-reduction-relation
preferred-production-threshold check-metafunction)
(provide/contract
[find-base-cases (-> compiled-lang? hash?)])

View File

@ -9,7 +9,7 @@
build-reduction-relation
reduction-relation?
empty-reduction-relation
make-rewrite-proc rewrite-proc? rewrite-proc-name
make-rewrite-proc rewrite-proc? rewrite-proc-name rewrite-proc-lhs
(struct-out rule-pict))
(define-struct rule-pict (arrow lhs rhs label side-conditions fresh-vars pattern-binds))
@ -20,13 +20,14 @@
;; we want to avoid doing it multiple times, so it is cached in a reduction-relation struct
(define-values (make-rewrite-proc rewrite-proc? rewrite-proc-name)
(define-values (make-rewrite-proc rewrite-proc? rewrite-proc-name rewrite-proc-lhs)
(let ()
(define-values (type constructor predicate accessor mutator)
(make-struct-type 'rewrite-proc #f 2 0 #f '() #f 0))
(make-struct-type 'rewrite-proc #f 3 0 #f '() #f 0))
(values constructor
predicate
(make-struct-field-accessor accessor 1 'name))))
(make-struct-field-accessor accessor 1 'name)
(make-struct-field-accessor accessor 2 'lhs))))
;; lang : compiled-language
;; make-procs = (listof (compiled-lang -> proc))

View File

@ -1,6 +1,9 @@
(module tl-test mzscheme
(require "../reduction-semantics.ss"
"test-util.ss")
"test-util.ss"
(only "matcher.ss" make-bindings make-bind)
scheme/match
"struct.ss")
(reset-count)
@ -537,6 +540,19 @@
(list '((2 3) 20)
'(6 (4 5))))
; shortcuts like this fail if compilation fails to preserve
; lexical context for side-conditions expressions.
(test (let ([x #t])
(apply-reduction-relation
(reduction-relation
grammar
(==> variable variable)
with
[(--> (a (side-condition number x)) b)
(==> a b)])
'(x 4)))
'(x))
(test (apply-reduction-relation/tag-with-names
(reduction-relation
grammar
@ -1099,4 +1115,48 @@
'x)
'(x1))
(let ([r (reduction-relation
grammar
(->1 1 2)
(->2 3 4)
(->4 5 6)
with
[(--> (side-condition (a number) (even? (term number))) b)
(->1 a b)]
[(--> (X
(number number)
(X_1 X_1)
(M_!_1 M_!_1)
(1 ..._1 1 ..._1)
(1 ..._!_1 1 ..._!_1))
b)
(->2 X b)]
[(--> (a 1) b)
(->3 a b)]
[(->3 (a 2) b)
(->4 a b)])])
; test that names are properly bound for side-conditions in shortcuts
(let* ([lhs (rewrite-proc-lhs (first (reduction-relation-make-procs r)))]
[proc (third lhs)]
[name (cadadr lhs)]
[bind (λ (n) (make-bindings (list (make-bind name n))))])
(test (and (proc (bind 4)) (not (proc (bind 3)))) #t))
; test binder renaming
(let ([sym-mtch? (λ (rx) (λ (s) (and (symbol? s) (regexp-match? rx (symbol->string s)))))])
(match (rewrite-proc-lhs (second (reduction-relation-make-procs r)))
[`(3
(,(and n1 (? (sym-mtch? #px"^number_\\d+$"))) ,n1)
(,(and n2 (? (sym-mtch? #px"^X_1\\d+$"))) ,n2)
(,(and m1 (? (sym-mtch? #px"^M_!_1\\d+$"))) ,m1)
(1 ,(and ...1 (? (sym-mtch? #px"^\\.\\.\\._1\\d+$"))) 1 ,...1)
(1 ,(and ...!1 (? (sym-mtch? #px"^\\.\\.\\._!_1\\d+$"))) 1 ,...!1))
#t]
[else #f]))
; test shortcut in terms of shortcut
(test (rewrite-proc-lhs (third (reduction-relation-make-procs r)))
'((5 2) 1)))
(print-tests-passed 'tl-test.ss))