Skip to content

Commit 7078cb2

Browse files
committed
Extract non-dx code handling into Dx.Defd.NonDx
1 parent 8f3e086 commit 7078cb2

File tree

2 files changed

+325
-240
lines changed

2 files changed

+325
-240
lines changed

lib/dx/defd/compiler.ex

Lines changed: 46 additions & 240 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ defmodule Dx.Defd.Compiler do
44
alias Dx.Defd.Ast
55
alias Dx.Defd.Ast.Loader
66
alias Dx.Defd.Ast.State
7+
alias Dx.Defd.NonDx
78
alias Dx.Defd.Util
89

910
import Ast.Guards
@@ -23,24 +24,24 @@ defmodule Dx.Defd.Compiler do
2324
defds = compile_prepare_arities(exports)
2425
all_arities = all_arities(exports)
2526

26-
state = %{
27-
module: module,
28-
file: file,
29-
line: line,
30-
function: nil,
31-
defds: defds,
32-
all_arities: all_arities,
33-
used_defds: MapSet.new(),
34-
args: MapSet.new(),
35-
var_index: 1,
36-
scope_args: [],
37-
eval_var: eval_var,
38-
warn_non_dx?: true,
39-
called_non_dx?: false,
40-
loaders: [],
41-
finalized_vars: MapSet.new(),
42-
rewrite_underscore?: false
43-
}
27+
state =
28+
%{
29+
module: module,
30+
file: file,
31+
line: line,
32+
function: nil,
33+
defds: defds,
34+
all_arities: all_arities,
35+
used_defds: MapSet.new(),
36+
args: MapSet.new(),
37+
var_index: 1,
38+
scope_args: [],
39+
eval_var: eval_var,
40+
loaders: [],
41+
finalized_vars: MapSet.new(),
42+
rewrite_underscore?: false
43+
}
44+
|> NonDx.init()
4445

4546
{quoted, state} =
4647
Enum.flat_map_reduce(exports, state, fn def, state ->
@@ -307,15 +308,17 @@ defmodule Dx.Defd.Compiler do
307308
case_ast = {:case, [line: line], [case_subject, [do: case_clauses]]}
308309

309310
{final_args_ast, final_args_state} =
310-
State.pass_in(
311-
state,
312-
[warn_non_dx?: false, finalized_vars: &Ast.collect_vars(state.all_args, &1)],
313-
fn state ->
314-
Ast.with_root_args(state.all_args, state, fn state ->
315-
Dx.Defd.Case.normalize(case_ast, state)
316-
end)
317-
end
318-
)
311+
NonDx.suppress_warnings(state, fn state ->
312+
State.pass_in(
313+
state,
314+
[finalized_vars: &Ast.collect_vars(state.all_args, &1)],
315+
fn state ->
316+
Ast.with_root_args(state.all_args, state, fn state ->
317+
Dx.Defd.Case.normalize(case_ast, state)
318+
end)
319+
end
320+
)
321+
end)
319322

320323
{{final_args_args, final_args_ast}, final_args_state} =
321324
maybe_unwrap_case(final_args_ast, final_args_state)
@@ -483,20 +486,8 @@ defmodule Dx.Defd.Compiler do
483486
|> with_state(state)
484487
end
485488

486-
def normalize({{:., meta, [Dx.Defd, :non_dx]}, _meta2, [ast]}, orig_state) do
487-
State.pass_in(orig_state, [warn_non_dx?: false, called_non_dx?: false], fn state ->
488-
{ast, state} = normalize(ast, state)
489-
490-
if orig_state.warn_non_dx? and not state.called_non_dx? do
491-
warn(meta, state, """
492-
No function was called that is not defined with defd.
493-
494-
Please remove the call to non_dx/1.
495-
""")
496-
end
497-
498-
{ast, state}
499-
end)
489+
def normalize({{:., _meta, [Dx.Defd, :non_dx]}, _meta2, [_ast]} = ast, orig_state) do
490+
NonDx.normalize(ast, orig_state)
500491
end
501492

502493
def normalize({:fn, _meta, clauses} = fun, state) when is_list(clauses) do
@@ -577,7 +568,7 @@ defmodule Dx.Defd.Compiler do
577568
end
578569

579570
# &local_fun/2
580-
def normalize({:&, meta, [{:/, [], [{fun_name, meta2, nil}, arity]}]}, state) do
571+
def normalize({:&, meta, [{:/, [], [{fun_name, meta2, nil}, arity]}]} = fun, state) do
581572
args = Macro.generate_arguments(arity, __MODULE__)
582573
line = meta[:line] || state.line
583574

@@ -605,23 +596,7 @@ defmodule Dx.Defd.Compiler do
605596
]}}
606597
|> with_state(state)
607598
else
608-
if state.warn_non_dx? do
609-
warn(meta, state, """
610-
#{fun_name}/#{arity} is not defined with defd.
611-
612-
Either define it using defd (preferred) or wrap the call in the non_dx/1 function:
613-
614-
non_dx(...(&#{fun_name}/#{arity}))
615-
""")
616-
end
617-
618-
quote line: line do
619-
{:ok,
620-
fn unquote_splicing(args) ->
621-
{:ok, unquote(fun_name)(unquote_splicing(args))}
622-
end}
623-
end
624-
|> with_state(%{state | called_non_dx?: true})
599+
NonDx.normalize(fun, state)
625600
end
626601
end
627602

