(in-package "ACL2") (include-book "huet-lang-algorithm") (include-book "merge-hint") (program) (set-state-ok t) ; Essay on the Design of Consider Hints ; [Note that this is NOT the similarly titled Essay on the ; Implementation of Consider Hint Processing!] ; :hints (("Goal" ... ; :CONSIDER ; (( ; [:pattern | :lhs ] ; [:target | :conclusion ] ; [:instance ] ; [:functional-instance ] ; ) ; ...) ; ...) ; The new hint will use second-order matching to compute a relevant ; instantiation of and that instantiation is added as a hypothesis, ; as when we supply :USE hints. ; The value of the :consider hint denotes a list of ; ``considerations.'' ; The user-level form of a consideration is ; ( ; [:pattern | :lhs ] ; [:target | :conclusion | :clause] ; [:instance ] ; [:functional-instance )] ; ) ; or just . ; We describe below some conventions for abbreviating a list of ; considerations. ; The :pattern specifies the pat used in second order matching. It is ; either a term or the keyword :lhs. The latter means ``use the lhs ; of the conclusion of .'' If :pattern is not specified, it ; defaults to :lhs. ; The :target specifies the concrete term to match. If it is ; :conclusion, we SEARCH through the conclusion of the subgoal clause ; for the first match of :pattern. If it is :clause, we SEARCH ; through the clause, from last literal to the first literal, for the ; first match of :pattern. If :target is :term, we use term and we ; don't search, we just match against term itself. If :target is not ; specified, it defaults to :clause. ; :Instance and :functional-instance specify substitutions in the ; usual form that users write them, e.g., lists of doublets. The ; first is a var-to-term substitution, the second is a ; function-symbol-to-function substitution. They are combined into a ; mixed substitution that is used by the second order pattern matcher ; as the starting substitution. That substitution must be extended in ; the match. This is a way to limit the possible matches. ; If either substitution is not specified, it defaults to nil. ; If no keywords are specified, all default as described. ; We must decide how to handle user-level input that denotes a list of ; considerations. For example, if the user writes :consider h-thm it ; means :consider (h-thm). If the user writes :consider (h-thm ; :pattern ...) it means :consider ((h-thm :pattern ...)). But if the ; user writes :consider (h-thm g-thm) it means just that: h-thm and ; g-thm are two considerations. See coerce-consideration-list. ; ; Translation-Time Processing: We check that is a named ; formula. Each is translated. If :lhs is written, we replace ; it by the translated lhs of the named formula, so :lhs will NEVER ; appear in a translated :consider hint. If :conclusion or :clause is ; written, we leave it as written, since we cannot resolve it until we ; see the goal clause. The two substitutions are combined at ; translate-time into a single psubst. ; Prove-Time Processing: Most of the work on :consider hints occurs at ; prove-time, when we use second order matching between the :pattern ; and the :target term of the subgoal clause to generate a list of ; substitutions. We rank the substitutions by syntactic criteria and ; choose the most highly ranked substitutions and produce the ; corresponding lemma instance, lmi. We do this for each of the ; consideration items listed. We are supposed to use one of the lmi's ; from each of the items. So we use all-picks to convert what is ; essentially a conjunction of disjuncts to a disjunction of ; conjuncts. Each list of lmi's is now an UNtranslated :USE hint and ; we will search for the first one that works. To do that, we convert ; each of these automatically generated :USE hints into internal form ; (analogous to the translation-time processing of :USE hints, but ; done in the waterfall without state), and then successively replace ; the :CONSIDER hint by the :USE hints and proceed. ; We anticipate that users might ultimately wish to see ALL of the ; candidate substitutions generated. After all, we choose only the ; most highly ranked ones and our rankings are entirely syntactic. We ; could have preserved all that information and included it in our ; normal output, but we decided that was too complicated and verbose. ; Instead, we expect to implement a diagnostic feature like ; show-all-matches that takes two terms, or a clause and consider ; item, and prints all of the matches. (defrec consideration (name ; name of a formula pattern ; a translated term to use as the pattern target ; a translated term or the symbol :conclusion or :clause psubst0 ; a psubst to seed hld's match user-level) ; user-level input giving rise to this nil) (defun listify-consideration-lst (arg) ; We disambiguate arg into a list of user-level considerations. That ; is, all we're doing is resolving the question of whether arg is a ; single consideration or a list of them. We know arg is not nil. If ; this function returns nil, it means we could not disambiguate arg. ; The elements of the disambiguated arg are still in external, ; user-level form! This function does not translate. ; Illustrative cases: ; arg value ; h-thm (h-thm) ; (h-thm) (h-thm) ; (h-thm :pattern ...) ((h-thm :pattern ...)) ; (h-thm x ...) (h-thm x ...) (cond ((atom arg) (list arg)) ((not (true-listp arg)) nil) ((null (cdr arg)) arg) ((and (symbolp (car arg)) (keywordp (cadr arg))) (list arg)) (t arg))) (defun get-lhs (term ctx wrld state) (let ((lst (unprettyify term))) ; pair = (hyps . concl) (cond ((equal (length lst) 1) (mv-let (equiv lhs rhs ttree) (interpret-term-as-rewrite-rule (cdr (car lst)) (ens state) wrld) (declare (ignore equiv rhs ttree)) (value lhs))) (t (er soft ctx "We cannot determine the :lhs of a formula because it ~ flattens to more than one clause."))))) (defun translate-consideration1 (name lst ctx wrld state) (cond ((not (and (symbolp name) (formula name nil wrld) (true-listp lst) (evenp (length lst)) (subsetp (evens lst) '(:pattern :target :instance :functional-instance)))) (er soft ctx "A :CONSIDER hint element should be a list consisting of an ~ event name followed by a list of even length containing ~ alternating keys and values, with the keys :PATTERN, ~ :TARGET, :INSTANCE, and :FUNCTIONAL-INSTANCE being legal. ~ Your hint element ~x0 is ill-formed!" (cons name lst))) (t (er-let* ((pattern (let ((temp (assoc-keyword :pattern lst))) (cond ((and temp (not (eq (cadr temp) :lhs))) (translate (cadr temp) t t t ctx wrld state)) (t (get-lhs (formula name nil wrld) ctx wrld state))))) (target (let ((temp (assoc-keyword :target lst))) (cond ((null temp) (value :clause)) ((or (eq (cadr temp) :conclusion) (eq (cadr temp) :clause)) (value (cadr temp))) (t (translate (cadr temp) t t t ctx wrld state))))) (var-alist (translate-substitution (cadr (assoc-keyword :instance lst)) ctx wrld state)) (fn-alist (translate-functional-substitution (cadr (assoc-keyword :functional-instance lst)) ctx wrld state))) (value (make consideration :name name :pattern pattern :target target :psubst0 (convert-var-and-fn-alists-to-psubst var-alist fn-alist wrld) :user-level (cons name lst))))))) (defun translate-consideration (x ctx wrld state) (cond ((atom x) (translate-consideration1 x nil ctx wrld state)) (t (translate-consideration1 (car x) (cdr x) ctx wrld state)))) (defun translate-consider-hint1 (lst ctx wrld state) ; Each element of lst is to be treated as a consideration. We translate ; each into a consideration record. (cond ((endp lst) (value nil)) (t (er-let* ((c (translate-consideration (car lst) ctx wrld state)) (rest (translate-consider-hint1 (cdr lst) ctx wrld state))) (value (cons c rest)))))) (defun translate-consider-hint (arg ctx wrld state) ; This function either causes an error or returns (as the value component of ; an error/value/state triple) a list of consideration records. ; See the Essay on the Design of Consider Hints. (cond ((null arg) (er soft ctx "Empty :CONSIDER hints are illegal.")) (t (let ((lst (listify-consideration-lst arg))) (cond ((null lst) (er soft ctx "The value of a :CONSIDER hint must be a symbol ~ or a true-list and ~x0 is neither." arg)) (t (translate-consider-hint1 lst ctx wrld state))))))) (deflabel consideration :doc ":Doc-Section Miscellaneous an object indicating that some instantiation is relevant~/ Considerations are the objects one provides as part of ~c[:consider] ~il[hints]. The most convenient form of a consideration is simply the name of a lemma. The system will then search for a relevant instantiation of the left-hand side of the conclusion of that lemma inside the specified goal clause, starting with the last literal. The system uses a heuristically modified version of the Huet-Lang second-order pattern matching algorithm and, in general, produces instantiations of both variable symbols and constrained function symbols in the lemma. If an instance is found, the consideration is turned into a ~il[lemma-instance] and ~c[:use]d. For example, suppose the following theorem has been proved: ~bv[] (defthm map-h-append (implies (and (true-listp x) (true-listp y)) (equal (map-h-append (append x y)) (append (map-h x) (map-h y)))) :rule-classes nil) ~ev[] and suppose ~c[map-h] is a defined function involving some constrained function symbol ~c[h]. Then the consideration ~c[map-h-append] attached to a clause identifier will cause the system to find the identified clause and search through it for an instance and/or functional instance of ~c[(map-h-append (append x y))] and to add that (functional) instance as a hypothesis when it finds it. If no instance is found, an error is signalled. The more elaborate form of a consideration allows you to specify what is used as the pattern and where that pattern is searched for in the clause.~/ The most general form of a consideration is ~bv[] (name :pattern pterm ; term or :lhs :target tterm ; term or :conclusion or :clause :instance vsubst ; variable substitution :functional-instance fsubst) ; functional substitution ~ev[] where ~c[name] is the name of a previously proved theorem, ~c[pterm] is either a term or the keyword ~c[:lhs], ~c[tterm] is either a term or the keyword ~c[conclusion] or the keyword ~c[:clause], ~c[vsubst] is a variable substitution as might be given in an ~c[:instance], e.g., ~c[((x (rev a)) (y (sort b)))], and ~c[fsubst] is a functional substitution as might be given in a ~c[:functional-instance], e.g., ~c[((map-h sumer))]. If ~c[pterm] is a term, that term is used as the pattern we try to instantiate. If ~c[pterm] is the keyword ~c[:lhs], the left-hand side of the conclusion of ~c[name] is used as the pattern. If no ~c[:pattern] is specified, ~c[:lhs] is used by default. If ~c[tterm] is a term, that term is matched against the pattern to generate the instantiation. If ~c[tterm] is ~c[:conclusion], a match of the pattern is searched for in the conclusion of the specified subgoal clause. If ~c[tterm] is ~c[:clause], a match of the pattern is searched for in the entire subgoal clause, starting with the conclusion and searching backwards toward the first hypothesis. The search is outer-most first, left-to-right recursive descent. The first subterm of the target producing a match of the pattern stops the search and generates the instantiation. Note that if ~c[tterm] is given explicitly, no search occurs. Note also that because we cannot do the search until we know what the subgoal clause is, the work for this hint -- the Huet-Lang second-order matching algorithm -- cannot commence until the indicated subgoal arises. The substitutions produced by second-order matching are what we called ``mixed substitutions'' by which we mean a given substitution will substitute both for variable symbols and hereditarily constrained function symbols. The effect, however, is the same as ~bv[] (:INSTANCE (:FUNCTIONAL-INSTANCE name (fn1 (lambda ...)) ...) (var1 term1) ...). ~ev[] Second-order matching typically produces a plethora of such substitutions. We rule many out on heuristic grounds. Thus, our heuristic modification of the Huet-Lang algorithm makes it incomplete. Still, it is typical for a lot of substitutions to be found and the system selects some to pursue in an DISJUNCTIVE way. That is, it takes each of the ``winning'' substitutions and generates a ~c[:use] hint for each of them separately to see if any one of them will prove the indicated subgoal. It is frequently necessary to give the matcher a ``hint'' to limit its consideration of all possible substitutions. The ~c[vsubst] and ~c[fsubst] are treated as ``seed'' substitutions. Any computed instance is an extension of the two seeds. That is, the variable pairs in the mixed substitutions extend ~c[vsubst] and the functional pairs in the mixed substitutions extend ~c[fsubst]. Both ~c[vsubst] and ~c[fsubst] default to ~c[nil]. ~/") ; -------------- ; Essay on the Implementation of Consider Hint Processing ; We are about to start dealing with the conversion of considerations ; to :use hints. A use hint is a list of lemma instances and in our ; case each is of the form (:instance (:functional-instance ...) ...). ; Let temp be (:consider . (consideration1 ; ... considerationn)), where each consideration is a record of that ; type. Naively, each consideration generates a lemma instance and we ; conjoin them all together across the n considerations. ; But less naively, each considerationi may generate multiple lemma ; instances (because of the multiplicity of functional instantiations ; produced by second order matching). Thus, the list of n ; considerations represents a conjunction of disjunctions. If ; consideration1 generates u1,1 ... u1,k1, where each u is a lemma ; instance, then we have (and (or u1,1 ... u1,k1) (or u2,1 ... u2,k2) ; ...) and we want a disjunction of conjunctions: (or (and u1,1 u2,1 ; ...) (and u1,1 u2,2 ...) ...). This transformation to DNF form is ; just performed by all-picks. Thus, we get k1*k2*... disjunctions, ; each of which represents a conjunction of lemma instances. But a ; conjunction of lemma instances is a :USE hint. Thus we ultimately ; produce a hint of the form ; :OR ((:USE (u1,1 u2,1 ...)) (:USE (u1,1 u2,2 ...)) ...) ; We go back and forth between the internal and external form of ; substitutions in this code, so we need these two functions. (defun convert-pairs-to-doublets (alist) ; Alist is of the form ((key . val) ...) and we convert it to the form ; ((key val) ...) (cond ((endp alist) nil) (t (cons (list (car (car alist)) (cdr (car alist))) (convert-pairs-to-doublets (cdr alist)))))) (defun convert-doublets-to-pairs (substn) ; Substn is of the form ((key val) ...) and we convert it to the form ; ((key . val) ...) (cond ((endp substn) nil) (t (cons (cons (car (car substn)) (cadr (car substn))) (convert-doublets-to-pairs (cdr substn)))))) ; We now develop the code that converts a consideration to an lmi. ; Here is the function that searches for the first (second order) ; match of pat in term extending psubst0. (mutual-recursion (defun hld-sweep (pat term psubst0 n wrld) (cond ((variablep term) nil) ((fquotep term) nil) (t (or (hld pat term psubst0 nil nil n wrld) (hld-sweep-lst pat (fargs term) psubst0 n wrld))))) (defun hld-sweep-lst (pat term-lst psubst0 n wrld) (cond ((endp term-lst) nil) (t (or (hld-sweep pat (car term-lst) psubst0 n wrld) (hld-sweep-lst pat (cdr term-lst) psubst0 n wrld)))))) (defun prettyify-substn (functionalp substn wrld) ; Substn is a list of pairs of the form (var term) or (fn function), ; where functionap = nil for the former and t for the latter. We ; untranslate it into the external form (var term') or (fn function'), ; where term' and function' are the untranslated versions of the term ; or function. (cond ((endp substn) nil) (t (cons (list (car (car substn)) (if functionalp (if (symbolp (cadr (car substn))) (cadr (car substn)) (make-lambda (lambda-formals (cadr (car substn))) (untranslate (lambda-body (cadr (car substn))) nil wrld))) (untranslate (cadr (car substn)) nil wrld))) (prettyify-substn functionalp (cdr substn) wrld))))) (defun prettyify-lmi (lmi wrld) (cond ((atom lmi) lmi) ((eq (car lmi) :instance) (list* :INSTANCE (prettyify-lmi (cadr lmi) wrld) (prettyify-substn nil (cddr lmi) wrld))) ((eq (car lmi) :functional-instance) (list* :FUNCTIONAL-INSTANCE (prettyify-lmi (cadr lmi) wrld) (prettyify-substn t (cddr lmi) wrld))) ((eq (car lmi) :theorem) (list :theorem (untranslate (cadr lmi) t wrld))) (t lmi))) (defun prettyify-lmi-lst (lmi-lst wrld) (cond ((endp lmi-lst) nil) (t (cons (prettyify-lmi (car lmi-lst) wrld) (prettyify-lmi-lst (cdr lmi-lst) wrld))))) (defun convert-mixed-subst-to-lmi (name thm mixed-subst wrld) (mv-let (var-alist initial-fn-alist) (strip-mixed-subst mixed-subst) (mv-let (fn-alist inverse-subst) (rename-free-vars-in-fn-substitution thm initial-fn-alist wrld) `(:INSTANCE (:FUNCTIONAL-INSTANCE ,name ,@(convert-pairs-to-doublets fn-alist)) ,@(convert-pairs-to-doublets var-alist) ,@inverse-subst)))) (defun convert-mixed-substs-to-lmi-lst (name thm mixed-substs wrld) (cond ((endp mixed-substs) nil) (t (cons (convert-mixed-subst-to-lmi name thm (car mixed-substs) wrld) (convert-mixed-substs-to-lmi-lst name thm (cdr mixed-substs) wrld))))) #| (defun prettyify-ranked-mixed-substs (name thm ranked-mixed-substs wrld) (cond ((endp ranked-mixed-substs) nil) (t (cons (list (car (car ranked-mixed-substs)) (prettyify-lmi (convert-mixed-subst-to-lmi name thm (cdr (car ranked-mixed-substs)) wrld) wrld)) (prettyify-ranked-mixed-substs name thm (cdr ranked-mixed-substs) wrld))))) |# (defun collect-high-ranking-mixed-substs (ranked-mixed-substs) ; Each element of lst is of the form (rank . mixed-subst) and the list ; is ordered descending by rank. We collect the mixed-substs with ; highest rank. Note that we do not collect the ranks. (cond ((endp ranked-mixed-substs) nil) ((endp (cdr ranked-mixed-substs)) (list (cdr (car ranked-mixed-substs)))) ((equal (car (car ranked-mixed-substs)) (car (cadr ranked-mixed-substs))) (cons (cdr (car ranked-mixed-substs)) (collect-high-ranking-mixed-substs (cdr ranked-mixed-substs)))) (t (list (cdr (car ranked-mixed-substs)))))) (defun convert-consideration-to-lmi-lst (cl consideration wrld all-flg ctx state) ; Note: The name in a user-level consideration denotes the ; UN-normalized formula, not the normalized formula. That is because ; we do pattern matching on the formula and we want to use the pattern ; the user wrote, not its normalization. The :USE hint generated will ; use the normalized version of the instantiated formula, of course. ; Note: In the hld sweeps below we limit the rewrite repetition to 1. ; That is, we will apply all known rewrite rules to every argument and ; recursively rewrite the resulting intermediate terms. But we will ; not rewrite those. That is (f (g '1)) might be rewritten to (f (g ; '2)), by applying a rule to (g '1), and then that might be rewritten ; to (h (g '2)), by applying a rule to (f (g '2)). But we don't ever ; try to rewrite (h (g '2)). If you want that to happen, change ; (certain of) the 1's to 2's below. ; We print and signal an error or return (value lmi-lst) where lmi-lst ; is the list of the highest ranking automatically-computed lmi's as ; might have been typed by a user in a :USE hint. ; Warning: The lmis we return are prettyified, so the terms in them ; are now untranslated! We return such things so that if the user ; activates show-custom-keyword-hint-expansion he sees pretty ; substitutions. (let* ((name (access consideration consideration :name)) (thm (formula name nil wrld)) (pat (access consideration consideration :pattern)) (psubst0 (access consideration consideration :psubst0)) (ans (cond ((eq (access consideration consideration :target) :conclusion) (hld-sweep pat (car (last cl)) psubst0 1 wrld)) ((eq (access consideration consideration :target) :clause) (hld-sweep-lst pat (reverse cl) psubst0 1 wrld)) (t (hld pat (access consideration consideration :target) psubst0 nil nil 1 wrld)))) (mixed-substs (if all-flg ans (collect-high-ranking-mixed-substs ans)))) (cond (ans (value (prettyify-lmi-lst (convert-mixed-substs-to-lmi-lst name thm mixed-substs wrld) wrld))) (t (er soft ctx "We were unable to compute a second-order instantiation ~ matching the pattern ~x0 with the target ~x1, as ~ directed by your :CONSIDER hint.~#2~[~/ The ~x1 in ~ question is ~x3.~]" pat (access consideration consideration :target) (if (or (eq (access consideration consideration :target) :conclusion) (eq (access consideration consideration :target) :clause)) 1 0) (cond ((eq (access consideration consideration :target) :conclusion) (car (last cl))) ((eq (access consideration consideration :target) :clause) (reverse cl)) (t nil))))))) ; Here is how we convert a list of considerations to a list of ; lmi-lsts. (defun convert-considerations-to-lmi-lst-lst (cl considerations wrld ctx state) ; We convert a list of considerations into a list of lmi lists. We ; always choose only the highest ranking substitutions. We return (mv ; erp val). If erp is t, val is an error msg. Else, val is a list of ; lmi lists. Don't be fooled by the messiness below. This is just ; a simple map and collect, handling the error pairs appropriately. (cond ((endp considerations) (value nil)) (t (er-let* ((lmi-lst (convert-consideration-to-lmi-lst cl (car considerations) wrld nil ctx state)) (lmi-lst-lst (convert-considerations-to-lmi-lst-lst cl (cdr considerations) wrld ctx state))) (value (cons lmi-lst lmi-lst-lst)))))) ; The code above converts a list of consideration records into a list of ; lists of lemma instances in the form a user could have written them, ; e.g., each lmi looks like (:instance (:functional-instance name ...) ; ...). ; If consideration1 generates u1,1 ... u1,k1, where each u is a lemma ; instance, then we have (and (or u1,1 ... u1,k1) (or u2,1 ... u2,k2) ; ...) and we turn that into a disjunction of conjunctions: (or (and ; u1,1 u2,1 ...) (and u1,1 u2,2 ...) ...). Thus, we get ; k1*k2*... disjunctions, each of which represents a :USE hint with ; multiple (conjoined) lemma instances in it. (defun dnf-size (cnf) ; Let cnf be a list of buckets. Suppose we choose one element from ; each bucket, in all possible ways, as with all-picks. E.g., ; (all-picks '((a1 a2) (b1 b2 b3) (c1 c2)) nil) = '((a1 b1 c1) (a2 b1 ; c1) ...). How many combinations are there? (cond ((endp cnf) 1) (t (* (len (car cnf)) (dnf-size (cdr cnf)))))) (defun convert-considerations-to-dnf (cl considerations wrld ctx state) ; Suppose the first item in considerations generates the lmi-lst (u1,1 ; ... u1,k1), where each u is a lemma instance. And suppose the rest ; generate analogously indexed lmi's. Then we have a conjunction of ; disjunctions (we get to use each lemma but have multiple choices for ; how): ; (and (or u1,1 ... u1,k1) (or u2,1 ... u2,k2) ...) ; We want a disjunction of conjunctions: ; (or (and u1,1 u2,1 ...) (and u1,1 u2,2 ...) ...). ; Thus, we get k1*k2*... disjunctions, each of which represents a :USE ; hint with multiple (conjoined) lemma instances in it. If we think ; there are too many combinations, we cause an error. (er-let* ((cnf (convert-considerations-to-lmi-lst-lst cl considerations wrld ctx state))) (cond ((< 100 (dnf-size cnf)) (er soft ctx "There are ~x0 combinations of substitutions ~ suggested by your :consider hint. We abort when ~ there are more than 100. Sorry." (dnf-size cnf))) (t (value (all-picks cnf nil)))))) ; So now we have a disjunction of conjunctions of lmi's, or, put ; another way, we have disjunction of :USE hints in the form a user ; might have written them, ((:instance (:functional-instance ...) ...) ; ...). (defun map-list (x lst) ; With apologies to the much more useful maplist! (cond ((endp lst) nil) (t (cons (list x (car lst)) (map-list x (cdr lst)))))) (defun process-considerations (val cl wrld ctx state) ; We use second-order matching to convert the given untranslated ; considerations into an :OR hint of :USE hints. We either print and ; signal an error or we return an error triple whose value is ; a hint segment to splice in instead of :CONSIDER val. ; Our answer is the list containing the segment: ; :OR ((:USE (u1,1 u2,1 ...)) ...) ; Note that this can generate a singleton :OR. But we do that on ; purpose because :OR takes the responsibility for printing the ; generated hints. ; We only consider the highest ranked substitutions generated for each ; consider item. We choose one such substitution for each item, in ; all possible ways, producing a disjunctive split. ; See the Essay on the Implementation of Consider Hint Processing. (er-let* ((considerations (translate-consider-hint val ctx wrld state)) (lmi-lst-lst (convert-considerations-to-dnf cl considerations wrld ctx state))) (value (cond ((and (consp lmi-lst-lst) (null (cdr lmi-lst-lst))) (list :USE (car lmi-lst-lst))) (t (list :OR (map-list :USE lmi-lst-lst))))))) (defun put-assoc-keyword (key val keyword-alist) (cond ((endp keyword-alist) (list key val)) ((eq key (car keyword-alist)) (cons key (cons val (cddr keyword-alist)))) (t (cons (car keyword-alist) (cons (cadr keyword-alist) (put-assoc-keyword key val (cddr keyword-alist))))))) (defun consider-hint-generator (val keyword-alist clause world ctx state) (er-let* ((or-seg (process-considerations val clause world ctx state))) (value (put-assoc-keyword :merge t (splice-keyword-alist :consider or-seg (splice-keyword-alist :merge nil keyword-alist)))))) (defun consider-hint-checker (val world ctx state) ; Just to make it obvious that the checker does not actually yeild ; translated considerations (even though it does indeed translate ; them), we define this function. (er-let* ((temp (translate-consider-hint val ctx world state))) (value nil))) (defmacro add-consider-hint () '(progn (add-merge-hint) (add-custom-keyword-hint :consider (consider-hint-generator val keyword-alist clause world ctx state) :checker (consider-hint-checker val world ctx state))))