How to get AST (Abstract Syntax Tree) of an Erlang local fun? - erlang

For some Erlang terms for example atom, tuple, list i can get AST using erl_parse:abstract/1. But it does not work for funs.
~ $ erl
Erlang/OTP 19 [erts-8.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.0 (abort with ^G)
1> erl_parse:abstract(foo).
{atom,0,foo}
2> erl_parse:abstract([bar]).
{cons,0,{atom,0,bar},{nil,0}}
3> erl_parse:abstract({baz}).
{tuple,0,[{atom,0,baz}]}
4> erlang:fun_info(fun() -> ok end, type).
{type,local} % So this is a local fun
5> erl_parse:abstract(fun() -> ok end).
** exception error: no function clause matching
erl_parse:abstract(#Fun<erl_eval.20.52032458>,0,
#Fun<erl_parse.3.3133389>) (erl_parse.yrl, line 1330)
I know that some local funs have their AST in their info. But this is not for all local funs.
~ $ erl
Erlang/OTP 19 [erts-8.0] [source] [64-bit] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V8.0 (abort with ^G)
1> erlang:fun_info(fun() -> ok end, env).
{env,[{[],
{eval,#Fun<shell.21.31625193>},
{value,#Fun<shell.5.31625193>},
[{clause,1,[],[],[{atom,1,ok}]}]}]} %% Here
2> foo:test(). %% Yields a fun
#Fun<foo.0.10202683>
3> erlang:fun_info(foo:test(), type).
{type,local} %% So this is a local fun too
4> erlang:fun_info(foo:test(), env).
{env,[]} %% : (
Getting AST of an external fun is not hard. my solution is to load its module beam chunks and get AST of that function. If you have better solution, please tell me. The main problem is for getting AST of local funs.

Maybe you can try also combine erl_scan:string/1 and erl_parse:parse_exprs/1, eg:
1> Fun = "fun() -> ok end.".
"fun() -> ok end."
2> {ok, Tokens, _EndLocation} = erl_scan:string(Fun).
{ok,[{'fun',1},
{'(',1},
{')',1},
{'->',1},
{atom,1,ok},
{'end',1},
{dot,1}],
1}
3> {ok, ExprList} = erl_parse:parse_exprs(Tokens).
{ok,[{'fun',1,{clauses,[{clause,1,[],[],[{atom,1,ok}]}]}}]}
Hope, this will be helpful.

erl_parse:abstract/1 cannot accept function object as argument. I think you are right in the rest.

Related

What does "Class FIFinderSyncExtensionHost is implemented in both mean" when running crashdump_viewer?

I started the crashdump_viewer and started looking at my crashdump. Then at some point I got an error log. What does this mean and how do I fix it?
➜ Desktop erl
Erlang/OTP 21 [erts-10.3.5.11] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]
Eshell V10.3.5.11 (abort with ^G)
1> l(crashdump_viewer).
{module,crashdump_viewer}
2> crashdump_viewer:st
start/0 start/1 start_link/0 stop/0 stop_debug/0
2> crashdump_viewer:start
start/0 start/1 start_link/0
2> crashdump_viewer:start().
objc[26293]: Class FIFinderSyncExtensionHost is implemented in both /System/Library/PrivateFrameworks/FinderKit.framework/Versions/A/FinderKit (0x7fffa1eec3d8) and /System/Library/PrivateFrameworks/FileProvider.framework/OverrideBundles/FinderSyncCollaborationFileProviderOverride.bundle/Contents/MacOS/FinderSyncCollaborationFileProviderOverride (0x1f551f50). One of the two will be used. Which one is undefined.
ok
3>
Erlang doesn't have classes, so the class in question probably has something to do with "the viewer" software. According to this, it's an Apple issue.

What is the difference between compile(export_all) and export([all/0]) in Erlang?

Seems like the two has the same effect when used as in all of the functions are exported when you compile your code. Is there a difference between the two ? Does export([all/0]). export all of the functions without needing to be compiled ?
I read Pouriya's answer several times, and it wasn't until I read it the third time that I understood what Pouriya was trying to say.
What Pouriya is trying to say is that export([all/0]) doesn't do what you think it does. Rather, it exports a single function named all(), and no other functions in the module will be exported. That is simple to test:
-module(my).
-export([all/0]).
all() -> ok.
go() -> ok.
In the shell:
~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)
1> c(my).
my.erl:5: Warning: function go/0 is unused
{ok,my}
Right away you get a warning: because go() isn't exported, which means you can't call it from outside the module, and because no functions inside the module call go(), the function go() is "unused". In other words, go() will never execute, so the compiler is wondering why you defined go() in the first place.
But, suppose you can't figure out what that warning means (and after all it's just a warning):
2> my:all().
ok
3> my:go().
** exception error: undefined function my:go/0
Seems like the two has the same effect
Nope:
-module(my).
-compile([export_all]).
all() -> ok.
go() -> ok.
In the shell:
/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)
1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
2>
No warning about go() being "unused". And:
2> my:all().
ok
3> my:go().
ok
4>
no error when you call go().

io:getline(Prompt) in spawned process: Prompt does not display

Here's the code:
-module(my).
-compile(export_all).
test() ->
register(go, spawn(my, init, []) ).
init() ->
Reply = io:get_line("enter:"),
io:format("Reply= ~s~n", [Reply]).
In the shell:
~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)
1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
2> my:test().
true
3>
If I add a timer:sleep() here:
test() ->
register(go, spawn(my, init, []) ).
init() ->
timer:sleep(1000), %%<<<==========HERE
Reply = io:get_line("enter:"),
io:format("Reply= ~s~n", [Reply]).
and run the new code in the shell, then after 1 second the shell prompt 3> in the first example's output suddenly disappears and is replaced by enter::
~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)
1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
2> my:test().
true
enter:
And if I enter something, then as expected I see Reply= xx.
If I move the timer:sleep() to here:
test() ->
register(go, spawn(my, init, []) ),
timer:sleep(1000). %%<<======HERE
init() ->
Reply = io:get_line("enter:"),
io:format("Reply= ~s~n", [Reply]).
and I run the code in the shell, I see:
~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)
1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
2> my:test().
enter:
then enter: suddently disappears and is replaced by ok:
1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
2> my:test().
ok
3>
This does work as expected:
1> Reply = io:get_line("enter: ").
enter: hello
"hello\n"
2> Reply.
"hello\n"
3>
Can someone explain what race condition is occurring in the first example?
Actually this is not exactly race condition. The issue is raised from fact that we have only one std_in stream and only one process in given time can use it.
1) In first code snippet process 'go' takes control on std_in with get_line/1 function first. But after that test/0 function returns to Erlang shell and shell takes control under std_in (Erlang shell is waiting for user input with prompt like ">"). So process 'go' now unlinked from std_in and does not receive any chars from std_in. All input goes to Erlang shell.
2) Second snippet has sleep/1 function in init/1. So situation is opposite. We allow Erlang shell reach waiting for input status and AFTER that execute get_line/1 in 'go' process. So now 'go' process takes control on std_in and receives chars from keyboard. But after 'go' process terminates then Erlang shell restores its state.
3) Third snippet is just 1st but aggravate situation with sleep/1 function.
4) Nothing explain for forth snippet. It works as expected.
5) To illustrate this point of view you can run code below:
-module(my).
-compile(export_all).
test() ->
spawn_link(my, init1, []),
spawn_link(my, init2, []).
init1() ->
timer:sleep(100),
init(0, "0").
init2() ->
timer:sleep(100),
init(0, "1").
init(N, Title) ->
% io:format(">>init(~p)~n", [N]),
Reply = io:get_line(Title ++ "> enter:"),
io:format("[~s] (e - exit) Reply= ~s", [Title, Reply]),
case Reply of
"e\n" -> ok;
_ -> init(N+1, Title)
end.
The module starts two processes with get_line/1. But only one process can except input from std_in in recent moment:
50> my:test().
<0.192.0>
1> enter:w
[1] (e - exit) Reply= w
1> enter:d
[1] (e - exit) Reply= d
1> enter:e
[1] (e - exit) Reply= e
0> enter:dd
[0] (e - exit) Reply= dd
0> enter:e
[0] (e - exit) Reply= e
51>

