other
(in-package "STD")
other
(include-book "define")
other
(include-book "tools/flag" :dir :system)
other
(set-state-ok t)
other
(program)
other
(defxdoc defines :parents (std/util mutual-recursion) :short "A very fine alternative to @(see mutual-recursion)." :long "<p>@('Defines') is a sophisticated macro for introducing mutually recursive functions using @(see define). It gives you:</p> <ul> <li>The usual benefits of @('define')—@(see extended-formals), concise @(see xargs) syntax, @(see returns-specifiers), @(see xdoc) integration, easy inlining, and so on.</li> <li>Automatic @(see acl2::make-flag) integration, so you can easily prove inductive theorems about your new definitions.</li> </ul> <h3>Examples</h3> @({ (defines terms :parents (syntax) :short "Example of terms in some simple, numeric language." (define my-termp (x) (if (atom x) (natp x) (and (symbolp (car x)) (my-term-listp (cdr x)))) /// (defthm natp-when-my-termp (implies (atom x) (equal (my-termp x) (natp x)))) (defthm my-termp-of-cons (equal (my-termp (cons fn args)) (and (symbolp fn) (my-term-listp args))))) (define my-term-listp (x) (if (atom x) t (and (my-termp (car x)) (my-term-listp (cdr x)))) /// (deflist my-term-listp (x) (my-termp x) :already-definedp t))) (defines flattening :parents (terms) :short "Collect up the arguments of terms." (define my-flatten-term ((x my-termp)) :flag term :returns (numbers true-listp :rule-classes :type-prescription) (if (atom x) (list x) (my-flatten-term-list (cdr x)))) (define my-flatten-term-list ((x my-term-listp)) :flag list :returns (numbers true-listp :rule-classes :type-prescription) (if (atom x) nil (append (my-flatten-term (car x)) (my-flatten-term-list (cdr x))))) /// (defthm-flattening-flag (defthm nat-listp-of-my-flatten-term (implies (my-termp x) (nat-listp (my-flatten-term x))) :flag term) (defthm nat-listp-of-my-flatten-term-list (implies (my-term-listp x) (nat-listp (my-flatten-term-list x))) :flag list))) }) <h3>Usage</h3> <p>The general form of @(see defines) is:</p> @({ (defines clique-name [global options] (define f1 ...) ... (define fN ...) [/// other-events]) }) <p>The @('clique-name') may be any symbol—we often just use the name of the first function in the clique, but this is not required. The name is used for documentation purposes, and also (by default) is used to name the flag function that will be introduced by @(see acl2::make-flag).</p> <p>The global options each have the form @(':name value'), and we describe these options below. We usually prefer to put these options at the front of the @('defines') form, as shown above, but the options can be put anywhere until the @('///') form.</p> <p>There must be at least two @('define') forms. These can use the usual @(see define) syntax, and mostly this all works out naturally. The most significant caveats have to do with how we try to prove the theorems for @(':returns') specifiers (see below).</p> <p>The @('other-events') are a structuring device that allow you to associate any arbitrary events. These events are submitted after the definitions, flag function, etc., have been processed. All of the functions in the clique are left enabled while processing these events.</p> <p>Note that individual @('define')s can have their own other-events. All of these individual sections are processed (with their own local scopes) before any global @('other-events').</p> <h4>Global Options</h4> <dl> <dt>:verbosep bool</dt> <dd>Normally most output is suppressed, but this can make it hard to understand failures. You can enable @(':verbosep t') for better debugging.</dd> <dt>:mode</dt> <dt>:guard-hints, :guard-debug</dt> <dt>:verify-guards val</dt> <dd>The value @('val') may be one of the following: @('t'), @('nil'), or @(':after-returns'). The first two correspond to what is described in @(see xargs). The keyword @(':after-returns') indicates that the guards of the functions are to be verified after the @(see returns-specifiers).</dd> <dt>:well-founded-relation, :measure-debug, :hints, :otf-flg</dt> <dt>:ruler-extenders</dt> <dd>In an ordinary @(see mutual-recursion), each of these @(see xargs) style options can be attached to any @('defun') in the clique. But we usually think of these as global options to the whole clique, so we make them available as top-level options to the @('defines') form. In the implementation, we just attach these options to the first @('defun') form generated, except for @(':ruler-extenders'), which we apply to all of the defuns.</dd> <dt>:prepwork</dt> <dd>Arbitrary events to be submitted before the definitions. Typically this might include @(see in-theory) calls, one-off supporting lemmas, etc. Prepwork is not automatically @(see local), so lemmas and theory changes should usually be explicitly made @(see local).</dd> <dd>Note that each individual @('define') may have its own @(':prepwork') section. All of these sections will be appended together in order, with the global @(':prepwork') coming first, and submitted before the definitions.</dd> <dt>:returns-hints hints</dt> <dt>:returns-no-induct bool</dt> <dd>You can give hints for the inductive @(':returns') theorem. Alternately, if you know you don't need to prove the returns theorems inductively, you can set @(':returns-no-induct t'), which may improve performance.</dd> <dt>:parents, :short, :long</dt> <dd>These are global @(see xdoc) options that will be associated with the @('clique-name') for this @('defines').</dd> <dd>Note that each individual @('define') may separately have its own documentation and rest-events. As a convenience, if global documentation is provided while individual documentation is not, a basic topic will be created whose @(':parents') point at the @('clique-name').</dd> <dd>A corner case is when the @('clique-name') agrees with an individual function's name. In this case we try to grab the documentation from <i>either</i> the named function or the global options. To prevent confusion, we cause an error if documentation is provided in both places.</dd> <dt>:ignore-ok val</dt> <dt>:irrelevant-formals-ok val</dt> <dt>:bogus-ok val</dt> <dd>Submit @(see set-ignore-ok), @(see set-irrelevant-formals-ok), and/or @(see set-bogus-mutual-recursion-ok) forms before the definitions. These options are @(see local) to the definitions; they do not affect the @('other-events') or any later events.</dd> <dt>:flag name</dt> <dd>You can also use @(':flag nil') to suppress the creation of a flag function. Alternately, you can use this option to customize the name of the flag function. The default is inferred from the @('clique-name'), i.e., it will look like @('<clique-name>-flag').</dd> <dt>:flag-var var</dt> <dt>:flag-defthm-macro name</dt> <dt>:flag-hints hints</dt> <dd>Control the flag variable name, flag macro name, and hints for @(see acl2::make-flag).</dd> <dt>:flag-local bool</dt> <dd>By default the flag function is created locally. This generally improves performance when later including your book, and is appropriate when all of your flag-based reasoning about the function is done in @(':returns') specifiers and in the @('///') section. If you need the flag function and its macros to exist beyond the @('defines') form, set @(':flag-local nil').</dd> <dt>:progn bool</dt> <dd>By default everything is submitted in an @('(encapsulate nil ...)'). If you set @(':progn t'), then we will instead submit everything in a @('progn'). This mainly affects what @('local') means within the @('///') section. This may slightly improve performance by avoiding the overhead of @(see encapsulate), and is mainly meant as a tool for macro developers.</dd> </dl>")
other
(defconst *defines-xargs-keywords* '(:ruler-extenders :guard-hints :hints :guard-debug :measure-debug :well-founded-relation :otf-flg :mode :verify-guards))
other
(defconst *defines-keywords* (append '(:ignore-ok :irrelevant-formals-ok :bogus-ok :parents :short :long :prepwork :flag :flag-var :flag-local :flag-defthm-macro :flag-hints :returns-no-induct :returns-hints :verbosep :progn) *defines-xargs-keywords*))
parse-definesfunction
(defun parse-defines (ctx forms extra-keywords world) (b* (((when (atom forms)) nil) (form1 (car forms)) ((unless (and (consp form1) (consp (cdr form1)) (eq (first form1) 'define))) (er hard? ctx "Expected a list of ~s0 forms, but found ~x1." 'define (car forms))) ((cons name args) (cdr form1)) (guts1 (parse-define name args extra-keywords world))) (cons guts1 (parse-defines ctx (cdr forms) extra-keywords world))))
collect-prepworksfunction
(defun collect-prepworks (gutslist) (if (atom gutslist) nil (append (getarg :prepwork nil (defguts->kwd-alist (car gutslist))) (collect-prepworks (cdr gutslist)))))
collect-macrosfunction
(defun collect-macros (gutslist) (b* (((when (atom gutslist)) nil) (macros1 (defguts->macro (car gutslist))) ((unless macros1) (collect-macros (cdr gutslist)))) (cons macros1 (collect-macros (cdr gutslist)))))
collect-ignore-oksfunction
(defun collect-ignore-oks (gutslist) (b* (((when (atom gutslist)) nil) (kwd-alist (defguts->kwd-alist (car gutslist))) ((when (assoc :ignore-ok kwd-alist)) (cons (cdr (assoc :ignore-ok kwd-alist)) (collect-ignore-oks (cdr gutslist))))) (collect-ignore-oks (cdr gutslist))))
collect-irrelevant-formals-oksfunction
(defun collect-irrelevant-formals-oks (gutslist) (b* (((when (atom gutslist)) nil) (kwd-alist (defguts->kwd-alist (car gutslist))) ((when (assoc :irrelevant-formals-ok kwd-alist)) (cons (cdr (assoc :irrelevant-formals-ok kwd-alist)) (collect-irrelevant-formals-oks (cdr gutslist))))) (collect-irrelevant-formals-oks (cdr gutslist))))
make-ignore-eventsfunction
(defun make-ignore-events (ctx kwd-alist gutslist) (b* ((ignore-oks (collect-ignore-oks gutslist)) (ignore-oks (if (assoc :ignore-ok kwd-alist) (cons (cdr (assoc :ignore-ok kwd-alist)) ignore-oks) ignore-oks)) (ignore-oks (remove-duplicates ignore-oks)) ((unless (or (atom ignore-oks) (atom (cdr ignore-oks)))) (er hard? ctx "Conflicting :ignore-ok settings.")) (irrel-oks (collect-irrelevant-formals-oks gutslist)) (irrel-oks (if (assoc :irrel-ok kwd-alist) (cons (cdr (assoc :ignore-ok kwd-alist)) irrel-oks) irrel-oks)) (irrel-oks (remove-duplicates irrel-oks)) ((unless (or (atom irrel-oks) (atom (cdr irrel-oks)))) (er hard? ctx "Conflicting :irrel-ok settings.")) (bogus-okp (cdr (assoc :bogus-ok kwd-alist)))) (append (and (car ignore-oks) '((set-ignore-ok t))) (and (car irrel-oks) '((set-irrelevant-formals-ok t))) (and bogus-okp '((set-bogus-mutual-recursion-ok t))))))
collect-main-defsfunction
(defun collect-main-defs (gutslist) (if (atom gutslist) nil (cons (defguts->main-def (car gutslist)) (collect-main-defs (cdr gutslist)))))
collect-macro-aliasesfunction
(defun collect-macro-aliases (gutslist) (if (atom gutslist) nil (append (add-macro-aliases-from-guts (car gutslist)) (collect-macro-aliases (cdr gutslist)))))
collect-guts-alist-extsfunction
(defun collect-guts-alist-exts (gutslist) (if (atom gutslist) nil (cons (extend-define-guts-alist (car gutslist)) (collect-guts-alist-exts (cdr gutslist)))))
collect-names-from-gutsfunction
(defun collect-names-from-guts (gutslist) (if (atom gutslist) nil (cons (defguts->name (car gutslist)) (collect-names-from-guts (cdr gutslist)))))
collect-defines-to-disablefunction
(defun collect-defines-to-disable (gutslist) (b* (((when (atom gutslist)) nil) ((defguts guts1) (car gutslist)) (enabled-p (getarg :enabled nil guts1.kwd-alist)) ((when enabled-p) (collect-defines-to-disable (cdr gutslist)))) (cons guts1.name-fn (collect-defines-to-disable (cdr gutslist)))))
make-fn-defsectionsfunction
(defun make-fn-defsections (guts cliquename process-returns divide-defsections) (b* (((defguts guts) guts) (short (getarg :short nil guts.kwd-alist)) (long (getarg :long nil guts.kwd-alist)) (parents (getarg :parents nil guts.kwd-alist)) (parents (if (assoc :parents guts.kwd-alist) parents (and cliquename (not (eq cliquename guts.name)) (list cliquename)))) (divide-defsections (and process-returns divide-defsections guts.rest-events)) (rest-events `((set-define-current-function ,STD::GUTS.NAME) (with-output :stack :pop (progn . ,STD::GUTS.REST-EVENTS))))) (mv `(defsection ,STD::GUTS.NAME ,@(AND STD::PARENTS `(:PARENTS ,STD::PARENTS)) ,@(AND STD::SHORT `(:SHORT ,STD::SHORT)) ,@(AND STD::LONG `(:LONG ,STD::LONG)) ,@(AND STD::PROCESS-RETURNS `((STD::MAKE-EVENT (LET* ((STD::WORLD (STD::W STD::STATE)) (STD::EVENTS (STD::RETURNSPEC-THMS ',STD::GUTS.NAME ',STD::GUTS.NAME-FN ',STD::GUTS.RETURNSPECS STD::WORLD))) `(STD::WITH-OUTPUT :STACK :POP (PROGN . ,STD::EVENTS)))))) ,@(AND (NOT STD::DIVIDE-DEFSECTIONS) STD::REST-EVENTS)) `(,@(AND STD::DIVIDE-DEFSECTIONS `((STD::MAKE-EVENT (LET ((STD::OLD-TOPIC (XDOC::FIND-TOPIC ',STD::GUTS.NAME (XDOC::GET-XDOC-TABLE (STD::W STD::STATE)))) (STD::DEFSECTION-NAME ',(PACKN-POS (LIST STD::GUTS.NAME 'STD::-REST-EVENTS) STD::GUTS.NAME)) (STD::EXTENSION ',STD::GUTS.NAME) (STD::REST-EVENTS ',STD::REST-EVENTS)) `(STD::DEFSECTION ,STD::DEFSECTION-NAME ,@(AND STD::OLD-TOPIC `(:EXTENSION ,STD::EXTENSION)) ,@STD::REST-EVENTS))))) (with-output :on (error) ,(STD::ADD-SIGNATURE-FROM-GUTS STD::GUTS))))))
collect-fn-defsectionsfunction
(defun collect-fn-defsections (gutslist cliquename process-returns divide-defsections) (if (atom gutslist) (mv nil nil) (b* (((mv section1 rest1) (make-fn-defsections (car gutslist) cliquename process-returns divide-defsections)) ((mv sections rest2) (collect-fn-defsections (cdr gutslist) cliquename process-returns divide-defsections))) (mv (cons section1 sections) (append rest1 rest2)))))
guts->flagfunction
(defun guts->flag (guts) (or (cdr (assoc :flag (defguts->kwd-alist guts))) (defguts->name guts)))
collect-flag-mappingfunction
(defun collect-flag-mapping (gutslist) (if (atom gutslist) nil (cons (list (defguts->name-fn (car gutslist)) (guts->flag (car gutslist))) (collect-flag-mapping (cdr gutslist)))))
returnspec-thm-bodiesfunction
(defun returnspec-thm-bodies (fnname binds retspecs world) (b* (((when (atom retspecs)) nil) (body (returnspec-thm-body fnname binds (car retspecs) world)) (rest (returnspec-thm-bodies fnname binds (cdr retspecs) world))) (if (eq body t) rest (cons body rest))))
returnspec-thm-ruleclasses-add-corollaryfunction
(defun returnspec-thm-ruleclasses-add-corollary (classes body) (b* (((when (atom classes)) nil) (class (car classes)) (rest (returnspec-thm-ruleclasses-add-corollary (cdr classes) body)) ((when (atom class)) (cons `(,CLASS :corollary ,STD::BODY) rest)) ((when (assoc-keyword :corollary (cdr class))) (cons class rest))) (cons `(,(CAR CLASS) :corollary ,STD::BODY . ,(CDR CLASS)) rest)))
returnspec-thm-ruleclassesfunction
(defun returnspec-thm-ruleclasses (fnname binds retspecs world) (b* (((when (atom retspecs)) nil) (body (returnspec-thm-body fnname binds (car retspecs) world)) (rest (returnspec-thm-ruleclasses fnname binds (cdr retspecs) world)) (classes (returnspec->rule-classes (car retspecs))) (classes (if (and (atom classes) classes) (list classes) classes)) (classes-with-corollaries (returnspec-thm-ruleclasses-add-corollary classes body))) (append classes-with-corollaries rest)))
returnspec-thm-hintsfunction
(defun returnspec-thm-hints (retspecs) (if (atom retspecs) nil (append (returnspec->hints (car retspecs)) (returnspec-thm-hints (cdr retspecs)))))
find-first-string-hintfunction
(defun find-first-string-hint (retspec-hints) (if (atom retspec-hints) nil (if (and (consp (car retspec-hints)) (stringp (caar retspec-hints))) (caar retspec-hints) (find-first-string-hint (cdr retspec-hints)))))
returnspecs-flag-entriesfunction
(defun returnspecs-flag-entries (retspecs flag fnname fnname-fn binds body-subst ruleclass-subst retspec-config world) (b* (((when (atom retspecs)) nil) (formula (returnspec-thm-body fnname-fn binds (car retspecs) world)) ((when (eq formula t)) (returnspecs-flag-entries (cdr retspecs) flag fnname fnname-fn binds body-subst ruleclass-subst retspec-config world)) ((returnspec x) (car retspecs)) (subgoal (find-first-string-hint x.hints)) (strsubst (returnspec-strsubst fnname fnname-fn)) (hints-sub-returnnames (getarg :hints-sub-returnnames (getarg :hints-sub-returnnames nil retspec-config) x.opts)) (- (and subgoal (er hard? 'defines "Error in returnspec theorems: unless using ~x0,~ subgoal hints should be given using the global ~x1 ~ option. Computed hints may be given on individual ~ :returns entries. Found hint for ~x2." :returns-no-induct :returns-hints subgoal)))) (cons `(defthm ,(STD::INTERN-IN-PACKAGE-OF-SYMBOL (CONCATENATE 'STRING "RETURN-TYPE-OF-" (SYMBOL-NAME STD::FNNAME) "." (SYMBOL-NAME STD::X.NAME)) STD::FNNAME) ,(STD::RETURNSPEC-SUBLIS STD::BODY-SUBST NIL STD::FORMULA) :hints ,(STD::RETURNSPEC-SUBLIS (IF STD::HINTS-SUB-RETURNNAMES STD::RULECLASS-SUBST STD::BODY-SUBST) STD::STRSUBST STD::X.HINTS) :rule-classes ,(STD::RETURNSPEC-SUBLIS STD::RULECLASS-SUBST NIL STD::X.RULE-CLASSES) :flag ,STD::FLAG) (returnspecs-flag-entries (cdr retspecs) flag fnname fnname-fn binds body-subst ruleclass-subst retspec-config world))))
fn-returnspec-flag-entriesfunction
(defun fn-returnspec-flag-entries (guts retspec-config world) (b* (((defguts guts) guts) (flag (guts->flag guts)) (formals (look-up-formals guts.name-fn world)) (fncall `(,STD::GUTS.NAME-FN . ,STD::FORMALS)) (binds (b* ((names (returnspeclist->names guts.returnspecs)) (ignorable-names (make-symbols-ignorable names))) (if (consp (cdr ignorable-names)) `((mv . ,STD::IGNORABLE-NAMES) ,STD::FNCALL) `(,(CAR STD::IGNORABLE-NAMES) ,STD::FNCALL)))) ((mv body-subst ruleclass-subst) (returnspec-return-value-subst guts.name guts.name-fn formals (returnspeclist->names guts.returnspecs)))) (returnspecs-flag-entries guts.returnspecs flag guts.name guts.name-fn binds body-subst ruleclass-subst retspec-config world)))
collect-returnspec-flag-thmsfunction
(defun collect-returnspec-flag-thms (gutslist retspec-config world) (if (atom gutslist) nil (append (fn-returnspec-flag-entries (car gutslist) retspec-config world) (collect-returnspec-flag-thms (cdr gutslist) retspec-config world))))
returnspec-mrec-default-default-hintfunction
(defun returnspec-mrec-default-default-hint (fnname id world) (let ((fns (recursivep fnname t world))) (and (eql 0 (access clause-id id :forcing-round)) (equal '(1) (access clause-id id :pool-lst)) `(:computed-hint-replacement ((and stable-under-simplificationp (expand-calls . ,STD::FNS))) :in-theory (disable . ,STD::FNS)))))
returnspec-mrec-default-hintsfunction
(defun returnspec-mrec-default-hints (fnname world) (let ((entry (cdr (assoc 'returnspec-mrec (table-alist 'default-hints-table world))))) (subst fnname 'fnname entry)))
set-returnspec-mrec-default-hintsmacro
(defmacro set-returnspec-mrec-default-hints (hint) `(table default-hints-table 'returnspec-mrec ',STD::HINT))
other
(set-returnspec-mrec-default-hints ((returnspec-mrec-default-default-hint 'fnname id world)))
returnspec-flag-thmfunction
(defun returnspec-flag-thm (defthm-macro gutslist returns-hints world) (let* ((thms (collect-returnspec-flag-thms gutslist (cdr (assoc 'returnspec-config (table-alist 'define world))) world)) (hints (if (eq returns-hints :none) (returnspec-mrec-default-hints (defguts->name-fn (car gutslist)) world) returns-hints))) (if thms `(,STD::DEFTHM-MACRO ,@STD::THMS :skip-others t :hints ,STD::HINTS) `(value-triple :skipped))))
other
(def-primitive-aggregate defines-guts
(name gutslist
kwd-alist
flag-mapping
flag-defthm-macro
guards-after-returns))
get-defines-alistfunction
(defun get-defines-alist (world) "Look up information about the current defines in the world." (cdr (assoc 'defines-alist (table-alist 'define world))))
extend-defines-alistfunction
(defun extend-defines-alist (guts) `(table define 'defines-alist (cons (cons ',(STD::DEFINES-GUTS->NAME STD::GUTS) ',STD::GUTS) (get-defines-alist world))))
gutslist-find-hintsfunction
(defun gutslist-find-hints (gutslist) (if (atom gutslist) nil (or (getarg :hints nil (defguts->kwd-alist (car gutslist))) (gutslist-find-hints (cdr gutslist)))))
apply-ruler-extenders-to-defsfunction
(defun apply-ruler-extenders-to-defs (ruler-extenders defs) (if (atom defs) nil (cons (append (take 3 (car defs)) `((declare (xargs :ruler-extenders ,STD::RULER-EXTENDERS))) (nthcdr 3 (car defs))) (apply-ruler-extenders-to-defs ruler-extenders (cdr defs)))))
maybe-inject-global-docsfunction
(defun maybe-inject-global-docs (name parentsp parents short long gutslist) (b* ((__function__ 'maybe-inject-global-docs) ((when (atom gutslist)) nil) (rest (maybe-inject-global-docs name parentsp parents short long (cdr gutslist))) (guts1 (car gutslist)) ((defguts guts1) guts1) ((unless (equal guts1.name name)) (cons (car gutslist) rest)) (sub-short (getarg :short nil guts1.kwd-alist)) (sub-long (getarg :long nil guts1.kwd-alist)) (sub-parents (getarg :parents nil guts1.kwd-alist)) (sub-parentsp (consp (assoc :parents guts1.kwd-alist))) ((when (and parentsp sub-parentsp)) (raise "Can't give :parents in (~x1 ~x0 ...) because there are global ~ :parents in the (~x2 ~x0 ...) form." name 'define 'defines)) ((when (and short sub-short)) (raise "Can't give :short in (~x1 ~x0 ...) because there is a global ~ :short in the (~x2 ~x0 ...) form." name 'define 'defines)) ((when (and long sub-long)) (raise "Can't give :long in (~x1 ~x0 ...) because there is a global ~ :long in the (~x2 ~x0 ...) form." name 'define 'defines)) (kwd-alist guts1.kwd-alist) (kwd-alist (remove1-assoc :short kwd-alist)) (kwd-alist (remove1-assoc :long kwd-alist)) (kwd-alist (remove1-assoc :parents kwd-alist)) (kwd-alist (acons :short (or short sub-short) kwd-alist)) (kwd-alist (acons :long (or long sub-long) kwd-alist)) (kwd-alist (acons :parents (if parentsp parents sub-parents) kwd-alist)) (new-guts (change-defguts guts1 :kwd-alist kwd-alist))) (cons new-guts rest)))
split-///-definesfunction
(defun split-///-defines (name args) (b* (((mv main-stuff rest-events) (split-/// name args)) ((mv rest-events1 rest-events2) (split-/// name rest-events)) ((mv rest-events1 rest-events2) (if rest-events2 (mv rest-events1 rest-events2) (mv nil rest-events1)))) (mv main-stuff rest-events1 rest-events2)))
defines-fnfunction
(defun defines-fn (name args world) (declare (xargs :guard (plist-worldp world))) (b* ((__function__ 'defines) ((unless (symbolp name)) (raise "Expected name to be a symbol, but found ~x0." name)) ((mv main-stuff rest-events1 rest-events2) (split-///-defines name args)) ((mv kwd-alist defs) (extract-keywords name *defines-keywords* main-stuff nil)) (gutslist (parse-defines name defs '(:flag) world)) ((unless (consp (cdr gutslist))) (raise "Error in ~x0: expected more than one function." name)) (verify-guards-after-returns (eq (cdr (assoc :verify-guards kwd-alist)) :after-returns)) (verify-guards-name (and verify-guards-after-returns (defguts->name-fn (car gutslist)))) (short (getarg :short nil kwd-alist)) (long (getarg :long nil kwd-alist)) (parents (getarg :parents nil kwd-alist)) (parents-p (consp (assoc :parents kwd-alist))) (parents (if parents-p parents (get-default-parents world))) (fnnames (collect-names-from-guts gutslist)) (want-xdoc-p (or short long parents-p)) ((mv short long parents kwd-alist gutslist) (if (and want-xdoc-p (member name fnnames)) (mv nil nil nil (remove1-assoc :parents (remove1-assoc :short (remove1-assoc :long kwd-alist))) (maybe-inject-global-docs name parents-p parents short long gutslist)) (mv short long parents kwd-alist gutslist))) (prepwork (getarg :prepwork nil kwd-alist)) ((unless (true-listp prepwork)) (raise "Error in ~x0: expected :prepwork to be a true-listp, but found ~x1." name prepwork)) (prepwork (append prepwork (collect-prepworks gutslist))) (macros (collect-macros gutslist)) (set-ignores (make-ignore-events name kwd-alist gutslist)) (extra-xargs (get-xargs-from-kwd-alist (if verify-guards-after-returns (put-assoc :verify-guards 'nil kwd-alist) kwd-alist))) (ruler-extenders (getarg :ruler-extenders nil kwd-alist)) (orig-defs (collect-main-defs gutslist)) (final-defs (if extra-xargs (cons (append (take 3 (car orig-defs)) `((declare (xargs . ,STD::EXTRA-XARGS))) (nthcdr 3 (car orig-defs))) (if ruler-extenders (apply-ruler-extenders-to-defs ruler-extenders (cdr orig-defs)) (cdr orig-defs))) orig-defs)) (mutual-rec (cons 'mutual-recursion final-defs)) (aliases (collect-macro-aliases gutslist)) (guts-table-exts (collect-guts-alist-exts gutslist)) (flag-name (if (assoc :flag kwd-alist) (cdr (assoc :flag kwd-alist)) (intern-in-package-of-symbol (concatenate 'string (symbol-name name) "-FLAG") name))) (flag-var (getarg :flag-var nil kwd-alist)) (flag-local (getarg :flag-local t kwd-alist)) (flag-defthm-macro (getarg :flag-defthm-macro nil kwd-alist)) (flag-hints (or (getarg :flag-hints nil kwd-alist) (getarg :hints nil kwd-alist) (gutslist-find-hints gutslist))) (flag-mapping (collect-flag-mapping gutslist)) ((unless (no-duplicatesp-eq (strip-cadrs flag-mapping))) (raise "Error: duplicated flag names!")) (returns-induct (and flag-name (not (getarg :returns-no-induct nil kwd-alist)))) (returns-hints (getarg :returns-hints :none kwd-alist)) ((when (and (not returns-induct) (not (eq returns-hints :none)))) (raise "Error in ~x0: returns-hints only is useful without ~ :returns-no-induct." name)) (thm-macro (and flag-name (or flag-defthm-macro (thm-macro-name flag-name)))) ((mv fn-sections sections-rest) (collect-fn-defsections gutslist (and want-xdoc-p name) (not returns-induct) (and verify-guards-after-returns (not returns-induct)))) (fns-to-disable (collect-defines-to-disable gutslist)) (defines-guts (make-defines-guts :name name :gutslist gutslist :kwd-alist kwd-alist :flag-mapping flag-mapping :flag-defthm-macro thm-macro :guards-after-returns verify-guards-after-returns)) (prognp (getarg :progn nil kwd-alist))) `(,@(IF STD::PROGNP '(PROGN) '(STD::ENCAPSULATE NIL)) (with-output :stack :pop (progn . ,STD::PREPWORK)) (defsection-progn ,STD::NAME ,@(AND STD::PARENTS-P `(:PARENTS ,STD::PARENTS)) ,@(AND STD::SHORT `(:SHORT ,STD::SHORT)) ,@(AND STD::LONG `(:LONG ,STD::LONG)) (with-output :on (error) (progn . ,STD::MACROS)) ,@(IF STD::SET-IGNORES `((STD::ENCAPSULATE NIL ,@STD::SET-IGNORES (STD::WITH-OUTPUT :STACK :POP ,STD::MUTUAL-REC))) `((STD::WITH-OUTPUT :STACK :POP ,STD::MUTUAL-REC))) (with-output :on (error) (progn . ,STD::ALIASES)) (with-output :on (error) (progn . ,STD::GUTS-TABLE-EXTS)) (with-output :on (error) ,(STD::EXTEND-DEFINES-ALIST STD::DEFINES-GUTS))) ,@(AND STD::FLAG-NAME `((STD::WITH-OUTPUT :ON (ERROR) (STD::MAKE-EVENT (IF (STD::LOGIC-MODE-P ',(STD::DEFGUTS->NAME-FN (CAR STD::GUTSLIST)) (STD::W STD::STATE)) (STD::VALUE '(FLAG::MAKE-FLAG ,STD::FLAG-NAME ,(STD::DEFGUTS->NAME-FN (CAR STD::GUTSLIST)) :FLAG-MAPPING ,STD::FLAG-MAPPING ,@(AND STD::FLAG-DEFTHM-MACRO `(:DEFTHM-MACRO-NAME ,STD::FLAG-DEFTHM-MACRO)) ,@(AND STD::FLAG-VAR `(:FLAG-VAR ,STD::FLAG-VAR)) ,@(AND STD::FLAG-LOCAL `(:LOCAL T)) ,@(AND STD::RULER-EXTENDERS `(:RULER-EXTENDERS ,STD::RULER-EXTENDERS)) :HINTS ,STD::FLAG-HINTS)) (STD::VALUE '(STD::VALUE-TRIPLE :INVISIBLE))))))) (defsection rest-events-1 ,@(AND STD::WANT-XDOC-P `(:EXTENSION ,STD::NAME)) ,@(AND STD::RETURNS-INDUCT `((STD::MAKE-EVENT (IF (STD::LOGIC-MODE-P ',(STD::DEFGUTS->NAME-FN (CAR STD::GUTSLIST)) (STD::W STD::STATE)) (LET ((STD::EVENT (STD::RETURNSPEC-FLAG-THM ',STD::THM-MACRO ',STD::GUTSLIST ',STD::RETURNS-HINTS (STD::W STD::STATE)))) `(STD::WITH-OUTPUT :STACK :POP ,STD::EVENT)) (STD::VALUE '(STD::VALUE-TRIPLE :INVISIBLE)))))) (with-output :stack :pop (progn . ,STD::REST-EVENTS1)) ,@(AND STD::VERIFY-GUARDS-AFTER-RETURNS STD::RETURNS-INDUCT `((STD::WITH-OUTPUT :STACK :POP (STD::VERIFY-GUARDS ,STD::VERIFY-GUARDS-NAME)))) ,@STD::FN-SECTIONS ,@(AND STD::VERIFY-GUARDS-AFTER-RETURNS (NOT STD::RETURNS-INDUCT) `((STD::WITH-OUTPUT :STACK :POP (STD::VERIFY-GUARDS ,STD::VERIFY-GUARDS-NAME)))) ,@STD::SECTIONS-REST (with-output :stack :pop (progn . ,STD::REST-EVENTS2))) ,@(AND (CONSP STD::FNS-TO-DISABLE) `((STD::MAKE-EVENT (IF (STD::LOGIC-MODE-P ',(STD::DEFGUTS->NAME-FN (CAR STD::GUTSLIST)) (STD::W STD::STATE)) '(STD::IN-THEORY (STD::DISABLE . ,STD::FNS-TO-DISABLE)) '(STD::VALUE-TRIPLE :INVISIBLE))))))))
definesmacro
(defmacro defines (name &rest args) (let* ((verbose-tail (member :verbosep args)) (verbosep (and verbose-tail (cadr verbose-tail)))) `(with-output :stack :push ,@(AND (NOT STD::VERBOSEP) '(:OFF :ALL)) (make-event (defines-fn ',STD::NAME ',STD::ARGS (w state))))))
other
(defxdoc defret-mutual :parents (define returns-specifiers) :short "Prove additional theorems about a mutual-recursion created using @(see defines), implicitly binding the return variables." :long "<p>@('defret-mutual') uses flag-function-based induction to prove theorems about a mutual recursion created using @(see defines). See also @(see acl2::make-flag) for information about the approach.</p> <p>@('defret-mutual') is mostly just a wrapper around the flag defthm macro created for the mutual recursion. It generates the individual theorems within the mutual induction using @(see defret), so the main features are inherited from defret, such as automatic binding of return value names and support for the @(':hyp') keyword.</p> <h5>Syntax:</h5> <p>Using the following mutual recursion as an example:</p> @({ (defines pseudo-term-vars (define pseudo-term-vars ((x pseudo-termp)) :returns (vars) ...) (define pseudo-term-list-vars ((x pseudo-term-listp)) :returns (vars) ...)) }) <p>Then we can use @('defret-mutual') as follows:</p> @({ (defret-mutual symbol-listp-of-pseudo-term-vars (defret symbol-listp-of-pseudo-term-vars (symbol-listp vars) :hints ... :fn pseudo-term-vars) (defret symbol-listp-of-pseudo-term-list-vars (symbol-listp vars) :hints ... :fn pseudo-term-list-vars) :mutual-recursion pseudo-term-vars ...) }) <p>If the @(':mutual-recursion') keyword is omitted, then the last mutual recursion introduced with @('defines') is assumed to be the subject.</p> <p>@('defret-mutual') supports all of the top-level options of flag defthm macros such as:</p> @({ :hints :no-induction-hint :skip-others :instructions :otf-flg }) <p>The individual @('defret') forms inside the mutual recursion support all the options supported by @(see defret), plus the @(':skip') option from the flag defthm macro, which if set to nonnil uses the theorem only as an inductive lemma for the overall mutually recursive proof, and doesn't export it.</p>")
pass-kwd-argsfunction
(defun pass-kwd-args (names kwd-alist) (if (atom names) nil (let ((look (assoc (car names) kwd-alist))) (if look `(,(CAR STD::NAMES) ,(CDR STD::LOOK) . ,(STD::PASS-KWD-ARGS (CDR STD::NAMES) STD::KWD-ALIST)) (pass-kwd-args (cdr names) kwd-alist)))))
defgutslist-findfunction
(defun defgutslist-find (name x) (if (atom x) nil (if (eq name (defguts->name (car x))) (car x) (defgutslist-find name (cdr x)))))
defret-mutual-process-defretfunction
(defun defret-mutual-process-defret (defret guts name world) (b* ((__function__ 'defret-mutual) ((defines-guts guts)) ((unless (and (<= 3 (len defret)) (member (car defret) '(defret defretd)) (symbolp (cadr defret)))) (raise "Invaid defret/defretd form in ~x0: ~x1" `(defret-mutual ,STD::NAME) defret)) (disabledp (eq (car defret) 'defretd)) (thmname (cadr defret)) (ctx `(,(CAR STD::DEFRET) ,STD::THMNAME)) (rest (cddr defret)) ((mv kwd-alist body-lst) (extract-keywords ctx '(:hyp :fn :hints :rule-classes :pre-bind :otf-flg :skip) rest nil)) ((unless (consp body-lst)) (raise "No body in ~x0" ctx)) ((unless (atom (cdr body-lst))) (raise "Extra args besides the body in ~x0" ctx)) (body (car body-lst)) (fn (cdr (assoc :fn kwd-alist))) ((unless fn) (raise "~x0: The function name must be specified (with :fn)." ctx)) (fnguts (defgutslist-find fn guts.gutslist)) ((defguts fnguts)) (flag (cadr (assoc fnguts.name-fn guts.flag-mapping))) ((unless flag) (raise "~x0: Invalid function name ~x1 (no flag mapping)." ctx fn)) (fnguts (defgutslist-find fn guts.gutslist)) ((unless fnguts) (raise "~x0: Invalid function name ~x1 (no define-guts)." ctx fn))) (append (defret-core thmname body kwd-alist disabledp fnguts world) `(:flag ,STD::FLAG :skip ,(CDR (ASSOC :SKIP STD::KWD-ALIST))))))
defret-mutual-process-defretsfunction
(defun defret-mutual-process-defrets (defrets guts name world) (if (atom defrets) nil (cons (defret-mutual-process-defret (car defrets) guts name world) (defret-mutual-process-defrets (cdr defrets) guts name world))))
defret-mutual-fnfunction
(defun defret-mutual-fn (name args world) (b* ((__function__ 'defret-mutual) ((unless (and (symbolp name) (not (keywordp name)))) (raise "Defret-mutual requires a name as the first argument.")) ((mv kwd-alist defrets) (extract-keywords `(defret-mutual ,STD::NAME) '(:mutual-recursion :hints :no-induction-hint :skip-others :instructions :otf-flg) args nil)) ((unless (consp defrets)) (raise "Defret-mutual needs at least 1 defret/defretd form.")) (defines-alist (get-defines-alist world)) (mutual-recursion (let ((look (assoc :mutual-recursion kwd-alist))) (if look (cdr look) (caar defines-alist)))) ((unless mutual-recursion) (raise "Defret-mutual needs a mutual recursion created with defines to work on.")) (guts (cdr (assoc mutual-recursion defines-alist))) ((unless guts) (raise "~x0 is not the name of a mutual recursion created with defines." mutual-recursion)) ((defines-guts guts)) ((unless guts.flag-defthm-macro) (raise "No flag defthm macro for ~x0." mutual-recursion)) (defthms (defret-mutual-process-defrets defrets guts name world))) `(,STD::GUTS.FLAG-DEFTHM-MACRO ,STD::NAME ,@STD::DEFTHMS . ,(STD::PASS-KWD-ARGS '(:HINTS :NO-INDUCTION-HINT :SKIP-OTHERS :INSTRUCTIONS :OTF-FLG) STD::KWD-ALIST))))
defret-mutualmacro
(defmacro defret-mutual (name &rest args) `(make-event (defret-mutual-fn ',STD::NAME ',STD::ARGS (w state))))