Evaluate an if statement within a @magicquil block.

    If the test value is a Quil Addr then unwind it into quil code equivalent to an if then statement using jumps. Both
    sides of the if statement need to be evaluated and placed into separate Programs, which is why we create new
    program contexts for their evaluation.

    If the test value is not a Quil Addr then fall back to what Python would normally do with an if statement.

    Params are:
        if <test>:
            <if_function>
        else:
            <else_function>

    NB: This function must be named exactly _if_statement and be in scope for the ast transformer

def _if_statement(test, if_function, else_function) -> None:
    """
    Evaluate an if statement within a @magicquil block.

    If the test value is a Quil Addr then unwind it into quil code equivalent to an if then statement using jumps. Both
    sides of the if statement need to be evaluated and placed into separate Programs, which is why we create new
    program contexts for their evaluation.

    If the test value is not a Quil Addr then fall back to what Python would normally do with an if statement.

    Params are:
        if <test>:
            <if_function>
        else:
            <else_function>

    NB: This function must be named exactly _if_statement and be in scope for the ast transformer
    """
    if isinstance(test, Addr):
        token = _program_context.set(Program())
        if_function()
        if_program = _program_context.get()
        _program_context.reset(token)

        if else_function:
            token = _program_context.set(Program())
            else_function()
            else_program = _program_context.get()
            _program_context.reset(token)
        else:
            else_program = None

        program = _program_context.get()
        program.if_then(test, if_program, else_program)
    else:
        if test:
            if_function()
        elif else_function:
            else_function()