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)))
)
|