@@ -672,23 +647,7 @@ defmodule Dx.Defd.Compiler do
672647
|> with_state(state)
673648

674649
true ->
675-
if state.warn_non_dx? do
676-
warn(meta, state, """
677-
#{fun_name}/#{arity} is not defined with defd.
678-
679-
Either define it using defd (preferred) or wrap the call in the non_dx/1 function:
680-
681-
non_dx(...(&#{module}.#{fun_name}/#{arity}))
682-
""")
683-
end
684-
685-
quote line: line do
686-
{:ok,
687-
fn unquote_splicing(args) ->
688-
{:ok, unquote(module).unquote(fun_name)(unquote_splicing(args))}
689-
end}
690-
end
691-
|> with_state(%{state | called_non_dx?: true})
650+
NonDx.normalize(fun, state)
692651
end
693652
end
694653

@@ -697,37 +656,17 @@ defmodule Dx.Defd.Compiler do
697656
when is_atom(fun_name) and is_list(args) do
698657
arity = length(args)
699658

700-
cond do
701-
{fun_name, arity} in state.defds ->
702-
defd_name = Util.defd_name(fun_name)
703-
704-
state = Map.update!(state, :used_defds, &MapSet.put(&1, {fun_name, arity}))
705-
706-
normalize_call_args(args, state, fn args ->
707-
{defd_name, meta, args ++ [state.eval_var]}
708-
end)
709-
|> Loader.add()
710-
711-
Util.has_function?(state.module, fun_name, arity) ->
712-
if state.warn_non_dx? do
713-
warn(meta, state, """
714-
#{fun_name}/#{arity} is not defined with defd.
715-
716-
Either define it using defd (preferred) or wrap the call in the non_dx/1 function:
717-
718-
non_dx(#{fun_name}(...))
719-
""")
720-
end
721-
722-
{ast, state} =
723-
normalize_external_call_args(args, state, fn args ->
724-
{fun_name, meta, args}
725-
end)
659+
if {fun_name, arity} in state.defds do
660+
defd_name = Util.defd_name(fun_name)
726661

727-
{{:ok, ast}, %{state | called_non_dx?: true}}
662+
state = Map.update!(state, :used_defds, &MapSet.put(&1, {fun_name, arity}))
728663

729-
true ->
730-
{{:ok, fun}, state}
664+
normalize_call_args(args, state, fn args ->
665+
{defd_name, meta, args ++ [state.eval_var]}
666+
end)
667+
|> Loader.add()
668+
else
669+
NonDx.normalize(fun, state)
731670
end
732671
end
733672

@@ -774,38 +713,8 @@ defmodule Dx.Defd.Compiler do
774713
end)
775714
|> Loader.add()
776715

777-
# avoid non_dx warning for `Dx.Scope.all/1`
778-
{module, fun_name, arity} == {Dx.Scope, :all, 1} ->
779-
normalize_external_call_args(args, state, fn args ->
780-
{{:., meta, [module, fun_name]}, meta2, args}
781-
end)
782-
|> Ast.ok()
783-
784-
Util.has_function?(module, fun_name, arity) ->
785-
if state.warn_non_dx? do
786-
warn(meta2, state, """
787-
#{inspect(module)}.#{fun_name}/#{arity} is not defined with defd.
788-
789-
Either define it using defd (preferred) or wrap the call in the non_dx/1 function:
790-
791-
non_dx(#{inspect(module)}.#{fun_name}(...))
792-
""")
793-
end
794-
795-
{ast, state} =
796-
normalize_external_call_args(args, state, fn args ->
797-
{{:., meta, [module, fun_name]}, meta2, args}
798-
end)
799-
800-
{{:ok, ast}, %{state | called_non_dx?: true}}
801-
802716
true ->
803-
{ast, state} =
804-
normalize_external_call_args(args, state, fn args ->
805-
{{:., meta, [module, fun_name]}, meta2, args}
806-
end)
807-
808-
{{:ok, ast}, state}
717+
NonDx.normalize(fun, state)
809718
end
810719
end
811720

