some progress, still fucked
This commit is contained in:
parent
3c7edc67da
commit
bb7187b0c7
332
new.exs
332
new.exs
@ -1,20 +1,22 @@
|
|||||||
defmodule Tdd.Debug do
|
defmodule Tdd.Debug do
|
||||||
@moduledoc "Helpers for debugging TDD structures and tracing function calls."
|
@moduledoc """
|
||||||
# alias Tdd.Store # Keep if used by your print functions
|
Provides macros to wrap `def` and `defp` for simple function call/return tracing.
|
||||||
|
Logs arguments as a list and return values using `IO.inspect`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# --- Agent for Tracing State ---
|
||||||
@agent_name Tdd.Debug.StateAgent
|
@agent_name Tdd.Debug.StateAgent
|
||||||
|
|
||||||
# --- Agent State Management ---
|
defp init_agent_if_needed do
|
||||||
def init do
|
|
||||||
case Process.whereis(@agent_name) do
|
case Process.whereis(@agent_name) do
|
||||||
nil -> Agent.start_link(fn -> MapSet.new() end, name: @agent_name)
|
nil -> Agent.start_link(fn -> MapSet.new() end, name: @agent_name)
|
||||||
_pid -> :ok # Agent already started
|
_pid -> :ok
|
||||||
end
|
end
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_traced_pid(pid) when is_pid(pid) do
|
defp add_traced_pid(pid) when is_pid(pid) do
|
||||||
init() # Ensure agent is started
|
init_agent_if_needed()
|
||||||
Agent.update(@agent_name, &MapSet.put(&1, pid))
|
Agent.update(@agent_name, &MapSet.put(&1, pid))
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -25,7 +27,7 @@ defmodule Tdd.Debug do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_pid_traced?(pid) when is_pid(pid) do
|
defp is_pid_traced?(pid) when is_pid(pid) do
|
||||||
case Process.whereis(@agent_name) do
|
case Process.whereis(@agent_name) do
|
||||||
nil ->
|
nil ->
|
||||||
false
|
false
|
||||||
@ -33,77 +35,37 @@ defmodule Tdd.Debug do
|
|||||||
try do
|
try do
|
||||||
Agent.get(agent_pid, &MapSet.member?(&1, pid), :infinity)
|
Agent.get(agent_pid, &MapSet.member?(&1, pid), :infinity)
|
||||||
rescue
|
rescue
|
||||||
_e in [Exit, ArgumentError] -> false # More specific rescue
|
_e in [Exit, ArgumentError] -> false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# --- Formatting Helpers ---
|
# --- Tracing Control Functions ---
|
||||||
@arg_length_limit 80
|
@doc "Enables function call tracing for the current process."
|
||||||
@total_args_length_limit 200
|
|
||||||
|
|
||||||
# MODIFIED: Return the specific atoms format_arg_value expects
|
|
||||||
def __internal_placeholder_for_ignored_arg__, do: :__tdd_debug_ignored_arg__
|
|
||||||
def __internal_placeholder_for_complex_pattern__, do: :__tdd_debug_complex_pattern__
|
|
||||||
|
|
||||||
def format_args_list(args_list) when is_list(args_list) do
|
|
||||||
formatted_args = Enum.map(args_list, &format_arg_value/1)
|
|
||||||
combined = Enum.join(formatted_args, ", ")
|
|
||||||
|
|
||||||
if String.length(combined) > @total_args_length_limit do
|
|
||||||
String.slice(combined, 0, @total_args_length_limit - 3) <> "..."
|
|
||||||
else
|
|
||||||
combined
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_arg_value(arg) do
|
|
||||||
inspected =
|
|
||||||
case arg do
|
|
||||||
:__tdd_debug_ignored_arg__ -> "_"
|
|
||||||
:__tdd_debug_complex_pattern__ -> "<pattern>"
|
|
||||||
_ -> inspect(arg, limit: @arg_length_limit, pretty: false, structs: true) # Use limit here too
|
|
||||||
end
|
|
||||||
|
|
||||||
# The inspect limit is now applied within the inspect call itself for individual args mostly.
|
|
||||||
# This outer limit is more for the overall string representation from inspect if it didn't truncate.
|
|
||||||
if String.length(inspected) > @arg_length_limit do
|
|
||||||
String.slice(inspected, 0, @arg_length_limit - 3) <> "..."
|
|
||||||
else
|
|
||||||
inspected
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# --- Tracing Control ---
|
|
||||||
def enable_tracing do
|
def enable_tracing do
|
||||||
init()
|
|
||||||
pid_to_trace = self()
|
pid_to_trace = self()
|
||||||
add_traced_pid(pid_to_trace)
|
add_traced_pid(pid_to_trace)
|
||||||
ref = Process.monitor(pid_to_trace)
|
ref = Process.monitor(pid_to_trace)
|
||||||
|
|
||||||
# Spawn linked to ensure monitor process dies if current process dies unexpectedly
|
Process.spawn_link(fn ->
|
||||||
Process.spawn(
|
receive do
|
||||||
fn ->
|
{:DOWN, ^ref, :process, ^pid_to_trace, _reason} ->
|
||||||
receive do
|
remove_traced_pid(pid_to_trace)
|
||||||
{:DOWN, ^ref, :process, ^pid_to_trace, _reason} ->
|
after
|
||||||
remove_traced_pid(pid_to_trace)
|
3_600_000 -> # 1 hour safety timeout
|
||||||
# after # Consider if timeout is strictly needed or if DOWN message is sufficient
|
remove_traced_pid(pid_to_trace)
|
||||||
# 3_600_000 -> # 1 hour timeout
|
end
|
||||||
# remove_traced_pid(pid_to_trace)
|
end)
|
||||||
end
|
|
||||||
end,
|
|
||||||
[:monitor]
|
|
||||||
# Removed [:monitor] option as spawn_link and Process.monitor achieve desired effect
|
|
||||||
)
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def disable_tracing() do
|
@doc "Disables function call tracing for the current process."
|
||||||
# init() # Not strictly necessary here as remove_traced_pid handles agent not existing
|
def disable_tracing do
|
||||||
remove_traced_pid(self())
|
remove_traced_pid(self())
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "Runs the given 0-arity function with tracing enabled, then disables it."
|
||||||
def run(fun) when is_function(fun, 0) do
|
def run(fun) when is_function(fun, 0) do
|
||||||
enable_tracing()
|
enable_tracing()
|
||||||
try do
|
try do
|
||||||
@ -113,103 +75,27 @@ defmodule Tdd.Debug do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# --- Process Dictionary for Depth ---
|
# --- Process Dictionary for Call Depth ---
|
||||||
defp get_depth, do: Process.get(:tdd_debug_depth, 0)
|
defp get_depth, do: Process.get(:tdd_debug_depth, 0)
|
||||||
|
defp increment_depth do
|
||||||
def increment_depth do # Made private as it's an internal helper
|
|
||||||
new_depth = get_depth() + 1
|
new_depth = get_depth() + 1
|
||||||
Process.put(:tdd_debug_depth, new_depth)
|
Process.put(:tdd_debug_depth, new_depth)
|
||||||
new_depth
|
new_depth
|
||||||
end
|
end
|
||||||
|
defp decrement_depth do
|
||||||
def decrement_depth do # Made private
|
|
||||||
new_depth = max(0, get_depth() - 1)
|
new_depth = max(0, get_depth() - 1)
|
||||||
Process.put(:tdd_debug_depth, new_depth)
|
Process.put(:tdd_debug_depth, new_depth)
|
||||||
new_depth
|
new_depth
|
||||||
end
|
end
|
||||||
|
|
||||||
# --- Core Macro Logic ---
|
# --- Core Macro Logic ---
|
||||||
|
@inspect_limit 100 # Default limit for inspect calls by this module
|
||||||
|
|
||||||
defmacro __using__(_opts) do
|
defmacro __using__(_opts) do
|
||||||
quote do
|
quote do
|
||||||
import Kernel, except: [def: 2, def: 1, defp: 2, defp: 1]
|
import Kernel, except: [def: 1, def: 2, defp: 1, defp: 2]
|
||||||
|
import Tdd.Debug
|
||||||
require Tdd.Debug
|
require Tdd.Debug
|
||||||
import Tdd.Debug # Imports def/defp from this module, and helper functions
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc false
|
|
||||||
defmacro do_instrument(type, call, clauses, _env) do
|
|
||||||
# As per your note, this macro might need updates.
|
|
||||||
# Main issues to check if used:
|
|
||||||
# 1. Hygiene of `actual_code_ast`: use Macro.escape/2.
|
|
||||||
# 2. Robustness of `arg_vars_for_runtime_access` for all pattern types (similar to generate_traced_function).
|
|
||||||
# Kept original logic for now based on your description.
|
|
||||||
{function_name, _meta_call, original_args_ast_nodes} = call
|
|
||||||
original_args_ast_nodes = original_args_ast_nodes || []
|
|
||||||
|
|
||||||
arg_vars_for_runtime_access =
|
|
||||||
Enum.map(original_args_ast_nodes, fn
|
|
||||||
{:_, _, ctx} when is_atom(ctx) or is_nil(ctx) ->
|
|
||||||
quote do Tdd.Debug.__internal_placeholder_for_ignored_arg__() end
|
|
||||||
{:=, _, [var_ast, _]} ->
|
|
||||||
var_ast
|
|
||||||
{var_name, _meta, ctx} = pattern_ast when is_atom(var_name) and (is_atom(ctx) or is_nil(ctx)) and
|
|
||||||
not (var_name in [:_ , :%{}, :{}, :|, :<<>>, :fn, :->, :&, :^]) ->
|
|
||||||
pattern_ast
|
|
||||||
{constructor, _, _} = pattern_ast when constructor in [:%{}, :{}, :|, :<<>>] ->
|
|
||||||
quote do Tdd.Debug.__internal_placeholder_for_complex_pattern__() end
|
|
||||||
pattern_ast ->
|
|
||||||
pattern_ast
|
|
||||||
end)
|
|
||||||
|
|
||||||
actual_code_ast =
|
|
||||||
case clauses do
|
|
||||||
kw when is_list(kw) -> Keyword.get(kw, :do)
|
|
||||||
_ -> clauses
|
|
||||||
end
|
|
||||||
|
|
||||||
traced_body =
|
|
||||||
quote location: :keep do
|
|
||||||
if Tdd.Debug.is_pid_traced?(self()) do
|
|
||||||
current_print_depth = Tdd.Debug.increment_depth() # Qualified
|
|
||||||
indent = String.duplicate(" ", current_print_depth - 1)
|
|
||||||
|
|
||||||
runtime_arg_values = [unquote_splicing(arg_vars_for_runtime_access)]
|
|
||||||
IO.inspect(runtime_arg_values, label: "runtime_arg_values")
|
|
||||||
IO.puts("runtime_arg_values 1")
|
|
||||||
args_string = Tdd.Debug.format_args_list(runtime_arg_values) # Qualified
|
|
||||||
caller_module_name = Module.split(__MODULE__) |> Enum.join(".")
|
|
||||||
|
|
||||||
IO.puts(
|
|
||||||
"#{indent}CALL: #{caller_module_name}.#{unquote(Macro.escape(function_name))}(#{args_string})"
|
|
||||||
)
|
|
||||||
|
|
||||||
try do
|
|
||||||
# Potential hygiene issue here if actual_code_ast clashes with macro vars
|
|
||||||
result = unquote(actual_code_ast) # Consider Macro.escape(actual_code_ast, unquote: true)
|
|
||||||
_ = Tdd.Debug.decrement_depth() # Qualified
|
|
||||||
IO.puts(
|
|
||||||
"#{indent}RETURN from #{caller_module_name}.#{unquote(Macro.escape(function_name))}: #{Tdd.Debug.format_arg_value(result)}" # Qualified
|
|
||||||
)
|
|
||||||
result
|
|
||||||
rescue
|
|
||||||
exception_class ->
|
|
||||||
_ = Tdd.Debug.decrement_depth() # Qualified
|
|
||||||
error_instance = exception_class
|
|
||||||
stacktrace = __STACKTRACE__
|
|
||||||
IO.puts(
|
|
||||||
"#{indent}ERROR in #{caller_module_name}.#{unquote(Macro.escape(function_name))}: #{Tdd.Debug.format_arg_value(error_instance)}" # Qualified
|
|
||||||
)
|
|
||||||
reraise error_instance, stacktrace
|
|
||||||
end
|
|
||||||
else
|
|
||||||
IO.puts("runtime_arg_values 2")
|
|
||||||
unquote(actual_code_ast) # Consider Macro.escape
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
quote do
|
|
||||||
Kernel.unquote(type)(unquote(call), do: unquote(traced_body))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -223,102 +109,99 @@ defmodule Tdd.Debug do
|
|||||||
generate_traced_function(:defp, call, clauses, __CALLER__)
|
generate_traced_function(:defp, call, clauses, __CALLER__)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp generate_traced_function(type, call_ast, clauses, _caller_env) do
|
defp generate_traced_function(type, call_ast, clauses, caller_env) do
|
||||||
{function_name_ast, _meta_call, original_args_patterns_ast} = call_ast
|
{function_name_ast, meta_call, original_args_patterns_ast} = call_ast
|
||||||
args_patterns_ast = original_args_patterns_ast || []
|
original_args_patterns_ast_list = original_args_patterns_ast || []
|
||||||
|
|
||||||
original_body_ast =
|
original_body_ast =
|
||||||
(if Keyword.keyword?(clauses) do
|
(if Keyword.keyword?(clauses) do
|
||||||
Keyword.get(clauses, :do, clauses) # clauses could be `[do: actual_body]` or just `actual_body`
|
Keyword.get(clauses, :do, clauses)
|
||||||
else
|
else
|
||||||
clauses
|
clauses # Body is directly provided
|
||||||
end) || quote(do: nil) # Default to a nil body if nothing provided
|
end) || quote(do: nil) # Default to `do: nil` if no body
|
||||||
|
|
||||||
# --- REVISED logging_expressions_ast_list ---
|
# Transform arguments: `pattern` becomes `__td_arg_N__ = pattern`
|
||||||
logging_expressions_ast_list =
|
# And collect the `__td_arg_N__` variables for logging.
|
||||||
Enum.map(args_patterns_ast, fn arg_pattern_ast_outer ->
|
|
||||||
core_pattern_ast_for_logging =
|
|
||||||
case arg_pattern_ast_outer do
|
|
||||||
{:when, _, [pattern, _guard]} -> pattern
|
|
||||||
other -> other
|
|
||||||
end
|
|
||||||
|
|
||||||
cond do
|
# Step 1: Map original patterns to a list of {new_pattern_ast, generated_var_ast} tuples
|
||||||
match?({:_, _, ctx} when is_atom(ctx) or is_nil(ctx), core_pattern_ast_for_logging) ->
|
# Enum.with_index provides the index for unique variable naming.
|
||||||
quote do Tdd.Debug.__internal_placeholder_for_ignored_arg__() end
|
mapped_and_generated_vars_tuples =
|
||||||
|
Enum.map(Enum.with_index(original_args_patterns_ast_list), fn {pattern_ast, index} ->
|
||||||
|
# Create a unique, hygienic variable name like __td_arg_0__
|
||||||
|
# Using caller_env.module for context makes the variable hygienic to the calling module.
|
||||||
|
generated_var_name = String.to_atom("__td_arg_#{index}__")
|
||||||
|
generated_var_ast = Macro.var(generated_var_name, caller_env.module)
|
||||||
|
|
||||||
match?({:=, _, [{_var_ast, _, _}, _sub_pattern_ast]}, core_pattern_ast_for_logging) ->
|
# This AST represents: __td_arg_N__ = original_pattern_N
|
||||||
{:=, _, [var_ast, _]} = core_pattern_ast_for_logging
|
new_pattern_ast = quote do
|
||||||
var_ast
|
unquote(generated_var_ast) = unquote(pattern_ast)
|
||||||
|
|
||||||
match?({constructor, _, _args} when constructor in [:%{}, :{}, :|, :<<>>], core_pattern_ast_for_logging) ->
|
|
||||||
quote do Tdd.Debug.__internal_placeholder_for_complex_pattern__() end
|
|
||||||
|
|
||||||
match?({name_atom, _, context} when is_atom(name_atom) and name_atom != :_ and (is_atom(context) or is_nil(context)), core_pattern_ast_for_logging) ->
|
|
||||||
{name_atom_inner, _meta, context_inner} = core_pattern_ast_for_logging
|
|
||||||
if is_nil(context_inner) and name_atom_inner not in [:true, :false, :nil] do
|
|
||||||
name_atom_inner
|
|
||||||
else
|
|
||||||
core_pattern_ast_for_logging
|
|
||||||
end
|
|
||||||
|
|
||||||
# Macro.is_literal(core_pattern_ast_for_logging) ->
|
|
||||||
# core_pattern_ast_for_logging
|
|
||||||
|
|
||||||
# Fallback for unhandled complex patterns or other ASTs.
|
|
||||||
# If it's a variable AST that somehow slipped through (e.g. context wasn't atom/nil for a var),
|
|
||||||
# it might be okay to pass `core_pattern_ast_for_logging` directly.
|
|
||||||
# However, to be safe and avoid `_` or other non-value ASTs, placeholder is better.
|
|
||||||
true ->
|
|
||||||
# This case implies a pattern that is not `_`, not `var=pattern`, not a common structure,
|
|
||||||
# not a simple var/literal atom, and not a known literal.
|
|
||||||
# It's likely a more complex pattern we haven't explicitly handled for logging.
|
|
||||||
quote do Tdd.Debug.__internal_placeholder_for_complex_pattern__() end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
{new_pattern_ast, generated_var_ast}
|
||||||
end)
|
end)
|
||||||
# --- END REVISED logging_expressions_ast_list ---
|
|
||||||
|
# Step 2: Unzip the list of tuples into two separate lists
|
||||||
|
{new_args_patterns_ast_list, generated_arg_vars_asts} =
|
||||||
|
Enum.unzip(mapped_and_generated_vars_tuples)
|
||||||
|
|
||||||
|
# Reconstruct the call_ast with the new argument patterns
|
||||||
|
# new_args_patterns_ast_list now contains ASTs like `[__td_arg_0__ = pattern0, __td_arg_1__ = pattern1, ...]`
|
||||||
|
new_call_ast = {function_name_ast, meta_call, new_args_patterns_ast_list}
|
||||||
|
|
||||||
traced_body_inner_ast =
|
traced_body_inner_ast =
|
||||||
quote do
|
quote do
|
||||||
if Tdd.Debug.is_pid_traced?(self()) do
|
if Tdd.Debug.is_pid_traced?(self()) do
|
||||||
current_print_depth = Tdd.Debug.increment_depth() # Qualified
|
current_print_depth = Tdd.Debug.increment_depth()
|
||||||
indent = String.duplicate(" ", current_print_depth - 1)
|
indent = String.duplicate(" ", current_print_depth - 1)
|
||||||
|
|
||||||
__runtime_arg_values_for_logging__ =
|
# runtime_arg_values will be a list of the actual values bound to __td_arg_0__, __td_arg_1__, etc.
|
||||||
[unquote_splicing(logging_expressions_ast_list)]
|
# generated_arg_vars_asts is `[__td_arg_0_ast, __td_arg_1_ast, ...]`
|
||||||
|
runtime_arg_values = [unquote_splicing(generated_arg_vars_asts)]
|
||||||
|
|
||||||
args_string = Tdd.Debug.format_args_list(__runtime_arg_values_for_logging__) # Qualified
|
|
||||||
caller_module_name_str = Module.split(__MODULE__) |> Enum.join(".")
|
caller_module_name_str = Module.split(__MODULE__) |> Enum.join(".")
|
||||||
|
|
||||||
__Printable_fn_name_intermediate__ = unquote(function_name_ast)
|
|
||||||
printable_function_name_str =
|
printable_function_name_str =
|
||||||
case __Printable_fn_name_intermediate__ do
|
case unquote(function_name_ast) do
|
||||||
fn_name_atom when is_atom(fn_name_atom) -> Atom.to_string(fn_name_atom)
|
fn_name_atom when is_atom(fn_name_atom) -> Atom.to_string(fn_name_atom)
|
||||||
fn_name_ast_complex -> Macro.to_string(fn_name_ast_complex) # Handles operators etc.
|
complex_fn_ast -> Macro.to_string(complex_fn_ast)
|
||||||
end
|
end
|
||||||
|
|
||||||
IO.puts(
|
IO.puts(
|
||||||
"#{indent}CALL: #{caller_module_name_str}.#{printable_function_name_str}(#{args_string})"
|
"#{indent}CALL: #{caller_module_name_str}.#{printable_function_name_str}"
|
||||||
|
)
|
||||||
|
IO.puts(
|
||||||
|
"#{indent} ARGS: #{inspect(runtime_arg_values)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
try do
|
try do
|
||||||
|
# The original_body_ast will execute in a context where __td_arg_N__ are bound
|
||||||
|
# to the values of the original patterns.
|
||||||
result = unquote(Macro.escape(original_body_ast, unquote: true))
|
result = unquote(Macro.escape(original_body_ast, unquote: true))
|
||||||
_ = Tdd.Debug.decrement_depth() # Qualified
|
|
||||||
|
_ = Tdd.Debug.decrement_depth()
|
||||||
IO.puts(
|
IO.puts(
|
||||||
"#{indent}RETURN from #{caller_module_name_str}.#{printable_function_name_str}: #{Tdd.Debug.format_arg_value(result)}" # Qualified
|
"#{indent}RETURN from #{caller_module_name_str}.#{printable_function_name_str}: #{inspect(result)}"
|
||||||
)
|
)
|
||||||
result
|
result
|
||||||
rescue
|
rescue
|
||||||
exception_class ->
|
exception_class ->
|
||||||
_ = Tdd.Debug.decrement_depth() # Qualified
|
|
||||||
error_instance = exception_class
|
error_instance = exception_class
|
||||||
stacktrace = __STACKTRACE__
|
stacktrace = __STACKTRACE__
|
||||||
|
_ = Tdd.Debug.decrement_depth()
|
||||||
IO.puts(
|
IO.puts(
|
||||||
"#{indent}ERROR in #{caller_module_name_str}.#{printable_function_name_str}: #{Tdd.Debug.format_arg_value(error_instance)}" # Qualified
|
"#{indent}ERROR in #{caller_module_name_str}.#{printable_function_name_str}: #{inspect(error_instance)}"
|
||||||
)
|
)
|
||||||
reraise error_instance, stacktrace
|
reraise error_instance, stacktrace
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
# If not traced, execute the original body. Note: this branch will *not* have
|
||||||
|
# the __td_arg_N__ variables bound. The `new_call_ast` with these assignments
|
||||||
|
# is only used if we go into the traced path. This is a subtle point.
|
||||||
|
# To ensure the __td_arg_N__ = pattern bindings always happen,
|
||||||
|
# the final_definition_ast should *always* use new_call_ast.
|
||||||
|
# The `if` condition should only gate the logging.
|
||||||
|
# Let's adjust this: the bindings MUST happen for the body to work with the new var names if it were changed.
|
||||||
|
# However, the original_body_ast uses the original pattern variable names.
|
||||||
|
# So, the original_body_ast is fine. The `new_call_ast` is what defines the function signature.
|
||||||
unquote(Macro.escape(original_body_ast, unquote: true))
|
unquote(Macro.escape(original_body_ast, unquote: true))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -326,31 +209,23 @@ defmodule Tdd.Debug do
|
|||||||
final_definition_ast =
|
final_definition_ast =
|
||||||
quote location: :keep do
|
quote location: :keep do
|
||||||
Kernel.unquote(type)(
|
Kernel.unquote(type)(
|
||||||
unquote(call_ast), # This unquotes the function head {name, meta, args_patterns}
|
unquote(new_call_ast), # Use the call_ast with instrumented args: `def my_fun(__td_arg_0__ = pattern0, ...)`
|
||||||
do: unquote(traced_body_inner_ast)
|
do: unquote(traced_body_inner_ast)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# For debugging the macro itself:
|
|
||||||
# require Logger
|
|
||||||
# Logger.debug("Generated AST for #{type} #{Macro.to_string(call_ast)}:\n#{Macro.to_string(final_definition_ast)}")
|
|
||||||
|
|
||||||
final_definition_ast
|
final_definition_ast
|
||||||
end
|
end
|
||||||
|
|
||||||
# --- TDD Graph Printing (Kept for completeness) ---
|
# --- TDD Graph Printing (Kept as it was, not directly related to call tracing simplification) ---
|
||||||
@doc "Prints a formatted representation of a TDD graph starting from an ID."
|
@doc "Prints a formatted representation of a TDD graph structure."
|
||||||
def print(id) do
|
def print_tdd_graph(id, store_module \\ Tdd.Store) do
|
||||||
# It's better to alias Tdd.Store at the top of the module if consistently used,
|
|
||||||
# or pass it as an argument if it's a dependency.
|
|
||||||
# For now, keeping alias local to this function if Tdd.Store is not used elsewhere in Debug.
|
|
||||||
alias Tdd.Store # Assuming Tdd.Store is available
|
|
||||||
IO.puts("--- TDD Graph (ID: #{id}) ---")
|
IO.puts("--- TDD Graph (ID: #{id}) ---")
|
||||||
do_print(id, 0, MapSet.new(), Tdd.Store) # Pass Store if it's an external module
|
do_print_tdd_node(id, 0, MapSet.new(), store_module)
|
||||||
IO.puts("------------------------")
|
IO.puts("------------------------")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_print(id, indent_level, visited, store_module) do # Accept store_module
|
defp do_print_tdd_node(id, indent_level, visited, store_module) do
|
||||||
prefix = String.duplicate(" ", indent_level)
|
prefix = String.duplicate(" ", indent_level)
|
||||||
|
|
||||||
if MapSet.member?(visited, id) do
|
if MapSet.member?(visited, id) do
|
||||||
@ -358,20 +233,17 @@ defmodule Tdd.Debug do
|
|||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
new_visited = MapSet.put(visited, id)
|
new_visited = MapSet.put(visited, id)
|
||||||
# alias Tdd.Store # Removed from here
|
case store_module.get_node(id) do # Assumes store_module.get_node/1 exists
|
||||||
case store_module.get_node(id) do # Use passed module
|
{:ok, :true_terminal} -> IO.puts("#{prefix}ID #{id} -> TRUE")
|
||||||
{:ok, :true_terminal} ->
|
{:ok, :false_terminal} -> IO.puts("#{prefix}ID #{id} -> FALSE")
|
||||||
IO.puts("#{prefix}ID #{id} -> TRUE")
|
{:ok, {var, y_id, n_id, dc_id}} ->
|
||||||
{:ok, :false_terminal} ->
|
|
||||||
IO.puts("#{prefix}ID #{id} -> FALSE")
|
|
||||||
{:ok, {var, y, n, d}} ->
|
|
||||||
IO.puts("#{prefix}ID #{id}: IF #{inspect(var)}")
|
IO.puts("#{prefix}ID #{id}: IF #{inspect(var)}")
|
||||||
IO.puts("#{prefix} ├─ Yes:")
|
IO.puts("#{prefix} ├─ Yes (to ID #{y_id}):")
|
||||||
do_print(y, indent_level + 2, new_visited, store_module)
|
do_print_tdd_node(y_id, indent_level + 2, new_visited, store_module)
|
||||||
IO.puts("#{prefix} ├─ No:")
|
IO.puts("#{prefix} ├─ No (to ID #{n_id}):")
|
||||||
do_print(n, indent_level + 2, new_visited, store_module)
|
do_print_tdd_node(n_id, indent_level + 2, new_visited, store_module)
|
||||||
IO.puts("#{prefix} └─ DC:")
|
IO.puts("#{prefix} └─ DC (to ID #{dc_id}):")
|
||||||
do_print(d, indent_level + 2, new_visited, store_module)
|
do_print_tdd_node(dc_id, indent_level + 2, new_visited, store_module)
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
IO.puts("#{prefix}ID #{id}: ERROR - #{reason}")
|
IO.puts("#{prefix}ID #{id}: ERROR - #{reason}")
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user