;
; Forest Rewriting
; Chris Pressey, late January 2006
;
; Copyright (c)2008 Cat's Eye Technologies. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
;
; 1. Redistributions of source code must retain the above copyright
; notices, this list of conditions and the following disclaimer.
; 2. Redistributions in binary form must reproduce the above copyright
; notices, this list of conditions, and the following disclaimer in
; the documentation and/or other materials provided with the
; distribution.
; 3. Neither the names of the copyright holders nor the names of their
; contributors may be used to endorse or promote products derived
; from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES INCLUDING, BUT NOT
; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
; FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
; COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
; BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
; ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
; POSSIBILITY OF SUCH DAMAGE.
;
; Given a term, a pattern to look for in that term, a replacement pattern,
; and a unifier (set of substitutions,) determine if the term could be
; rewritten, and if so, return a vector consisting of:
;
; - a partially rewritten term. The replacement pattern is substituted into
; the term but WITHOUT the variables expanded into ground terms.
; - a (possibly new) unifier
;
; The rewrite process first recurses into the term's children (bottom-up
; rewriting.) If the given pattern fails to unify anywhere in the term,
; #f is returned.
;
(define partial-rewrite
(lambda (term pattern replacement unifier)
(cond ((list? term)
(let loop ((subterms term) ; first try to unify with each child
(acc '())) ; keep track of subterms we've seen
(cond ((null? subterms) ; no more children. lament failure.
(direct-partial-rewrite term pattern replacement unifier))
(else
(let* ((subterm (car subterms))
(rest (cdr subterms))
(result (partial-rewrite subterm pattern replacement unifier)))
(cond (result ; this child succeeded. pass along its result
(let* ((result-term (vector-ref result 0))
(result-unifier (vector-ref result 1))
(front (reverse acc))
(back (cons result-term rest))
(spliced-term (append front back)))
(vector spliced-term result-unifier)))
(else ; this child failed,
(loop rest (cons subterm acc))))))))) ; try the next one.
(else
(direct-partial-rewrite term pattern replacement unifier)))))
;
; Essentially a helper function for partial-rewrite; if the given
; pattern unifies with the given term, just return a vector containing
; the replacement and the (updated) unifier, else return #f.
;
(define direct-partial-rewrite
(lambda (term pattern replacement unifier)
(let* ((new-unifier (unify term pattern unifier))) ; try to unify
(cond (new-unifier ; successfully unified, so rewrite
(vector replacement new-unifier))
(else
#f)))))
;
; Given a vector of terms with variable placeholders in them, and
; a unifier, modify the vector so that the variables are replaced
; by their respective replacements (substitutions) in the unifier,
; and return the modified vector.
;
(define expand-forest
(lambda (terms unifier)
(let loop ((terms terms)
(term-num (- (vector-length terms) 1)))
(cond ((< term-num 0)
terms)
(else
(let* ((term (vector-ref terms term-num))
(new-term (expand-vars term unifier))
(new-terms (vector-store terms term-num new-term))
(next-term-num (- term-num 1)))
(loop new-terms next-term-num)))))))
;
; Rewrite a vector of terms in tandem using a list of rules, with a
; shared unifier (so that variable matches are common to all terms.)
; Return a vector of rewritten terms, if the rule list matched, otherwise #f.
;
(define rewrite-terms-with-compound-rule
(lambda (original-terms original-compound-rule)
(let loop ((terms original-terms)
(compound-rule original-compound-rule)
(unifier '()))
(cond ((null? compound-rule) ; when we reach the end of the list,
(expand-forest terms unifier)) ; expand variables in all the new terms
(else
(let* ((rule (car compound-rule))
(rest-rules (cdr compound-rule))
(targ-term-no (vector-ref rule 0))
(pattern (vector-ref rule 1))
(replacement (vector-ref rule 2))
(term (vector-ref terms targ-term-no))
(result (partial-rewrite term pattern replacement unifier)))
(cond (result ; we matched. update term, and try the next rule
(let* ((new-term (vector-ref result 0))
(new-unifier (vector-ref result 1))
(new-terms (vector-store terms targ-term-no new-term)))
(loop new-terms rest-rules new-unifier)))
(else ; no match. abort the entire thing.
#f))))))))
;
; Given a vector(#2) of:
; a vector of terms, and
; a list of compound rules,
; rewrite all terms simultaneously with each of the compound rules.
; Rewriting a set of terms simultaneously means that the variables in the
; compound rule are shared across the terms, and will only unify with subterms
; that are common to all of the terms.
;
; Keep applying compound rules until there are none that apply any longer.
;
; Return a vector of terms so rewritten.
;
(define rewrite-forest
(lambda (everything)
(let* ((original-terms (vector-ref everything 0))
(all-compound-rules (vector-ref everything 1)))
(let loop ((terms original-terms)
(compound-rules all-compound-rules))
(cond ((null? compound-rules)
terms) ; terminate and return new termlist
(else
(let* ((compound-rule (car compound-rules))
(new-terms (rewrite-terms-with-compound-rule
terms compound-rule)))
(cond (new-terms ; successfully rewrote.
(loop new-terms all-compound-rules)) ; try again, using all compound-rules
(else
(loop terms (cdr compound-rules))))))))))) ; try again, using rest of compound-rules