(define (pop stack)
 (let ((var (car stack))
               (ret-stack (cdr stack)))
       (values var ret-stack)))

(define (push var stack)
 (append (list var) stack))

(define (dup stack)
 (let ((head (car stack)))
       (append (list head) stack)))

(define (fact x)
 (define (fact-iter n current)
       (if (= n 1)
         current
         (fact-iter (- n 1) (* n current))))
 (fact-iter x 1))

(define (rpn-func func stack)
 (let*-values (((var1 stack) (pop stack))
                               ((var2 stack) (pop stack)))
       (push (func var1 var2) stack)))

(define (insert-into-alist key val alist)
 (let ((mem? (assq key alist)))
       (if mem?
         (update-alist key val alist)
         (append alist (list (cons key val))))))

(define (index-in-alist key alist)
 (let loop ((list (list-copy alist))
                        (index 0))
       (if (= (length list) 0)
         #f
         (let ((list-head-key (car (car list))))
               (if (eq? list-head-key key)
                 index
                 (loop (cdr list) (+ index 1)))))))

(define (update-alist key new-val alist)
 (let ((index (index-in-alist key alist)))
       (list-set! alist index (list (cons key new-val)))
       alist))

(define (run-func sym dict stack)
 (let ((func (assq sym dict)))
       (if func
         ((cdr func) stack)
         (begin
               (display "ERROR: symbol not in dictionary: ")
               (display sym)
               (newline)
               stack))))

(define init-dict
 (list (cons '$ (lambda (stack)
                                  (let-values (((var stack) (pop stack)))
                                        (display var)
                                        (newline)
                                        stack)))
               (cons '+ (lambda (stack) (rpn-func + stack)))
               (cons '- (lambda (stack) (rpn-func - stack)))
               (cons '* (lambda (stack) (rpn-func * stack)))
               (cons '/ (lambda (stack) (rpn-func / stack)))
               (cons '% (lambda (stack) (rpn-func modulo stack)))
               (cons '! (lambda (stack)
                                  (let-values (((var stack) (pop stack)))
                                        (push (fact var) stack))))
               (cons 'dup (lambda (stack) (dup stack)))))

(let loop ((input (read))
                  (stack '())
                  (dict init-dict))
 (cond
  ((number? input) (loop (read) (push input stack) dict))
  ((symbol? input) (loop (read) (run-func input dict stack) dict))
  (else (begin
                  (display "ERROR not valid input: ")
                  (display input)
                  (newline)
                  (loop (read) stack dict)))))