18 (defvar *repl-env
* (env:create-mal-env
))
20 (dolist (binding core
:ns
)
21 (env:set-env
*repl-env
*
25 (defvar mal-def
! (make-mal-symbol "def!"))
26 (defvar mal-let
* (make-mal-symbol "let*"))
27 (defvar mal-do
(make-mal-symbol "do"))
28 (defvar mal-if
(make-mal-symbol "if"))
29 (defvar mal-fn
* (make-mal-symbol "fn*"))
30 (defvar mal-quote
(make-mal-symbol "quote"))
31 (defvar mal-quasiquote
(make-mal-symbol "quasiquote"))
32 (defvar mal-unquote
(make-mal-symbol "unquote"))
33 (defvar mal-splice-unquote
(make-mal-symbol "splice-unquote"))
34 (defvar mal-cons
(make-mal-symbol "cons"))
35 (defvar mal-concat
(make-mal-symbol "concat"))
37 (defun eval-sequence (sequence env
)
39 (lambda (ast) (mal-eval ast env
))
40 (mal-data-value sequence
)))
42 (defun eval-hash-map (hash-map env
)
43 (let ((hash-map-value (types:mal-data-value hash-map
))
44 (new-hash-table (types:make-mal-value-hash-table
)))
45 (genhash:hashmap
(lambda (key value
)
46 (setf (genhash:hashref
(mal-eval key env
) new-hash-table
)
47 (mal-eval value env
)))
49 (types:make-mal-hash-map new-hash-table
)))
51 (defun eval-ast (ast env
)
53 (types:symbol
(env:get-env env ast
))
54 (types:list
(eval-sequence ast env
))
55 (types:vector
(make-mal-vector (apply 'vector
(eval-sequence ast env
))))
56 (types:hash-map
(eval-hash-map ast env
))
59 (defun is-pair (value)
60 (and (or (mal-list-p value
)
62 (< 0 (length (types:mal-data-value value
)))))
64 (defun quasiquote (ast)
65 (if (not (is-pair ast
))
66 (types:make-mal-list
(list mal-quote ast
))
67 (let ((forms (map 'list
#'identity
(mal-data-value ast
))))
69 ((mal-data-value= mal-unquote
(first forms
))
72 ((and (is-pair (first forms
))
73 (mal-data-value= mal-splice-unquote
74 (first (mal-data-value (first forms
)))))
75 (types:make-mal-list
(list mal-concat
76 (second (mal-data-value (first forms
)))
77 (quasiquote (make-mal-list (cdr forms
))))))
79 (t (types:make-mal-list
(list mal-cons
80 (quasiquote (first forms
))
81 (quasiquote (make-mal-list (cdr forms
))))))))))
83 (defun mal-read (string)
84 (reader:read-str string
))
86 (defun mal-eval (ast env
)
89 ((null ast
) (return types
:mal-nil
))
90 ((not (types:mal-list-p ast
)) (return (eval-ast ast env
)))
91 ((zerop (length (mal-data-value ast
))) (return ast
))
92 (t (let ((forms (mal-data-value ast
)))
94 ((mal-data-value= mal-quote
(first forms
))
95 (return (second forms
)))
97 ((mal-data-value= mal-quasiquote
(first forms
))
98 (setf ast
(quasiquote (second forms
))))
100 ((mal-data-value= mal-def
! (first forms
))
101 (return (env:set-env env
(second forms
) (mal-eval (third forms
) env
))))
103 ((mal-data-value= mal-let
* (first forms
))
104 (let ((new-env (env:create-mal-env
:parent env
))
105 (bindings (utils:listify
(types:mal-data-value
(second forms
)))))
107 (mapcar (lambda (binding)
110 (mal-eval (or (cdr binding
)
114 for
(symbol value
) on bindings
116 collect
(cons symbol value
)))
117 (setf ast
(third forms
)
120 ((mal-data-value= mal-do
(first forms
))
121 (mapc (lambda (form) (mal-eval form env
))
122 (butlast (cdr forms
)))
123 (setf ast
(car (last forms
))))
125 ((mal-data-value= mal-if
(first forms
))
126 (let ((predicate (mal-eval (second forms
) env
)))
127 (setf ast
(if (or (mal-data-value= predicate types
:mal-nil
)
128 (mal-data-value= predicate types
:mal-false
))
132 ((mal-data-value= mal-fn
* (first forms
))
133 (return (let ((arglist (second forms
))
134 (body (third forms
)))
135 (types:make-mal-fn
(lambda (&rest args
)
136 (mal-eval body
(env:create-mal-env
:parent env
139 (mal-data-value arglist
))
141 :attrs
(list (cons 'params arglist
)
145 (t (let* ((evaluated-list (eval-ast ast env
))
146 (function (car evaluated-list
)))
147 ;; If first element is a mal function unwrap it
148 (if (not (types:mal-fn-p function
))
149 (return (apply (mal-data-value function
)
150 (cdr evaluated-list
)))
151 (let* ((attrs (types:mal-data-attrs function
)))
152 (setf ast
(cdr (assoc 'ast attrs
))
153 env
(env:create-mal-env
:parent
(cdr (assoc 'env attrs
))
156 (mal-data-value (cdr (assoc 'params attrs
))))
157 :exprs
(cdr evaluated-list
)))))))))))))
159 (defun mal-print (expression)
160 (printer:pr-str expression
))
164 (mal-print (mal-eval (mal-read string
)
171 (env:set-env
*repl-env
*
172 (types:make-mal-symbol
"eval")
173 (types:make-mal-builtin-fn
(lambda (ast)
174 (mal-eval ast
*repl-env
*))))
176 (rep "(def! not (fn* (a) (if a false true)))")
177 (rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
179 (defvar *use-readline-p
* nil
)
181 (defun raw-input (prompt)
182 (format *standard-output
* prompt
)
183 (force-output *standard-output
*)
184 (read-line *standard-input
* nil
))
186 (defun mal-readline (prompt)
188 (cl-readline:readline
:prompt prompt
190 :novelty-check
(lambda (old new
)
191 (not (string= old new
))))
194 (defun mal-writeline (string)
197 (force-output *standard-output
*)))
200 (loop do
(let ((line (mal-readline "user> ")))
202 (mal-writeline (rep line
))
205 (defun run-file (file)
206 (rep (format nil
"(load-file \"~a\")" file
)))
208 (defun main (&optional
(argv nil argv-provided-p
))
210 (setf *use-readline-p
* (not (or (string= (utils:getenv
"PERL_RL") "false")
211 (string= (utils:getenv
"TERM") "dumb"))))
213 ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort
214 ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment
215 ;; variable which the test runner sets causing `read-line' on *standard-input*
216 ;; to fail with an empty stream error. The following reinitializes the
219 ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html
220 #+clisp
(setf *standard-input
* (ext:make-stream
:input
)
221 *standard-output
* (ext:make-stream
:output
:buffered t
)
222 *error-output
* (ext:make-stream
:error
:buffered t
))
224 (let ((args (if argv-provided-p
226 (cdr (utils:raw-command-line-arguments
)))))
227 (env:set-env
*repl-env
*
228 (types:make-mal-symbol
"*ARGV*")
229 (types:wrap-value
(cdr args
) :listp t
))
232 (run-file (car args
)))))
234 ;;; Workaround for CMUCL's printing of "Reloaded library ... " messages when an
235 ;;; image containing foreign libraries is restored. The extra messages cause the
236 ;;; MAL testcases to fail
239 (defvar *old-standard-output
* *standard-output
*
240 "Keep track of current value standard output, this is restored after image restore completes")
242 (defun muffle-output ()
243 (setf *standard-output
* (make-broadcast-stream)))
245 (defun restore-output ()
246 (setf *standard-output
* *old-standard-output
*))
248 (pushnew #'muffle-output ext
:*after-save-initializations
*)
249 (setf ext
:*after-save-initializations
*
250 (append ext
:*after-save-initializations
* (list #'restore-output
))))