aboutsummaryrefslogtreecommitdiff
path: root/src/lang/interpreter.clj
blob: e79ec44d0e705c07ab13d83a3ea7f241dee07ca7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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)))
  )