Stack Based Interpreter In Clojure - Part 4


We now have our instructions and our stack, we need a program that we can run.

Let's call this Code

(ns com.vadelabs.code.core
(:refer-clojure :exclude [empty])
[com.vadelabs.code.builder :as vm.builder]
[com.vadelabs.instruction.interface :as vm.instruction]))
(defprotocol ICode
(symbols [this])
(-code [this])
(-data [this])
(labels [this])
(label-ip [this item]))
(defrecord Code [symbols code data labels]
(symbols [this]
(get-in this [:symbols]))
(-code [this]
(get-in this [:code]))
(-data [this]
(get-in this [:data]))
(labels [this]
(get-in this [:labels]))
(label-ip [this item]
(ffirst (filter
(fn [label]
(= (second label) item))
(get-in this [:labels])))))
(defn data
(-> code (-data)))
([code idx]
(-> code (-data) (get idx))))
(defn code
(-> code (-code)))
([code idx]
(-> code (-code) (get idx))))
(defn make-code
(map->Code {:symbols []
:code []
:data []
:labels []}))
(let [symbols (into [] (-> builder
code (-> builder
data (-> builder
label-map (-> builder (vm.builder/labels))
labels (map (fn [label] [(get label-map label) label]) (keys label-map))
labels (into [] (sort-by first labels))]
(map->Code {:symbols symbols
:code code
:data data
:labels labels}))))

Here's our Builder:

(ns com.vadelabs.code.builder
[com.vadelabs.instruction.interface :as vm.instruction]))
(defprotocol IBuilder
(push [this item args])
(push-data [this args])
(instruction-table [this])
(instructions [this])
(labels [this])
(add-label [this item])
(data [this])
(lenth [this]))
(defrecord Builder [instruction-table instructions labels data]
(push-data [this args]
(let [data-items (apply conj (get-in this [:data]) args)]
(-> this
(assoc-in [:data] (into [] (distinct data-items))))))
(push [this item args]
(let [it (get-in this [:instruction-table])
instr (vm.instruction/by-name it item)
arity (vm.instruction/arity instr)
_ (when-not instr
(throw (Exception. (ex-info "Unable to find instruction" {:name item}))))
_ (when-not (= (count args) arity)
(throw (Exception. (ex-info "Instruction has different arity than passed arguments"
{:name item
:arity arity
:args (count args)}))))
builder (-> this (push-data args))
indices (map (fn [arg]
(.indexOf (-> builder (get-in [:data])) arg))
;; indices (map-indexed (fn [idx _] idx) (-> builder (get-in [:data])))
instrs (apply conj instructions
(vm.instruction/op-code instr)
(vm.instruction/arity instr)
(assoc-in builder [:instructions] instrs)))
(instruction-table [this]
(get-in this [:instruction-table]))
(instructions [this]
(get-in this [:instructions]))
(labels [this]
(get-in this [:labels]))
(data [this]
(get-in this [:data]))
(add-label [this item]
(let [idx (count instructions)]
(update-in this [:labels] assoc item idx))))
Algorithms & Data Structures In Clojure(Script)