Fix #1790: Rework statements in while condition
This avoids compiling them more than once while also applying some simplification.
This commit is contained in:
parent
3a4e31c209
commit
289f172d56
1
NEWS.rst
1
NEWS.rst
@ -12,6 +12,7 @@ Removals
|
|||||||
Bug Fixes
|
Bug Fixes
|
||||||
------------------------------
|
------------------------------
|
||||||
* Statements in the second argument of `assert` are now executed.
|
* Statements in the second argument of `assert` are now executed.
|
||||||
|
* Fixed the expression of a while loop that contains statements being compiled twice.
|
||||||
|
|
||||||
0.17.0
|
0.17.0
|
||||||
==============================
|
==============================
|
||||||
|
@ -1373,33 +1373,46 @@ class HyASTCompiler(object):
|
|||||||
def compile_while_expression(self, expr, root, cond, body, else_expr):
|
def compile_while_expression(self, expr, root, cond, body, else_expr):
|
||||||
cond_compiled = self.compile(cond)
|
cond_compiled = self.compile(cond)
|
||||||
|
|
||||||
|
body = self._compile_branch(body)
|
||||||
|
body += body.expr_as_stmt()
|
||||||
|
body_stmts = body.stmts or [asty.Pass(expr)]
|
||||||
|
|
||||||
if cond_compiled.stmts:
|
if cond_compiled.stmts:
|
||||||
# We need to ensure the statements for the condition are
|
# We need to ensure the statements for the condition are
|
||||||
# executed on every iteration. Rewrite the loop to use a
|
# executed on every iteration. Rewrite the loop to use a
|
||||||
# single anonymous variable as the condition.
|
# single anonymous variable as the condition, i.e.:
|
||||||
cond_var = self.get_anon_var()
|
# anon_var = True
|
||||||
return self.compile(mkexpr(
|
# while anon_var:
|
||||||
'do',
|
# condition stmts...
|
||||||
mkexpr('setv', cond_var, 'True'),
|
# anon_var = condition expr
|
||||||
mkexpr('while', cond_var,
|
# if anon_var:
|
||||||
# Cast the condition to a bool in case it's mutable and
|
# while loop body
|
||||||
# changes its truth value, but use (not (not ...)) instead of
|
cond_var = asty.Name(cond, id=self.get_anon_var(), ctx=ast.Load())
|
||||||
# `bool` in case `bool` has been redefined.
|
def make_not(operand):
|
||||||
mkexpr('setv', cond_var, mkexpr('not', mkexpr('not', [cond]))),
|
return asty.UnaryOp(cond, op=ast.Not(), operand=operand)
|
||||||
mkexpr('if*', cond_var, mkexpr('do', rest=body)),
|
|
||||||
*([mkexpr('else', rest=else_expr)] if else_expr is not None else []))).replace(expr)) # noqa
|
body_stmts = cond_compiled.stmts + [
|
||||||
|
asty.Assign(cond, targets=[self._storeize(cond, cond_var)],
|
||||||
|
# Cast the condition to a bool in case it's mutable and
|
||||||
|
# changes its truth value, but use (not (not ...)) instead of
|
||||||
|
# `bool` in case `bool` has been redefined.
|
||||||
|
value=make_not(make_not(cond_compiled.force_expr))),
|
||||||
|
asty.If(cond, test=cond_var, body=body_stmts, orelse=[]),
|
||||||
|
]
|
||||||
|
|
||||||
|
cond_compiled = (Result()
|
||||||
|
+ asty.Assign(cond, targets=[self._storeize(cond, cond_var)],
|
||||||
|
value=asty.Name(cond, id="True", ctx=ast.Load()))
|
||||||
|
+ cond_var)
|
||||||
|
|
||||||
orel = Result()
|
orel = Result()
|
||||||
if else_expr is not None:
|
if else_expr is not None:
|
||||||
orel = self._compile_branch(else_expr)
|
orel = self._compile_branch(else_expr)
|
||||||
orel += orel.expr_as_stmt()
|
orel += orel.expr_as_stmt()
|
||||||
|
|
||||||
body = self._compile_branch(body)
|
|
||||||
body += body.expr_as_stmt()
|
|
||||||
|
|
||||||
ret = cond_compiled + asty.While(
|
ret = cond_compiled + asty.While(
|
||||||
expr, test=cond_compiled.force_expr,
|
expr, test=cond_compiled.force_expr,
|
||||||
body=body.stmts or [asty.Pass(expr)],
|
body=body_stmts,
|
||||||
orelse=orel.stmts)
|
orelse=orel.stmts)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
@ -197,7 +197,23 @@
|
|||||||
(.append l 1)
|
(.append l 1)
|
||||||
(len l))
|
(len l))
|
||||||
(while (!= (f) 4) (do))
|
(while (!= (f) 4) (do))
|
||||||
(assert (= l [1 1 1 1])))
|
(assert (= l [1 1 1 1]))
|
||||||
|
|
||||||
|
; only compile the condition once
|
||||||
|
; https://github.com/hylang/hy/issues/1790
|
||||||
|
(global while-cond-var)
|
||||||
|
(setv while-cond-var 10)
|
||||||
|
(eval
|
||||||
|
'(do
|
||||||
|
(defmacro while-cond []
|
||||||
|
(global while-cond-var)
|
||||||
|
(assert (= while-cond-var 10))
|
||||||
|
(+= while-cond-var 1)
|
||||||
|
`(do
|
||||||
|
(setv x 3)
|
||||||
|
False))
|
||||||
|
(while (while-cond))
|
||||||
|
(assert (= x 3)))))
|
||||||
|
|
||||||
(defn test-while-loop-else []
|
(defn test-while-loop-else []
|
||||||
(setv count 5)
|
(setv count 5)
|
||||||
|
Loading…
Reference in New Issue
Block a user