Skip to content

Commit 46fe8fb

Browse files
authored
Add REPL utility vars (#171)
* Add REPL utility vars * Fix a typechecking error * Silence linter
1 parent 67da90a commit 46fe8fb

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

basilisp/cli.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import importlib
12
# noinspection PyUnresolvedReferences
23
import readline # noqa: F401
34
import traceback
@@ -7,8 +8,10 @@
78

89
import basilisp.compiler as compiler
910
import basilisp.lang.runtime as runtime
11+
import basilisp.lang.symbol as sym
1012
import basilisp.main as basilisp
1113
import basilisp.reader as reader
14+
from basilisp.util import Maybe
1215

1316

1417
@click.group()
@@ -33,10 +36,27 @@ def eval_str(s: str, ctx: compiler.CompilerContext, module: types.ModuleType):
3336
return last
3437

3538

39+
def bootstrap_repl(which_ns: str) -> types.ModuleType:
40+
"""Bootstrap the REPL with a few useful vars and returned the
41+
bootstrapped module so it's functions can be used by the REPL
42+
command."""
43+
repl_ns = runtime.Namespace.get_or_create(sym.symbol('basilisp.repl'))
44+
ns = runtime.Namespace.get_or_create(sym.symbol(which_ns))
45+
repl_module = importlib.import_module('basilisp.repl')
46+
ns.add_alias(sym.symbol('basilisp.repl'), repl_ns)
47+
for name in ['*1', '*2', '*3', '*e', 'doc', 'pydoc', 'source']:
48+
ns.intern(sym.symbol(name),
49+
Maybe(runtime.Var.find(sym.symbol(name, ns='basilisp.repl'))).or_else_raise(
50+
lambda: runtime.RuntimeException(
51+
f"Var basilisp.repl/{name} not found!"))) # pylint: disable=cell-var-from-loop
52+
return repl_module
53+
54+
3655
@cli.command(short_help='start the Basilisp REPL')
3756
@click.option('--default-ns', default=runtime._REPL_DEFAULT_NS, help='default namespace to use for the REPL')
3857
def repl(default_ns):
3958
basilisp.init()
59+
repl_module = bootstrap_repl(default_ns)
4060
ctx = compiler.CompilerContext()
4161
ns_var = runtime.set_current_ns(default_ns)
4262
while True:
@@ -53,16 +73,21 @@ def repl(default_ns):
5373
continue
5474

5575
try:
56-
print(compiler.lrepr(eval_str(lsrc, ctx, ns.module)))
76+
result = eval_str(lsrc, ctx, ns.module)
77+
print(compiler.lrepr(result))
78+
repl_module.mark_repl_result(result)
5779
except reader.SyntaxError as e:
5880
traceback.print_exception(reader.SyntaxError, e, e.__traceback__)
81+
repl_module.mark_exception(e)
5982
continue
6083
except compiler.CompilerException as e:
6184
traceback.print_exception(compiler.CompilerException, e,
6285
e.__traceback__)
86+
repl_module.mark_exception(e)
6387
continue
6488
except Exception as e:
6589
traceback.print_exception(Exception, e, e.__traceback__)
90+
repl_module.mark_exception(e)
6691
continue
6792

6893

basilisp/repl.lpy

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
(in-ns 'basilisp.repl)
2+
3+
(import* inspect)
4+
5+
(def ^:dynamic *1 nil)
6+
(def ^:dynamic *2 nil)
7+
(def ^:dynamic *3 nil)
8+
(def ^:dynamic *e nil)
9+
10+
(defn mark-repl-result
11+
"Mark the REPL result and move each result to the next slot
12+
down."
13+
[result]
14+
(set! '*3 *2)
15+
(set! '*2 *1)
16+
(set! '*1 result))
17+
18+
(defn mark-exception
19+
"Set the variable *e to the last exception at the REPL."
20+
[e]
21+
(set! '*e e))
22+
23+
(defn pydoc
24+
"Print the Python docstring for a function."
25+
[o]
26+
(print (inspect/getdoc o)))
27+
28+
(defn print-doc
29+
"Print the docstring from an interned var."
30+
[v]
31+
(let [var-meta (meta v)]
32+
(if var-meta
33+
(print (:doc var-meta))
34+
nil)))
35+
36+
(defmacro doc
37+
"Print the docstring from an interned Var if found."
38+
[s]
39+
`(print-doc (var ~s)))
40+
41+
(defn print-source
42+
"Print the source forms for a function."
43+
[v]
44+
(throw (builtins/NotImplementedError)))
45+
46+
(defmacro source
47+
"Print the source code for a form if found."
48+
[s]
49+
`(print-source (var ~s)))

0 commit comments

Comments
 (0)