Skip to content

Instantly share code, notes, and snippets.

@ralexstokes
Last active March 10, 2018 00:29
Show Gist options
  • Save ralexstokes/990d83e512825d2c87d2355b38e7e16e to your computer and use it in GitHub Desktop.
Save ralexstokes/990d83e512825d2c87d2355b38e7e16e to your computer and use it in GitHub Desktop.
Non-recursive prefix calculator as an excuse to try out `clojure.spec`
(ns spec-calculator.core
(:require [clojure.spec.alpha :as s]))
(def ops {'+ +
'- -
'* *
'/ /})
(defn op-for [op]
(get ops op))
(def input-regex
(re-pattern
(str "^\\(\\S[\\s+\\d+]*\\)$")))
(s/def ::input
(s/and string?
#(re-matches input-regex %)))
(defn conform-or-throw [spec input msg]
(let [c (s/conform spec input)]
(if (= c ::s/invalid)
(throw (ex-info msg (s/explain-data spec input)))
c)))
(defn parse-input [input]
(read-string (conform-or-throw ::input input "invalid input")))
(s/def ::operator?
(s/and symbol?
(into #{} (keys ops))))
(s/def ::computation
(s/cat :operator ::operator?
:args (s/+ number?)))
(defn parse-computation [c]
(conform-or-throw ::computation c "invalid computation for the calculator"))
(defn run [input]
(let [{:keys [operator args]} (-> input
parse-input
parse-computation)]
(apply (op-for operator) args)))
(comment
(run "(+ 1 1)"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment