The HP67 emulator, the stack

Now, we start to define the stack object.  It will be a structure that holds the current stack as a list, the defined length of the stack, and the last-X register.  It will also have flags to indicate whether we are forcing rational numbers, whether complex numbers are allowed, and a flag that indicates the stack is in an error state.

If a complex number is passed to the stack when they are no allowed, it should raise a condition, so we’ll also define that.  Then, we have a few stack manipulation functions.

The HP-67 calculator had a 4-register stack, so we’ll default to that, but allow the flexibility to make it longer, or even unlimited.  When the HP-67 calculator popped an item from the stack, the deepest element was duplicated.  That is, the empty space created was not filled with a zero, but kept the value that used to be there.  We will replicate this behaviour, except in the case of unlimited stack depth.  In that case, newly-introduced elements will be zero.

Stack operations will be forbidden if the stack is in an error state.

So, the first version of these components is as follows:
calc1.lisp

(define-condition not-real-number (error)
  ((val         :initarg value
                :reader get-val))
  (:documentation "Complex number encountered in real-only mode.")
  (:report (lambda (c s)
             (format s "The complex value ~A was encountered."
                     (get-val c)))))

(defstruct (stack)
  (registers            (list 0 0 0 0))
  (num-registers        4)
  (last-x               nil)

  (use-rationals-p      nil)
  (complex-allowed-p    nil)
  (error-state          nil))


(defun trim-list-to-length (list num)
  (assert (<= num (length list)))
  (dotimes (i (1- num))
    (setf list (cdr list)))
  (setf (cdr list) '()))


(defun set-stack-size (stack num)
  "Changes the size of the stack.  Size 0 means unlimited."
  (assert (and (integerp num)
               (>= num 0)))
  (when (and (/= num 0)
             (/= (stack-num-registers stack) num))
    (let ((num-new-entries (- num
                              (length (stack-registers stack)))))
      (cond
        ((> num-new-entries 0)
         (setf (stack-registers stack)
               (append (stack-registers stack)
                       (make-sequence 'list
                                      num-new-entries
                                      :initial-element 0))))
        (t
         (trim-list-to-length (stack-registers stack) num)))))

  (setf (stack-num-registers stack) num))


(defun pop-stack (stack)
  "Returns the first element from the stack."
  (unless (stack-error-state stack)
    (let (rv)
      (cond
        ((and (= 0 (stack-num-registers stack))
              (null stack))
         (setf rv 0))
        (t
         (let ((previous-contents (stack-registers stack)))
           (setf rv (pop previous-contents))
           (when (/= (stack-num-registers stack) 0)
             (setf (stack-registers stack)
                   (append previous-contents (last previous-contents)))))))
      (if (stack-use-rationals-p stack)
          (rational rv)
          rv))))


(defun push-stack (stack val)
  "Pushes an element on the stack."
  (assert (numberp val))
  (unless (stack-error-state stack)
    (when (and (complexp val)
               (not (stack-complex-allowed-p stack)))
      (error (make-condition 'not-real-number
                             :value val)))
    (when (stack-use-rationals-p stack)
      (setf val (rational val)))

    (push val (stack-registers stack))
    (when (/= 0 (stack-num-registers stack))
      (trim-list-to-length (stack-registers stack)
                           (stack-num-registers stack))))
  val)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

反垃圾邮件 / Anti-spam question * Time limit is exhausted. Please reload CAPTCHA.