Skip to content

Instantly share code, notes, and snippets.

@armstnp
Created December 3, 2016 06:56
Show Gist options
  • Save armstnp/2ca668d72117c5f8fe5ca58ea750acba to your computer and use it in GitHub Desktop.
Save armstnp/2ca668d72117c5f8fe5ca58ea750acba to your computer and use it in GitHub Desktop.
A micro interpreter presuming error-free input, based on a community challenge from codingame.
(ns Solution
(:require [clojure.string :as str])
(:gen-class))
(defn parse-int
"Parses an integer from the beginning of the given string
(in lieu of Java's Integer class)"
[s]
(with-in-str s (read)))
(defn environment
"Creates a fresh environment with the given initial register states"
[a-val b-val c-val d-val]
{:a a-val
:b b-val
:c c-val
:d d-val
:instruction 0})
(def str->constant parse-int)
(def str->register keyword)
(defn gen-fetch-constant
"Returns a function that provides the constant value c given an environment."
[c]
(fn [env] (str->constant c)))
(defn gen-fetch-register
"Returns a function that provides the value of register r given an environment."
[r]
(fn [env] (get env (str->register r))))
(defn register?
"Returns whether the given symbol is a valid register symbol."
[sym]
(re-matches #"[abcd]" sym))
(defn gen-fetch
"Returns a function that provides the value of symbol sym given an environment,
whether the symbol is a constant value or the name of a register."
[sym]
(if (register? sym)
(gen-fetch-register sym)
(gen-fetch-constant sym)))
(defn gen-put
"Returns an environment transformer that accepts a value, and places it in the
register with the given symbolic name."
[sym]
(let [register (str->register sym)]
(fn [env val] (assoc env register val))))
(defn inc-instruction
"Returns an environment with the instruction index incremented."
[env]
(assoc env :instruction (inc (get env :instruction))))
(def set-instruction (gen-put "instruction"))
(defn gen-mov
"Returns an environment transformer with the value of src placed
in the register with the symbolic name dest. Increments the
environment's instruction index."
[dest src]
(let [fetch-src (gen-fetch src)
put-dest (gen-put dest)]
(fn [env]
(-> env
(put-dest (fetch-src env))
inc-instruction))))
(defn gen-binary
"Returns an environment transformer with the value of the given
operator op evaluated against the values of src-a and src-b
placed in the register with the symbolic name dest. Increments
the environment's instruction index."
[dest src-a src-b op]
(let [fetch-src-a (gen-fetch src-a)
fetch-src-b (gen-fetch src-b)
put-dest (gen-put dest)]
(fn [env]
(-> env
(put-dest (op (fetch-src-a env)
(fetch-src-b env)))
inc-instruction))))
(defn gen-add
"Returns an environment transformer with the sum of the
values of src-a and src-b placed in the register with the
symbolic name dest. Increments the environment's instruction
index."
[dest src-a src-b]
(gen-binary dest src-a src-b +))
(defn gen-sub
"Returns an environment transformer with the difference of the
values of src-a and src-b placed in the register with the
symbolic name dest. Increments the environment's instruction
index."
[dest src-a src-b]
(gen-binary dest src-a src-b -))
(defn gen-jne
"Returns an environment transformer with the instruction index
set to inst if the values of src and compare are not equal, or
simply incremented if they are equal."
[inst src compare]
(let [instruction (str->constant inst)
fetch-src (gen-fetch-register src)
fetch-compare (gen-fetch compare)]
(fn [env] (if (not= (fetch-src env) (fetch-compare env))
(set-instruction env instruction)
(inc-instruction env)))))
(def instruction->gen
{"MOV" gen-mov
"ADD" gen-add
"SUB" gen-sub
"JNE" gen-jne})
(defn gen-instruction
"Takes an instruction as a sequence of strings and returns an
environment transformer function."
[[inst & args]]
(apply (instruction->gen inst) args))
(defn complete?
"Returns whether the given environment has been marked as
complete, i.e. can no longer run instructions."
[env]
(= (:instruction env) :complete))
(defn gen-instruction-list
"Returns a function that applies the current instruction
for the given environment and returns the resulting environment.
If the environment is complete, the returned function will simply
hand back the passed environment."
[instructions]
(let [total-inst (count instructions)]
(fn [env]
(let [current-inst (:instruction env)]
(if (and (not (complete? env))
(< current-inst total-inst))
((nth instructions current-inst) env)
(assoc env :instruction :complete))))))
(defn str->instruction-list
"Given a sequence of instruction strings, returns an
environment transformer that applies the current environment
instruction and returns the updated environment."
[lines]
(->> lines
(map #(re-seq #"\S+" %))
(map gen-instruction)
gen-instruction-list))
(defn run-instructions
"Runs the instructions on the initial environment repeatedly until
the environment is complete, then returns the resulting environment."
[instructions init-env]
(first (filter complete? (iterate instructions init-env))))
(defn read-n-lines
"Reads a sequence of n lines from the current input stream."
[n]
(loop [n n
lines []]
(if (zero? n)
lines
(recur (dec n) (conj lines (read-line))))))
(defn -main [& args]
(let [init-a (read)
init-b (read)
init-c (read)
init-d (read)
n (read)
_ (read-line)
raw-instructions (read-n-lines n)
instructions (str->instruction-list raw-instructions)
start-env (environment init-a init-b init-c init-d)
result-env (run-instructions instructions start-env)
{:keys [a b c d]} result-env]
(println (str/join " " [a b c d]))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment