1 (require "dependencies")
14 (define-condition invalid-function
(types:mal-error
)
15 ((form :initarg
:form
:reader form
)
16 (context :initarg
:context
:reader context
))
17 (:report
(lambda (condition stream
)
19 "Invalid function '~a' provided while ~a"
20 (printer:pr-str
(form condition
))
21 (if (string= (context condition
) "apply")
25 (defvar *repl-env
* (env:create-mal-env
))
27 (dolist (binding core
:ns
)
28 (env:set-env
*repl-env
*
32 (defvar mal-quote
(make-mal-symbol "quote"))
33 (defvar mal-quasiquote
(make-mal-symbol "quasiquote"))
34 (defvar mal-unquote
(make-mal-symbol "unquote"))
35 (defvar mal-splice-unquote
(make-mal-symbol "splice-unquote"))
36 (defvar mal-cons
(make-mal-symbol "cons"))
37 (defvar mal-concat
(make-mal-symbol "concat"))
38 (defvar mal-macroexpand
(make-mal-symbol "macroexpand"))
39 (defvar mal-def
! (make-mal-symbol "def!"))
40 (defvar mal-defmacro
! (make-mal-symbol "defmacro!"))
41 (defvar mal-let
* (make-mal-symbol "let*"))
42 (defvar mal-do
(make-mal-symbol "do"))
43 (defvar mal-if
(make-mal-symbol "if"))
44 (defvar mal-fn
* (make-mal-symbol "fn*"))
46 (env:set-env
*repl-env
*
47 (types:make-mal-symbol
"eval")
48 (types:make-mal-builtin-fn
(lambda (ast)
49 (mal-eval ast
*repl-env
*))))
51 (defun eval-sequence (sequence env
)
53 (lambda (ast) (mal-eval ast env
))
54 (mal-data-value sequence
)))
56 (defun eval-hash-map (hash-map env
)
57 (let ((hash-map-value (mal-data-value hash-map
))
58 (new-hash-table (make-hash-table :test
'types
:mal-value
=)))
60 for key being the hash-keys of hash-map-value
61 do
(setf (gethash (mal-eval key env
) new-hash-table
)
62 (mal-eval (gethash key hash-map-value
) env
)))
63 (make-mal-hash-map new-hash-table
)))
65 (defun eval-ast (ast env
)
67 (types:symbol
(env:get-env env ast
))
68 (types:list
(eval-sequence ast env
))
69 (types:vector
(make-mal-vector (apply 'vector
(eval-sequence ast env
))))
70 (types:hash-map
(eval-hash-map ast env
))
73 (defun is-pair (value)
74 (and (or (mal-list-p value
)
76 (not (zerop (length (mal-data-value value
))))))
78 (defun quasiquote (ast)
79 (if (not (is-pair ast
))
80 (types:make-mal-list
(list mal-quote
82 (let ((forms (map 'list
#'identity
(mal-data-value ast
))))
84 ((mal-value= mal-unquote
(first forms
))
87 ((and (is-pair (first forms
))
88 (mal-value= mal-splice-unquote
89 (first (mal-data-value (first forms
)))))
90 (types:make-mal-list
(list mal-concat
91 (second (mal-data-value (first forms
)))
92 (quasiquote (make-mal-list (cdr forms
))))))
94 (t (types:make-mal-list
(list mal-cons
95 (quasiquote (first forms
))
96 (quasiquote (make-mal-list (cdr forms
))))))))))
98 (defun is-macro-call (ast env
)
99 (when (and (types:mal-list-p ast
)
100 (not (zerop (length (mal-data-value ast
)))))
101 (let* ((func-symbol (first (mal-data-value ast
)))
102 (func (when (types:mal-symbol-p func-symbol
)
103 (env:find-env env func-symbol
))))
105 (types:mal-fn-p func
)
106 (cdr (assoc 'is-macro
(types:mal-data-attrs func
)))))))
108 (defun mal-macroexpand (ast env
)
110 while
(is-macro-call ast env
)
111 do
(let* ((forms (types:mal-data-value ast
))
112 (func (env:get-env env
(first forms
))))
113 (setf ast
(apply (mal-data-value func
)
117 (defun mal-eval (ast env
)
119 do
(setf ast
(mal-macroexpand ast env
))
121 ((null ast
) (return types
:mal-nil
))
122 ((not (types:mal-list-p ast
)) (return (eval-ast ast env
)))
123 ((zerop (length (mal-data-value ast
))) (return ast
))
124 (t (let ((forms (mal-data-value ast
)))
126 ((mal-value= mal-quote
(first forms
))
127 (return (second forms
)))
129 ((mal-value= mal-quasiquote
(first forms
))
130 (setf ast
(quasiquote (second forms
))))
132 ((mal-value= mal-macroexpand
(first forms
))
133 (return (mal-macroexpand (second forms
) env
)))
135 ((mal-value= mal-def
! (first forms
))
136 (return (env:set-env env
(second forms
) (mal-eval (third forms
) env
))))
138 ((mal-value= mal-defmacro
! (first forms
))
139 (let ((value (mal-eval (third forms
) env
)))
140 (return (if (types:mal-fn-p value
)
144 (setf (cdr (assoc 'is-macro
(types:mal-data-attrs value
))) t
)
146 (error 'invalid-function
148 :context
"macro")))))
150 ((mal-value= mal-let
* (first forms
))
151 (let ((new-env (env:create-mal-env
:parent env
))
152 ;; Convert a potential vector to a list
155 (mal-data-value (second forms
)))))
157 (mapcar (lambda (binding)
160 (mal-eval (or (cdr binding
)
164 for
(symbol value
) on bindings
166 collect
(cons symbol value
)))
167 (setf ast
(third forms
)
170 ((mal-value= mal-do
(first forms
))
171 (mapc (lambda (form) (mal-eval form env
))
172 (butlast (cdr forms
)))
173 (setf ast
(car (last forms
))))
175 ((mal-value= mal-if
(first forms
))
176 (let ((predicate (mal-eval (second forms
) env
)))
177 (setf ast
(if (or (mal-value= predicate types
:mal-nil
)
178 (mal-value= predicate types
:mal-false
))
182 ((mal-value= mal-fn
* (first forms
))
183 (return (let ((arglist (second forms
))
184 (body (third forms
)))
185 (types:make-mal-fn
(lambda (&rest args
)
186 (mal-eval body
(env:create-mal-env
:parent env
189 (mal-data-value arglist
))
191 :attrs
(list (cons 'params arglist
)
194 (cons 'is-macro nil
))))))
196 (t (let* ((evaluated-list (eval-ast ast env
))
197 (function (car evaluated-list
)))
198 ;; If first element is a mal function unwrap it
199 (cond ((types:mal-fn-p function
)
200 (let* ((attrs (types:mal-data-attrs function
)))
201 (setf ast
(cdr (assoc 'ast attrs
))
202 env
(env:create-mal-env
:parent
(cdr (assoc 'env attrs
))
205 (mal-data-value (cdr (assoc 'params attrs
))))
206 :exprs
(cdr evaluated-list
)))))
207 ((types:mal-builtin-fn-p function
)
208 (return (apply (mal-data-value function
)
209 (cdr evaluated-list
))))
210 (t (error 'invalid-function
212 :context
"apply")))))))))))
214 (defun mal-read (string)
215 (reader:read-str string
))
217 (defun mal-print (expression)
218 (printer:pr-str expression
))
222 (mal-print (mal-eval (mal-read string
)
224 (types:mal-error
(condition)
233 (rep "(def! not (fn* (a) (if a false true)))")
234 (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
235 (rep "(def! *ARGV* (list))")
236 (rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
237 (rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
239 (env:set-env
*repl-env
*
240 (types:make-mal-symbol
"*ARGV*")
241 (types:wrap-value
(cdr common-lisp-user
::*args
*)
245 ;;; The test runner sets this environment variable, in which case we do
246 ;;; use readline since tests do not work with the readline interface
247 (defvar use-readline-p
(not (string= (ext:getenv
"PERL_RL") "false")))
249 (defvar *history-file
* (namestring (merge-pathnames (user-homedir-pathname)
250 ".mal-clisp-history")))
252 (defun load-history ()
253 (readline:read-history
*history-file
*))
255 (defun save-history ()
256 (readline:write-history
*history-file
*))
262 (defun raw-input (prompt)
263 (format *standard-output
* prompt
)
264 (force-output *standard-output
*)
265 (read-line *standard-input
* nil
))
267 (defun mal-readline (prompt)
268 (let ((input (if use-readline-p
269 (readline:readline prompt
)
270 (raw-input prompt
))))
271 (when (and use-readline-p
273 (not (zerop (length input
))))
274 (readline:add-history input
))
277 (defun mal-writeline (string)
279 (write-line string
)))
282 (loop do
(let ((line (mal-readline "user> ")))
284 (mal-writeline (rep line
))
290 (if (null common-lisp-user
::*args
*)
291 ;; Do not start REPL inside Emacs
292 (unless (member :swank
*features
*)
296 (car common-lisp-user
::*args
*)))))