(in-package "REWRITE-CODE") (program) (set-state-ok t) ; recognizer for ``augmented naturals'', which include 'inf as infinity (defun aug-natp (x) (or (natp x) (equal x 'inf))) ; < relation for ``augmented naturals'' (defun aug-nat-< (x y) (and (not (eq x 'inf)) (or (eq y 'inf) (< x y)))) ; decrement function for ``augmented naturals'' (defun aug-nat-dec (x) (cond ((eq x 'inf) 'inf) ((zp x) 0) (t (1- x)))) ; recognizer for intervals over ``augmented naturals'' (defun multiplicity-rangep (x) (or (null x) ; empty range (and (consp x) (aug-natp (car x)) (aug-natp (cdr x)) (not (aug-nat-< (cdr x) (car x)))))) ; decrement function for intervals over ``augmented naturals'' (defun multiplicity-range-dec (x) (if (or (endp x) (equal (cdr x) 0)) nil ; empty range (cons (aug-nat-dec (car x)) (aug-nat-dec (cdr x))))) ; prepend the reverse of one list onto another (defun rev-prepend (torev onto) ; (declare (xargs :guard (and (true-listp torev) (true-listp onto)))) (if (endp torev) onto (rev-prepend (cdr torev) (cons (car torev) onto)))) ; n times, pops an element from ,from and pushes it on ,to (defun pop-push-n (n from to) (if (or (zp n) (endp from)) (mv from to) (pop-push-n (1- n) (cdr from) (cons (car from) to)))) ; update an entry in an alist, or add it to the end (defun update-alist (key val lst) (cond ((endp lst) (list (cons key val))) ((and (consp (car lst)) (equal (caar lst) key)) (cons (cons key val) (cdr lst))) (t (cons (car lst) (update-alist key val (cdr lst)))))) ; replaces occurances in ,expr of the symbols that are keys in ,assn-alist ; with their bindings in ,assn-alist. ; (defun replace-assns (expr assn-alist) ;(declare (xargs :guard (symbol-alistp assn-alist))) (if (consp expr) (cons (replace-assns (car expr) assn-alist) (replace-assns (cdr expr) assn-alist)) (if (symbolp expr) (let ((assn (assoc-eq expr assn-alist))) (if (consp assn) (cdr assn) expr)) expr))) ; ,a1 and ,a2 are each assumed an alist with no keys duplicated. if the keys ; don't overlap between ,a1 and ,a2 or they agree on all the bindings, the ; "union" is returned. if they disagree on any bindings, ,t is returned. ; (defun merge-matches (a1 a2) (cond ((endp a1) a2) ((endp a2) a1) (t (let* ((sym (caar a1)) (binding2 (assoc-eq sym a2))) (if (consp binding2) (let ((val1 (cdar a1)) (val2 (cdr binding2))) (if (equal val1 val2) (merge-matches (cdr a1) a2) t)) (merge-matches (cdr a1) (cons (car a1) a2))))))) ; returns (smallest) assn-alist satisfying ; (equal (replace-assns pat assn-alist) expr) ; if one exists. otherwise, returns t (indicating, roughly, that ; no assignments to ,vars in ,pat generate ,expr) ; (defun match-pattern (expr pat vars) ;(declare (xargs :guard (symbol-listp vars))) (cond ((member-eq pat vars) (list (cons pat expr))) ((and (consp pat) (consp expr)) (let ((car-result (match-pattern (car expr) (car pat) vars)) (cdr-result (match-pattern (cdr expr) (cdr pat) vars))) (if (or (equal car-result t) (equal cdr-result t)) t ; bad match (merge-matches car-result cdr-result)))) ((equal pat expr) ()) (t t) ; bad match )) ; predicates for the parsed pieces of a rewrite-spec, a rewrite-def and its ; constituent pieces (mutual-recursion (defun rewrite-defp (v) ; a list (sequenced) of lists (simultaneous) (declare (xargs :measure (acl2-count v) :hints (("Goal" :in-theory (disable (:definition multiplicity-rangep)))))) (or (null v) (and (consp v) (rewrite-simul-defp (car v)) (rewrite-defp (cdr v))))) (defun rewrite-simul-defp (v) ; a list (simultaneous) of entries (declare (xargs :measure (acl2-count v))) (or (null v) (and (consp v) (rewrite-entryp (car v)) (rewrite-simul-defp (cdr v))))) (defun rewrite-entryp (v) (declare (xargs :measure (acl2-count v))) (and (consp v) (multiplicity-rangep (car v)) ; global (consp (cdr v)) (rewrite-var-entry-lstp (cadr v)) ; no binding -> recursive (consp (cddr v)) (symbol-listp (caddr v)) ; vars (consp (cdddr v)) ; (cadddr v) is pat ; (cddddr v) is repl )) (defun rewrite-var-entry-lstp (v) ; var -> rewrite-def alist (declare (xargs :measure (acl2-count v))) (or (null v) (and (consp v) (rewrite-var-entryp (car v)) (rewrite-var-entry-lstp (cdr v))))) (defun rewrite-var-entryp (v) ; ( var . rewrite-def ) (declare (xargs :measure (acl2-count v))) (and (consp v) (symbolp (car v)) (rewrite-defp (cdr v))))) ; some accessors (defmacro entry-multrng (v) `(car ,v)) (defmacro entry-alist (v) `(cadr ,v)) (defmacro entry-vars (v) `(caddr ,v)) (defmacro entry-pat (v) `(cadddr ,v)) (defmacro entry-repl (v) `(cddddr ,v)) ; some modifiers (defun dec-entry (v) (cons (multiplicity-range-dec (car v)) (cdr v))) (defun update-entry-alist (v alist) (cons (car v) (cons alist (cddr v)))) (defun update-entry-alist-entry (v var def) (update-entry-alist v (update-alist var def (entry-alist v)))) ; the guts of code rewriting (mutual-recursion (defun rewrite-seq (form seq-from seq-to) (declare (xargs :guard (and (rewrite-defp seq-from) (rewrite-defp seq-to)))) (if (consp seq-from) (mv-let (form updated) (rewrite-simul form (car seq-from) nil) (rewrite-seq form (cdr seq-from) (cons updated seq-to))) (mv form (reverse seq-to)))) (defun rewrite-simul (form simul-from simul-to) (declare (xargs :guard (and (rewrite-simul-defp simul-from) (rewrite-simul-defp simul-to)))) (if (consp simul-from) (let* ((entry (car simul-from)) (assns-opt (match-pattern form (entry-pat entry) (entry-vars entry)))) (if (eq 't assns-opt) ; no match (rewrite-simul form (cdr simul-from) (cons entry simul-to)) ; match! (let* ((entry (dec-entry entry)) (simul-from (cons entry (cdr simul-from))) (idx (len simul-to))) (rewrite-assns (entry-repl entry) assns-opt idx (rev-prepend simul-to simul-from))))) ;; no more rules to try (let ((simul-all (reverse simul-to))) (if (consp form) ;; descend (mv-let (car-form simul-all) (rewrite-simul (car form) simul-all nil) (mv-let (cdr-form simul-all) (rewrite-simul (cdr form) simul-all nil) (mv (cons car-form cdr-form) simul-all))) (mv form simul-all))))) (defun rewrite-assns (repl assns entry-idx simul-all) (declare (xargs :guard (and (rewrite-simul-defp simul-all) (natp entry-idx) (< entry-idx (len simul-all)) (symbol-alistp assns)))) (cond ((consp repl) (mv-let (car-form simul-all) (rewrite-assns (car repl) assns entry-idx simul-all) (mv-let (cdr-form simul-all) (rewrite-assns (cdr repl) assns entry-idx simul-all) (mv (cons car-form cdr-form) simul-all)))) ((symbolp repl) (let ((assn-opt (assoc-eq repl assns))) (if (consp assn-opt) (let* ((form (cdr assn-opt)) (entry (nth entry-idx simul-all)) (non-rec-defs (entry-alist entry)) (non-rec-def-opt (assoc-eq repl non-rec-defs))) (if (consp non-rec-def-opt) ; some non-recursive specification (mv-let (form updated-def) (rewrite-seq form (cdr non-rec-def-opt) nil) (mv form (update-nth entry-idx (update-entry-alist-entry entry repl updated-def) simul-all))) ; no non-rec-def => recursively apply simul-all (if (and (symbolp (entry-pat entry)) (member-eq (entry-pat entry) (entry-vars entry))) ; illegal recursive rewrite that matches everything. ; for now, we'll just break the recursion. (mv form simul-all) ; text to match against getting smaller => ; recursively apply simul-all (rewrite-simul form simul-all nil)))) (mv repl simul-all)))) (t (mv repl simul-all))))) ; code for checking that the multiplicities allow zero. used after they have ; been decremented as many times as they have been used. (mutual-recursion (defun assert-zero-allowed-def (v state) (if (endp v) state (pprogn (assert-zero-allowed-simul-def (car v) state) (assert-zero-allowed-def (cdr v) state)))) (defun assert-zero-allowed-simul-def (v state) (if (endp v) state (pprogn (assert-zero-allowed-entry (car v) state) (assert-zero-allowed-simul-def (cdr v) state)))) (defun assert-zero-allowed-entry (v state) (if (or (endp v) (endp (cdr v))) state (let ((rng (car v)) (var-entries (cadr v))) (pprogn (if (or (endp rng) (not (equal (car rng) 0))) ; zero not allowed (pprogn (acl2::f-put-global 'erp t state) (fms (if (endp rng) "Code rewrite entry used too many times:~% ~xp -> ~xr~%" "Code rewrite entry used too few times (at least ~xn applications remaining):~% ~xp -> ~xr~%") `((#\p . ,(entry-pat v)) (#\r . ,(entry-repl v)) (#\n . ,(and (consp rng) (car rng)))) (standard-co state) state (acl2::default-evisc-tuple state))) ; ok state) (assert-zero-allowed-entry-lst var-entries state))))) (defun assert-zero-allowed-entry-lst (v state) (if (endp v) state (pprogn (assert-zero-allowed-var-entry (car v) state) (assert-zero-allowed-entry-lst (cdr v) state)))) (defun assert-zero-allowed-var-entry (v state) (if (endp v) state (assert-zero-allowed-def (cdr v) state)))) (defun er-if-zero-not-allowed-def (def state) (acl2::state-global-let* ((erp nil)) (pprogn (assert-zero-allowed-def def state) (mv (@ erp) :invisible state)))) (defun rewrite-fn (form def state) (if (not (rewrite-defp def)) (er acl2::soft 'rewrite "Code rewrite definition illegal:~%~x0" def) (mv-let (result new-def) (rewrite-seq form def nil) (er-progn (er-if-zero-not-allowed-def new-def state) (acl2::value result))))) #| (rewrite-fn '(a b (c (c d)) (x c v)) '((((0 . 2) ((%)) (%) (c . %) . (f . %)))) state) |# ; now i build up a more convenient specification language ; first, instead of always (n . m) as range, we also allow ; + == (1 . inf) ; * == (0 . inf) ; n == (n . n) ; predicate for new multiplicity specs (defun multiplicity-specp (x) (declare (xargs :guard t)) (or (and (multiplicity-rangep x) (not (equal (car x) 'inf))) (equal x '*) (equal x '+) (natp x))) (defun multiplicity-spec-to-noninf-range (x) (declare (xargs :guard (multiplicity-specp x))) (cond ((and (multiplicity-rangep x) (not (equal (car x) 'inf))) x) ((equal x '*) '(0 . inf)) ((equal x '+) '(1 . inf)) ((natp x) (cons x x)) (t nil) ; invalid / unrecognized )) ; now we build up some macros that build rewrite-defs from a more natural, ; flexible specification language. ; roughly speaking, entries/rules consist of some pieces: ; :pat = "pattern" to match or :carpat = pattern that must be in a car ; :repl = "replacement" to put in place of pattern (defaults to the pattern) ; :vars = "variables" = a list of symbols that should not be taken ; literally in the pattern, but can stand for any substructure ; :recvars = "recursive variables" = like :vars but the current rules also ; get applied to what these variables match ; :mult = "multiplicity" = an assertion on how many applications of this ; rule are to be made. (violation results in post facto error) ; ; entries can be combined using "simultaneous" combination: ; (:simul e1 e2 e3) ; to indicate that at each step in our pre-order search we attempt to match ; e1, then e2, then e3. if no matches, we go deeper. ; ; entries and/or :simul combinations can be combined with "sequences": ; (:seq s1 s2 s3) ; to indicate that we apply s1 to our form, apply that result to s1, and ; apply that result to s3. ; syntax more precisely: ; above relevant functions, i have kind-of a BNF for the language. the ; parentheses and dots are required cons structure. symbols are also ; matched literally, unless they are surrounded with _ _. items in [] are ; optional, with a default value given after an =. ... is a postfix shorthand ; for 0 or more of something. ; to the right is an indication of what is shorthand for what. if something ; is canonical, "(canonical)" appears, meaning it should be pretty obvious ; how this maps to the lower level structure, if you understand it. ;) ; _def_ ::= () => (:seq) ; | (_def_) => _def_ ; | (:seq _simul-def_...) (canonical) ; | _simul-def_ => (:seq _simul-def_) (defmacro quote-rewrite-def (&rest v) (cond ((endp v) ''nil) ((and (consp (car v)) (null (cdr v))) `(quote-rewrite-def . ,(car v))) ((eq ':seq (car v)) `(quote-rewrite-def-rest . ,(cdr v))) (t `(list (quote-rewrite-simul-def . ,v))))) (defmacro quote-rewrite-def-rest (&rest v) (if (endp v) ''nil `(cons (quote-rewrite-simul-def . ,(car v)) (quote-rewrite-def-rest . ,(cdr v))))) ; _simul-def_ ::= () => (:simul) ; | (:simul _entry_...) (canonical) ; | _entry_ => (:simul _entry_) (defmacro quote-rewrite-simul-def (&rest v) (cond ((endp v) ''nil) ((eq ':simul (car v)) `(quote-rewrite-simul-def-rest . ,(cdr v))) (t `(list (quote-rewrite-entry . ,v))))) (defmacro quote-rewrite-simul-def-rest (&rest v) (if (endp v) ''nil `(cons (quote-rewrite-entry . ,(car v)) (quote-rewrite-simul-def-rest . ,(cdr v))))) ; some stuff for entries (defun namep (v) (and (symbolp v) (not (null v)) (not (keywordp v)))) ; _var-spec_ ::= () (canonical) ; | _name_ => ((_name_)) ; | (_name_ . _var-spec_) => ((_name_) . _var-spec_) ; | ((_name_ . _def_) . _var-spec_) (canonical) (defun var-specp (v) (or (null v) (namep v) (and (consp v) (or (namep (car v)) (and (consp (car v)) (namep (caar v)))) (var-specp (cdr v))))) ; _recvar-spec_ ::= () (canonical) ; | _name_ => (_name_) ; | (_name_ . _var-spec_) (canonical) (defun recvar-specp (v) (or (null v) (namep v) (and (consp v) (namep (car v)) (recvar-specp (cdr v))))) (defun get-var-names (var-spec) (if (consp var-spec) (cons (if (consp (car var-spec)) (caar var-spec) (car var-spec)) (get-var-names (cdr var-spec))) (if (null var-spec) nil (list var-spec)))) (defun canonicalize-var-bindings (var-spec) (if (consp var-spec) (cons (if (consp (car var-spec)) (car var-spec) (list (car var-spec))) (canonicalize-var-bindings (cdr var-spec))) (if (null var-spec) nil (list (list var-spec))))) ; _entry_ ::= (:pat _pat_ (canonical) ; [:repl _repl_=_pat_] ; [:mult _mult_=*] ; [:vars _var-spec_=()] ; [:recvars _recvar-spec_=()]) ; | (:carpat _pat_ => (:pat (_pat_ . %cdr%) ; [:repl _repl_=_pat_] :repl (_repl_ . %cdr%) ; [:mult _mult_=*] :recvars (%cdr% . _recvar-spec_) ; [:vars _var-spec_=()] :vars _var-spec_ :mult _mult_) ; [:recvars _recvar-spec_=()]) (defmacro quote-rewrite-entry (&key (pat '() patp) (carpat '() carpatp) (repl '() replp) (mult '*) (vars '()) (recvars '())) (declare (xargs :guard (and (multiplicity-specp mult) (var-specp vars) (recvar-specp recvars) (not (and patp carpatp)) (or (and patp (not (member pat (get-var-names recvars)))) (and carpatp (not (member carpat (get-var-names recvars))))) (not (intersectp-eq (get-var-names vars) (get-var-names recvars)))))) (let* ((cdrvar '%cdr-reserved%) (nrcvar-names (get-var-names vars)) (recvar-names (append (if carpatp (list cdrvar) '()) (get-var-names recvars))) (var-names (append nrcvar-names recvar-names)) (pat (if patp pat (cons carpat cdrvar))) (repl (if replp (if carpatp (cons repl cdrvar) repl) pat))) `(list* ',(multiplicity-spec-to-noninf-range mult) (quote-rewrite-var-entry-lst . ,(canonicalize-var-bindings vars)) ',var-names ',pat ',repl))) ; see _var-spec_ above (defmacro quote-rewrite-var-entry-lst (&rest bindings) (if (endp bindings) ''nil `(cons (quote-rewrite-var-entry ,(car bindings)) (quote-rewrite-var-entry-lst . ,(cdr bindings))))) (defmacro quote-rewrite-var-entry (v) (declare (xargs :guard (and (consp v) (namep (car v))))) `(cons ',(car v) (quote-rewrite-def . ,(cdr v)))) ; and now to put the more convenient language on top of the code rewriting: ; for "export" (defmacro er-rewrite-form (form &rest def) `(rewrite-fn ,form (quote-rewrite-def . ,def) state)) ; EXAMPLES, for understanding the semantics more precisely: #| ACL2 !>(er-rewrite-form 'a) A ACL2 !>(er-rewrite-form 'a (:pat a :repl b)) B ACL2 !>(er-rewrite-form 'a (:seq (:pat a :repl b) (:pat b :repl c))) C ACL2 !>(er-rewrite-form '(a . b) (:seq (:pat a :repl b) (:pat b :repl c))) (C . C) ACL2 !>(er-rewrite-form '(a . b) (:simul (:pat a :repl b) (:pat b :repl c))) (B . C) ACL2 !>(er-rewrite-form '(b . a) (:simul (:pat a :repl b) (:pat b :repl c))) (C . B) ACL2 !>(er-rewrite-form '(+ (fn1 42) (fn1 53)) (:pat (fn1 %) :repl (fn2 %) :vars (%))) (+ (FN2 42) (FN2 53)) ACL2 !>(er-rewrite-form '(+ (fn1 42) (fn1 53)) (:pat (fn1 %) :repl (fn2 %) :vars %)) (+ (FN2 42) (FN2 53)) ACL2 !>(er-rewrite-form '(+ (fn1 (fn1 42)) (fn1 53)) (:pat (fn1 %) :repl (fn2 %) :vars %)) (+ (FN2 (FN1 42)) (FN2 53)) ACL2 !>(er-rewrite-form '(+ (fn1 (fn1 42)) (fn1 53)) (:pat (fn1 %) :repl (fn2 %) :recvars %)) (+ (FN2 (FN2 42)) (FN2 53)) ACL2 !>(er-rewrite-form '(+ (stuff fn1 42) (fn1 42)) (:pat (fn1 %) :repl (fn2 %) :vars %)) (+ (STUFF FN2 42) (FN2 42)) ACL2 !>(er-rewrite-form '(+ (stuff fn1 42) (fn1 42)) (:pat ((fn1 %1) . %2) :repl ((fn2 %1) . %2) :vars (%1 %2))) (+ (STUFF FN1 42) (FN2 42)) ACL2 !>(er-rewrite-form '(+ (stuff fn1 42) (fn1 42) (fn1 53)) (:pat ((fn1 %1) . %2) :repl ((fn2 %1) . %2) :vars (%1 %2))) (+ (STUFF FN1 42) (FN2 42) (FN1 53)) ACL2 !>(er-rewrite-form '(+ (stuff fn1 42) (fn1 42) (fn1 53)) (:pat ((fn1 %1) . %2) :repl ((fn2 %1) . %2) :recvars (%1 %2))) (+ (STUFF FN1 42) (FN2 42) (FN2 53)) |# ; this demonstrates that in order to rewrite a function call in a more ; robust way, we should do ; (:pat ((old-fn . %params%) . %cdr%) ; :repl ((new-fn . %params%) . %cdr%) ; :vars %params% :recvars %cdr%) ; rather than ; (:pat (old-fn . %params%) ; :repl (new-fn . %params%) ; :vars %params%) ; but if we do it this way, will we match a function called in the ; top level of a function? consider (defun x (v) (y v)). rather than ; rewriting (y v) at the top level, we shall rewrite ((y v)), which is the ; last cons of the defun. we will use this below in ; compute-copy-defun+rewrite. ; ; however, we offer :carpat as a shortcut for such specifications. the ; following are equivalent: #| ACL2 !>(er-rewrite-form '(+ (stuff fn1 42) (fn1 42) (fn1 53)) (:pat ((fn1 %1) . %2) :repl ((fn2 %1) . %2) :recvars (%1 %2))) (+ (STUFF FN1 42) (FN2 42) (FN2 53)) ACL2 !>(er-rewrite-form '(+ (stuff fn1 42) (fn1 42) (fn1 53)) (:carpat (fn1 %1) :repl (fn2 %1) :recvars %1)) (+ (STUFF FN1 42) (FN2 42) (FN2 53)) |# ; also, note that I, by convention, wrap my variables in %% to make them ; stand out. but any non-keyword, non-nil symbol can be a variable. ; and now we want to support rewriting function definitions: ; for "export" ; ; looks up a function definition, returning it in an error triple (defun get-defun (name state) (let* ((ev-wrld (acl2::decode-logical-name name (w state))) (cltl-command (and ev-wrld (let ((cltl-cmd (getprop 'cltl-command 'global-value nil 'current-acl2-world ev-wrld))) (and (consp cltl-cmd) (equal (car cltl-cmd) 'defuns) (= (len cltl-cmd) 4) cltl-cmd))))) (and cltl-command (let* ((mode (second cltl-command)) (defuns-body (fourth cltl-command)) (ll (cadr defuns-body)) (tail (cddr defuns-body)) (stobjs (remove nil (getprop name 'stobjs-in nil 'current-acl2-world ev-wrld))) (dec `(declare (xargs :mode ,mode :stobjs ,stobjs)))) `(defun ,name ,ll ,dec . ,tail))))) ; for "export" ; ; compute the ,defun-like event to execute if we want to define ,dst to be ; like ,src except for rewriting the code according to ,rwdef. ; ; ,src and ,dst may certainly be the same ; ; as mentioned above, ,rwdef is applied to the body in a singleton list; ; e.g. (defun x (v) (y v)) applies rwdef to ((y v)), so if we want to, for ; example, put a let around the body, we would use either ; (:pat (%body%) ; :repl ((let (...) %body%)) ; :vars %body%) ; or ; (:pat %bodycons% ; :repl ((let (...) . %bodycons%)) ; notice the dot! ; :vars %bodycons%) ; or ; (:carpat %body% ; :repl (let (...) %body%) ; :vars %body%) (defun compute-copy-defun+rewrite (src dst rwdef defun-like state) (if (and (null rwdef) (eq src dst)) (value '(value-triple :nothing-to-do)) (let* ((src-defun (get-defun src state))) (if src-defun (value (let* ((tuple (cddr src-defun)) ; remove 'defun and name (bodycons (last tuple)) (tuple-no-body (butlast tuple 1))) (if (null rwdef) (list* defun-like dst tuple) `(make-event (er-let* ((b2 (er-rewrite-form ',bodycons . ,rwdef))) (value `(,',defun-like ,',dst ,@',tuple-no-body . ,b2))))))) (er soft 'compute-copy-defun+rewrite "Illegal or missing defun for ~x0." src))))) ; for "export" ; ; asserts that the definition of a function at certify-book time ; time matches that at include-book time. this is an extra check that ; can be useful in the presence of redefinitions. (defmacro assert-include-defun-matches-certify-defun (name) (declare (xargs :guard (symbolp name))) `(make-event `(acl2::assert-event (let ((certify-time-defun ',(get-defun ',name state)) (include-time-defun (get-defun ',',name state))) (or (equal certify-time-defun include-time-defun) (cw "Certify time def: ~x0~%Include time def: ~x1~%" certify-time-defun include-time-defun))) :on-skip-proofs t))) ; for "export" ; ; defun ,dst to be like ,src except for rewriting the code according to ; ,rewrite-spec ; ; ,src and ,dst may be the same (if redefinition allowed) ; ; see compute-copy-defun+rewrite for more info ; (defmacro copy-defun+rewrite (src dst &rest rewrite-spec) (declare (xargs :guard (and (symbolp src) (symbolp dst)))) `(progn (assert-include-defun-matches-certify-defun ,src) (make-event (compute-copy-defun+rewrite ',src ',dst ',rewrite-spec 'defun state)))) ; for "export" ; ; defun ,dst to be like ,src ; ; ,src and ,dst may be the same (if redefinition allowed) ; ; see compute-copy-defun+rewrite for more info ; (defmacro copy-defun (src dst) `(copy-defun+rewrite ,src ,dst))