Skip to content

Instantly share code, notes, and snippets.

@angusiguess
Last active November 26, 2016 17:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save angusiguess/1f9e8fa4049f41461d3f674468d4d3f8 to your computer and use it in GitHub Desktop.
Save angusiguess/1f9e8fa4049f41461d3f674468d4d3f8 to your computer and use it in GitHub Desktop.
(ns forth.core
(:require [clojure.edn :as edn]
[clojure.string :as str]
[clojure.core :as c])
(:refer-clojure :exclude [read pop + - * /]))
;; Every function in the runtime operates on env, stack and returns env, stack, ret
;; Where env is a dict of lookups to functions
;; Let's cheat and lean on clojure's reader for a bunch of stuff. for example
;; we'll just steal numbers, strings, characters, etc as literals
;; Core words
(defn pop [env stack word]
[env (rest stack) (first stack)])
(defn dup [env stack word]
(let [top (first stack)]
[env (conj stack top) nil]))
(defn dyadic
"For any function that pops two values off the stack and pushes the result"
[env stack f]
(let [[a b & rest] stack
ret (f a b)]
[env (conj rest ret) ret]))
(defn + [env stack word]
(dyadic env stack c/+))
(defn - [env stack word]
(dyadic env stack c/-))
(defn * [env stack word]
(dyadic env stack c/*))
(defn / [env stack word]
(dyadic env stack c//))
(def init-env {'pop pop
'dup dup
'+ +
'- -
'* *
'/ /})
;; Read-execute
(defmulti exec
"We want to execute each of our individual words, but anything that we don't
read as a symbol should be executed as a literal e.g pushed onto the stack."
(fn [env stack word] (type word)))
(defmethod exec java.lang.Long [env stack word]
[env (conj stack word) nil])
(defmethod exec clojure.lang.Symbol [env stack word]
(if-let [function (get env word)]
(function env stack word)
(throw (ex-info "Couldn't find word in the environment"
{:word word
:env env
:stack stack}))))
(defmethod exec :default [x]
(throw (ex-info "I don't know what to do with this word"
{:type (type x)})))
(defn handle-program-exception [words env stack word]
(try (exec env stack word)
(catch Exception e
(println "Error executing program"
(ex-data e))
[env stack ::error])))
(defn error? [ret]
(= ::error ret))
(defn exec-program [init-words]
(loop [words init-words
env init-env
stack '()]
(if (seq words)
(let [[curr & rest] words
[env stack ret] (handle-program-exception words env stack curr)]
(println env stack ret)
(cond (error? ret) ret
:else (do (println ret)
(recur rest env stack))))
nil)))
(defn read
"Read in a program string"
[s]
(let [words (str/split s #"\s+")
tokens (map edn/read-string words)]
tokens))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment