src/basilisp/lang/compiler/generator.py
def _def_to_py_ast(  # pylint: disable=too-many-branches
    ctx: GeneratorContext, node: Def
) -> GeneratedPyAST:
    """Return a Python AST Node for a `def` expression."""
    assert node.op == NodeOp.DEF

    defsym = node.name
    is_defn = False

    if node.init is not None:
        # Since Python function definitions always take the form `def name(...):`,
        # it is redundant to assign them to the their final name after they have
        # been defined under a private alias. This codepath generates `defn`
        # declarations by directly generating the Python `def` with the correct
        # function name and short-circuiting the default double-declaration.
        if node.init.op == NodeOp.FN:
            assert isinstance(node.init, Fn)
            def_ast = _fn_to_py_ast(ctx, node.init, def_name=defsym.name)
            is_defn = True
        elif (
            node.init.op == NodeOp.WITH_META
            and isinstance(node.init, WithMeta)
            and node.init.expr.op == NodeOp.FN
        ):
            assert isinstance(node.init, WithMeta)
            def_ast = _with_meta_to_py_ast(ctx, node.init, def_name=defsym.name)
            is_defn = True
        else:
            def_ast = gen_py_ast(ctx, node.init)
    else:
        def_ast = GeneratedPyAST(node=ast.NameConstant(None))

    ns_name = ast.Call(func=_NEW_SYM_FN_NAME, args=[_NS_VAR_NAME], keywords=[])
    def_name = ast.Call(func=_NEW_SYM_FN_NAME, args=[ast.Str(defsym.name)], keywords=[])
    safe_name = munge(defsym.name)

    assert node.meta is not None, "Meta should always be attached to Def nodes"
    def_meta = node.meta.form
    assert isinstance(def_meta, lmap.Map), "Meta should always be a map"

    # If the Var is marked as dynamic, we need to generate a keyword argument
    # for the generated Python code to set the Var as dynamic
    is_dynamic = def_meta.entry(SYM_DYNAMIC_META_KEY, False)
    dynamic_kwarg = (
        [ast.keyword(arg="dynamic", value=ast.NameConstant(is_dynamic))]
        if is_dynamic
        else []
    )

    # Warn if this symbol is potentially being redefined
    if __should_warn_on_redef(ctx, defsym, safe_name, def_meta):
        logger.warning(
            f"redefining local Python name '{safe_name}' in module '{ctx.current_ns.module.__name__}'"
        )

    meta_ast = gen_py_ast(ctx, node.meta)

    # For defn style def generation, we specifically need to generate the
    # global declaration prior to emitting the Python `def` otherwise the
    # Python compiler will throw an exception during compilation
    # complaining that we assign the value prior to global declaration.
    if is_defn:
        def_dependencies = list(
            chain(
                [] if node.top_level else [ast.Global(names=[safe_name])],
                def_ast.dependencies,
                [] if meta_ast is None else meta_ast.dependencies,
            )
        )
    else:
        def_dependencies = list(
            chain(
                def_ast.dependencies,
                [] if node.top_level else [ast.Global(names=[safe_name])],
                [
                    ast.Assign(
                        targets=[ast.Name(id=safe_name, ctx=ast.Store())],
                        value=def_ast.node,
                    )
                ],
                [] if meta_ast is None else meta_ast.dependencies,
            )
        )

    return GeneratedPyAST(
        node=ast.Call(
            func=_INTERN_VAR_FN_NAME,
            args=[ns_name, def_name, ast.Name(id=safe_name, ctx=ast.Load())],
            keywords=list(
                chain(
                    dynamic_kwarg,
                    []
                    if meta_ast is None
                    else [ast.keyword(arg="meta", value=meta_ast.node)],
                )
            ),
        ),
        dependencies=def_dependencies,
    )
