5
5
import sys
6
6
import traceback
7
7
import types
8
- from typing import Any , Callable , Optional , Sequence
8
+ from typing import Any , Callable , Optional , Sequence , Type
9
9
10
10
from basilisp import main as basilisp
11
11
from basilisp .lang import compiler as compiler
20
20
STDIN_INPUT_FILE_PATH = "<stdin>"
21
21
STDIN_FILE_NAME = "-"
22
22
23
+ BOOL_TRUE = frozenset ({"true" , "t" , "1" , "yes" , "y" })
24
+ BOOL_FALSE = frozenset ({"false" , "f" , "0" , "no" , "n" })
25
+
26
+ DEFAULT_COMPILER_OPTS = {k .name : v for k , v in compiler .compiler_opts ().items ()}
27
+
23
28
24
29
def eval_file (filename : str , ctx : compiler .CompilerContext , ns : runtime .Namespace ):
25
30
"""Evaluate a file with the given name into a Python module AST node."""
@@ -56,42 +61,137 @@ def bootstrap_repl(ctx: compiler.CompilerContext, which_ns: str) -> types.Module
56
61
return importlib .import_module (REPL_NS )
57
62
58
63
64
+ def _to_bool (v : Optional [str ]) -> Optional [bool ]:
65
+ """Coerce a string argument to a boolean value, if possible."""
66
+ if v is None :
67
+ return v
68
+ elif v .lower () in BOOL_TRUE :
69
+ return True
70
+ elif v .lower () in BOOL_FALSE :
71
+ return False
72
+ else :
73
+ raise argparse .ArgumentTypeError ("Unable to coerce flag value to boolean." )
74
+
75
+
76
+ def _set_envvar_action (
77
+ var : str , parent : Type [argparse .Action ] = argparse .Action
78
+ ) -> Type [argparse .Action ]:
79
+ """Return an argparse.Action instance (deriving from `parent`) that sets the value
80
+ as the default value of the environment variable `var`."""
81
+
82
+ class EnvVarSetterAction (parent ): # type: ignore
83
+ def __call__ ( # pylint: disable=signature-differs
84
+ self ,
85
+ parser : argparse .ArgumentParser ,
86
+ namespace : argparse .Namespace ,
87
+ values : Any ,
88
+ option_string : str ,
89
+ ):
90
+ os .environ .setdefault (var , str (values ))
91
+
92
+ return EnvVarSetterAction
93
+
94
+
59
95
def _add_compiler_arg_group (parser : argparse .ArgumentParser ) -> None :
60
- group = parser .add_argument_group ("compiler arguments" )
96
+ group = parser .add_argument_group (
97
+ "compiler arguments" ,
98
+ description = (
99
+ "The compiler arguments below can be used to tweak warnings emitted by the "
100
+ "compiler during compilation and in some cases, tweak emitted code. Note "
101
+ "that Basilisp, like Python, aggressively caches compiled namespaces so "
102
+ "you may need to disable namespace caching or modify your file to see the "
103
+ "compiler argument changes take effect."
104
+ ),
105
+ )
61
106
group .add_argument (
62
107
"--warn-on-shadowed-name" ,
63
- action = "store_const" ,
64
- const = True ,
65
- default = os .getenv ("BASILISP_WARN_ON_SHADOWED_NAME" ),
66
- help = "if provided, emit warnings if a local name is shadowed by another local name" ,
108
+ action = "store" ,
109
+ nargs = "?" ,
110
+ const = os .getenv ("BASILISP_WARN_ON_SHADOWED_NAME" ),
111
+ type = _to_bool ,
112
+ help = (
113
+ "if true, emit warnings if a local name is shadowed by another local "
114
+ "name (env: BASILISP_WARN_ON_SHADOWED_NAME; default: "
115
+ f"{ DEFAULT_COMPILER_OPTS ['warn-on-shadowed-name' ]} )"
116
+ ),
67
117
)
68
118
group .add_argument (
69
119
"--warn-on-shadowed-var" ,
70
- action = "store_const" ,
71
- const = True ,
72
- default = os .getenv ("BASILISP_WARN_ON_SHADOWED_VAR" ),
73
- help = "if provided, emit warnings if a Var name is shadowed by a local name" ,
120
+ action = "store" ,
121
+ nargs = "?" ,
122
+ const = os .getenv ("BASILISP_WARN_ON_SHADOWED_VAR" ),
123
+ type = _to_bool ,
124
+ help = (
125
+ "if true, emit warnings if a Var name is shadowed by a local name "
126
+ "(env: BASILISP_WARN_ON_SHADOWED_VAR; default: "
127
+ f"{ DEFAULT_COMPILER_OPTS ['warn-on-shadowed-var' ]} )"
128
+ ),
74
129
)
75
130
group .add_argument (
76
131
"--warn-on-unused-names" ,
77
- action = "store_const" ,
78
- const = True ,
79
- default = os .getenv ("BASILISP_WARN_ON_UNUSED_NAMES" ),
80
- help = "if provided, emit warnings if a local name is bound and unused" ,
132
+ action = "store" ,
133
+ nargs = "?" ,
134
+ const = os .getenv ("BASILISP_WARN_ON_UNUSED_NAMES" ),
135
+ type = _to_bool ,
136
+ help = (
137
+ "if true, emit warnings if a local name is bound and unused "
138
+ "(env: BASILISP_WARN_ON_UNUSED_NAMES; default: "
139
+ f"{ DEFAULT_COMPILER_OPTS ['warn-on-unused-names' ]} )"
140
+ ),
141
+ )
142
+ group .add_argument (
143
+ "--warn-on-non-dynamic-set" ,
144
+ action = "store" ,
145
+ nargs = "?" ,
146
+ const = os .getenv ("BASILISP_WARN_ON_NON_DYNAMIC_SET" ),
147
+ type = _to_bool ,
148
+ help = (
149
+ "if true, emit warnings if the compiler detects an attempt to set! "
150
+ "a Var which is not marked as ^:dynamic (env: "
151
+ "BASILISP_WARN_ON_NON_DYNAMIC_SET; default: "
152
+ f"{ DEFAULT_COMPILER_OPTS ['warn-on-non-dynamic-set' ]} )"
153
+ ),
81
154
)
82
155
group .add_argument (
83
156
"--use-var-indirection" ,
84
- action = "store_const" ,
85
- const = True ,
86
- default = os .getenv ("BASILISP_USE_VAR_INDIRECTION" ),
87
- help = "if provided, all Var accesses will be performed via Var indirection" ,
157
+ action = "store" ,
158
+ nargs = "?" ,
159
+ const = os .getenv ("BASILISP_USE_VAR_INDIRECTION" ),
160
+ type = _to_bool ,
161
+ help = (
162
+ "if true, all Var accesses will be performed via Var indirection "
163
+ "(env: BASILISP_USE_VAR_INDIRECTION; default: "
164
+ f"{ DEFAULT_COMPILER_OPTS ['use-var-indirection' ]} )"
165
+ ),
88
166
)
89
167
group .add_argument (
90
168
"--warn-on-var-indirection" ,
91
- action = "store_const" ,
169
+ action = "store" ,
170
+ nargs = "?" ,
171
+ const = os .getenv ("BASILISP_WARN_ON_VAR_INDIRECTION" ),
172
+ type = _to_bool ,
173
+ help = (
174
+ "if true, emit warnings if a Var reference cannot be direct linked "
175
+ "(env: BASILISP_WARN_ON_VAR_INDIRECTION; default: "
176
+ f"{ DEFAULT_COMPILER_OPTS ['warn-on-var-indirection' ]} )"
177
+ ),
178
+ )
179
+
180
+
181
+ def _add_debug_arg_group (parser : argparse .ArgumentParser ) -> None :
182
+ group = parser .add_argument_group ("debug options" )
183
+ group .add_argument (
184
+ "--disable-ns-cache" ,
185
+ action = _set_envvar_action (
186
+ "BASILISP_DO_NOT_CACHE_NAMESPACES" , parent = argparse ._StoreAction
187
+ ),
188
+ nargs = "?" ,
92
189
const = True ,
93
- default = os .getenv ("BASILISP_WARN_ON_VAR_INDIRECTION" ),
94
- help = "if provided, emit warnings if a Var reference cannot be direct linked" ,
190
+ type = _to_bool ,
191
+ help = (
192
+ "if true, disable attempting to load cached namespaces "
193
+ "(env: BASILISP_DO_NOT_CACHE_NAMESPACES; default: false)"
194
+ ),
95
195
)
96
196
97
197
@@ -131,41 +231,71 @@ def repl(
131
231
)
132
232
basilisp .init (opts )
133
233
ctx = compiler .CompilerContext (filename = REPL_INPUT_FILE_PATH , opts = opts )
134
- repl_module = bootstrap_repl (ctx , args .default_ns )
135
- ns_var = runtime .set_current_ns (args .default_ns )
136
234
prompter = get_prompter ()
137
235
eof = object ()
138
- while True :
139
- ns : runtime .Namespace = ns_var .value
140
- try :
141
- lsrc = prompter .prompt (f"{ ns .name } => " )
142
- except EOFError :
143
- break
144
- except KeyboardInterrupt : # pragma: no cover
145
- print ("" )
146
- continue
147
-
148
- if len (lsrc ) == 0 :
149
- continue
150
-
151
- try :
152
- result = eval_str (lsrc , ctx , ns , eof )
153
- if result is eof : # pragma: no cover
236
+
237
+ # Bind user-settable dynamic Vars to their existing value to allow users to
238
+ # conveniently (set! *var* val) at the REPL without needing `binding`.
239
+ with runtime .bindings (
240
+ {
241
+ var : var .value
242
+ for var in map (
243
+ lambda name : runtime .Var .find_safe (
244
+ sym .symbol (name , ns = runtime .CORE_NS )
245
+ ),
246
+ [
247
+ "*e" ,
248
+ "*1" ,
249
+ "*2" ,
250
+ "*3" ,
251
+ "*assert*" ,
252
+ "*data-readers*" ,
253
+ "*resolver*" ,
254
+ runtime .PRINT_DUP_VAR_NAME ,
255
+ runtime .PRINT_LEVEL_VAR_NAME ,
256
+ runtime .PRINT_READABLY_VAR_NAME ,
257
+ runtime .PRINT_LEVEL_VAR_NAME ,
258
+ runtime .PRINT_META_VAR_NAME ,
259
+ ],
260
+ )
261
+ }
262
+ ):
263
+ repl_module = bootstrap_repl (ctx , args .default_ns )
264
+ ns_var = runtime .set_current_ns (args .default_ns )
265
+
266
+ while True :
267
+ ns : runtime .Namespace = ns_var .value
268
+ try :
269
+ lsrc = prompter .prompt (f"{ ns .name } => " )
270
+ except EOFError :
271
+ break
272
+ except KeyboardInterrupt : # pragma: no cover
273
+ print ("" )
274
+ continue
275
+
276
+ if len (lsrc ) == 0 :
277
+ continue
278
+
279
+ try :
280
+ result = eval_str (lsrc , ctx , ns , eof )
281
+ if result is eof : # pragma: no cover
282
+ continue
283
+ prompter .print (runtime .lrepr (result ))
284
+ repl_module .mark_repl_result (result ) # type: ignore
285
+ except reader .SyntaxError as e :
286
+ traceback .print_exception (reader .SyntaxError , e , e .__traceback__ )
287
+ repl_module .mark_exception (e ) # type: ignore
288
+ continue
289
+ except compiler .CompilerException as e :
290
+ traceback .print_exception (
291
+ compiler .CompilerException , e , e .__traceback__
292
+ )
293
+ repl_module .mark_exception (e ) # type: ignore
294
+ continue
295
+ except Exception as e :
296
+ traceback .print_exception (Exception , e , e .__traceback__ )
297
+ repl_module .mark_exception (e ) # type: ignore
154
298
continue
155
- prompter .print (runtime .lrepr (result ))
156
- repl_module .mark_repl_result (result ) # type: ignore
157
- except reader .SyntaxError as e :
158
- traceback .print_exception (reader .SyntaxError , e , e .__traceback__ )
159
- repl_module .mark_exception (e ) # type: ignore
160
- continue
161
- except compiler .CompilerException as e :
162
- traceback .print_exception (compiler .CompilerException , e , e .__traceback__ )
163
- repl_module .mark_exception (e ) # type: ignore
164
- continue
165
- except Exception as e :
166
- traceback .print_exception (Exception , e , e .__traceback__ )
167
- repl_module .mark_exception (e ) # type: ignore
168
- continue
169
299
170
300
171
301
@_subcommand (
@@ -181,6 +311,7 @@ def _add_repl_subcommand(parser: argparse.ArgumentParser) -> None:
181
311
help = "default namespace to use for the REPL" ,
182
312
)
183
313
_add_compiler_arg_group (parser )
314
+ _add_debug_arg_group (parser )
184
315
185
316
186
317
def run (
@@ -248,6 +379,7 @@ def _add_run_subcommand(parser: argparse.ArgumentParser):
248
379
"--in-ns" , default = runtime .REPL_DEFAULT_NS , help = "namespace to use for the code"
249
380
)
250
381
_add_compiler_arg_group (parser )
382
+ _add_debug_arg_group (parser )
251
383
252
384
253
385
def test (parser : argparse .ArgumentParser , args : argparse .Namespace ): # pragma: no cover
0 commit comments