Skip to content

Instantly share code, notes, and snippets.

@sri
Last active December 15, 2015 18:39
Show Gist options
  • Save sri/5305691 to your computer and use it in GitHub Desktop.
Save sri/5305691 to your computer and use it in GitHub Desktop.
Clojure RPN version
(ns user (:use clojure.pprint))
(def all-known-operators {})
(defn op-known? [op]
(contains? all-known-operators op))
(defn op-eval [op stack size]
(let [fn (get all-known-operators op)]
(when fn
(fn stack size))))
;; DefOp DSL:
;; Usage:
;; (defop <operator> <arguments> <body>)
;; The variables "stack" and "size" are visible within body
;; and are bound to the stack and the size of the stack, respectively.
;; The function "error-if":
;; (error-if <condition> <message>)
;; is also visible within the body and raises and error if
;; condition evaluates to a truthy value.
;; If a number is returned, then the DEFOP macro takes care of
;; popping & pushing the new number on the stack. Otherwise,
;; a 2 element list is expected where the first element contains
;; the new stack and the second element contains size of that
;; stack.
(defmacro defop [op arglist & body]
(let [size (if (nil? arglist) nil (.size arglist))
args-error-msg (if (nil? arglist)
""
(format "Operator %s needs %d arguments on stack"
op (.size arglist)))]
`(do
(letfn [(fn# [~'stack ~'size]
(if (and ~size (< ~'size ~size))
(do (println ~args-error-msg)
(list ~'stack ~'size))
(letfn [(~'error-if [condition# msg#]
(when condition#
(throw (Exception. msg#))))
(ufn# ~arglist
(try
(do ~@body)
(catch Exception e#
(println (format "** error: %s"
(.getMessage e#)))
(list ~'stack ~'size))))]
(let [result# (apply ufn# (take ~size ~'stack))]
(if (number? result#)
(do (println result#)
(flush)
(list (cons result# (drop ~size ~'stack))
(+ (- ~'size ~size) 1)))
result#)))))]
(def ~'all-known-operators
(assoc ~'all-known-operators '~op fn#))))))
#_(pprint (macroexpand '(defop + [a b]
(+ a b))))
(defop + [a b]
(+ a b))
(defop - [a b]
(- a b))
(defop / [a b]
(error-if (zero? b) "/ cannot divide by zero!")
(/ a b))
(defop * [a b]
(* a b))
(defop clear []
(list '() 0))
(defn prompt-and-read-line []
(printf "> ")
(flush)
(read-line))
(defn rpn-calc []
(loop [input (prompt-and-read-line)
stack '()
size 0]
(cond (or (nil? input) (= input "q"))
(println "goodbye")
(= input "")
(recur (prompt-and-read-line) stack size)
(= input "?")
(do (print stack)
(newline)
(recur (prompt-and-read-line) stack size))
:else
(let [obj (read-string input)]
(cond (number? obj)
(do (println obj)
(recur (prompt-and-read-line)
(cons obj stack)
(inc size)))
(op-known? obj)
(let [[new-stack new-size] (op-eval obj stack size)]
(recur (prompt-and-read-line) new-stack new-size))
:else
(do (printf "Unknown op %s\n" obj)
(recur (prompt-and-read-line) stack size)))))))
(rpn-calc)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment