Skip to content

Commit af47e84

Browse files
authored
Print and family functions respect value bound to *out* now (#257)
* Print and family functions respect value bound to *out* now * Add tests for print functions
1 parent 2b92c03 commit af47e84

File tree

3 files changed

+171
-4
lines changed

3 files changed

+171
-4
lines changed

src/basilisp/core/__init__.lpy

Lines changed: 136 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,6 @@
171171

172172
;;;;;;;;;;;; full support for syntax quote begins here ;;;;;;;;;;;;
173173

174-
(def print
175-
(fn print [& args]
176-
(apply builtins/print args)))
177-
178174
(def count
179175
(fn count [coll]
180176
(try
@@ -912,6 +908,17 @@
912908
[pred coll]
913909
[(take-while pred coll) (drop-while pred coll)])
914910

911+
(defn interpose
912+
"Return a lazy sequence of elements of coll separated by sep. If
913+
coll is empty, return an empty sequence."
914+
[sep coll]
915+
(lazy-seq
916+
(when (seq coll)
917+
(if (seq (rest coll))
918+
(cons (first coll)
919+
(cons sep (interpose sep (rest coll))))
920+
(cons (first coll) nil)))))
921+
915922
(defn cycle
916923
"Cycle the items in coll infinitely."
917924
[coll]
@@ -971,10 +978,135 @@
971978
(finally
972979
(. (var ~vvar) ~'pop-bindings)))))
973980

981+
;;;;;;;;;;;;;;;;;;;;;;
982+
;; Output Utilities ;;
983+
;;;;;;;;;;;;;;;;;;;;;;
984+
974985
(def ^:dynamic *in* sys/stdin)
975986
(def ^:dynamic *out* sys/stdout)
976987
(def ^:dynamic *err* sys/stderr)
977988

989+
(def ^:dynamic *print-sep* " ")
990+
991+
(defn repr
992+
"Return the reader representation of an object."
993+
[x]
994+
(basilisp.lang.util/lrepr x))
995+
996+
(defn flush
997+
"Flush the buffer currently bound to *out*."
998+
[]
999+
(.flush *out*))
1000+
1001+
(defmacro with-out-str
1002+
"Capture the contents of text sent to *out* and return the contents as a
1003+
string."
1004+
[& body]
1005+
`(binding [*out* (io/StringIO)]
1006+
~@body
1007+
(. *out* ~'getvalue)))
1008+
1009+
(defn pr
1010+
"Print the arguments to the stream bound to *out* in a format which
1011+
is readable by the reader. Multiple arguments will be separated by
1012+
the string value bound to *print-sep* (default is an ASCII space).
1013+
1014+
Note that some dynamically created Basilisp forms (such keywords and
1015+
symbols) and Python objects may not be readable again."
1016+
([] nil)
1017+
([x]
1018+
(.write *out* (repr x))
1019+
nil)
1020+
([x & args]
1021+
(let [stdout *out*
1022+
sep *print-sep*
1023+
repr-args (interpose sep (map repr args))]
1024+
(.write stdout (repr x))
1025+
(.write stdout sep)
1026+
(.write stdout (apply str repr-args))
1027+
nil)))
1028+
1029+
(defn prn
1030+
"Same as pr, but appending a newline afterwards."
1031+
([]
1032+
(.write *out* \newline)
1033+
nil)
1034+
([x]
1035+
(let [stdout *out*]
1036+
(.write stdout (repr x))
1037+
(.write stdout \newline)
1038+
nil))
1039+
([x & args]
1040+
(let [stdout *out*
1041+
sep *print-sep*
1042+
repr-args (interpose sep (map repr args))]
1043+
(.write stdout (repr x))
1044+
(.write stdout sep)
1045+
(.write stdout (apply str repr-args))
1046+
(.write stdout \newline)
1047+
nil)))
1048+
1049+
(defn pr-str
1050+
"Return the contents of calling pr on the args as a string."
1051+
[& args]
1052+
(with-out-str
1053+
(apply pr args)))
1054+
1055+
(defn prn-str
1056+
"Return the contents of calling prn on the args as a string."
1057+
[& args]
1058+
(with-out-str
1059+
(apply prn args)))
1060+
1061+
(defn print
1062+
"Print the arguments to the stream bound to *out* in a format which
1063+
is readable by humans. Multiple arguments will be separated by
1064+
the string value bound to *print-sep* (default is an ASCII space)."
1065+
([] (print ""))
1066+
([x]
1067+
(.write *out* (str x))
1068+
nil)
1069+
([x & args]
1070+
(let [stdout *out*
1071+
sep *print-sep*
1072+
repr-args (interpose sep (map str args))]
1073+
(.write stdout (str x))
1074+
(.write stdout sep)
1075+
(.write stdout (apply str repr-args))
1076+
nil)))
1077+
1078+
(defn println
1079+
"Print the arguments to the stream bound to *out* in a format which
1080+
is readable by humans. Multiple arguments will be separated by
1081+
the string value bound to *print-sep* (default is an ASCII space)."
1082+
([] (println ""))
1083+
([x]
1084+
(let [stdout *out*]
1085+
(.write stdout (str x))
1086+
(.write stdout \newline)
1087+
nil))
1088+
([x & args]
1089+
(let [stdout *out*
1090+
sep *print-sep*
1091+
repr-args (interpose sep (map str args))]
1092+
(.write stdout (str x))
1093+
(.write stdout sep)
1094+
(.write stdout (apply str repr-args))
1095+
(.write stdout \newline)
1096+
nil)))
1097+
1098+
(defn print-str
1099+
"Return the contents of calling print on the args as a string."
1100+
[& args]
1101+
(with-out-str
1102+
(apply print args)))
1103+
1104+
(defn println-str
1105+
"Return the contents of calling println on the args as a string."
1106+
[& args]
1107+
(with-out-str
1108+
(apply println args)))
1109+
9781110
;;;;;;;;;;;;;;;;;;;;
9791111
;; REPL Utilities ;;
9801112
;;;;;;;;;;;;;;;;;;;;

src/basilisp/lang/runtime.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ class Namespace:
234234
to without an alias in this namespace.
235235
"""
236236
DEFAULT_IMPORTS = atom.Atom(lset.set(seq(['builtins',
237+
'io',
237238
'operator',
238239
'sys',
239240
'basilisp.lang.atom',

tests/basilisp/core_test.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,12 @@ def test_reduce_with_lazy_seq():
543543
assert 25 == core.reduce(core.__PLUS__, 0, core.filter_(core.odd__Q__, vec.v(1, 2, 3, 4, 5, 6, 7, 8, 9)))
544544

545545

546+
def test_interpose():
547+
assert llist.List.empty() == core.interpose(",", vec.Vector.empty())
548+
assert llist.l("hi") == core.interpose(",", vec.v("hi"))
549+
assert llist.l("hi", ",", "there") == core.interpose(",", vec.v("hi", "there"))
550+
551+
546552
def test_comp():
547553
assert 1 == core.comp()(1)
548554
assert "hi" == core.comp()("hi")
@@ -613,6 +619,34 @@ def test_merge():
613619
lmap.map({kw.keyword("a"): 53}))
614620

615621

622+
def test_pr_str():
623+
assert '' == core.pr_str()
624+
assert '""' == core.pr_str("")
625+
assert ':kw' == core.pr_str(kw.keyword('kw'))
626+
assert ':hi "there" 3' == core.pr_str(kw.keyword('hi'), "there", 3)
627+
628+
629+
def test_prn_str():
630+
assert '\n' == core.prn_str()
631+
assert '""\n' == core.prn_str("")
632+
assert ':kw\n' == core.prn_str(kw.keyword('kw'))
633+
assert ':hi "there" 3\n' == core.prn_str(kw.keyword('hi'), "there", 3)
634+
635+
636+
def test_print_str():
637+
assert '' == core.print_str()
638+
assert '' == core.print_str("")
639+
assert 'kw' == core.print_str(kw.keyword('kw'))
640+
assert 'hi there 3' == core.print_str(kw.keyword('hi'), "there", 3)
641+
642+
643+
def test_println_str():
644+
assert '\n' == core.println_str()
645+
assert '\n' == core.println_str("")
646+
assert 'kw\n' == core.println_str(kw.keyword('kw'))
647+
assert 'hi there 3\n' == core.println_str(kw.keyword('hi'), "there", 3)
648+
649+
616650
def test_re_find():
617651
assert None is core.re_find(re.compile(r"\d+"), "abcdef")
618652
assert "12345" == core.re_find(re.compile(r"\d+"), "abc12345def")

0 commit comments

Comments
 (0)