(require ['clojure.string :as 'string])
(def state-machine
[
(fn [c] (case c
(\a \b) (get state-machine 1)
"Error 1"))
(fn [c] (case c
(\b) (get state-machine 1)
(\c) (get state-machine 2)
"Error 2"))
(fn [c] (case c
(\d) (get state-machine 3)
"Error 3") )
(fn [c] (case c
:end "Win"
"Error 4"))
])
(defn state-machine-driver [v machine]
(loop [fun (get machine 0) st v]
(println (first st))
(let [ans (fun (first st))]
(cond (empty? st) (fun :end)
(string? ans) ans
:else (recur ans (rest st))))))
;; I think this needs to be a macro because the case statement can only take compile-time
;; constants and I think I need a macro to write them in
;; Of course, I'm a noob. I could be wrong
(defmacro make-sm-func [idx or-list self-list star-list]
(if (empty? star-list)
`#(case %
~or-list (get ~state-machine (inc ~idx)) ;; (a|b)c*d, this would be at, say, c* and us choosing (\d) to go on
~self-list (get ~state-machine ~idx) ;; (a|b)c*d, this is where we pick more c instead of going to (\d)
"Error")
`#(case %
~star-list (get ~state-machine (inc ~idx)) ;;In (a|b)c*d, this is (\c) going on to the '(\c)' state
~or-list (get ~state-machine (+ 2 ~idx)) ;; In (a|b)c*d, this will be (\a \b) for (a|b)
~self-list (get ~state-machine ~idx) ;; We picked a (\c) and this is the option for more (\c)
"Error")))
(defn get-parenth [r depth prefix]
(let [fc (first r)]
(println fc r depth prefix)
(case fc
nil :error
\( (recur (rest r) (inc depth) (conj prefix fc))
\) (if (= depth 0)
(conj prefix \) )
(recur (rest r) (dec depth) (conj prefix fc)))
(recur (rest r) depth (conj prefix fc)))))
(defn get-command [re]
;; Obviously this is really ugly
(if (empty? re)
[:empty []]
(if (= \( (first re))
(let [a (let [exp (get-parenth (rest re) 0 [\(])]
(if (keyword? exp)
:error
(subvec exp 1 (- (count exp) 1))))
b (nth re (+ 2 (count a)))
re-tail (drop (+ (count a) 3) re)]
(cond (= \* b) [:star a re-tail]
(= \| b) [:or a re-tail]
:else [:concat a re-tail]))
(let [a (vector (first re))
b (nth re 1)
re-tail (rest re)]
(cond (= \* b) [:star a re-tail]
(= \| b) [:or a re-tail]
:else [:concat a re-tail])))))
(defn make-regex [re]
(let [comm (get-command re)
fc (first comm)]
(cond (empty? fc) (make-terminal fc)
(parenth? fc) (make-regex (contents fc))
(starred? fc) (make-none+ fc)
(concat? fc) (make-concat fc)
(or? fc) (make-or fc)
:else "Unknown error")))
;; (...)
;; a* highest priority
;; ab medium priority
;; a|b lowest priority
;;