Pid as erlang maps key?

Can Pid be maps key ?
Build maps from #{} syntax, The error says Pid can not be key.
Bug build with maps module, Pid can be key.
18>
18> Pid = self().
<0.39.0>
19> #{Pid => 1}.
* 1: illegal use of variable 'Pid' in map
20>
20> M1 = maps:from_list([{Pid, 1}]).
#{<0.39.0> => 1}
21>
21> #{Pid := V} = M1.
* 2: illegal use of variable 'Pid' in map
22>
22> maps:get(Pid, M1).
1
Support for arbitrary keys in map patterns is already available in "Erlang 18 (release candidate 2)".
$ erl
Erlang/OTP 18 [RELEASE CANDIDATE 2] [erts-7.0] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V7.0 (abort with ^G)
1> Pid = self().
<0.33.0>
2> #{Pid => 1}.
#{<0.33.0> => 1}
The problem doesn't come from the usage of pid, but from the usage of a variable as a key in the map definition. It is not yet implemented (and don't know when it could be done).

How to know the id of a node in erlang

When we run a node() command we get the pid of the node. In the format <0.X.0> if we are on the same node and we get a result of the form < X.Y.0> when running the same command from some other node. I want to know how to get the value X from < X.Y.0> on the same node.
Do you mean the integer value of the node part of a pid or the name of the node. For the name there is the BIF node/1 which returns the name of the node to which that pid refers. So
node(self()) ==> 'mynode#my_host.com'
node(RemotePid) ==> 'remote_node#remote_host.com'
It also works for ports and refs which are node specific. The value of the first field is always 0 for the current node and the value will vary for remote nodes. The values in references to the same remote node will be different on different nodes.
N.B. What the representation of a pid <X.Y.Z> actually means is not defined so don't depend too much on it. Although it is unlikely to change.
This definitely doesn't make sens. <0.X.0> is your local Pid, <D.X.0> - distributed variant, where D is a node's number. More information about Pid strcuture. But D will be different for different nodes. So locally you can't get this information.
Off course, you can implement special RPC call, that will return to caller his (caller's) distributed Pid. But results will differ in answers. To ensure this, try simple:
Start three different nodes and register shell as self.
First
erl -sname one
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [smp:8:8] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
(one#Alexeys-MacBook-Pro)1> node().
'one#Alexeys-MacBook-Pro'
(one#Alexeys-MacBook-Pro)2> register(shell, self()).
true
Second
erl -sname two
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [smp:8:8] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
(two#Alexeys-MacBook-Pro)1> node().
'two#Alexeys-MacBook-Pro'
(two#Alexeys-MacBook-Pro)2> register(shell, self()).
true
Third
erl -sname three
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [smp:8:8] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
(three#Alexeys-MacBook-Pro)1> node().
'three#Alexeys-MacBook-Pro'
(three#Alexeys-MacBook-Pro)2> register(shell, self()).
true
Now return to node one, and try
(one#Alexeys-MacBook-Pro)6> net_kernel:connect('two#Alexeys-MacBook-Pro').
true
(one#Alexeys-MacBook-Pro)7> net_kernel:connect('threeAlexeys-MacBook-Pro').
true
(one#Alexeys-MacBook-Pro)8> {shell, 'two#Alexeys-MacBook-Pro'} ! {hello, from, self()}.
{hello,from,<0.147.0>}
(one#Alexeys-MacBook-Pro)82> {shell, 'three#Alexeys-MacBook-Pro'} ! {hello, from, self()}.
{hello,from,<0.147.0>}
Check result on nodes two
(two#Alexeys-MacBook-Pro)3> flush().
Shell got {hello,from,<6767.147.0>}
ok
and tree
(three#Alexeys-MacBook-Pro)3> flush().
Shell got {hello,from,<6803.147.0>}
ok
As you can see, first part of Pid differs: <6767.147.0> and <6803.147.0>.

Resources