Clojure ETA interpreter
I challenged myself to write an ETA interpreter in Clojure.
It turned out to not be as easy as I first thought it might be, and it's farily lengthy, but I am a pretty inexperienced Clojure programmer at this point.
At least it works, and plays ETA noughts and crosses very nicely. You can find the source here and a compiled jar file with everything you need is here.
You can run the jar like this:
java -jar eta-1.0.0-standalone.jar program.eta
Here is the program source
; clojure ETA interpreter
; S Sykes 2011
;
; project.clj
; (defproject eta "1.0.0"
; :description "Clojure ETA interpreter"
; :dependencies [[org.clojure/clojure "1.2.1"] [org.clojure/clojure-contrib "1.2.0"]]
; :main eta.core)
(ns eta.core
(:gen-class)
(:import (java.io BufferedReader FileReader))
(:use [clojure.contrib.io :only (reader)]))
(def rdr (reader *in*))
(defn stack-new [] (vector-of :int))
(defn stack-push [s e] (conj s (int e)))
(defn stack-top [s] (get s (dec (count s))))
(defn stack-pop [s] (if (empty? s) s (pop s)))
(defn stack-empty? [s] (empty? s))
(defn halibut [s n]
(if (<= n 0)
(conj s (get s (+ (count s) (dec n))))
(vec
(concat
(subvec s 0 (- (count s) (inc n)))
(subvec s (- (count s) n))
(list (get s (- (count s) (inc n))))))))
(defn state-new [code]
(let [code-list (re-seq #".*\r?\n" code)]
{:lineno 1
:charno 0
:stack (stack-new)
:num false
:code code-list
:linecount (count code-list)
:cur-num 0
}))
(defn state-stack [s] (:stack s))
(defn state-lineno [s] (:lineno s))
(defn state-charno [s] (:charno s))
(defn state-num [s] (:num s))
(defn state-input [s] (:input s))
(defn state-code [s] (:code s))
(defn state-linecount [s] (:linecount s))
(defn state-cur-num [s] (:cur-num s))
(defn setval [orig newval] newval)
(defn state-set-charno [s n] (update-in s [:charno] setval n))
(defn state-set-lineno [s n] (-> s
(update-in [:lineno] setval n)
(update-in [:charno] setval -1)))
(defn state-set-num [s n] (update-in s [:num] setval n))
(defn state-set-cur-num [s n] (update-in s [:cur-num] setval n))
(defn state-set-stack [s n] (update-in s [:stack] setval n))
(defn state-add-to-cur-num [s n] (state-set-cur-num s (+ (* 7 (state-cur-num s)) n)))
(defn inc-lineno [s] (let [state (state-set-lineno s (inc (state-lineno s)))]
(if (> (state-lineno state) (count (state-code state)))
(state-set-lineno state 0) state)))
(defn inc-charno [s] (let [
state (update-in s [:charno] inc)
cur-line (nth (state-code state) (dec (state-lineno state)))
]
(if (>= (state-charno state) (count cur-line))
(-> state
(inc-lineno)
(state-set-charno 0)) state)))
(defn execute [state chr]
(let [top (stack-top (state-stack state)) top2 (stack-top (stack-pop (state-stack state))) stack (state-stack state)]
(if (state-num state)
(case chr
(\h \H) (state-add-to-cur-num state 0)
(\t \T) (state-add-to-cur-num state 1)
(\a \A) (state-add-to-cur-num state 2)
(\o \O) (state-add-to-cur-num state 3)
(\i \I) (state-add-to-cur-num state 4)
(\n \N) (state-add-to-cur-num state 5)
(\s \S) (state-add-to-cur-num state 6)
(\e \E) (do
(-> state
(state-set-stack (stack-push stack (state-cur-num state)))
(state-set-num false)
(state-set-cur-num 0)))
state)
(case chr
(\o \O) (do
(when top (print (char top)))
(state-set-stack state (stack-pop stack)))
(\a \A) (state-set-stack state (stack-push stack (inc (state-lineno state))))
(\n \N) (state-set-num state true)
(\i \I) (do
(flush)
(state-set-stack state (stack-push stack (.read rdr))))
(\h \H) (state-set-stack state (halibut (stack-pop stack) top))
(\e \E) (state-set-stack state
(stack-push
(stack-push (stack-pop (stack-pop stack)) (/ top2 top))
(rem top2 top)))
(\s \S) (state-set-stack state (stack-push (stack-pop (stack-pop stack)) (- top2 top)))
(\t \T) (if (not= 0 top2)
(-> state
(state-set-lineno top)
(state-set-stack (stack-pop (stack-pop stack))))
(state-set-stack state (stack-pop (stack-pop stack))))
state))))
(defn process-file [file-name]
(loop [state (state-new (slurp file-name))]
(let [line (nth (state-code state) (dec (state-lineno state)))
new-state (execute state (nth line (state-charno state)))
lineno (state-lineno new-state)]
(if (and (> lineno 0) (< lineno (state-linecount new-state))) (recur (inc-charno new-state))))))
(defn -main [& args]
(do
(process-file (first args))
(flush)))
Clojure was fun - what language next for ETA?

