MSG

construct a ``message'' suitable for the ~@ directive of fmt
Major Section:  ACL2-BUILT-INS

See fmt for background on formatted printing with ACL2.

We document msg precisely below, but first, we give an informal introduction and illustrate with an example. Suppose you are writing a program that is to do some printing. Typically, you will either pass the ACL2 state around (see programming-with-state) and use formatted printing functions that take the state as an argument (see fmt)), or else you might avoid using state by calling the utility, cw, to do you printing. Alternatively, you might print error messages upon encountering illegal situations; see er. But there are times where instead of printing immediately, you may prefer to pass messages around, for example to accumulate them before printing a final message. In such cases, it may be desirable to construct ``message'' objects to pass around.

For example, consider the following pair of little programs. The first either performs a computation or prints an error, and the second calls the first on two inputs.


(defun invert1 (x)
  (if (consp x)
      (cons (cdr x) (car x))
    (prog2$ (cw "ERROR: ~x0 expected a cons, but was given ~x1.~|"
                'invert1 x)
            nil)))

(defun invert2 (x1 x2)
  (list (invert1 x1) (invert1 x2)))

For example:

  ACL2 !>(invert1 '(3 . 4))
  (4 . 3)
  ACL2 !>(invert1 'a)
  ERROR: INVERT1 expected a cons, but was given A.
  NIL
  ACL2 !>(invert2 '(3 . 4) '(5 . 6))
  ((4 . 3) (6 . 5))
  ACL2 !>(invert2 'a 'b)
  ERROR: INVERT1 expected a cons, but was given A.
  ERROR: INVERT1 expected a cons, but was given B.
  (NIL NIL)
  ACL2 !>

Notice that when there are errors, there is no attempt to explain that these are due to a call of invert2. That could be fixed, of course, by arranging for invert2 to print its own error; but for complicated programs it can be awkward to coordinate printing from many sources. So let's try a different approach. This time, the first function returns two results. The first result is an ``error indicator'' -- either a message object or nil -- while the second is the computed value (only of interest when the first result is nil). Then the higher-level function can print a single error message that includes the error message(s) from the lower-level function, as shown below.

(defun invert1a (x)
  (if (consp x)
      (mv nil
          (cons (cdr x) (car x)))
    (mv (msg "ERROR: ~x0 expected a cons, but was given ~x1.~|"
             'invert1a x)
        nil)))

(defun invert2a (x1 x2)
  (mv-let (erp1 y1)
          (invert1a x1)
          (mv-let (erp2 y2)
                  (invert1a x2)
                  (if erp1
                      (if erp2
                          (cw "~x0 failed with two errors:~|  ~@1  ~@2"
                              'invert2a erp1 erp2)
                        (cw "~x0 failed with one error:~|  ~@1"
                            'invert2a erp1))
                    (if erp2
                        (cw "~x0 failed with one error:~|  ~@1"
                            'invert2a erp2)
                      (list y1 y2))))))
For example:
  ACL2 !>(invert2a '(3 . 4) '(5 . 6))
  ((4 . 3) (6 . 5))
  ACL2 !>(invert2a '(3 . 4) 'b)
  INVERT2A failed with one error:
    ERROR: INVERT1A expected a cons, but was given B.
  NIL
  ACL2 !>(invert2a 'a 'b)
  INVERT2A failed with two errors:
    ERROR: INVERT1A expected a cons, but was given A.
    ERROR: INVERT1A expected a cons, but was given B.
  NIL
  ACL2 !>

If you study the example above, you might well understand msg. But we conclude with precise documentation.

General Form:
(msg str arg1 ... argk)
where str is a string and k is at most 9.

This macro returns a pair suitable for giving to the fmt directive ~@. Thus, suppose that #\c is bound to the value of (msg str arg1 ... argk), where c is a character and k is at most 9. Then the fmt directive ~@c will print out the string, str, in the context of the alist in which the successive fmt variables #\0, #\1, ..., #\k are bound to the successive elements of (arg1 ... argk).