@@ -430,6 +430,8 @@ def _is_redefable(v: Var) -> bool:
430
430
_FIND_VAR_FN_NAME = _load_attr (f"{ _VAR_ALIAS } .find_safe" )
431
431
_COLLECT_ARGS_FN_NAME = _load_attr (f"{ _RUNTIME_ALIAS } ._collect_args" )
432
432
_COERCE_SEQ_FN_NAME = _load_attr (f"{ _RUNTIME_ALIAS } .to_seq" )
433
+ _BASILISP_FN_FN_NAME = _load_attr (f"{ _RUNTIME_ALIAS } ._basilisp_fn" )
434
+ _FN_WITH_ATTRS_FN_NAME = _load_attr (f"{ _RUNTIME_ALIAS } ._with_attrs" )
433
435
_TRAMPOLINE_FN_NAME = _load_attr (f"{ _RUNTIME_ALIAS } ._trampoline" )
434
436
_TRAMPOLINE_ARGS_FN_NAME = _load_attr (f"{ _RUNTIME_ALIAS } ._TrampolineArgs" )
435
437
@@ -568,6 +570,14 @@ def _def_to_py_ast( # pylint: disable=too-many-branches
568
570
assert isinstance (node .init , Fn )
569
571
def_ast = _fn_to_py_ast (ctx , node .init , def_name = defsym .name )
570
572
is_defn = True
573
+ elif (
574
+ node .init .op == NodeOp .WITH_META
575
+ and isinstance (node .init , WithMeta )
576
+ and node .init .expr .op == NodeOp .FN
577
+ ):
578
+ assert isinstance (node .init , WithMeta )
579
+ def_ast = _with_meta_to_py_ast (ctx , node .init , def_name = defsym .name )
580
+ is_defn = True
571
581
else :
572
582
def_ast = gen_py_ast (ctx , node .init )
573
583
else :
@@ -684,6 +694,9 @@ def _synthetic_do_to_py_ast(ctx: GeneratorContext, node: Do) -> GeneratedPyAST:
684
694
)
685
695
686
696
697
+ MetaNode = Union [Const , MapNode ]
698
+
699
+
687
700
def __fn_name (s : Optional [str ]) -> str :
688
701
"""Generate a safe Python function name from a function name symbol.
689
702
If no symbol is provided, generate a name with a default prefix."""
@@ -730,9 +743,32 @@ def __fn_args_to_py_ast(
730
743
return fn_args , varg , fn_body_ast
731
744
732
745
746
+ def __fn_meta (
747
+ ctx : GeneratorContext , meta_node : Optional [MetaNode ] = None
748
+ ) -> Tuple [Iterable [ast .AST ], Iterable [ast .AST ]]:
749
+ if meta_node is not None :
750
+ meta_ast = gen_py_ast (ctx , meta_node )
751
+ return (
752
+ meta_ast .dependencies ,
753
+ [
754
+ ast .Call (
755
+ func = _FN_WITH_ATTRS_FN_NAME ,
756
+ args = [],
757
+ keywords = [ast .keyword (arg = "meta" , value = meta_ast .node )],
758
+ )
759
+ ],
760
+ )
761
+ else :
762
+ return (), ()
763
+
764
+
733
765
@_with_ast_loc_deps
734
766
def __single_arity_fn_to_py_ast (
735
- ctx : GeneratorContext , node : Fn , method : FnMethod , def_name : Optional [str ] = None
767
+ ctx : GeneratorContext ,
768
+ node : Fn ,
769
+ method : FnMethod ,
770
+ def_name : Optional [str ] = None ,
771
+ meta_node : Optional [MetaNode ] = None ,
736
772
) -> GeneratedPyAST :
737
773
"""Return a Python AST node for a function with a single arity."""
738
774
assert node .op == NodeOp .FN
@@ -753,34 +789,56 @@ def __single_arity_fn_to_py_ast(
753
789
fn_args , varg , fn_body_ast = __fn_args_to_py_ast (
754
790
ctx , method .params , method .body
755
791
)
792
+ meta_deps , meta_decorators = __fn_meta (ctx , meta_node )
756
793
return GeneratedPyAST (
757
794
node = ast .Name (id = py_fn_name , ctx = ast .Load ()),
758
- dependencies = [
759
- py_fn_node (
760
- name = py_fn_name ,
761
- args = ast .arguments (
762
- args = fn_args ,
763
- kwarg = None ,
764
- vararg = varg ,
765
- kwonlyargs = [],
766
- defaults = [],
767
- kw_defaults = [],
768
- ),
769
- body = fn_body_ast ,
770
- decorator_list = [_TRAMPOLINE_FN_NAME ]
771
- if ctx .recur_point .has_recur
772
- else [],
773
- returns = None ,
795
+ dependencies = list (
796
+ chain (
797
+ meta_deps ,
798
+ [
799
+ py_fn_node (
800
+ name = py_fn_name ,
801
+ args = ast .arguments (
802
+ args = fn_args ,
803
+ kwarg = None ,
804
+ vararg = varg ,
805
+ kwonlyargs = [],
806
+ defaults = [],
807
+ kw_defaults = [],
808
+ ),
809
+ body = fn_body_ast ,
810
+ decorator_list = list (
811
+ chain (
812
+ meta_decorators ,
813
+ [_BASILISP_FN_FN_NAME ],
814
+ [_TRAMPOLINE_FN_NAME ]
815
+ if ctx .recur_point .has_recur
816
+ else [],
817
+ )
818
+ ),
819
+ returns = None ,
820
+ )
821
+ ],
774
822
)
775
- ] ,
823
+ ) ,
776
824
)
777
825
778
826
779
- def __multi_arity_dispatch_fn (
827
+ def __handle_async_return (node : ast .AST ) -> ast .Return :
828
+ return ast .Return (value = ast .Await (value = node ))
829
+
830
+
831
+ def __handle_return (node : ast .AST ) -> ast .Return :
832
+ return ast .Return (value = node )
833
+
834
+
835
+ def __multi_arity_dispatch_fn ( # pylint: disable=too-many-arguments,too-many-locals
836
+ ctx : GeneratorContext ,
780
837
name : str ,
781
838
arity_map : Dict [int , str ],
782
839
default_name : Optional [str ] = None ,
783
840
max_fixed_arity : Optional [int ] = None ,
841
+ meta_node : Optional [MetaNode ] = None ,
784
842
is_async : bool = False ,
785
843
) -> GeneratedPyAST :
786
844
"""Return the Python AST nodes for a argument-length dispatch function
@@ -803,6 +861,9 @@ def fn(*args):
803
861
dispatch_keys .append (ast .Num (k ))
804
862
dispatch_vals .append (ast .Name (id = v , ctx = ast .Load ()))
805
863
864
+ # Async functions should return await, otherwise just return
865
+ handle_return = __handle_async_return if is_async else __handle_return
866
+
806
867
nargs_name = genname ("nargs" )
807
868
method_name = genname ("method" )
808
869
body = [
@@ -833,8 +894,8 @@ def fn(*args):
833
894
comparators = [ast .Name (id = method_name , ctx = ast .Load ())],
834
895
),
835
896
body = [
836
- ast . Return (
837
- value = ast .Call (
897
+ handle_return (
898
+ ast .Call (
838
899
func = ast .Name (id = method_name , ctx = ast .Load ()),
839
900
args = [
840
901
ast .Starred (
@@ -858,8 +919,8 @@ def fn(*args):
858
919
comparators = [ast .Num (max_fixed_arity )],
859
920
),
860
921
body = [
861
- ast . Return (
862
- value = ast .Call (
922
+ handle_return (
923
+ ast .Call (
863
924
func = ast .Name (id = default_name , ctx = ast .Load ()),
864
925
args = [
865
926
ast .Starred (
@@ -891,28 +952,34 @@ def fn(*args):
891
952
]
892
953
893
954
py_fn_node = ast .AsyncFunctionDef if is_async else ast .FunctionDef
955
+ meta_deps , meta_decorators = __fn_meta (ctx , meta_node )
894
956
return GeneratedPyAST (
895
957
node = ast .Name (id = name , ctx = ast .Load ()),
896
- dependencies = [
897
- ast .Assign (
898
- targets = [ast .Name (id = dispatch_map_name , ctx = ast .Store ())],
899
- value = ast .Dict (keys = dispatch_keys , values = dispatch_vals ),
900
- ),
901
- py_fn_node (
902
- name = name ,
903
- args = ast .arguments (
904
- args = [],
905
- kwarg = None ,
906
- vararg = ast .arg (arg = _MULTI_ARITY_ARG_NAME , annotation = None ),
907
- kwonlyargs = [],
908
- defaults = [],
909
- kw_defaults = [],
910
- ),
911
- body = body ,
912
- decorator_list = [],
913
- returns = None ,
914
- ),
915
- ],
958
+ dependencies = chain (
959
+ [
960
+ ast .Assign (
961
+ targets = [ast .Name (id = dispatch_map_name , ctx = ast .Store ())],
962
+ value = ast .Dict (keys = dispatch_keys , values = dispatch_vals ),
963
+ )
964
+ ],
965
+ meta_deps ,
966
+ [
967
+ py_fn_node (
968
+ name = name ,
969
+ args = ast .arguments (
970
+ args = [],
971
+ kwarg = None ,
972
+ vararg = ast .arg (arg = _MULTI_ARITY_ARG_NAME , annotation = None ),
973
+ kwonlyargs = [],
974
+ defaults = [],
975
+ kw_defaults = [],
976
+ ),
977
+ body = body ,
978
+ decorator_list = list (chain (meta_decorators , [_BASILISP_FN_FN_NAME ])),
979
+ returns = None ,
980
+ )
981
+ ],
982
+ ),
916
983
)
917
984
918
985
@@ -922,6 +989,7 @@ def __multi_arity_fn_to_py_ast( # pylint: disable=too-many-locals
922
989
node : Fn ,
923
990
methods : Collection [FnMethod ],
924
991
def_name : Optional [str ] = None ,
992
+ meta_node : Optional [MetaNode ] = None ,
925
993
) -> GeneratedPyAST :
926
994
"""Return a Python AST node for a function with multiple arities."""
927
995
assert node .op == NodeOp .FN
@@ -974,10 +1042,13 @@ def __multi_arity_fn_to_py_ast( # pylint: disable=too-many-locals
974
1042
)
975
1043
976
1044
dispatch_fn_ast = __multi_arity_dispatch_fn (
1045
+ ctx ,
977
1046
py_fn_name ,
978
1047
arity_to_name ,
979
1048
default_name = rest_arity_name ,
980
1049
max_fixed_arity = node .max_fixed_arity ,
1050
+ meta_node = meta_node ,
1051
+ is_async = node .is_async ,
981
1052
)
982
1053
983
1054
return GeneratedPyAST (
@@ -988,16 +1059,21 @@ def __multi_arity_fn_to_py_ast( # pylint: disable=too-many-locals
988
1059
989
1060
@_with_ast_loc
990
1061
def _fn_to_py_ast (
991
- ctx : GeneratorContext , node : Fn , def_name : Optional [str ] = None
1062
+ ctx : GeneratorContext ,
1063
+ node : Fn ,
1064
+ def_name : Optional [str ] = None ,
1065
+ meta_node : Optional [MetaNode ] = None ,
992
1066
) -> GeneratedPyAST :
993
1067
"""Return a Python AST Node for a `fn` expression."""
994
1068
assert node .op == NodeOp .FN
995
1069
if len (node .methods ) == 1 :
996
1070
return __single_arity_fn_to_py_ast (
997
- ctx , node , next (iter (node .methods )), def_name = def_name
1071
+ ctx , node , next (iter (node .methods )), def_name = def_name , meta_node = meta_node
998
1072
)
999
1073
else :
1000
- return __multi_arity_fn_to_py_ast (ctx , node , node .methods , def_name = def_name )
1074
+ return __multi_arity_fn_to_py_ast (
1075
+ ctx , node , node .methods , def_name = def_name , meta_node = meta_node
1076
+ )
1001
1077
1002
1078
1003
1079
@_with_ast_loc_deps
@@ -1674,9 +1750,6 @@ def _maybe_host_form_to_py_ast(
1674
1750
#########################
1675
1751
1676
1752
1677
- MetaNode = Union [Const , MapNode ]
1678
-
1679
-
1680
1753
@_with_ast_loc
1681
1754
def _map_to_py_ast (
1682
1755
ctx : GeneratorContext , node : MapNode , meta_node : Optional [MetaNode ] = None
@@ -1816,22 +1889,24 @@ def _py_tuple_to_py_ast(ctx: GeneratorContext, node: PyTuple) -> GeneratedPyAST:
1816
1889
1817
1890
1818
1891
_WITH_META_EXPR_HANDLER = { # type: ignore
1819
- NodeOp .FN : None , # TODO: function with meta
1892
+ NodeOp .FN : _fn_to_py_ast ,
1820
1893
NodeOp .MAP : _map_to_py_ast ,
1821
1894
NodeOp .SET : _set_to_py_ast ,
1822
1895
NodeOp .VECTOR : _vec_to_py_ast ,
1823
1896
}
1824
1897
1825
1898
1826
- def _with_meta_to_py_ast (ctx : GeneratorContext , node : WithMeta ) -> GeneratedPyAST :
1899
+ def _with_meta_to_py_ast (
1900
+ ctx : GeneratorContext , node : WithMeta , ** kwargs
1901
+ ) -> GeneratedPyAST :
1827
1902
"""Generate a Python AST node for Python interop method calls."""
1828
1903
assert node .op == NodeOp .WITH_META
1829
1904
1830
1905
handle_expr = _WITH_META_EXPR_HANDLER .get (node .expr .op )
1831
1906
assert (
1832
1907
handle_expr is not None
1833
1908
), "No expression handler for with-meta child node type"
1834
- return handle_expr (ctx , node .expr , meta_node = node .meta ) # type: ignore
1909
+ return handle_expr (ctx , node .expr , meta_node = node .meta , ** kwargs ) # type: ignore
1835
1910
1836
1911
1837
1912
#################
0 commit comments