Paste number 71536: advice wanted: calc program

Paste number 71536: advice wanted: calc program
Pasted by: _theHAM
When:6 months, 4 weeks ago
Share:Tweet this! | http://paste.lisp.org/+1J74
Channel:#lisp
Paste contents:
Raw Source | XML | Display As
;;; Entirely new to lisp and seeking advice on any way to improve this

;;; Simple calculator
;;; Expects one command per line, like "add 1, 3" or "sub 3". 
;;; If a command is short on parameters, it is assumed to refer to a previous value, i.e. to operate on the result of the previous command

;; Our value stack
(defparameter *stack* nil)

(defparameter *debug* nil)

;; Operator table
(defparameter *operators* (make-hash-table :test #'equal))
  (setf (gethash "add" *operators*)  (cons 2 #'(lambda (&rest nums) (reduce #'+ nums))))
  (setf (gethash "sub" *operators*)  (cons 2 #'(lambda (&rest nums) (reduce #'- nums))))
  (setf (gethash "mul" *operators*)  (cons 2 #'(lambda (&rest nums) (reduce #'* nums))))
  (setf (gethash "div" *operators*)  (cons 2 #'(lambda (&rest nums) (reduce #'/ nums))))
  (setf (gethash "pow" *operators*)  (cons 2 #'(lambda (&rest nums) (reduce #'expt nums))))
  (setf (gethash "neg" *operators*)  (cons 1 #'(lambda (x) (- x))))
  (setf (gethash "disp" *operators*) (cons 0 #'(lambda () (format t "~a~%" (first *stack*)))))
  (setf (gethash "exit" *operators*) (cons 0 #'exit))

;; Destructively fetch n items off of the stack
;; If the stack is smaller than n, returns excess 0s
(defun stack-pop (n) 
  (let ((ret nil))
    (unless (plusp n) (return-from stack-pop nil))
    (dotimes (i n (nreverse ret))
      (if *stack* (push (pop *stack*) ret) 0))))

;; Fetch the label of a command, the "add" of "add 1 3"
(defun label (str) (subseq str 0 (position #\Space str)))

;; Given a string, return a list of all integers found in the string
(defun numbers-from-str (str)
  (let ((vals nil) (index 0)) 
    (loop
      (multiple-value-bind (value next) (parse-integer str :start index :junk-allowed t)  
        (if (> next index) (setf index next) (incf index))
        (if value (push value vals))
        (if (>= index (length str)) (return (nreverse vals)))))))

;; Main calculator loop
(defun calculator (stream)
  (loop
    (multiple-value-bind (data done) (read-line stream nil)
      (if done (return))
      (let* 
        ((op (label data))
        (param (numbers-from-str data)) 
        (num-ops (car (gethash op *operators*)))
        (handler (cdr (gethash op *operators*))))
        (when handler
          (let ((value (apply handler (append param (stack-pop (- num-ops (list-length param)))))))
          (if value (push value *stack*))
          (if *debug* (format t "stack:~a~%" *stack*))))))))

;; Program entry
(if *args*
  (let ((file (open (first *args*) :if-does-not-exist nil)))
    (if file
      (calculator file)
      (format t "File \"~a\" is not available~%" (first *args*))))
  (calculator nil))

This paste has no annotations.

Colorize as:
Show Line Numbers

Lisppaste pastes can be made by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively.