Last active
November 26, 2016 17:43
-
-
Save angusiguess/1f9e8fa4049f41461d3f674468d4d3f8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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