Last active
December 15, 2015 18:39
-
-
Save sri/5305691 to your computer and use it in GitHub Desktop.
Clojure RPN version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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