Skip to content

Commit 84282f5

Browse files
authored
Improve Python generation for do and letfn* forms (#795)
Fixes #794
1 parent 06c720f commit 84282f5

File tree

3 files changed

+55
-32
lines changed

3 files changed

+55
-32
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
* Allow vars to be callable to adhere to Clojure conventions (#767)
1919
* Adjust input path compatibility in `basilisp.core/load` input path to be relative to the namespace or the root path (#782)
2020
* No longer warn on unused bindings when their name begins with `_` (#756)
21-
* Improve the Python generation for `if` and `let*` forms to avoid unnecessary extra assignments (#793)
21+
* Improve the Python generation for `do`, `if`, `let*`, and `letfn*` forms to avoid unnecessary extra assignments (#793, #794)
2222

2323
### Fixed
2424
* Fix issue with `(count nil)` throwing an exception (#759)

src/basilisp/lang/compiler/generator.py

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,13 +1520,18 @@ def _do_to_py_ast(ctx: GeneratorContext, node: Do) -> GeneratedPyAST:
15201520
)
15211521

15221522
fn_body_ast: List[ast.AST] = []
1523-
do_result_name = genname(_DO_PREFIX)
15241523
fn_body_ast.extend(map(statementize, body_ast.dependencies))
1525-
fn_body_ast.append(
1526-
ast.Assign(
1527-
targets=[ast.Name(id=do_result_name, ctx=ast.Store())], value=body_ast.node
1524+
1525+
if isinstance(body_ast.node, ast.Name):
1526+
do_result_name = body_ast.node.id
1527+
else:
1528+
do_result_name = genname(_DO_PREFIX)
1529+
fn_body_ast.append(
1530+
ast.Assign(
1531+
targets=[ast.Name(id=do_result_name, ctx=ast.Store())],
1532+
value=body_ast.node,
1533+
)
15281534
)
1529-
)
15301535

15311536
return GeneratedPyAST(
15321537
node=ast.Name(id=do_result_name, ctx=ast.Load()), dependencies=fn_body_ast
@@ -2311,6 +2316,7 @@ def _let_to_py_ast(ctx: GeneratorContext, node: Let) -> GeneratedPyAST:
23112316
if node.env.pos == NodeSyntacticPosition.EXPR:
23122317
if isinstance(body_ast.node, ast.Name):
23132318
let_result_node = body_ast.node
2319+
assert isinstance(let_result_node.ctx, ast.Load)
23142320
else:
23152321
let_result_name = genname(_LET_RESULT_PREFIX)
23162322
let_result_node = ast.Name(id=let_result_name, ctx=ast.Load())
@@ -2320,10 +2326,7 @@ def _let_to_py_ast(ctx: GeneratorContext, node: Let) -> GeneratedPyAST:
23202326
value=body_ast.node,
23212327
)
23222328
)
2323-
return GeneratedPyAST(
2324-
node=let_result_node,
2325-
dependencies=let_body_ast,
2326-
)
2329+
return GeneratedPyAST(node=let_result_node, dependencies=let_body_ast)
23272330
else:
23282331
let_body_ast.append(body_ast.node)
23292332
return GeneratedPyAST(node=_noop_node(), dependencies=let_body_ast)
@@ -2356,21 +2359,23 @@ def _letfn_to_py_ast(ctx: GeneratorContext, node: LetFn) -> GeneratedPyAST:
23562359
)
23572360
)
23582361

2359-
letfn_result_name = genname(_LETFN_RESULT_PREFIX)
23602362
body_ast = _synthetic_do_to_py_ast(ctx, node.body)
23612363
letfn_body_ast.extend(map(statementize, body_ast.dependencies))
23622364

23632365
if node.env.pos == NodeSyntacticPosition.EXPR:
2364-
letfn_body_ast.append(
2365-
ast.Assign(
2366-
targets=[ast.Name(id=letfn_result_name, ctx=ast.Store())],
2367-
value=body_ast.node,
2366+
if isinstance(body_ast.node, ast.Name):
2367+
letfn_result_node = body_ast.node
2368+
assert isinstance(letfn_result_node.ctx, ast.Load)
2369+
else:
2370+
letfn_result_name = genname(_LETFN_RESULT_PREFIX)
2371+
letfn_result_node = ast.Name(id=letfn_result_name, ctx=ast.Load())
2372+
letfn_body_ast.append(
2373+
ast.Assign(
2374+
targets=[ast.Name(id=letfn_result_name, ctx=ast.Store())],
2375+
value=body_ast.node,
2376+
)
23682377
)
2369-
)
2370-
return GeneratedPyAST(
2371-
node=ast.Name(id=letfn_result_name, ctx=ast.Load()),
2372-
dependencies=letfn_body_ast,
2373-
)
2378+
return GeneratedPyAST(node=letfn_result_node, dependencies=letfn_body_ast)
23742379
else:
23752380
letfn_body_ast.append(body_ast.node)
23762381
return GeneratedPyAST(node=_noop_node(), dependencies=letfn_body_ast)

tests/basilisp/compiler_test.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2080,18 +2080,32 @@ def test_type_is_not_irecord(
20802080
lcompile(f"#{test_ns}.NewType{{:a 1 :b 2}}")
20812081

20822082

2083-
def test_do(lcompile: CompileFn, ns: runtime.Namespace):
2084-
code = """
2085-
(do
2086-
(def first-name :Darth)
2087-
(def last-name "Vader"))
2088-
"""
2089-
ns_name = ns.name
2090-
assert lcompile(code) == Var.find_in_ns(
2091-
sym.symbol(ns_name), sym.symbol("last-name")
2092-
)
2093-
assert lcompile("first-name") == kw.keyword("Darth")
2094-
assert lcompile("last-name") == "Vader"
2083+
class TestDo:
2084+
def test_do(self, lcompile: CompileFn, ns: runtime.Namespace):
2085+
code = """
2086+
(do
2087+
(def first-name :Darth)
2088+
(def last-name "Vader"))
2089+
"""
2090+
ns_name = ns.name
2091+
assert lcompile(code) == Var.find_in_ns(
2092+
sym.symbol(ns_name), sym.symbol("last-name")
2093+
)
2094+
assert lcompile("first-name") == kw.keyword("Darth")
2095+
assert lcompile("last-name") == "Vader"
2096+
2097+
def test_do_returning_name(self, lcompile: CompileFn, ns: runtime.Namespace):
2098+
assert (
2099+
lcompile(
2100+
"""
2101+
(let* [a :a]
2102+
(do
2103+
(def some-value a)
2104+
a))
2105+
"""
2106+
)
2107+
== kw.keyword("a")
2108+
)
20952109

20962110

20972111
class TestFunctionShadowName:
@@ -3453,6 +3467,10 @@ def test_letfn_may_have_empty_body(self, lcompile: CompileFn):
34533467
assert None is lcompile("(letfn* [])")
34543468
assert None is lcompile("(letfn* [a (fn* a [])])")
34553469

3470+
def test_let_return_bound_name(self, lcompile: CompileFn):
3471+
f = lcompile("(letfn* [a (fn* a [])] a)")
3472+
assert f() is None
3473+
34563474
def test_letfn(self, lcompile: CompileFn):
34573475
assert lcompile("(letfn* [a (fn* a [] 1)] (a))") == 1
34583476
assert lcompile("(letfn* [a (fn* a [] 1) b (fn* b [] 2)] (b))") == 2

0 commit comments

Comments
 (0)