Modified, as ! does not need to be escaped
This is a very very rough but functioning utility version of what I wanted to implement.
I wanted to quickly build this and continue working through the books, but I thought this may come in handy for other people learning or people who wanted to quickly access insight into the code-bases with which they are working in the iex. This serves as a quick code snippet review.
I’ve not yet covered elixir best practices for documentation. This is just to provide a quick solution to anyone who had a similar question to mine.
God bless.
defmodule GetMod do
#Determines source of module using data from IEx.Info.info (this form of info outputs a value that can be handled, unlike the i helpfer function).
#Module bytcode and Source are keys in the map that IEx.Info.info outputs
#These can be used to determine the source of compiled and built-in modules.
#However compiled modules can be located using IEx.Info.info "Source" by itself
def source_of(module) do
info = &IEx.Info.info/1
get_tuple = &(Enum.filter(&1, fn x -> elem(x, 0) === &2 end)|> Enum.at(0))
get_sub_paths = &(get_tuple.(&1, &2) |> elem(1))
runner? = &{Regex.match?(~r{[/|\runner.*]}, &1), &1}
remove_runner = fn
{x, y} when x === false-> y
{x, y} when x === true ->
Regex.replace(~r[^/.*?(elixir/){2}], y, "")
end
old_prefix = get_sub_paths.(info.(module), "Module bytecode")
new_prefix = (old_prefix !== "[]" && Regex.replace(~r{bin.*}, old_prefix, "")) || ""
input_source = get_sub_paths.(info.(module),"Source")
suffix = input_source |> runner?.() |> remove_runner.()
new_prefix <> suffix
end
def get_file_contents(source) do
File.read(source) |> elem(1)
end
def get(module) do
source = source_of module
get_file_contents(source)
end
#Check if the function is a boolean or potentially error yielding, and return the special character
def despec_string(str) do
Regex.match?(~r/\?/, str)
&& "?"
|| Regex.match?(~r/\!/, str)
&& "!"
end
#f_name = spec === "?" ... replaces the special character in the function name with an escaped version
#reg_run = ~r... matches reg_run with regular expression for capturing multi-line string that begins with the function signature
#reg_rep = ~r...matches reg_rep with the regular expression for capturing any string that starts with a function signature other than that of the target function
def get_func(module, f_name) do
spec = despec_string(f_name)
f_name = spec === "?"
&& Regex.replace(~r/.{0}(?=\?)/, f_name, "\\?")
|| f_name
contents = get module
reg_run = ~r/\n?\s?def(macro|struct|p)?\s(#{f_name}(\(|\s)|#{f_name}(\(|,))+.*/s #if you need the regex explained just let me know in the thread.
reg_rep = ~r/\n?\s?def(macro|struct|p)?\s(?!(#{f_name}(\(|\s)|#{f_name}(\(|,)))+.*|(@doc).*/s
funcs = Regex.run(reg_run, contents) #string of module contents starting at first instance of f_name, this string has been *checked* for f_name
funcs !== :nil &&
eval_contents({"", funcs, {reg_run, reg_rep}}) || "No function named #{f_name} in module" #starts from first instance of f_name with empty string
#Regex.replace(reg_rep, Enum.at(funcs, 0), "#\nEnd of string; ostensibly other functions defined beyond this point.") || "No function named #{f_name} in module"
end
def process_string(string, r) do
{reg_run, reg_rep} = r
funcs = Regex.run(reg_run, string)
Regex.replace(reg_rep, Enum.at(funcs, 0), "")
end
def eval_contents({string, checked, _,}) when checked === nil do
string <> "End of string; ostensibly other functions defined beyond this point."
end
def eval_contents({string, checked, r}) when checked !== nil do
{regm, regr} = {_reg_run, _reg_rep} = r
checked_string = checked |> Enum.at(0)
str = string <> "... \n" <> process_string(checked_string, r)
c_str = Regex.run(regm, Regex.run(regr, checked_string) |> Enum.at(0))
eval_contents({str, c_str, r}) # check beyond current uninterrupted fnc def for matches
end
end
Update Example:
#consider
defmodule GetMod do
def get(module) do
source = source_of module
get_file_contents(source)
end
...other functions
defmacro get(g, i, o) do
end
defp get(g,h) do
end
def get do
end
end
iex(35)> IO.puts get_func GetMod, "get"
...
def get(module) do
source = source_of module
get_file_contents(source)
end
#Check if the function is a boolean or potentially error yielding, and return the special character
...
defmacro get(g, i, o) do
end
defp get(g,h) do
end
def get do
end
End of string; ostensibly other functions defined beyond this point.
:ok
Example:
iex(254)> IO.puts get_func GetMod, "get_func"
def get_func(module, f_name) do
spec = despec_string(f_name)
f_name = spec === "?"
&& Regex.replace(~r/.{0}(?=\?)/, f_name, "\\?")
|| |f_name
reg_run = ~r/\n?\s?def(macro|struct|p)?\s(#{f_name}(\(|\s)|#{f_name}(\(|,))+.*/s #if you need the regex explained just let me know in the thread.
reg_rep = ~r/\n?\s?def(macro|struct|p)?\s(?!(#{f_name}(\(|\s)|#{f_name}(\(|,)))+.*|(#
End of string; ostensibly other functions defined beyond this point.
:ok
iex(234)> IO.puts get_func Kernel, "round" #built-in
def round(number) do
:erlang.round(number)
end
#
End of string; ostensibly other functions defined beyond this point.
:ok
iex(235)> IO.puts get_func Kernel, "pop_in" #built-in
def pop_in(data, keys)
def pop_in(nil, [key | _]) do
raise ArgumentError, "could not pop key #{inspect(key)} on a nil value"
end
def pop_in(data, [_ | _] = keys) do
pop_in_data(data, keys)
end
#
End of string; ostensibly other functions defined beyond this point.
:ok
iex(236)> IO.puts get_func Kernel, "match?" #built-in
defmacro match?(pattern, expr) do
success =
quote do
unquote(pattern) -> true
end
failure =
quote generated: true do
_ -> false
end
{:case, [], [expr, [do: success ++ failure]]}
end
#
End of string; ostensibly other functions defined beyond this point.
:ok
This should work on most windows systems, as the file system on linux/mac devices are different it may not work on those.