| 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: |
;;; 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.