• Top
    • Documentation
      • Xdoc
      • ACL2-doc
      • Recursion-and-induction
      • Loop$-primer
        • Lp-section-8
        • Lp-section-10
        • Lp-section-6
        • Lp-section-9
        • Lp-section-17
        • Lp-section-16
        • Lp-section-15
          • Lp-section-11
          • Lp-section-4
          • Lp-section-7
          • Lp-section-13
          • Lp-section-12
          • Lp-section-14
          • Lp-section-5
          • Lp-section-0
          • Lp-section-2
          • Lp-section-18
          • Lp-section-3
          • Lp-section-1
        • Operational-semantics
        • Pointers
        • Doc
        • Documentation-copyright
        • Course-materials
        • Publications
        • Args
        • ACL2-doc-summary
        • Finding-documentation
        • Broken-link
        • Doc-terminal-test-2
        • Doc-terminal-test-1
      • Books
      • Boolean-reasoning
      • Projects
      • Debugging
      • Std
      • Proof-automation
      • Macro-libraries
      • ACL2
      • Interfacing-tools
      • Hardware-verification
      • Software-verification
      • Math
      • Testing-utilities
    • Loop$-primer

    Lp-section-15

    Informal Syntax and Semantics of DO Loop$s

    LP15: Informal Syntax and Semantics of DO Loop$s

    The “most elaborate” DO loop$ looks like this.

    (LOOP$ WITH var1 OF-TYPE spec1 = init1 ; a WITH declaration
           WITH var2 OF-TYPE spec2 = init2
           ...
           DO
           :measure m
           :guard do-guard
           :values v
           do-body
           FINALLY
           :guard fin-guard
           fin-body)

    where much of that is optional: “OF-TYPE speci”, “= initi” (when “OF-TYPE speci” is present), “:MEASURE m”, the two “:GUARD ...” clauses, “:VALUES v”, and “FINALLY fin-body”. If the :MEASURE is omitted, ACL2 tries to guess a likely measure using the same heuristic it does with recursive defuns. If :VALUES is omitted then v defaults to (nil).

    All ACL2 function symbols in m, do-body, and fin-body must be badged so apply$ can handle them. Furthermore, they must be warranted if proofs are to be done about them or if they are in logic mode and are called during evaluation.

    As you've already inferred from our examples, do-body and fin-body are not normal ACL2 terms! They allow restricted uses of RETURN, PROGN, SETQ, MV-SETQ, and LOOP-FINISH.

    As for semantics, every legal DO loop$ translates into a term of the form

    (DO$ m-lambda
         alist
         do-body-lambda
         fin-body-lambda
         a5
         a6)

    where m-lambda, do-body-lambda, and fin-body-lambda are quoted LAMBDA objects derived from the respective terms in the loop$ statement. Alist is an association list that maps the variables of those terms to their initial values. We discuss the other three arguments later.

    All three of these LAMBDA objects operate on alist. The m-lambda may return a natural, and otherwise must return a list of naturals which is treated as a lexicographic tuple whose first component is the most significant. The other two LAMBDA objects return a triple of the form (exit-token value new-alist). The exit-token is :RETURN, :LOOP-FINISH, or NIL, and indicates what happens next: the value is to be returned as the value of the loop$, the fin-body-lambda is to be applied to the new-alist, or the loop$ is to iterate again on new-alist. But before the iteration, the m-lambda is applied to the new-alist and must be of smaller measure according to l< for iteration to continue.

    If the given measure fails to decrease, then, logically speaking, a5 is used to compute a default answer. (That argument is actually the output signature of the loop$ specifying how many values are to be returned and whether each value is an ordinary object, a double-float, or a stobj.) However, in execution, an error is signaled if the measure fails to decrease. Such runtime errors (including OF-TYPE and guard violations if guards are being checked) are reported using a6 which is just a quoted constant about the original loop$ statement. (In fact, a6 is logically irrelevant and the theorem prover replaces quoted non-nil final argument to do$ by nil in proofs as part of the cleaning-up process.)

    Consider this simple DO loop$ and its cleaned-up semantics as shown by the :tcp command. (We have re-pretty-printed it to add comments and highlight some symmetries.)

      ACL2 !>:tcp (loop$ with i = n
                         with ans = 0
                         do
                         (if (zp i)
                             (return ans)
                             (progn (setq ans (+ 1 ans))
                                    (setq i (- i 1)))))
       (DO$
    ;    measure lambda:
         (LAMBDA$ (ALIST)
           (ACL2-COUNT (CDR (ASSOC-EQ-SAFE 'I ALIST))))
    
    ;    initial alist:
         (LIST (CONS 'I N)
               (CONS 'ANS 0))
    
    ;    do-body lambda:
         (LAMBDA$ (ALIST)
           (IF (ZP (CDR (ASSOC-EQ-SAFE 'I ALIST)))
               (LIST :RETURN
                     (CDR (ASSOC-EQ-SAFE 'ANS ALIST))
                     (LIST (CONS 'I (CDR (ASSOC-EQ-SAFE 'I ALIST)))
                           (CONS 'ANS (CDR (ASSOC-EQ-SAFE 'ANS ALIST)))))
               (LIST NIL
                     NIL
                     (LIST (CONS 'I (+ -1 (CDR (ASSOC-EQ-SAFE 'I ALIST))))
                           (CONS 'ANS (+ 1 (CDR (ASSOC-EQ-SAFE 'ANS ALIST))))))))
    
    ;    fin-body lambda (irrelevant here)
         (LAMBDA$ (ALIST)
           (LIST NIL
                 NIL
                 (LIST (CONS 'I (CDR (ASSOC-EQ-SAFE 'I ALIST)))
                       (CONS 'ANS (CDR (ASSOC-EQ-SAFE 'ANS ALIST))))))
    
    ;    irrelevant args a5 and a6
         '(NIL) NIL)

    ASSOC-EQ-SAFE is just ASSOC-EQ with a slightly weaker guard. Think of (CDR (ASSOC-EQ-SAFE 'var ALIST)) as the current value of the local variable var. The measure is that the ACL2-COUNT of the current value of I decreases. The output of the do-body lambda is a triple. The true branch of the IF indicate iteration is to stop and return the current value of ANS. The false branch indicates iteration is to continue with the new alist given as the third element of the triple. The fin-body lambda is irrelevant because there is no :LOOP-FINISH exit in the do-body.

    Of course, the semantics of do$ is explicit in its definition. So you might want to execute :pe do$ in your ACL2 session and just read how do$ operates.

    We realize the above descriptions are pretty sketchy. But in the coming discussions and proof problems we'll limit ourselves to DO loop$s without of-types or guards, they'll all return single non-stobj values so the :values option won't be needed, and you won't really need to use the finally clause or any fancier bodies than we show in our examples.

    For more details about both the syntax and semantics of DO loop$s see do-loop$. But beware! That link takes you out of the loop$ primer! To get back here either use your browser's “back” button or remember to return to lp-section-15!

    Now go to lp-section-16 (or return to the Table of Contents).