• Top
    • Documentation
    • Books
    • Boolean-reasoning
    • Projects
    • Debugging
    • Std
    • Proof-automation
    • Macro-libraries
    • ACL2
      • Theories
      • Rule-classes
      • Proof-builder
      • Recursion-and-induction
      • Hons-and-memoization
      • Events
      • Parallelism
      • History
      • Programming
        • Defun
        • Declare
        • System-utilities
        • Stobj
        • State
        • Mutual-recursion
        • Memoize
        • Mbe
        • Io
        • Defpkg
        • Apply$
        • Loop$
        • Programming-with-state
        • Arrays
        • Characters
        • Time$
        • Defmacro
        • Loop$-primer
        • Fast-alists
        • Defconst
        • Evaluation
        • Guard
        • Equality-variants
        • Compilation
        • Hons
        • ACL2-built-ins
        • Developers-guide
          • Developers-guide-background
          • Developers-guide-maintenance
          • Developers-guide-build
          • Developers-guide-utilities
            • Developers-guide-logic
            • Developers-guide-evaluation
            • Developers-guide-programming
            • Developers-guide-introduction
            • Developers-guide-extending-knowledge
            • Developers-guide-examples
            • Developers-guide-contributing
            • Developers-guide-prioritizing
            • Developers-guide-other
            • Developers-guide-emacs
            • Developers-guide-style
            • Developers-guide-miscellany
            • Developers-guide-releases
            • Developers-guide-ACL2-devel
            • Developers-guide-pitfalls
          • System-attachments
          • Advanced-features
          • Set-check-invariant-risk
          • Numbers
          • Efficiency
          • Irrelevant-formals
          • Introduction-to-programming-in-ACL2-for-those-who-know-lisp
          • Redefining-programs
          • Lists
          • Invariant-risk
          • Errors
          • Defabbrev
          • Conses
          • Alists
          • Set-register-invariant-risk
          • Strings
          • Program-wrapper
          • Get-internal-time
          • Basics
          • Packages
          • Oracle-eval
          • Defmacro-untouchable
          • <<
          • Primitive
          • Revert-world
          • Unmemoize
          • Set-duplicate-keys-action
          • Symbols
          • Def-list-constructor
          • Easy-simplify-term
          • Defiteration
          • Fake-oracle-eval
          • Defopen
          • Sleep
        • Operational-semantics
        • Real
        • Start-here
        • Debugging
        • Miscellaneous
        • Output-controls
        • Macros
        • Interfacing-tools
      • Interfacing-tools
      • Hardware-verification
      • Software-verification
      • Math
      • Testing-utilities
    • Developers-guide

    Developers-guide-utilities

    Data Structures and Utilities

    This topic introduces some of the data structures and utilities built into ACL2. Also see system-utilities and programming-with-state. Those topics have lots of useful information not covered in this overview topic. Just to give one example: someone once needed a utility to print clauses nicely, so the utility prettyify-clause, not mentioned below, was added to the list of utilities in system-utilities.

    State and the logical world.

    Much of this is covered in developers-guide-background.

    The state is, logically, a data structure with many fields. However, it is represented in ACL2 by a symbol, which is the value of *the-live-state*:

    ACL2 !>:q
    
    Exiting the ACL2 read-eval-print loop.  To re-enter, execute (LP).
    ? state
    ACL2_INVISIBLE::|The Live State Itself|
    ? *the-live-state*
    ACL2_INVISIBLE::|The Live State Itself|
    ?

    For technical reasons involving (as best recalled at this writing) tail recursion removal in some host Lisps, state is not declared as a Common Lisp special variable. So raw Lisp code (say, conditioned by #-acl2-loop-only) should reference *the-live-state* when state is not lexically bound, to avoid compiler warnings.

    ? (defun foo () state)
    ;Compiler warnings :
    ;   In FOO: Undeclared free variable STATE
    FOO
    ? (defun foo () *the-live-state*)
    FOO
    ?

    As discussed in an earlier chapter of this Guide (see developers-guide-background), a key logical field of state is the global-table, which is an alist associating state global variables with their values. Also see programming-with-state, which explains this idea further along with many other key data structures and programming idioms that pertain to state.

    The logical world is the value of state global variable current-acl2-world, but is normally accessed using the function, w.

    ACL2 !>(equal (f-get-global 'current-acl2-world state)
                  (w state))
    T
    ACL2 !>(set-guard-checking nil)
    
    Masking guard violations but still checking guards except for self-
    recursive calls.  To avoid guard checking entirely, :SET-GUARD-CHECKING
    :NONE.  See :DOC set-guard-checking.
    
    ACL2 >(eq (f-get-global 'current-acl2-world state)
              (w state))
    T
    ACL2 >

    Enabled structures

    THIS SECTION IS WORTH COVERING IN ITS ENTIRETY.

    ACL2 handles theories using enabled structures. Ideally you could learn about enabled structures by visiting the defrec form for enabled-structure. As of this writing, there are virtually no comments in that defrec form! Fortunately, the field names are suggestive of their meaning; but that is not really adequate documentation. This is one of those place in the ACL2 sources where additional comments would be useful. In the meantime, you can learn about enabled structures by seeing how they are used, by doing an Emacs tags-search for ``enabled-structure'' or perhaps ``(access enabled-structure''. Another option is to follow the definition of disabledp to find the definition of enabled-runep, where you'll see that (enabled-runep rune ens wrld) is (enabled-numep (fnume rune wrld) ens). Note that ens is a variable that is commonly used for enabled structures, and fnume returns the nume of a rune, which is a unique number corresponding to the rune. The definition of enabled-numep helps to explain the fields of an enabled structure.

    Function: enabled-numep

    (defun enabled-numep (nume ens)
      (declare (type (or null (unsigned-byte 60)) nume)
               (xargs :guard (enabled-structure-p ens)))
      (cond ((null nume) t)
            ((> nume
                (the-fixnum (access enabled-structure
                                    ens :index-of-last-enabling)))
             t)
            (t (aref1 (access enabled-structure ens :array-name)
                      (access enabled-structure ens :theory-array)
                      (the-fixnat nume)))))

    An ACL2 developer sometimes needs to be able to follow definitions like this to learn about ACL2 data structures — sad, but true.

    Tag trees

    PERHAPS WE'LL JUST LOOK AT A CALL OF PUSH-LEMMA IN REWRITE.

    A tag tree, or ttree (pronounced ``tee-tree''), is a structure that is used for recording information generated by the prover. There is no defrec form for tag trees, but fortunately, there is a long comment in the ACL2 sources labeled ``; Essay on Tag-Trees''. Here is a high-level introduction that may be helpful before you read that Essay.

    Abstractly a tag-tree represents a list of sets, each member set having a name given by one of the ``tags'' (which are symbols) of the ttree. The elements of the set named tag are all of the objects tagged tag in the tree. Definitions of primitives in the source code for manipulating tag trees are labeled with the comment ``; Note: Tag-tree primitive''.

    Many ACL2 prover functions take and return tag trees. The function rewrite, for example, takes a term and a ttree (among other things), and returns a new term, term', and new ttree, ttree'. Term' is provably equivalent to the input term (under the current assumptions) and ttree' is an extension of the input ttree. If we focus just on the set associated with the tag LEMMA in the ttrees, then the set for ttree' is the extension of that for the input ttree, which is obtained by unioning into it all the runes used by the rewriter. The set associated with tag LEMMA can be obtained by (tagged-objects 'LEMMA ttree). Tag trees contain useful prover information based not only on lemmas used but also on hints, assumptions generated by force, forward-chaining, and so on. It is critical not to avoid tag trees that contain assumptions (see developers-guide-pitfalls).

    The Essay on Tag-Trees describes some of the legal tags for a ttree, but the definitive list is the one enforced by function all-runes-in-ttree. Here for example is an interesting clause from a case expression in the body of that definition.

               (assumption
    ; Shape: assumption record
                ans)

    If you see this, then you might be curious about the notion of an ``assumption record''. Then you can simply go to the definition of assumption (typically, using the Emacs command, meta-.). You'll see quite a few lines of comments in that vicinity, which may help to get your head around these records.

    Macros

    Many macros that are useful in the ACL2 source code are also helpful to users, and hence are documented. Among these are defrec (already discussed in the chapter, developers-guide-background)), defabbrev, er-progn, pprogn, state-global-let*, and revert-world. Others could reasonably have their own documentation topics but are discussed in other topics; for example, see programming-with-state for a discussion of er-let*. Still others are not mentioned in the xdoc documentation but have Lisp comments in the source code, for example, revert-world-on-error, with-ctx-summarized, io?, and acl2-unwind-protect. Perhaps the best way to learn about the variety of available macros for ACL2 system programming is to notice their usage in existing ACL2 source code, then looking them up in the xdoc documentation and/or in the source code (typically with the Emacs command, meta-.).

    Evaluators

    Source code often contains calls that evaluate terms. A prime example is the implementation of the read-eval-print loop, as explained in the chapter, developers-guide-background. There are several evaluators available. The most familiar to users may be trans-eval, which is actually a combination of translation and evaluation. If you look at the source code and drill down (following definitions) from trans-eval, you'll see that there are several optimizations, in part to support lazy treatment of if calls: based on the test, perhaps only one of the two branches will need to be translated, let alone evaluated. If you keep drilling down, you may ultimately see a call of ev, which is an evaluator for (translated) terms. In a sense ev is the most basic term evaluator. Note that ev takes and returns state. A related evaluator, ev-w, may be used if state and stobjs are not involved.

    There is more to evaluation, such as the handling of stobjs via so-called ``latches'' and the user-stobj-alist, which pertain to the subtle notion that user-defined stobjs are actually part of the ACL2 state; you can read comments in the sources to learn more about that when the need arises. Also see the chapter, developers-guide-evaluation.

    Using return-last

    THIS SECTION IS WORTH COVERING IN ITS ENTIRETY.

    There are occasions in which a utility is naturally defined as a function in ACL2 but as a macro in Common Lisp. Consider mbe. Even though mbe is itself a macro, it must expand to a function call involving its :logic and :exec arguments, so that a suitable guard proof obligation can be generated. However, in raw Lisp we want an mbe call to be fast, by simply running the :exec argument. Let's see how this is all arranged.

    ACL2 !>:trans (mbe :logic (zp n) :exec (= n 0))
    
    (RETURN-LAST 'MBE1-RAW (= N '0) (ZP N))
    
    => *
    
    ACL2 !>:q
    
    Exiting the ACL2 read-eval-print loop.  To re-enter, execute (LP).
    ? (macroexpand '(mbe :logic (zp n) :exec (= n 0)))
    (= N 0)
    T
    ?

    The key, obviously, is to use return-last, which is the only ACL2 utility that is defined to be a function in the ACL2 loop but is defined to be a macro in raw Lisp. Before you add a new utility that, like mbe, needs to operate as a function in the ACL2 loop but as a macro in raw Lisp, commit yourself to defining it using return-last. The reason is that handling any such utility is tricky (see for example the definition of ev-rec-return-last), so it would be ill-advised to replicate all the work done already for return-last in handling any additional such utility. Of course, the first step then is to become familiar with return-last. The xdoc documentation on return-last is quite extensive, and may suffice; of course, it is also advisable to read the comments in the source code definition of return-last.

    The use of return-last provides an example of the suggestion to use precedents (see developers-guide-maintenance). Imagine that you want to add a function to ACL2 that is implemented under-the-hood as a macro in raw Lisp. Ideally, you would look at an existing such utility, such as mbe, to see how it is implemented. This would lead you to return-last, which you would then use similarly to implement your new utility.

    Type-alists

    A fundamental data structure in the ACL2 prover is the type-alist. Since some user-level utilities display the type-alist, there is user-level documentation for this data structure; see type-alist, which contains important system-level background. Perusal of the source code will reveal utilities for computing with type-alists. Two key such utilities are type-set, which computes the type-set of a term with respect to a given type-alist, and which again has user-level documentation (see type-set) that also serves to provide important system-level background; and assume-true-false, which extends a type-alist as one dives into the true or false branch of a call of IF. Before creating type-alists with lower-level or new utilities, be sure to read the ``Essay on the Invariants on Type-alists, and Canonicality.'' In general, look for essays on any topic that is relevant to changes that you are making, unless you are reasonably confident that the essay is at a lower level than you need. For example, if you call assume-true-false to extend an existing type-alist, then you are using a well-worn interface and you needn't be concerned about the well-formedness of the resulting type-alists.

    NEXT SECTION: developers-guide-logic