aboutsummaryrefslogtreecommitdiff
path: root/src/lang/interpreter.clj
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lang/interpreter.clj115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/lang/interpreter.clj b/src/lang/interpreter.clj
new file mode 100644
index 000000000..e79ec44d0
--- /dev/null
+++ b/src/lang/interpreter.clj
@@ -0,0 +1,115 @@
+(ns lang.interpreter
+ (:refer-clojure :exclude [eval resolve])
+ (:require [clojure.core.match :refer [match]]
+ (lang [util :as &util :refer [exec return* return fail fail*
+ repeat-m try-m try-all-m map-m
+ apply-m]]
+ [parser :as &parser]
+ [lexer :as &lexer])
+ :reload))
+
+(declare eval-form)
+
+;; [Utils]
+(def ^:private +state+
+ {:globals {"*" (fn [x] (fn [y] (* x y)))}
+ :stack {}
+ :forms '()})
+
+(defn wrap [x]
+ (update-in +state+ [:forms] conj x))
+
+(defn resolve [ident]
+ (fn [state]
+ (if-let [value (get-in state [:globals ident])]
+ (return* state value)
+ (fail* (str "Unrecognized identifier: " ident)))))
+
+(defn fn-call [f args]
+ (return (reduce #(%1 %2) f args)))
+
+(defmacro ^:private defeval [name match return]
+ `(def ~name
+ (fn [state#]
+ (let [token# (first (:forms state#))]
+ ;; (prn '~name token#)
+ (match token#
+ ~match
+ (~return (update-in state# [:forms] rest))
+ _#
+ (fail* (str "Unknown syntax: " (pr-str token#))))))))
+
+(defeval eval-ident
+ [::&parser/ident ?ident]
+ (resolve ?ident))
+
+(defeval eval-int
+ [::&parser/int ?int]
+ (return ?int))
+
+(defeval eval-def
+ [::&parser/def ?form ?body]
+ (match ?form
+ [::&parser/fn-call ?name ?args]
+ (exec [=body (apply-m eval-form (wrap ?body))
+ =args (map-m (fn [arg] (apply-m eval-form (wrap arg)))
+ ?args)]
+ (return `(fn ~(vec =args) ~=body)))))
+
+(defeval eval-fn-call
+ [::&parser/fn-call ?fn ?args]
+ (exec [=fn (apply-m eval-form (wrap ?fn))
+ =args (map-m (fn [arg] (apply-m eval-form (wrap arg)))
+ ?args)]
+ (fn-call =fn =args)))
+
+(def eval-form
+ (try-all-m [eval-ident
+ eval-int
+ eval-def
+ eval-fn-call]))
+
+;; [::def [::fn-call [::ident "**"] ([::ident "base"] [::ident "exp"])]
+;; [::fn-call [::ident "reduce"] ([::ident "*"]
+;; [::int 1]
+;; [::fn-call [::ident "repeat"] ([::ident "exp"]
+;; [::ident "base"])])]]
+
+(defn eval [state]
+ (match (eval-form state)
+ [::&util/ok [?state ?datum]]
+ (if (empty? (:forms ?state))
+ ?datum
+ (assert false (str "Unconsumed input: " ?state)))
+
+ [::&util/failure ?message]
+ (assert false ?message)))
+
+(comment
+ (let [source-code (slurp "src/example/test1.lang")
+ tokens (&lexer/lex source-code)
+ syntax (&parser/parse (list tokens))]
+ ;; (prn 'syntax syntax)
+ (eval (update-in +state+ [:forms] concat (list syntax))))
+
+ ;; (clojure.core/fn [base exp] (fold * 1 (repeat exp base)))
+
+ ;; (* 5 6)
+
+ ;; (defdata (List x)
+ ;; (#Nil [])
+ ;; (#Cons [x] (List x)))
+
+ ;; (def (repeat n val)
+ ;; (if (> v n)
+ ;; (#Nil [])
+ ;; (#Cons [val (repeat (- n 1) val)])))
+
+ ;; (def (fold f init inputs)
+ ;; (case input
+ ;; (#Nil _) init
+ ;; (#Cons [head tail]) (fold f (f init head) tail)))
+
+ ;; (def (** base exp)
+ ;; (fold * 1 (repeat exp base)))
+ )