def _def_to_py_ast(  # pylint: disable=too-many-branches
    ctx: GeneratorContext, node: Def
) -> GeneratedPyAST:
    """Return a Python AST Node for a `def` expression."""
    assert node.op == NodeOp.DEF

    defsym = node.name
    is_defn = False

    if node.init is not None:
        # Since Python function definitions always take the form `def name(...):`,
        # it is redundant to assign them to the their final name after they have
        # been defined under a private alias. This codepath generates `defn`
        # declarations by directly generating the Python `def` with the correct
        # function name and short-circuiting the default double-declaration.
        if node.init.op == NodeOp.FN:
            assert isinstance(node.init, Fn)
            def_ast = _fn_to_py_ast(ctx, node.init, def_name=defsym.name)
            is_defn = True
        elif (
            node.init.op == NodeOp.WITH_META
            and isinstance(node.init, WithMeta)
            and node.init.expr.op == NodeOp.FN
        ):
            assert isinstance(node.init, WithMeta)
            def_ast = _with_meta_to_py_ast(ctx, node.init, def_name=defsym.name)
            is_defn = True
        else:
            def_ast = gen_py_ast(ctx, node.init)
    else:
        def_ast = GeneratedPyAST(node=ast.NameConstant(None))

    ns_name = ast.Call(func=_NEW_SYM_FN_NAME, args=[_NS_VAR_NAME], keywords=[])
    def_name = ast.Call(func=_NEW_SYM_FN_NAME, args=[ast.Str(defsym.name)], keywords=[])
    safe_name = munge(defsym.name)

    assert node.meta is not None, "Meta should always be attached to Def nodes"
    def_meta = node.meta.form
    assert isinstance(def_meta, lmap.Map), "Meta should always be a map"

    # If the Var is marked as dynamic, we need to generate a keyword argument
    # for the generated Python code to set the Var as dynamic
    is_dynamic = def_meta.entry(SYM_DYNAMIC_META_KEY, False)
    dynamic_kwarg = (
        [ast.keyword(arg="dynamic", value=ast.NameConstant(is_dynamic))]
        if is_dynamic
        else []
    )

    # Warn if this symbol is potentially being redefined
    if __should_warn_on_redef(ctx, defsym, safe_name, def_meta):
        logger.warning(
            f"redefining local Python name '{safe_name}' in module '{ctx.current_ns.module.__name__}'"
        )

    meta_ast = gen_py_ast(ctx, node.meta)

    # For defn style def generation, we specifically need to generate the
    # global declaration prior to emitting the Python `def` otherwise the
    # Python compiler will throw an exception during compilation
    # complaining that we assign the value prior to global declaration.
    if is_defn:
        def_dependencies = list(
            chain(
                [] if node.top_level else [ast.Global(names=[safe_name])],
                def_ast.dependencies,
                [] if meta_ast is None else meta_ast.dependencies,
            )
        )
    else:
        def_dependencies = list(
            chain(
                def_ast.dependencies,
                [] if node.top_level else [ast.Global(names=[safe_name])],
                [
                    ast.Assign(
                        targets=[ast.Name(id=safe_name, ctx=ast.Store())],
                        value=def_ast.node,
                    )
                ],
                [] if meta_ast is None else meta_ast.dependencies,
            )
        )

    return GeneratedPyAST(
        node=ast.Call(
            func=_INTERN_VAR_FN_NAME,
            args=[ns_name, def_name, ast.Name(id=safe_name, ctx=ast.Load())],
            keywords=list(
                chain(
                    dynamic_kwarg,
                    []
                    if meta_ast is None
                    else [ast.keyword(arg="meta", value=meta_ast.node)],
                )
            ),
        ),
        dependencies=def_dependencies,
    )
def _def_to_py_ast ( # pylint: disable=too-many-branches ctx : GeneratorContext , node : Def ) -> GeneratedPyAST : assert node . op == NodeOp . DEF defsym = node . name is_defn = False if node . init is not None : # Since Python function definitions always take the form `def name(...):`, # it is redundant to assign them to the their final name after they have # been defined under a private alias. This codepath generates `defn` # declarations by directly generating the Python `def` with the correct # function name and short-circuiting the default double-declaration. if node . init . op == NodeOp . FN : assert isinstance ( node . init , Fn ) def_ast = _fn_to_py_ast ( ctx , node . init , def_name = defsym . name ) is_defn = True elif ( node . init . op == NodeOp . WITH_META and isinstance ( node . init , WithMeta ) and node . init . expr . op == NodeOp . FN ) : assert isinstance ( node . init , WithMeta ) def_ast = _with_meta_to_py_ast ( ctx , node . init , def_name = defsym . name ) is_defn = True else : def_ast = gen_py_ast ( ctx , node . init ) else : def_ast = GeneratedPyAST ( node = ast . NameConstant ( None ) ) ns_name = ast . Call ( func = _NEW_SYM_FN_NAME , args = [ _NS_VAR_NAME ] , keywords = [ ] ) def_name = ast . Call ( func = _NEW_SYM_FN_NAME , args = [ ast . Str ( defsym . name ) ] , keywords = [ ] ) safe_name = munge ( defsym . name ) assert node . meta is not None , "Meta should always be attached to Def nodes" def_meta = node . meta . form assert isinstance ( def_meta , lmap . Map ) , "Meta should always be a map" # If the Var is marked as dynamic, we need to generate a keyword argument # for the generated Python code to set the Var as dynamic is_dynamic = def_meta . entry ( SYM_DYNAMIC_META_KEY , False ) dynamic_kwarg = ( [ ast . keyword ( arg = "dynamic" , value = ast . NameConstant ( is_dynamic ) ) ] if is_dynamic else [ ] ) # Warn if this symbol is potentially being redefined if __should_warn_on_redef ( ctx , defsym , safe_name , def_meta ) : logger . warning ( f"redefining local Python name '{safe_name}' in module '{ctx.current_ns.module.__name__}'" ) meta_ast = gen_py_ast ( ctx , node . meta ) # For defn style def generation, we specifically need to generate the # global declaration prior to emitting the Python `def` otherwise the # Python compiler will throw an exception during compilation # complaining that we assign the value prior to global declaration. if is_defn : def_dependencies = list ( chain ( [ ] if node . top_level else [ ast . Global ( names = [ safe_name ] ) ] , def_ast . dependencies , [ ] if meta_ast is None else meta_ast . dependencies , ) ) else : def_dependencies = list ( chain ( def_ast . dependencies , [ ] if node . top_level else [ ast . Global ( names = [ safe_name ] ) ] , [ ast . Assign ( targets = [ ast . Name ( id = safe_name , ctx = ast . Store ( ) ) ] , value = def_ast . node , ) ] , [ ] if meta_ast is None else meta_ast . dependencies , ) ) return GeneratedPyAST ( node = ast . Call ( func = _INTERN_VAR_FN_NAME , args = [ ns_name , def_name , ast . Name ( id = safe_name , ctx = ast . Load ( ) ) ] , keywords = list ( chain ( dynamic_kwarg , [ ] if meta_ast is None else [ ast . keyword ( arg = "meta" , value = meta_ast . node ) ] , ) ) , ) , dependencies = def_dependencies , )
Return a Python AST Node for a `def` expression.
Return a Python AST Node for a def expression .