@@ -902,115 +811,12 @@ defmodule Dx.Defd.Compiler do
902811
:error
903812
end
904813

905-
# extracts only loaders based on variables bound outside of the external anonymous function
906-
defp normalize_external_fn(ast, state) do
907-
Macro.prewalk(ast, state, fn
908-
{{:., _meta, [_module, fun_name]}, meta2, args} = fun, state
909-
when is_atom(fun_name) and is_list(args) ->
910-
# Access.get/2
911-
if meta2[:no_parens] do
912-
case maybe_capture_loader(fun, state) do
913-
{:ok, _loader_ast, state} ->
914-
subject = root_var_from_access_chain(fun)
915-
data_req = data_req_from_access_chain(fun, %{})
916-
917-
{{:ok, var}, state} =
918-
quote do
919-
Dx.Defd.Runtime.fetch(
920-
unquote(subject),
921-
unquote(Macro.escape(data_req)),
922-
unquote(state.eval_var)
923-
)
924-
end
925-
|> Loader.add(state)
926-
927-
replace_root_var(fun, var)
928-
|> with_state(state)
929-
930-
:error ->
931-
{fun, state}
932-
end
933-
else
934-
{fun, state}
935-
end
936-
937-
ast, state ->
938-
{ast, state}
939-
end)
940-
end
941-
942-
def root_var_from_access_chain({{:., _meta, [ast, _fun_name]}, _meta2, []}) do
943-
root_var_from_access_chain(ast)
944-
end
945-
946-
def root_var_from_access_chain(var) when is_var(var) do
947-
var
948-
end
949-
950-
def replace_root_var({{:., meta, [ast, fun_name]}, meta2, []}, new_var) do
951-
{{:., meta, [replace_root_var(ast, new_var), fun_name]}, meta2, []}
952-
end
953-
954-
def replace_root_var(var, new_var) when is_var(var) do
955-
new_var
956-
end
957-
958-
def data_req_from_access_chain({{:., _meta, [ast, fun_name]}, _meta2, []}, acc) do
959-
acc = %{fun_name => acc}
960-
data_req_from_access_chain(ast, acc)
961-
end
962-
963-
def data_req_from_access_chain(var, acc) when is_var(var) do
964-
acc
965-
end
966-
967-
def normalize_external_call_args(args, state, fun) do
968-
{args, new_state} =
969-
Enum.map_reduce(args, state, fn
970-
{:fn, meta, [{:->, meta2, [args, body]}]}, state ->
971-
{body, new_state} = normalize_external_fn(body, state)
972-
973-
{:ok, {:fn, meta, [{:->, meta2, [args, body]}]}}
974-
|> with_state(new_state)
975-
976-
{:&, _meta, [{:/, [], [{{:., _meta2, [_mod, _fun_name]}, _meta3, []}, _arity]}]} = fun,
977-
state ->
978-
{{:ok, fun}, state}
979-
980-
{:&, _meta, [{:/, [], [{_fun_name, _meta2, nil}, _arity]}]} = fun, state ->
981-
{{:ok, fun}, state}
982-
983-
arg, state ->
984-
normalize(arg, state)
985-
end)
986-
987-
{args, new_state} = args |> Enum.map(&Ast.unwrap/1) |> finalize_args(new_state)
988-
989-
do_normalize_call_args(args, new_state, fun)
990-
end
991-
992-
def finalize_args(args, state) do
993-
Enum.map_reduce(args, state, fn arg, state ->
994-
vars = Ast.collect_vars(arg)
995-
996-
if MapSet.subset?(vars, state.finalized_vars) do
997-
{:ok, arg}
998-
|> with_state(state)
999-
else
1000-
quote do
1001-
Dx.Defd.Runtime.finalize(unquote(arg), unquote(state.eval_var))
1002-
end
1003-
|> Loader.add(state)
1004-
end
1005-
end)
1006-
end
1007-
1008814
def normalize_call_args(args, state, fun) do
1009815
{args, state} = Enum.map_reduce(args, state, &normalize/2)
1010816
do_normalize_call_args(args, state, fun)
1011817
end
1012818

1013-
defp do_normalize_call_args(args, state, fun) do
819+
def do_normalize_call_args(args, state, fun) do
1014820
args
1015821
|> Enum.map(&Ast.unwrap/1)
1016822
|> fun.()

0 commit comments

Comments
 (0)