/
main.clj
140 lines (134 loc) · 5.36 KB
/
main.clj
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
;; Modified / stripped version of clojure.main for use with babashka on
;; GraalVM.
;;
;; Copyright (c) Rich Hickey All rights reserved. The use and
;; distribution terms for this software are covered by the Eclipse Public
;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can be found
;; in the file epl-v10.html at the root of this distribution. By using this
;; software in any fashion, you are agreeing to be bound by the terms of
;; this license. You must not remove this notice, or any other, from this
;; software.
;; Originally contributed by Stephen C. Gilardi
(ns ^{:doc "Top-level main function for Clojure REPL and scripts."
:author "Stephen C. Gilardi and Rich Hickey"
:no-doc true}
babashka.impl.clojure.main
(:refer-clojure :exclude [with-bindings])
(:import (java.io StringReader)
(clojure.lang LineNumberingPushbackReader LispReader$ReaderException)))
(defmacro with-bindings
"Executes body in the context of thread-local bindings for several vars
that often need to be set!: *ns* *warn-on-reflection* *math-context*
*print-meta* *print-length* *print-level* *compile-path*
*command-line-args* *1 *2 *3 *e"
[& body]
`(binding [*ns* *ns*
*warn-on-reflection* *warn-on-reflection*
*math-context* *math-context*
*print-meta* *print-meta*
*print-length* *print-length*
*print-level* *print-level*
*print-namespace-maps* true
*data-readers* *data-readers*
*default-data-reader-fn* *default-data-reader-fn*
*command-line-args* *command-line-args*
*unchecked-math* *unchecked-math*
*assert* *assert*
;; clojure.spec.alpha/*explain-out* clojure.spec.alpha/*explain-out*
*1 nil
*2 nil
*3 nil
*e nil]
~@body))
(defn repl-prompt
"Default :prompt hook for repl"
[]
(print "bb=> " ))
(defn repl-caught
"Default :caught hook for repl"
[e]
(binding [*out* *err*]
(println (.getMessage ^Exception e))
(flush)))
(defn repl
"Generic, reusable, read-eval-print loop. By default, reads from *in*,
writes to *out*, and prints exception summaries to *err*. If you use the
default :read hook, *in* must either be an instance of
LineNumberingPushbackReader or duplicate its behavior of both supporting
.unread and collapsing CR, LF, and CRLF into a single \\newline. Options
are sequential keyword-value pairs. Available options and their defaults:
- :init, function of no arguments, initialization hook called with
bindings for set!-able vars in place.
default: #()
- :need-prompt, function of no arguments, called before each
read-eval-print except the first, the user will be prompted if it
returns true.
default: (if (instance? LineNumberingPushbackReader *in*)
#(.atLineStart *in*)
#(identity true))
- :prompt, function of no arguments, prompts for more input.
default: repl-prompt
- :flush, function of no arguments, flushes output
default: flush
- :read, function of two arguments, reads from *in*:
- returns its first argument to request a fresh prompt
- depending on need-prompt, this may cause the repl to prompt
before reading again
- returns its second argument to request an exit from the repl
- else returns the next object read from the input stream
default: repl-read
- :eval, function of one argument, returns the evaluation of its
argument
default: eval
- :print, function of one argument, prints its argument to the output
default: prn
- :caught, function of one argument, a throwable, called when
read, eval, or print throws an exception or error
default: repl-caught"
[& options]
(let [{:keys [init need-prompt prompt flush read eval print caught]
:or {need-prompt (if (instance? LineNumberingPushbackReader *in*)
#(.atLineStart ^LineNumberingPushbackReader *in*)
#(identity true))
prompt repl-prompt
flush flush
print prn
caught repl-caught}}
(apply hash-map options)
request-prompt (Object.)
request-exit (Object.)
read-eval-print
(fn []
(try
(let [input (read request-prompt request-exit)]
(or (#{request-prompt request-exit} input)
(let [value (eval input)]
(set! *3 *2)
(set! *2 *1)
(set! *1 value)
(try
(print value)
(catch Throwable e
(throw (ex-info nil {:clojure.error/phase :print-eval-result} e)))))))
(catch Throwable e
(caught e)
(set! *e e))))]
(with-bindings
(try
(init)
(catch Throwable e
(caught e)
(set! *e e)))
(prompt)
(flush)
(loop []
(when-not
(try (identical? (read-eval-print) request-exit)
(catch Throwable e
(caught e)
(set! *e e)
nil))
(when (need-prompt)
(prompt)
(flush))
(recur))))))