7 (:import-from
:cl-readline
10 (:import-from
:genhash
20 (defvar *repl-env
* (make-mal-value-hash-table))
22 (setf (genhash:hashref
(make-mal-symbol "+") *repl-env
*)
23 (make-mal-builtin-fn (lambda (value1 value2
)
24 (make-mal-number (+ (mal-data-value value1
)
25 (mal-data-value value2
))))))
27 (setf (genhash:hashref
(make-mal-symbol "-") *repl-env
*)
28 (make-mal-builtin-fn (lambda (value1 value2
)
29 (make-mal-number (- (mal-data-value value1
)
30 (mal-data-value value2
))))))
32 (setf (genhash:hashref
(make-mal-symbol "*") *repl-env
*)
33 (make-mal-builtin-fn (lambda (value1 value2
)
34 (make-mal-number (* (mal-data-value value1
)
35 (mal-data-value value2
))))))
37 (setf (genhash:hashref
(make-mal-symbol "/") *repl-env
*)
38 (make-mal-builtin-fn (lambda (value1 value2
)
39 (make-mal-number (/ (mal-data-value value1
)
40 (mal-data-value value2
))))))
42 (defun lookup-env (symbol env
)
43 (let ((value (genhash:hashref symbol env
)))
46 (error 'env
:undefined-symbol
47 :symbol
(format nil
"~a" (mal-data-value symbol
))))))
49 (defun eval-sequence (sequence env
)
51 (lambda (ast) (mal-eval ast env
))
52 (mal-data-value sequence
)))
54 (defun eval-hash-map (hash-map env
)
55 (let ((hash-map-value (mal-data-value hash-map
))
56 (new-hash-table (make-mal-value-hash-table)))
57 (genhash:hashmap
(lambda (key value
)
58 (setf (genhash:hashref
(mal-eval key env
) new-hash-table
)
59 (mal-eval value env
)))
61 (make-mal-hash-map new-hash-table
)))
63 (defun eval-ast (ast env
)
65 (types:symbol
(lookup-env ast env
))
66 (types:list
(eval-sequence ast env
))
67 (types:vector
(make-mal-vector (apply 'vector
(eval-sequence ast env
))))
68 (types:hash-map
(eval-hash-map ast env
))
71 (defun mal-read (string)
72 (reader:read-str string
))
74 (defun mal-eval (ast env
)
76 ((not (mal-list-p ast
)) (eval-ast ast env
))
77 ((zerop (length (mal-data-value ast
))) ast
)
79 (let ((evaluated-list (eval-ast ast env
)))
80 (apply (mal-data-value (car evaluated-list
))
81 (cdr evaluated-list
)))))))
83 (defun mal-print (expression)
84 (printer:pr-str expression
))
88 (mal-print (mal-eval (mal-read string
) *repl-env
*))
90 (format nil
"~a" condition
))))
92 (defvar *use-readline-p
* nil
)
94 (defun complete-toplevel-symbols (input &rest ignored
)
95 (declare (ignorable ignored
))
98 (loop for key being the hash-keys of
*repl-env
*
99 when
(let ((pos (search input key
))) (and pos
(zerop pos
)))
100 do
(push key candidates
))
102 (if (= 1 (length candidates
))
103 (cons (car candidates
) candidates
)
104 (cons (apply #'utils
:common-prefix candidates
) candidates
))))
106 (defun raw-input (prompt)
107 (format *standard-output
* prompt
)
108 (force-output *standard-output
*)
109 (read-line *standard-input
* nil
))
111 (defun mal-readline (prompt)
113 (rl:readline
:prompt prompt
:add-history t
:novelty-check
#'string
/=)
116 (defun mal-writeline (string)
119 (force-output *standard-output
*)))
121 (defun main (&optional
(argv nil argv-provided-p
))
122 (declare (ignorable argv argv-provided-p
))
124 (setf *use-readline-p
* (not (or (string= (utils:getenv
"PERL_RL") "false")
125 (string= (utils:getenv
"TERM") "dumb"))))
127 ;; In GNU CLISP's batch mode the standard-input seems to be set to some sort
128 ;; of input string-stream, this interacts wierdly with the PERL_RL enviroment
129 ;; variable which the test runner sets causing `read-line' on *standard-input*
130 ;; to fail with an empty stream error. The following reinitializes the
133 ;; See http://www.gnu.org/software/clisp/impnotes/streams-interactive.html
134 #+clisp
(setf *standard-input
* (ext:make-stream
:input
)
135 *standard-output
* (ext:make-stream
:output
:buffered t
)
136 *error-output
* (ext:make-stream
:error
:buffered t
))
138 ;; CCL fails with a error while registering completion function
139 ;; See also https://github.com/mrkkrp/cl-readline/issues/5
140 #-ccl
(rl:register-function
:complete
#'complete-toplevel-symbols
)
142 (loop do
(let ((line (mal-readline "user> ")))
143 (if line
(mal-writeline (rep line
)) (return)))))
145 ;;; Workaround for CMUCL's printing of "Reloaded library ... " messages when an
146 ;;; image containing foreign libraries is restored. The extra messages cause the
147 ;;; MAL testcases to fail
150 (defvar *old-standard-output
* *standard-output
*
151 "Keep track of current value standard output, this is restored after image restore completes")
153 (defun muffle-output ()
154 (setf *standard-output
* (make-broadcast-stream)))
156 (defun restore-output ()
157 (setf *standard-output
* *old-standard-output
*))
159 (pushnew #'muffle-output ext
:*after-save-initializations
*)
160 (setf ext
:*after-save-initializations
*
161 (append ext
:*after-save-initializations
* (list #'restore-output
))))